[英]Display message when hovering over something with mouse cursor in Python
I have a GUI made with TKinter in Python.我有一个用 TKinter 在 Python 中制作的 GUI。 I would like to be able to display a message when my mouse cursor goes, for example, on top of a label or button.我希望能够在我的鼠标光标移动到标签或按钮顶部时显示一条消息。 The purpose of this is to explain to the user what the button/label does or represents.这样做的目的是向用户解释按钮/标签的作用或代表什么。
Is there a way to display text when hovering over a tkinter object in Python?将鼠标悬停在 Python 中的 tkinter 对象上时,有没有办法显示文本?
I think this would meet your requirements.我认为这将满足您的要求。
Here's what the output looks like:输出如下所示:
First, A class named ToolTip
which has methods showtip
and hidetip
is defined as follows:首先,命名为A类ToolTip
其具有方法showtip
和hidetip
定义如下:
from tkinter import *
class ToolTip(object):
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, text):
"Display text in tooltip window"
self.text = text
if self.tipwindow or not self.text:
return
x, y, cx, cy = self.widget.bbox("insert")
x = x + self.widget.winfo_rootx() + 57
y = y + cy + self.widget.winfo_rooty() +27
self.tipwindow = tw = Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
label = Label(tw, text=self.text, justify=LEFT,
background="#ffffe0", relief=SOLID, borderwidth=1,
font=("tahoma", "8", "normal"))
label.pack(ipadx=1)
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
tw.destroy()
def CreateToolTip(widget, text):
toolTip = ToolTip(widget)
def enter(event):
toolTip.showtip(text)
def leave(event):
toolTip.hidetip()
widget.bind('<Enter>', enter)
widget.bind('<Leave>', leave)
The widget is where you want to add the tip.小部件是您要添加提示的位置。 For example, if you want the tip when you hover over a button or entry or label, the instance of the same should be provided at the call time.例如,如果您希望在将鼠标悬停在按钮或条目或标签上时获得提示,则应在调用时提供相同的实例。
Quick note : the code above uses from tkinter import *
which is not suggested by some of the programmers out there, and they have valid points.快速说明:上面的代码使用from tkinter import *
这不是一些程序员所建议的,并且他们有有效的观点。 You might want to make necessary changes in such case.在这种情况下,您可能需要进行必要的更改。
To move the tip to your desired location, you can change x
and y
in the code.要将尖端移动到您想要的位置,您可以在代码中更改x
和y
。 The function CreateToolTip()
helps to create this tip easily.函数CreateToolTip()
有助于轻松创建此提示。 Just pass the widget and string you want to display in the tipbox to this function, and you're good to go.只需将要在提示框中显示的小部件和字符串传递给此函数,就可以了。
This is how you call the above part:这就是你如何调用上面的部分:
button = Button(root, text = 'click mem')
button.pack()
CreateToolTip(button, text = 'Hello World\n'
'This is how tip looks like.'
'Best part is, it\'s not a menu.\n'
'Purely tipbox.')
Do not forget to import the module if you save the previous outline in different python file, and don't save the file as CreateToolTip
or ToolTip
to avoid confusion.如果将之前的大纲保存在不同的python文件中,请不要忘记导入模块,并且不要将文件保存为CreateToolTip
或ToolTip
以避免混淆。 This post from Fuzzyman shares some similar thoughts, and worth checking out. Fuzzyman 的这篇文章分享了一些类似的想法,值得一看。
You need to set a binding on the <Enter>
and <Leave>
events.您需要在<Enter>
和<Leave>
事件上设置绑定。
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.会发生的事情是它会导致一个 leave 事件触发,因为光标离开标签并进入弹出窗口。 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.然后,您的离开处理程序将关闭窗口,您的光标将进入标签,这将导致一个 enter 事件,该事件弹出该窗口,这将导致一个离开事件,该事件关闭该窗口,这将导致一个进入事件,...广告无限。
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>
.创建工具提示或其他显示信息的方式仍然以绑定到<Enter>
和<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()
You can refer to this- HoverClass你可以参考这个- HoverClass
It is exactly what you need.这正是您所需要的。 Nothing more, nothing less不多也不少
from Tkinter import *
import re
class HoverInfo(Menu):
def __init__(self, parent, text, command=None):
self._com = command
Menu.__init__(self,parent, tearoff=0)
if not isinstance(text, str):
raise TypeError('Trying to initialise a Hover Menu with a non string type: ' + text.__class__.__name__)
toktext=re.split('\n', text)
for t in toktext:
self.add_command(label = t)
self._displayed=False
self.master.bind("<Enter>",self.Display )
self.master.bind("<Leave>",self.Remove )
def __del__(self):
self.master.unbind("<Enter>")
self.master.unbind("<Leave>")
def Display(self,event):
if not self._displayed:
self._displayed=True
self.post(event.x_root, event.y_root)
if self._com != None:
self.master.unbind_all("<Return>")
self.master.bind_all("<Return>", self.Click)
def Remove(self, event):
if self._displayed:
self._displayed=False
self.unpost()
if self._com != None:
self.unbind_all("<Return>")
def Click(self, event):
self._com()
Example app using HoverInfo:使用 HoverInfo 的示例应用程序:
from Tkinter import *
from HoverInfo import HoverInfo
class MyApp(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.grid()
self.lbl = Label(self, text='testing')
self.lbl.grid()
self.hover = HoverInfo(self, 'while hovering press return \n for an exciting msg', self.HelloWorld)
def HelloWorld(self):
print('Hello World')
app = MyApp()
app.master.title('test')
app.mainloop()
Screenshot:截图:
I have a very hacky solution but it has some advantages over the current answers so I figured I would share it.我有一个非常hacky的解决方案,但它比当前的答案有一些优势,所以我想我会分享它。
lab=Label(root,text="hover me")
lab.bind("<Enter>",popup)
def do_popup(event):
# display the popup menu
root.after(1000, self.check)
popup = Menu(root, tearoff=0)
popup.add_command(label="Next")
popup.tk_popup(event.x_root, event.y_root, 0)
def check(event=None):
x, y = root.winfo_pointerxy()
widget = root.winfo_containing(x, y)
if widget is None:
root.after(100, root.check)
else:
leave()
def leave():
popup.delete(0, END)
The only real issue with this is it leaves behind a small box that moves focus away from the main window If anyone knows how to solve these issues let me know唯一真正的问题是它留下了一个小框,将焦点从主窗口移开 如果有人知道如何解决这些问题,请告诉我
If anyone is on Mac OSX and tool tip isn't working, check out the example in:如果有人使用 Mac OSX 并且工具提示不起作用,请查看以下示例:
https://github.com/python/cpython/blob/master/Lib/idlelib/tooltip.py https://github.com/python/cpython/blob/master/Lib/idlelib/tooltip.py
Basically, the two lines that made it work for me on Mac OSX were:基本上,在 Mac OSX 上让它对我有用的两行是:
tw.update_idletasks() # Needed on MacOS -- see #34275.
tw.lift() # work around bug in Tk 8.5.18+ (issue #24570)
Here is a simple solution to your problem that subclasses the tk.Button
object.这是您的问题的一个简单解决方案,它是tk.Button
对象的子类。 We make a special class that tk.Button
inherits from, giving it tooltip functionality.我们创建了一个tk.Button
继承自的特殊类, tk.Button
提供工具提示功能。 The same for tk.Label
s. tk.Label
。
I don't know what would be cleanest and the easiest way to maintain code for keeping track of the text that goes into the tooltips.我不知道什么是最干净和最简单的方法来维护代码以跟踪进入工具提示的文本。 I present here one way, in which I pass unique widget IDs to MyButton
s, and access a dictionary for storing the tooltip texts.我在这里介绍一种方法,我将唯一的小部件 ID 传递给MyButton
,并访问用于存储工具提示文本的字典。 You could store this file as a JSON, or as a class attribute, or as a global variable (as below).您可以将此文件存储为 JSON、类属性或全局变量(如下所示)。 Alternatively, perhaps it would be better to define a setter method in MyButton, and just call this method every time you create a new widget that should have a tooltip.或者,也许最好在 MyButton 中定义一个 setter 方法,并且每次创建应该有工具提示的新小部件时都调用此方法。 Although you would have to store the widget instance in a variable, adding one extra line for all widgets to include.尽管您必须将小部件实例存储在一个变量中,但要为所有小部件添加额外的一行。
One drawback in the code below is that the self.master.master
syntax relies on determining the "widget depth".下面代码的一个缺点是self.master.master
语法依赖于确定“小部件深度”。 A simple recursive function will catch most cases (only needed for entering a widget, since by definition you leave somewhere you once were).一个简单的递归函数将捕获大多数情况(仅在输入小部件时才需要,因为根据定义,您会离开曾经所在的地方)。
Anyway, below is a working MWE for anyone interested.无论如何,下面是任何感兴趣的人的工作 MWE。
import tkinter as tk
tooltips = {
'button_hello': 'Print a greeting message',
'button_quit': 'Quit the program',
'button_insult': 'Print an insult',
'idle': 'Hover over button for help',
'error': 'Widget ID not valid'
}
class ToolTipFunctionality:
def __init__(self, wid):
self.wid = wid
self.widet_depth = 1
self.widget_search_depth = 10
self.bind('<Enter>', lambda event, i=1: self.on_enter(event, i))
self.bind('<Leave>', lambda event: self.on_leave(event))
def on_enter(self, event, i):
if i > self.widget_search_depth:
return
try:
cmd = f'self{".master"*i}.show_tooltip(self.wid)'
eval(cmd)
self.widget_depth = i
except AttributeError:
return self.on_enter(event, i+1)
def on_leave(self, event):
cmd = f'self{".master" * self.widget_depth}.hide_tooltip()'
eval(cmd)
class MyButton(tk.Button, ToolTipFunctionality):
def __init__(self, parent, wid, **kwargs):
tk.Button.__init__(self, parent, **kwargs)
ToolTipFunctionality.__init__(self, wid)
class MyLabel(tk.Label, ToolTipFunctionality):
def __init__(self, parent, wid, **kwargs):
tk.Label.__init__(self, parent, **kwargs)
ToolTipFunctionality.__init__(self, wid)
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.tooltip = tk.StringVar()
self.tooltip.set(tooltips['idle'])
self.frame = tk.Frame(self, width=50)
self.frame.pack(expand=True)
MyLabel(self.frame, '', text='One Cool Program').pack()
self.subframe = tk.Frame(self.frame, width=40)
self.subframe.pack()
MyButton(self.subframe, 'button_hello', text='Hello!', command=self.greet, width=20).pack()
MyButton(self.subframe, 'button_insutl', text='Insult', command=self.insult, width=20).pack()
MyButton(self.subframe, 'button_quit', text='Quit', command=self.destroy, width=20).pack()
tk.Label(self.subframe, textvar=self.tooltip, width=20).pack()
def show_tooltip(self, wid):
try:
self.tooltip.set(tooltips[wid])
except KeyError:
self.tooltip.set(tooltips['error'])
def hide_tooltip(self):
self.tooltip.set(tooltips['idle'])
def greet(self):
print('Welcome, Fine Sir!')
def insult(self):
print('You must be dead from the neck up')
if __name__ == '__main__':
app = Application()
app.mainloop()
The best way I have found to create a popup help window is to use the tix.Balloon
.我发现创建弹出式帮助窗口的最佳方法是使用tix.Balloon
。 I have modified it below to make it look better and show an example (note the use of tix.Tk
):我在下面修改了它以使其看起来更好并显示一个示例(注意tix.Tk
的使用):
import tkinter as tk
import tkinter.tix as tix
class Balloon(tix.Balloon):
# A modified tix popup balloon (to change the default delay, bg and wraplength)
init_after = 1250 # Milliseconds
wraplength = 300 # Pixels
def __init__(self, master):
bg = root.cget("bg")
# Call the parent
super().__init__(master, initwait=self.init_after)
# Change background colour
for i in self.subwidgets_all():
i.config(bg=bg)
# Modify the balloon label
self.message.config(wraplength=self.wraplength)
root = tix.Tk()
l = tk.Label(root, text="\n".join(["text"] * 5))
l.pack()
b = Balloon(root.winfo_toplevel())
b.bind_widget(l, balloonmsg="Some random text")
root.mainloop()
OLD ANSWER:旧答案:
Here is an example using <enter>
and <leave>
as @bryanoakley suggested with a toplevel (with overridedirect
set to true).这是一个使用<enter>
和<leave>
作为 @bryanoakley 建议的顶级示例( overridedirect
设置为 true)。 Use the hover_timer
class for easy use of this.使用hover_timer
类可以轻松使用它。 This needs the widget and help-text (with an optional delay argument - default 0.5s) and can be easily called just by initiating the class and then cancelling it.这需要小部件和帮助文本(带有可选的延迟参数 - 默认为 0.5 秒)并且可以通过启动类然后取消它来轻松调用。
import threading, time
from tkinter import *
class hover_window (Toplevel):
def __init__ (self, coords, text):
super ().__init__ ()
self.geometry ("+%d+%d" % (coords [0], coords [1]))
self.config (bg = "white")
Label (self, text = text, bg = "white", relief = "ridge", borderwidth = 3, wraplength = 400, justify = "left").grid ()
self.overrideredirect (True)
self.update ()
self.bind ("<Enter>", lambda event: self.destroy ())
class hover_timer:
def __init__ (self, widget, text, delay = 2):
self.wind, self.cancel_var, self.widget, self.text, self.active, self.delay = None, False, widget, text, False, delay
threading.Thread (target = self.start_timer).start ()
def start_timer (self):
self.active = True
time.sleep (self.delay)
if not self.cancel_var: self.wind = hover_window ((self.widget.winfo_rootx (), self.widget.winfo_rooty () + self.widget.winfo_height () + 20), self.text)
self.active = False
def delayed_stop (self):
while self.active: time.sleep (0.05)
if self.wind:
self.wind.destroy ()
self.wind = None
def cancel (self):
self.cancel_var = True
if not self.wind: threading.Thread (target = self.delayed_stop).start ()
else:
self.wind.destroy ()
self.wind = None
def start_help (event):
# Create a new help timer
global h
h = hover_timer (l, "This is some additional information.", 0.5)
def end_help (event):
# If therre is one, end the help timer
if h: h.cancel ()
if __name__ == "__main__":
# Create the tkinter window
root = Tk ()
root.title ("Hover example")
# Help class not created yet
h = None
# Padding round label
Frame (root, width = 50).grid (row = 1, column = 0)
Frame (root, height = 50).grid (row = 0, column = 1)
Frame (root, width = 50).grid (row = 1, column = 2)
Frame (root, height = 50).grid (row = 2, column = 1)
# Setup the label
l = Label (root, text = "Hover over me for information.", font = ("sans", 32))
l.grid (row = 1, column = 1)
l.bind ("<Enter>", start_help)
l.bind ("<Leave>", end_help)
# Tkinter mainloop
root.mainloop ()
I wanted to contribute to the answer of @squareRoot17 as he inspired me to shorten his code while providing the same functionality:我想为@squareRoot17的答案做出贡献,因为他激励我在提供相同功能的同时缩短他的代码:
import tkinter as tk
class ToolTip(object):
def __init__(self, widget, text):
self.widget = widget
self.text = text
def enter(event):
self.showTooltip()
def leave(event):
self.hideTooltip()
widget.bind('<Enter>', enter)
widget.bind('<Leave>', leave)
def showTooltip(self):
self.tooltipwindow = tw = tk.Toplevel(self.widget)
tw.wm_overrideredirect(1) # window without border and no normal means of closing
tw.wm_geometry("+{}+{}".format(self.widget.winfo_rootx(), self.widget.winfo_rooty()))
label = tk.Label(tw, text = self.text, background = "#ffffe0", relief = 'solid', borderwidth = 1).pack()
def hideTooltip(self):
tw = self.tooltipwindow
tw.destroy()
self.tooltipwindow = None
This class can then be imported and used as:然后可以导入此类并将其用作:
import tkinter as tk
from tooltip import ToolTip
root = tk.Tk()
your_widget = tk.Button(root, text = "Hover me!")
ToolTip(widget = your_widget, text = "Hover text!")
root.mainloop()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.