嗨,我正在尝试创建一个Tkinter按钮,该按钮在悬停时带有工具提示气泡。我希望工具提示在输入时被延迟,并且仅显示一定的时间。我正在使用线程显示工具提示,以免阻止单击按钮。我似乎非常接近解决方案,但是我认为存在一个与线程有关的问题。工具提示是没有任何窗口装饰(标题栏,边框等)的顶层窗口。输入按钮时,会显示工具提示,但有时会带有装饰,有时没有,有时也会出现在正确的位置,有时则没有。我不知道为什么会发生这种行为,下面我张贴了用于解决此问题的测试代码,但现在我迷路了,不胜感激。
from time import sleep
from tkinter import Tk, Button, Label, Toplevel
from threading import Thread
class _Button(Button):
def __init__(self, parent, *args, **kwargs):
self.tooltip_text = kwargs.pop('tooltip', None)
super().__init__(*args, **kwargs)
self.t = None
self.parent = parent
self.btn_tooltip = None
def tooltip(self):
if not self.t:
self.t = Thread(target=self.tooltip_render)
self.t.start()
def tooltip_render(self):
sleep(0.5)
if not self.btn_tooltip:
self.btn_tooltip = Toplevel()
self.btn_tooltip.wm_overrideredirect(True)
x, y, cx, cy = self.bbox('insert')
x += self.winfo_rootx() + 25
y += self.winfo_rooty() + 25
self.btn_tooltip.geometry('+%d+%d' % (x, y))
label = Label(
self.btn_tooltip, text=self.tooltip_text, background='yellow', borderwidth=1,
)
label.pack(ipadx=5, ipady=2)
sleep(1)
if self.btn_tooltip:
self.btn_tooltip.destroy()
self.btn_tooltip = None
self.t = None
def enter(e):
e.widget.tooltip()
def leave(e):
if e.widget.btn_tooltip:
e.widget.btn_tooltip.destroy()
e.widget.btn_tooltip = None
e.widget.t = None
root = Tk()
bt1 = _Button(root, text='Button 1', tooltip='Tooltip1')
bt1.bind('<Enter>', enter)
bt1.bind('<Leave>', leave)
bt1.grid()
bt2 = _Button(root, text='Button 2', tooltip='Tooltip2')
bt2.bind('<Enter>', enter)
bt2.bind('<Leave>', leave)
bt2.grid(row=0, column=1)
root.mainloop()
答案 0 :(得分:1)
您不需要进行穿线即可显示工具提示,然后在几秒钟后消失。即使您有丰富的线程处理经验,线程处理也很困难。如果您不这样做,则更加困难,而将其与基于事件的程序结合起来则更加困难。
相反,您应该使用tkinter提供的功能。即,after
方法可以安排代码运行到将来。
您需要做的就是使用after
显示工具提示,然后再次使用after
将其关闭。
基本模式如下:
def tooltip_render(self):
# create the tooltip
self.btn_tooltip = Toplevel()
...
<the rest of your code to render the tooltip>
...
# schedule it to go away
self.after(1000, self.btn_tooltip.destroy)
接下来,使用render_tooltip
再次呼叫after
:
def tooltip(self):
self.after(500, self.tooltip_render)
仅此一项并不能为您提供完美的工具提示。如果用户快速移动鼠标,您仍然需要避免尝试渲染和销毁多个工具提示,但这可以为您提供通用框架,而无需求助于线程。
答案 1 :(得分:-1)
from tkinter import Tk, Toplevel, TclError, Label, Button
class Tooltip:
def __init__(self, widget, text, delay=750, duration=1500):
self.widget = widget
self._tooltip = None
self._hide_id = None
self._render_id = None
self._tooltip_text = text
self._tooltip_delay = delay
self._tooltip_duration = duration
self._enter_bind = self.widget.bind("<Enter>", self.show)
self._leave_bind = self.widget.bind("<Leave>", self.hide)
self._button_bind = self.widget.bind("<Button>", self.hide)
def __del__(self):
try:
self.widget.unbind("<Enter>", self._enter_bind)
self.widget.unbind("<Leave>", self._leave_bind)
self.widget.unbind("<Button>", self._button_bind)
except TclError:
pass
def show(self, _):
def render():
if not self._tooltip:
self._tooltip = tw = Toplevel(self.widget)
tw.wm_overrideredirect(True)
x, y = 20, self.widget.winfo_height() + 1
root_x = self.widget.winfo_rootx() + x
root_y = self.widget.winfo_rooty() + y
self._tooltip.wm_geometry("+%d+%d" % (root_x, root_y))
label = Label(
self._tooltip,
text=self._tooltip_text,
justify='left',
background="#ffffe0",
relief='solid',
borderwidth=1
)
label.pack()
self._tooltip.update_idletasks() # Needed on MacOS -- see #34275.
self._tooltip.lift()
self._hide_id = self.widget.after(self._tooltip_duration, self.hide)
if self._tooltip_delay:
if self._render_id:
self.widget.after_cancel(self._render_id)
self._render_id = self.widget.after(self._tooltip_delay, render)
else:
render()
def hide(self, _=None):
try:
if self._hide_id:
self.widget.after_cancel(self._hide_id)
if self._render_id:
self.widget.after_cancel(self._render_id)
except TclError:
pass
tooltip = self._tooltip
if self._tooltip:
try:
tooltip.destroy()
except TclError:
pass
self._tooltip = None
def app():
top = Toplevel()
top.title("Test tooltip")
button1 = Button(top, text="Button 1")
button1.pack()
Tooltip(button1, "Tooltip for Button 1", delay=500, duration=1500)
button2 = Button(top, text="Button 2")
button2.pack()
Tooltip(button2, "Tooltip for Button 2", delay=0, duration=1500)
if __name__ == '__main__':
tk = Tk()
app(tk)
tk.mainloop()