How to Display Tooltips in Tkinter

How do I display tooltips in Tkinter?

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.

python tkinter tooltip on modal window

I found a solution.

Apparently, someone had the same problem, although with a different class for tooltips.

I refer to this question and answer.

The solution is to add the line tw.wm_attributes("-topmost", 1) somewhere in the showtip method of the TooltipBase class.

I did it as a last line, but I'm not sure it doesn't work some other place; I know it doesn't if immediately after tw.wm_overrideredirect(1).

Display message when hovering over something with mouse cursor in Python

You need to set a binding on the <Enter> and <Leave> events.

Note: if you choose to pop up a window (ie: a tooltip) make sure you don't pop it up directly under the mouse. What will happen is that it will cause a leave event to fire because the cursor leaves the label and enters the popup. Then, your leave handler will dismiss the window, your cursor will enter the label, which causes an enter event, which pops up the window, which causes a leave event, which dismisses the window, which causes an enter event, ... ad infinitum.

For simplicity, here's an example that updates a label, similar to a statusbar that some apps use. Creating a tooltip or some other way of displaying the information still starts with the same core technique of binding to <Enter> and <Leave>.

import Tkinter as tk

class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.l1 = tk.Label(self, text="Hover over me")
self.l2 = tk.Label(self, text="", width=40)
self.l1.pack(side="top")
self.l2.pack(side="top", fill="x")

self.l1.bind("<Enter>", self.on_enter)
self.l1.bind("<Leave>", self.on_leave)

def on_enter(self, event):
self.l2.configure(text="Hello world")

def on_leave(self, enter):
self.l2.configure(text="")

if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand="true")
root.mainloop()

Hovertip/Tooltip for each item in Python ttk combobox

The idea is to make a class similar to Hovertip but that works with listbox items instead of widgets. The current item is stored in self._current_item and when the mouse moves in the listbox, the displaying of the tooltip is rescheduled if the item changes. The text for each item is stored in a dictionary self.tips = {item index: text, ...}.

The main issue is that the Listbox displaying the Combobox's choices is not directly accessible with python. So I had to use some Tcl commands to do it:

proc callback {y} {
event generate .!combobox <<OnMotion>> -y $y
}

set popdown [ttk::combobox::PopdownWindow .!combobox]
bind $popdown.f.l <Motion> {callback %y}

The above code generates a virtual event <<OnMotion>> in the combobox when the mouse moves (where .!combobox is the name of the Combobox widget and y is the mouse relative y coordinate in the widget).

Then I bind the _on_motion() method of the combobox to this <<OnMotion>> event to check if the current item has changed. To get the current item I use the Listbox method nearest(y) but from Tcl.

I also modified the get_position() method to display the tooltip just below the current item and showcontents() to display the text corresponding to the item.

Here is the full code:

import tkinter as tk
from tkinter import ttk
from idlelib.tooltip import OnHoverTooltipBase

class ComboboxTip(OnHoverTooltipBase):
def __init__(self, combobox_widget, hover_delay=1000):
super(ComboboxTip, self).__init__(combobox_widget, hover_delay=hover_delay)
self.tips = {}
self._current_item = 0

combobox_widget.tk.eval("""
proc callback {y} {
event generate %(cb)s <<OnMotion>> -y $y
}

set popdown [ttk::combobox::PopdownWindow %(cb)s]
bind $popdown.f.l <Motion> {callback %%y}
""" % ({"cb": combobox_widget}))

self._id4 = combobox_widget.bind("<<OnMotion>>", self._on_motion)

def _on_motion(self, event):
current_item = int(self.anchor_widget.tk.eval("$popdown.f.l nearest %i" % event.y))
if current_item != self._current_item:
self._current_item = current_item
self.hidetip()
if current_item in self.tips:
self.schedule()
else:
self.unschedule()

def __del__(self):
try:
self.anchor_widget.unbind("<<OnMotion>>", self._id4)
except tk.TclError:
pass
super(ComboboxTip, self).__del__()

def add_tooltip(self, index, text):
self.tips[index] = text

def get_position(self):
"""choose a screen position for the tooltip"""
try:
h = self.anchor_widget.winfo_height()
bbox = self.anchor_widget._getints(self.anchor_widget.tk.eval("$popdown.f.l bbox %i" % self._current_item))
return bbox[0] + bbox[2], bbox[1] + bbox[-1] + h
except Exception:
return 20, self.anchor_widget.winfo_height() + 1

def showcontents(self):
label = tk.Label(self.tipwindow, text=self.tips[self._current_item], justify=tk.LEFT,
background="#ffffe0", relief=tk.SOLID, borderwidth=1)
label.pack()

names = ["One", "Two", "Three"]
root = tk.Tk()
cb = ttk.Combobox(root, values=names)
cb.pack()

t = ComboboxTip(cb)
t.add_tooltip(0, "This is One")
t.add_tooltip(1, "This is Two")
t.add_tooltip(2, "This is Three")

root.mainloop()

Modify tooltips class for Tkinter

Just like you wrote in the question, add a tooltip_width argument to the CreateToolTip.__init__ method and use it to set self.wraplength instead of 180.

class CreateToolTip(object):
"""
create a tooltip for a given widget
"""
def __init__(self, widget, tooltip_width=180, text='widget info'):
self.waittime = 500 #miliseconds
self.wraplength = tooltip_width #pixels
...

# testing ...
if __name__ == '__main__':
root = tk.Tk()
btn1 = tk.Button(root, text="button 1")
btn1.pack(padx=10, pady=5)
button1_ttp = CreateToolTip(btn1, 180,\
'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, 500,\
"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()

Tkinter Tooltip over a text only in a text widget

You can add tags to a span of characters in a text widget. You can then bind the mouse events <Enter> and <Leave. to those tags.

Here's a very contrived example:

import tkinter as tk

def show_info(text):
label.configure(text=text)

root = tk.Tk()
text_widget = tk.Text(root)
label = tk.Label(root)

label.pack(side="top", fill="x")
text_widget.pack(fill="both", expand=True)

for color in ("red", "orange", "yellow", "green", "blue", "indigo", "violet"):
tag = color
text = color
text_widget.insert("end", text+"\n", (tag, ))

text_widget.tag_configure(tag, background=color, foreground="white")
text_widget.tag_bind(tag, "<Enter>",
lambda event, color=color: show_info(color))
text_widget.tag_bind(tag, "<Leave>",
lambda event, color=color: show_info(""))

tk.mainloop()

How to add a Pmw tooltip to a rectangle object in a tkinter canvas?

Use balloon.tagbind(Canvas/Text, tag, "tooltip text").

Minimal example:

import Pmw
from tkinter import *

root = Tk()
Pmw.initialise(root)
canvas = Canvas(root, width=800, height=700, bg="white")
canvas.pack()

bar1 = canvas.create_rectangle(50, 50, 100, 100, fill="pink")
bar2 = canvas.create_rectangle(300, 300, 500, 500, fill="red")

balloon = Pmw.Balloon()
balloon.tagbind(canvas, bar1, "first tooltip")
balloon.tagbind(canvas, bar2, "second tooltip")

root.mainloop()

How to change the color of a tooltip using the Pmw module in python?

Get the label component of the tooltip using balloon.component("label") then use config on that label.

Here is an example:

from tkinter import *
import Pmw

root = Tk()
Pmw.initialise(root)

# Create some random widget
button = Button(root, text=" This is a Test", pady=30)
button.pack(pady=10)

# create balloon object and bind it to the widget
balloon = Pmw.Balloon(root)
balloon.bind(button, "Text for the tool tip")

lbl = balloon.component("label")
lbl.config(background="black", foreground="white")
# Pmw.Color.changecolor(lbl, background="black", foreground="white")

root.mainloop()


Related Topics



Leave a reply



Submit