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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20399243/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:20:09  来源:igfitidea点击:

Display message when hovering over something with mouse cursor in Python

pythontkintertooltip

提问by maupertius

I have a GUI made with TKinter in Python. 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.

我有一个用 TKinter 在 Python 中制作的 GUI。我希望能够在我的鼠标光标移动到标签或按钮顶部时显示一条消息。这样做的目的是向用户解释按钮/标签的作用或代表。

Is there a way to display text when hovering over a tkinter object in Python?

将鼠标悬停在 Python 中的 tkinter 对象上时,有没有办法显示文本?

采纳答案by Bryan Oakley

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. 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.

注意:如果您选择弹出一个窗口(即:工具提示),请确保不要直接在鼠标下方弹出。将会发生的事情是它会导致一个 leave 事件触发,因为光标离开标签并进入弹出窗口。然后,您的离开处理程序将关闭窗口,您的光标将进入标签,这将导致一个 enter 事件,该事件弹出该窗口,这将导致一个离开事件,该事件关闭该窗口,这将导致一个进入事件,...广告无限。

Here's an example that merely updates a label, similar to a statusbar that some apps use.

这是一个仅更新标签的示例,类似于某些应用程序使用的状态栏。

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()

回答by Alok

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:

截屏:

Testing hoverbox

测试悬浮盒

回答by Minion Jim

The best way I have found to create a popup help window is to use the tix.Balloon. I have modified it below to make it look better and show an example (note the use of tix.Tk):

我发现创建弹出式帮助窗口的最佳方法是使用tix.Balloon. 我在下面修改了它以使其看起来更好并显示一个示例(注意使用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 overridedirectset to true). Use the hover_timerclass for easy use of this. 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.

这是一个使用<enter><leave>作为@bryanoakley 建议的顶级示例(overridedirect设置为true)。使用hover_timer该类可以轻松使用它。这需要小部件和帮助文本(带有可选的延迟参数 - 默认为 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 ()

回答by Supamee

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

唯一真正的问题是它留下了一个小框,将焦点从主窗口移开 如果有人知道如何解决这些问题,请告诉我

回答by squareRoot17

I think this would meet your requirements.

我认为这将满足您的要求。

Here's what the output looks like:

输出如下所示:

the output

输出

First, A class named ToolTipwhich has methods showtipand hidetipis defined as follows:

首先,一个名为的类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. You might want to make necessary changes in such case.

快速说明:上面的代码使用from tkinter import *了一些程序员不建议使用的代码,并且它们具有有效的要点。在这种情况下,您可能需要进行必要的更改。

To move the tip to your desired location, you can change xand yin the code. The function CreateToolTip()helps to create this tip easily. Just pass the widget and string you want to display in the tipbox to this function, and you're good to go.

要将提示移动到您想要的位置,您可以在代码中更改xy。该功能CreateToolTip()有助于轻松创建此提示。只需将要在提示框中显示的小部件和字符串传递给此函数,就可以了。

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 CreateToolTipor ToolTipto avoid confusion. Thispost from Fuzzyman shares some similar thoughts, and worth checking out.

如果将之前的大纲保存在不同的python文件中,请不要忘记导入模块,并且不要将文件另存为CreateToolTipToolTip以免混淆。 Fuzzyman 的这篇文章分享了一些类似的想法,值得一看。

回答by kamion

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)