from tkinter import *
class TooltipBase:
def __init__(self, anchor_widget):
self.anchor_widget = anchor_widget
self.tipwindow = None
def __del__(self):
self.hidetip()
def showtip(self):
if self.tipwindow:
return
self.tipwindow = tw = Toplevel(self.anchor_widget)
tw.wm_overrideredirect(1)
try:
tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
"help", "noActivates")
except TclError:
pass
self.position_window()
self.showcontents()
self.tipwindow.update_idletasks() self.tipwindow.lift()
def position_window(self):
x, y = self.get_position()
root_x = self.anchor_widget.winfo_rootx() + x
root_y = self.anchor_widget.winfo_rooty() + y
self.tipwindow.wm_geometry("+%d+%d" % (root_x, root_y))
def get_position(self):
return 20, self.anchor_widget.winfo_height() + 1
def showcontents(self):
raise NotImplementedError
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
try:
tw.destroy()
except TclError: pass
class OnHoverTooltipBase(TooltipBase):
def __init__(self, anchor_widget, hover_delay=1000):
super().__init__(anchor_widget)
self.hover_delay = hover_delay
self._after_id = None
self._id1 = self.anchor_widget.bind("<Enter>", self._show_event)
self._id2 = self.anchor_widget.bind("<Leave>", self._hide_event)
self._id3 = self.anchor_widget.bind("<Button>", self._hide_event)
def __del__(self):
try:
self.anchor_widget.unbind("<Enter>", self._id1)
self.anchor_widget.unbind("<Leave>", self._id2) self.anchor_widget.unbind("<Button>", self._id3) except TclError:
pass
super().__del__()
def _show_event(self, event=None):
if self.hover_delay:
self.schedule()
else:
self.showtip()
def _hide_event(self, event=None):
self.hidetip()
def schedule(self):
self.unschedule()
self._after_id = self.anchor_widget.after(self.hover_delay,
self.showtip)
def unschedule(self):
after_id = self._after_id
self._after_id = None
if after_id:
self.anchor_widget.after_cancel(after_id)
def hidetip(self):
try:
self.unschedule()
except TclError: pass
super().hidetip()
class Hovertip(OnHoverTooltipBase):
"A tooltip that pops up when a mouse hovers over an anchor widget."
def __init__(self, anchor_widget, text, hover_delay=1000,
foreground="#000000", background="#ffffe0"):
super().__init__(anchor_widget, hover_delay=hover_delay)
self.text = text
self.foreground = foreground
self.background = background
def showcontents(self):
label = Label(self.tipwindow, text=self.text, justify=LEFT,
relief=SOLID, borderwidth=1,
foreground=self.foreground, background=self.background)
label.pack()
def _tooltip(parent): top = Toplevel(parent)
top.title("Test tooltip")
x, y = map(int, parent.geometry().split('+')[1:])
top.geometry("+%d+%d" % (x, y + 150))
label = Label(top, text="Place your mouse over buttons")
label.pack()
button1 = Button(top, text="Button 1 -- 1/2 second hover delay")
button1.pack()
Hovertip(button1, "This is tooltip text for button1.", hover_delay=500)
button2 = Button(top, text="Button 2 -- no hover delay")
button2.pack()
Hovertip(button2, "This is tooltip\ntext for button2.", hover_delay=None)
if __name__ == '__main__':
from unittest import main
main('idlelib.idle_test.test_tooltip', verbosity=2, exit=False)
from idlelib.idle_test.htest import run
run(_tooltip)