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
How to Get the Current Time in Milliseconds in Python
Why Doesn't Os.Path.Join() Work in This Case
Regex Error - Nothing to Repeat
Why Are Slice and Range Upper-Bound Exclusive
How to Declare an Array in Python
How to Use Youtube-Dl from a Python Program
Matplotlib and Ipython-Notebook: Displaying Exactly the Figure That Will Be Saved
Multiple Inputs and Outputs in Python Subprocess Communicate
Dll Load Failed When Importing Pyqt5
Running a Process in Pythonw with Popen Without a Console
Multiprocessing.Pool: What's the Difference Between Map_Async and Imap
What Are Data Classes and How Are They Different from Common Classes
Pytz Localize VS Datetime Replace
Accessing Every 1St Element of Pandas Dataframe Column Containing Lists