How do I display tooltips in Tkinter? How do I display tooltips in Tkinter? tkinter tkinter

How do I display tooltips in Tkinter?


I tried the code in the blog post mentioned by ars, and also tried the code from the IDLE lib.

While both worked, I didn't like how the tooltip from IDLE was limited in size (had to manually enter new lines as separate lists) , and how the tips appeared immediately in the code form the blog post.

So I made a hybrid between the two. It lets you specify a wrap length and hover time, with no restriction on each:

""" tk_ToolTip_class101.pygives a Tkinter widget a tooltip as the mouse is above the widgettested with Python27 and Python34  by  vegaseat  09sep2014www.daniweb.com/programming/software-development/code/484591/a-tooltip-class-for-tkinterModified to include a delay time by Victor Zaccardo, 25mar16"""try:    # for Python2    import Tkinter as tkexcept ImportError:    # for Python3    import tkinter as tkclass CreateToolTip(object):    """    create a tooltip for a given widget    """    def __init__(self, widget, text='widget info'):        self.waittime = 500     #miliseconds        self.wraplength = 180   #pixels        self.widget = widget        self.text = text        self.widget.bind("<Enter>", self.enter)        self.widget.bind("<Leave>", self.leave)        self.widget.bind("<ButtonPress>", self.leave)        self.id = None        self.tw = None    def enter(self, event=None):        self.schedule()    def leave(self, event=None):        self.unschedule()        self.hidetip()    def schedule(self):        self.unschedule()        self.id = self.widget.after(self.waittime, self.showtip)    def unschedule(self):        id = self.id        self.id = None        if id:            self.widget.after_cancel(id)    def showtip(self, event=None):        x = y = 0        x, y, cx, cy = self.widget.bbox("insert")        x += self.widget.winfo_rootx() + 25        y += self.widget.winfo_rooty() + 20        # creates a toplevel window        self.tw = tk.Toplevel(self.widget)        # Leaves only the label and removes the app window        self.tw.wm_overrideredirect(True)        self.tw.wm_geometry("+%d+%d" % (x, y))        label = tk.Label(self.tw, text=self.text, justify='left',                       background="#ffffff", relief='solid', borderwidth=1,                       wraplength = self.wraplength)        label.pack(ipadx=1)    def hidetip(self):        tw = self.tw        self.tw= None        if tw:            tw.destroy()# testing ...if __name__ == '__main__':    root = tk.Tk()    btn1 = tk.Button(root, text="button 1")    btn1.pack(padx=10, pady=5)    button1_ttp = CreateToolTip(btn1, \   'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, '   'consectetur, adipisci velit. Neque porro quisquam est qui dolorem ipsum '   'quia dolor sit amet, consectetur, adipisci velit. Neque porro quisquam '   'est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.')    btn2 = tk.Button(root, text="button 2")    btn2.pack(padx=10, pady=5)    button2_ttp = CreateToolTip(btn2, \    "First thing's first, I'm the realest. Drop this and let the whole world "    "feel it. And I'm still in the Murda Bizness. I could hold you down, like "    "I'm givin' lessons in  physics. You should want a bad Vic like this.")    root.mainloop()

Screenshot:

Example of hovertext


The Pmw.Balloon class from the Pmw toolkit for Tkinter will draw tool tips.

Also take a look at this blog post, which adapts some code from IDLE used for displaying tool tips with Tkinter.


First of all, I really like Alberto Vassena's tool tip and I tried to comment on his post with this bug correction, but as a new user I do not have enough points to make a comment, so I am making an answer. I hope this is acceptable.

There was a very small bug in Alberto Vassena's excellent answer and improved ToolTip.

Bug:For the actual label his code calls ttk.Label instead of tk.LabelThis resulted in the tooltip box being rendered but not the actual text until a further UI event such as another mouse move or a keyboard event.

Here is the corrected code for a full copy & paste:

import tkinter as tkimport tkinter.ttk as ttkclass Tooltip:    '''    It creates a tooltip for a given widget as the mouse goes on it.    see:    http://stackoverflow.com/questions/3221956/           what-is-the-simplest-way-to-make-tooltips-           in-tkinter/36221216#36221216    http://www.daniweb.com/programming/software-development/           code/484591/a-tooltip-class-for-tkinter    - Originally written by vegaseat on 2014.09.09.    - Modified to include a delay time by Victor Zaccardo on 2016.03.25.    - Modified        - to correct extreme right and extreme bottom behavior,        - to stay inside the screen whenever the tooltip might go out on          the top but still the screen is higher than the tooltip,        - to use the more flexible mouse positioning,        - to add customizable background color, padding, waittime and          wraplength on creation      by Alberto Vassena on 2016.11.05.      Tested on Ubuntu 16.04/16.10, running Python 3.5.2    TODO: themes styles support    '''    def __init__(self, widget,                 *,                 bg='#FFFFEA',                 pad=(5, 3, 5, 3),                 text='widget info',                 waittime=400,                 wraplength=250):        self.waittime = waittime  # in miliseconds, originally 500        self.wraplength = wraplength  # in pixels, originally 180        self.widget = widget        self.text = text        self.widget.bind("<Enter>", self.onEnter)        self.widget.bind("<Leave>", self.onLeave)        self.widget.bind("<ButtonPress>", self.onLeave)        self.bg = bg        self.pad = pad        self.id = None        self.tw = None    def onEnter(self, event=None):        self.schedule()    def onLeave(self, event=None):        self.unschedule()        self.hide()    def schedule(self):        self.unschedule()        self.id = self.widget.after(self.waittime, self.show)    def unschedule(self):        id_ = self.id        self.id = None        if id_:            self.widget.after_cancel(id_)    def show(self):        def tip_pos_calculator(widget, label,                               *,                               tip_delta=(10, 5), pad=(5, 3, 5, 3)):            w = widget            s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight()            width, height = (pad[0] + label.winfo_reqwidth() + pad[2],                             pad[1] + label.winfo_reqheight() + pad[3])            mouse_x, mouse_y = w.winfo_pointerxy()            x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1]            x2, y2 = x1 + width, y1 + height            x_delta = x2 - s_width            if x_delta < 0:                x_delta = 0            y_delta = y2 - s_height            if y_delta < 0:                y_delta = 0            offscreen = (x_delta, y_delta) != (0, 0)            if offscreen:                if x_delta:                    x1 = mouse_x - tip_delta[0] - width                if y_delta:                    y1 = mouse_y - tip_delta[1] - height            offscreen_again = y1 < 0  # out on the top            if offscreen_again:                # No further checks will be done.                # TIP:                # A further mod might automagically augment the                # wraplength when the tooltip is too high to be                # kept inside the screen.                y1 = 0            return x1, y1        bg = self.bg        pad = self.pad        widget = self.widget        # creates a toplevel window        self.tw = tk.Toplevel(widget)        # Leaves only the label and removes the app window        self.tw.wm_overrideredirect(True)        win = tk.Frame(self.tw,                       background=bg,                       borderwidth=0)        label = tk.Label(win,                          text=self.text,                          justify=tk.LEFT,                          background=bg,                          relief=tk.SOLID,                          borderwidth=0,                          wraplength=self.wraplength)        label.grid(padx=(pad[0], pad[2]),                   pady=(pad[1], pad[3]),                   sticky=tk.NSEW)        win.grid()        x, y = tip_pos_calculator(widget, label)        self.tw.wm_geometry("+%d+%d" % (x, y))    def hide(self):        tw = self.tw        if tw:            tw.destroy()        self.tw = Noneif __name__ == '__main__':    import random    def further_text():        # texts generated at http://lorem-ipsum.perbang.dk/        short_text = ('Lorem ipsum dolor sit amet, mauris tellus, '                     'porttitor torquent eu. Magna aliquet lorem, '                     'cursus sit ac, in in. Dolor aliquet, cum integer. '                     'Proin aliquet, porttitor pulvinar mauris. Tellus '                     'lectus, amet cras, neque lacus quis. Malesuada '                     'nibh. Eleifend nam, in eget a. Nec turpis, erat '                     'wisi semper')        medium_text = ('Lorem ipsum dolor sit amet, suspendisse aenean '                       'ipsum sollicitudin, pellentesque nunc ultrices ac '                       'ut, arcu elit turpis senectus convallis. Ac orci '                       'pretium sed gravida, tortor nulla felis '                       'consectetuer, mauris egestas est erat. Ut enim '                       'tellus at diam, ac sagittis vel proin. Massa '                       'eleifend orci tortor sociis, scelerisque in pede '                       'metus phasellus, est tempor gravida nam, ante '                       'fusce sem tempor. Mi diam auctor vel pede, mus '                       'non mi luctus luctus, lectus sit varius repellat '                       'eu')        long_text = ('Lorem ipsum dolor sit amet, velit eu nam cursus '                     'quisque gravida sollicitudin, felis arcu interdum '                     'error quam quis massa, et velit libero ligula est '                     'donec. Suspendisse fringilla urna ridiculus dui '                     'volutpat justo, quisque nisl eget sed blandit '                     'egestas, libero nullam magna sem dui nam, auctor '                     'vehicula nunc arcu vel sed dictum, tincidunt vitae '                     'id tristique aptent platea. Lacus eros nec proin '                     'morbi sollicitudin integer, montes suspendisse '                     'augue lorem iaculis sed, viverra sed interdum eget '                     'ut at pulvinar, turpis vivamus ac pharetra nulla '                     'maecenas ut. Consequat dui condimentum lectus nulla '                     'vitae, nam consequat fusce ac facilisis eget orci, '                     'cras enim donec aenean sed dolor aliquam, elit '                     'lorem in a nec fringilla, malesuada curabitur diam '                     'nonummy nisl nibh ipsum. In odio nunc nec porttitor '                     'ipsum, nunc ridiculus platea wisi turpis praesent '                     'vestibulum, suspendisse hendrerit amet quis vivamus '                     'adipiscing elit, ut dolor nec nonummy mauris nec '                     'libero, ad rutrum id tristique facilisis sed '                     'ultrices. Convallis velit posuere mauris lectus sit '                     'turpis, lobortis volutpat et placerat leo '                     'malesuada, vulputate id maecenas at a volutpat '                     'vulputate, est augue nec proin ipsum pellentesque '                     'fringilla. Mattis feugiat metus ultricies repellat '                     'dictum, suspendisse erat rhoncus ultricies in ipsum, '                     'nulla ante pellentesque blandit ligula sagittis '                     'ultricies, sed tortor sodales pede et duis platea')        text = random.choice([short_text, medium_text, long_text, long_text])        return '\nFurther info: ' + text    def main_01(wraplength=200):        # alias        stuff = further_text        root = tk.Tk()        frame = ttk.Frame(root)        btn_ne = ttk.Button(frame, text='North East')        btn_se = ttk.Button(frame, text='South East')        btn_sw = ttk.Button(frame, text='South West')        btn_nw = ttk.Button(frame, text='North West')        btn_center = ttk.Button(frame, text='Center')        btn_n = ttk.Button(frame, text='North')        btn_e = ttk.Button(frame, text='East')        btn_s = ttk.Button(frame, text='South')        btn_w = ttk.Button(frame, text='West')        Tooltip(btn_nw, text='North West' + stuff(), wraplength=wraplength)        Tooltip(btn_ne, text='North East' + stuff(), wraplength=wraplength)        Tooltip(btn_se, text='South East' + stuff(), wraplength=wraplength)        Tooltip(btn_sw, text='South West' + stuff(), wraplength=wraplength)        Tooltip(btn_center, text='Center' + stuff(), wraplength=wraplength)        Tooltip(btn_n, text='North' + stuff(), wraplength=wraplength)        Tooltip(btn_e, text='East' + stuff(), wraplength=wraplength)        Tooltip(btn_s, text='South' + stuff(), wraplength=wraplength)        Tooltip(btn_w, text='West' + stuff(), wraplength=wraplength)        r = 0        c = 0        pad = 10        btn_nw.grid(row=r, column=c, padx=pad, pady=pad, sticky=tk.NW)        btn_n.grid(row=r, column=c + 1, padx=pad, pady=pad, sticky=tk.N)        btn_ne.grid(row=r, column=c + 2, padx=pad, pady=pad, sticky=tk.NE)        r += 1        btn_w.grid(row=r, column=c + 0, padx=pad, pady=pad, sticky=tk.W)        btn_center.grid(row=r, column=c + 1, padx=pad, pady=pad,                    sticky=tk.NSEW)        btn_e.grid(row=r, column=c + 2, padx=pad, pady=pad, sticky=tk.E)        r += 1        btn_sw.grid(row=r, column=c, padx=pad, pady=pad, sticky=tk.SW)        btn_s.grid(row=r, column=c + 1, padx=pad, pady=pad, sticky=tk.S)        btn_se.grid(row=r, column=c + 2, padx=pad, pady=pad, sticky=tk.SE)        frame.grid(sticky=tk.NSEW)        for i in (0, 2):            frame.rowconfigure(i, weight=1)            frame.columnconfigure(i, weight=1)        root.rowconfigure(0, weight=1)        root.columnconfigure(0, weight=1)        root.title('Tooltip wraplength = {}'.format(wraplength))        root.mainloop()    def main():        print('Trying out three different wraplengths:')        for i, wl in enumerate((200, 250, 400), 1):            print(' ', i)            main_01(wl)        print('Done.')    main()