如何在菜单项中添加工具提示?

3

我正在尝试为菜单栏项(例如[保存])添加工具提示,但我无法获取所需的菜单项实例。我能不能添加这个工具提示? 我正在使用Python2.7的Tkinter。

def createMenu(self):
    menu = Menu(root)
    root.config(menu=menu)
    filemenu = Menu(menu, tearoff=0)
    menu.add_cascade(label="File", menu=filemenu)
    filemenu.add_command(label="Save", command=self.openBlankPy)
    filemenu.add_separator()
    filemenu.add_command(label="Exit", command=self.exitApp)

可能是如何在Tkinter中显示工具提示?的重复问题。 - jeanggi90
这不是重复的问题 - 他正在询问如何获取菜单项的实例。 - Henry Yik
我知道如何为按钮添加工具提示,但如何将其添加到菜单栏项目中? - Denis
2个回答

2
问题: 给菜单项添加工具提示
我不知道如何将 bind 绑定到菜单项。
以下的 class MenuTooltip 使用 <Motion> 事件,如果 Pointer 的 y 坐标位于菜单项的 .yposition 内,则查找菜单项。
class MenuTooltip(tk.Menu):
    def __init__(self, parent):
        """
        :param parent: The parent of this Menu, either 'root' or 'Menubar'
         .tooltip == List of tuple (yposition, text)
         .tooltip_active == Index (0-based) of the active shown Tooltip
         Bind events <Leave>, <Motion>
        """
        super().__init__(parent, tearoff=0)
        self.tooltip = []
        self.tooltip_active = None

        self.bind('<Leave>', self.leave)
        self.bind('<Motion>', self.on_motion)

    def add_command(self, *cnf, **kwargs):
        tooltip = kwargs.get('tooltip')
        if tooltip:
            del kwargs['tooltip']
        super().add_command(*cnf, **kwargs)
        self.add_tooltip(len(self.tooltip), tooltip)

    def add_tooltip(self, index, tooltip):
        """
        :param index: Index (0-based) of the Menu Item 
        :param tooltip: Text to show as Tooltip
        :return: None
        """
        self.tooltip.append((self.yposition(index) + 2, tooltip))

    def on_motion(self, event):
        """
        Loop .tooltip to find matching Menu Item
        """
        for idx in range(len(self.tooltip) - 1, -1, -1):
            if event.y >= self.tooltip[idx][0]:
                self.show_tooltip(idx)
                break

    def leave(self, event):
        """
        On leave, destroy the Tooltip and reset .tooltip_active to None
        """
        if not self.tooltip_active is None:
            print('leave()'.format())
            # destroy(<tooltip_active>)
            self.tooltip_active = None

    def show_tooltip(self, idx):
        """
        Show the Tooltip if not already shown, destroy the active Tooltip
        :param idx: Index of the Tooltip to show
        :return: None 
        """
        if self.tooltip_active != idx:
            # destroy(<tooltip_active>)
            self.tooltip_active = idx
            print('{}'.format(self.tooltip[idx][1]))

用法:

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        menu = MenuTooltip(self)
        menu.add_command(label='Help 1', tooltip='\tToolTip.Help 1')
        menu.add_command(label='Help 2', tooltip='\tToolTip.Help 2')
        self.menubar.add_cascade(label="Help", menu=menu)

if __name__ == "__main__":
    App().mainloop()

使用Python: 3.5测试通过

注意:无法在Python 2.7上测试,请报告是否适用于2.7


谢谢,这非常有帮助。 - Denis
@Denis:请报告关于Python 2.7的情况? - stovfl

0

讨论了如何为按钮添加工具提示。

how-do-i-display-tooltips-in-tkinter

我知道一个方法(上面的帖子),可以为菜单项添加工具提示。

how-can-i-add-a-tooltip-to-menu-item

我尝试将它们结合起来并对两种方法进行必要的更改。缺点是:我不知道如何可靠地获取菜单项的包围盒。为了解决这个问题,我不得不使用等宽字体TkFixedFont,并对菜单项的边距/边框进行一些假设。这些都在代码中。

tipwin.py

import re
import tkinter as tk


def parse_geometry(g):
    m = re.match(r'(\d+)x(\d+)\+(\d+)\+(\d+)', g)
    return map(int, m.groups())


class TipWin(tk.Toplevel):
    WRAPLENGTH = 180

    def __init__(self, parent, start, end, text):
        x, y = list(parse_geometry(parent.master.geometry()))[-2:]
        self.parent, self.start, self.end, self.text = \
            parent, start, end, text
        super().__init__(parent)
        self.wm_overrideredirect(True)
        label = tk.Label(
            self, text=self.text, justify='left',
            background="#ffffff", relief='solid', borderwidth=1,
            wraplength=self.WRAPLENGTH)
        self.geometry(f'+{x + start}+{y}')
        label.pack(ipadx=1)

    def destroy(self):
        super().destroy()

menubar.py

import tkinter as tk
from tipwin import TipWin
# Don't know how to get bbox for menubar. Resorted to use monospaced
# font and assumption.
CW = 8                          # monospaced char width
PAD = 13                        # menu item margins (left+right)


class Menu(tk.Menu):
    def __init__(self, parent, **kwargs):
        super().__init__(parent, **kwargs)
        self.parent = parent
        self.tiplist = []       # ((start1, end1, TIP1), ...)
        self.activetip = -1     # active tip index or -1
        self.nexttip = 1        # next tip index
        self.bind('<Enter>', self.on_enter)
        self.bind('<Motion>', self.on_motion)
        self.bind('<Leave>', self.on_leave)

    def on_enter(self, event):
        self.hidetip()
        if self.activetip != 1:
            self.showtip(self.activetip, event)

    def on_leave(self, event):
        self.hidetip()

    def on_motion(self, event):
        x = event.x
        for i in range(len(self.tiplist)):
            if self.tiplist[i][0] <= x < self.tiplist[i][1]:
                self.showtip(i, event)
                return
        self.hidetip()

    def add_command(self, *args, **kwargs):
        tooltip = kwargs.get('tooltip')
        if tooltip:
            del kwargs['tooltip']
        label = kwargs.get('label')
        super().add_command(*args, **kwargs)
        self.addtip(label, tooltip)

    def addtip(self, label, tooltip):
        def _w(nc):
            return nc * CW + PAD

        x1 = self.nexttip
        self.nexttip += _w(len(label))
        self.tiplist.append((x1, self.nexttip, tooltip))

    def showtip(self, i, event):
        if self.activetip != -1:
            if i != self.activetip:
                self.hidetip()
        t = self.tiplist[i][2]
        if t:
            if self.activetip != i:
                # create a tooltip
                self.tipwin = TipWin(self, *self.tiplist[i])
                self.activetip = i

    def hidetip(self):
        # destroy the tooltip
        if self.activetip != -1:
            if self.tipwin:
                self.tipwin.destroy()
                self.tipwin = None
            self.activetip = -1


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title('Menubar')
        self.geometry('600x300+400+300')
        # tearoff is a MUST, without it <Enter> isn't fired
        menubar = Menu(self, tearoff=0)
        self.config(menu=menubar)
        menubar.add_command(
            label='Notes',
            font='TkFixedFont',
            tooltip='Edit items metadata')
        menubar.add_command(
            label='Application',
            font='TkFixedFont',
            tooltip='Save Toplevel windows geometry')
        menubar.add_command(
            label='Quit',
            font='TkFixedFont',
            tooltip='Quit')


if __name__ == '__main__':
    App().mainloop()

它已经在Python 3.9.2和Debian GNU/Linux 11 (bullseye)上进行了测试。

enter image description here enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接