简体   繁体   English

在 Python 中用鼠标光标悬停在某物上时显示消息

[英]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其具有方法showtiphidetip定义如下:

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.要将尖端移动到您想要的位置,您可以在代码中更改xy 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文件中,请不要忘记导入模块,并且不要将文件保存为CreateToolTipToolTip以避免混淆。 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.

相关问题 当 cursor 悬停在 Python (tkinter) 中的 canvas 时显示消息 - Display message when the cursor is hovering a canvas in Python (tkinter) 将光标悬停在画布上时如何显示画布坐标? - How to display canvas coordinates when hovering cursor over canvas? 单击时显示 hover 而不是将鼠标悬停在 plotly python 中的数据点上? - Display hover when clicked instead of hovering over a datapoint in plotly python? 将鼠标悬停在 QMenuBar 中的按钮上后写入 StatusBar 中的消息会更改,但当鼠标离开按钮时不会恢复 - Message written in StatusBar is changed after hovering over a button in QMenuBar but is not reverted when the mouse leaves the button 将鼠标悬停在输入框或图像上时,如何使消息出现在鼠标旁边? - How to make message appear next to mouse when hovering over a entry box or image? Selenium/Python - 如何单击仅在光标悬停在其上时才出现的按钮? - Selenium/Python - How to click on a button that appears only when cursor is hovering over it? 如何通过将光标悬停在热点图元素上来显示其值? - How do I display the value of a heatmap element by hovering the cursor over it? Python Matplotlib 在图形上悬停时显示 cursor - Python Matplotlib show the cursor when hovering on graph Python &amp; Opencv:将鼠标悬停在图片上时实时获取 RGB 值 - Python & Opencv : Realtime Get RGB Values When hovering mouse over picture 将光标置于python中的按钮上方时显示文本 - Display text when cursor is placed over button in python
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM