Display message when hovering over something with mouse cursor in Python

前端 未结 7 758
走了就别回头了
走了就别回头了 2020-12-01 10:57

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 th

相关标签:
7条回答
  • 2020-12-01 11:16

    I have a very hacky solution but it has some advantages over the current answers so I figured I would share it.

    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

    0 讨论(0)
  • 2020-12-01 11:21

    I wanted to contribute to the answer of @squareRoot17 as he inspired me to shorten his code while providing the same functionality:

    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()
    
    0 讨论(0)
  • 2020-12-01 11:23

    If anyone is on Mac OSX and tool tip isn't working, check out the example in:

    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:

        tw.update_idletasks()  # Needed on MacOS -- see #34275.
        tw.lift()  # work around bug in Tk 8.5.18+ (issue #24570)
    
    0 讨论(0)
  • 2020-12-01 11:25

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

    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). Use the hover_timer class 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.

    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 ()
    
    0 讨论(0)
  • 2020-12-01 11:31

    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:

    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.

    To move the tip to your desired location, you can change x and y in 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.

    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. This post from Fuzzyman shares some similar thoughts, and worth checking out.

    0 讨论(0)
  • 2020-12-01 11:32

    You need to set a binding on the <Enter> and <Leave> events.

    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.

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

    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()
    
    0 讨论(0)
提交回复
热议问题