Commit 5cb6efed authored by André Anjos's avatar André Anjos 💬

[widgets] Use idlelib.tooltip to implement Tooltips (closes #4)

parent d3fc8c6d
......@@ -6,6 +6,7 @@
import tkinter
import tkinter.ttk
import tkinter.scrolledtext
import idlelib.tooltip
import warnings
import platform
......@@ -500,164 +501,28 @@ class Annotation(object):
class Tooltip(idlelib.tooltip.OnHoverTooltipBase):
"A tooltip that pops up when a mouse hovers over an anchor widget."
class Tooltip(object):
'''Creates a tooltip for a given widget as the mouse goes on it.
def __init__(self, anchor_widget, text, hover_delay=1000):
"""Create a text tooltip with a mouse hover delay.
anchor_widget: the widget next to which the tooltip will be shown
hover_delay: time to delay before showing the tooltip, in milliseconds
Note that a widget will only be shown when showtip() is called,
e.g. after hovering over the anchor widget with the mouse for enough
super(Tooltip, self).__init__(anchor_widget, hover_delay=hover_delay)
self.text = text
def showcontents(self):
- 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,
pad=(5, 3, 5, 3),
text='widget info',
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.on_enter)
self.widget.bind("<Leave>", self.on_leave)
self.widget.bind("<ButtonPress>", self.on_leave) = bg
self.pad = pad = None = None
def on_enter(self, event=None):
def on_leave(self, event=None):
def schedule(self):
self.unschedule() = self.widget.after(self.waittime,
def unschedule(self):
id_ = = None
if 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 =
pad = self.pad
widget = self.widget
# creates a toplevel window = tkinter.Toplevel(widget)
# Leaves only the label and removes the app window
win = tkinter.Frame(, background=bg, borderwidth=0)
label = tkinter.Label(win,
label.grid(padx=(pad[0], pad[2]), pady=(pad[1], pad[3]),
x, y = tip_pos_calculator(widget, label)"+%d+%d" % (x, y))
def hide(self):
tw =
if tw:
tw.destroy() = None
label = tkinter.Label(self.tipwindow, text=self.text,
justify=tkinter.LEFT, background="#ffffe0", relief=tkinter.SOLID,
borderwidth=1, wraplength=250)
class ImageCarousel(tkinter.Canvas):
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment