Search code examples
pythontkintermenutooltip

How can i add a tooltip to menu item?


I am trying to add a tooltip for menubar item (e.g [Save]), but i can't get instanse of needed menu item. Can i add this tooltip at all? I am using Tkinter with python 2.7

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)

Solution

  • Question: add a tooltip to menu item

    I'm not aware to bind to a Menu Item.

    The following class MenuTooltip use the Event <Motion> to find the Menu Item if the Pointer y-Position is inside a Menu Items .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]))
    

    Usage:

    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()
    

    Tested with Python: 3.5

    Note: Couldn't test with Python 2.7, please report back if it works with 2.7