web-dev-qa-db-fra.com

Afficher un message lorsque vous survolez quelque chose avec le curseur de la souris dans Python

J'ai une interface graphique faite avec TKinter en Python. J'aimerais pouvoir afficher un message lorsque le curseur de ma souris passe, par exemple, au-dessus d'une étiquette ou d'un bouton. Le but est d'expliquer à l'utilisateur ce que fait ou représente le bouton/l'étiquette.

Existe-t-il un moyen d'afficher du texte lorsque vous survolez un objet tkinter en Python?

15
maupertius

Vous devez définir une liaison sur le <Enter> et <Leave> événements.

Remarque: si vous choisissez de faire apparaître une fenêtre (c'est-à-dire une info-bulle), assurez-vous de ne pas la faire apparaître directement sous la souris. Ce qui se passera, c'est qu'il provoquera un événement de congé, car le curseur quitte l'étiquette et entre dans la fenêtre contextuelle. Ensuite, votre gestionnaire de congés fermera la fenêtre, votre curseur entrera dans l'étiquette, ce qui provoquera un événement enter, qui fera apparaître la fenêtre, ce qui provoquera un événement Leave, qui rejettera la fenêtre, ce qui provoquera un événement enter, ... ad infini.

Voici un exemple qui met simplement à jour une étiquette, semblable à une barre d'état utilisée par certaines applications.

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()
13
Bryan Oakley

Vous pouvez vous référer à ceci - HoverClass

C'est exactement ce dont vous avez besoin. Ni plus ni moins

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

Exemple d'application utilisant 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()

Capture d'écran:

Testing hoverbox

10
Alok

Je pense que cela répondrait à vos besoins.

Voici à quoi ressemble la sortie:

the output

Tout d'abord, j'ai créé une classe nommée ToolTip qui a les méthodes showtip et 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)

Le widget est l'endroit où vous souhaitez ajouter le conseil. Par exemple, si vous souhaitez obtenir une astuce lorsque vous survolez un bouton, une entrée ou une étiquette, l'instance de celle-ci doit être fournie au moment de l'appel.

Note rapide : le code ci-dessus utilise from tkinter import * Qui n'est pas suggéré par certains programmeurs là-bas, et ils ont des points valides. Vous voudrez peut-être apporter les modifications nécessaires dans ce cas.

Pour déplacer le conseil à l'emplacement souhaité, vous pouvez modifier x et y dans le code. La fonction CreateToolTip() permet de créer facilement cette astuce. Passez simplement le widget et la chaîne que vous souhaitez afficher dans la boîte à astuces à cette fonction, et vous êtes prêt à partir.

Voici comment vous appelez la partie ci-dessus:

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

N'oubliez pas d'importer le module si vous enregistrez le plan précédent dans différents fichiers python, et n'enregistrez pas le fichier sous CreateToolTip ou ToolTip pour éviter confusion.

6
Bunny

J'ai une solution très hacky mais elle a certains avantages par rapport aux réponses actuelles, donc j'ai pensé que je la partagerais.

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)

Le seul vrai problème avec cela est qu'il laisse derrière lui une petite boîte qui éloigne le focus de la fenêtre principale Si quelqu'un sait comment résoudre ces problèmes, faites le moi savoir

0
Supamee

La meilleure façon que j'ai trouvée pour créer une fenêtre d'aide contextuelle est d'utiliser le tix.Balloon. Je l'ai modifié ci-dessous pour le rendre plus joli et montrer un exemple (notez l'utilisation de 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()


ANCIENNE RÉPONSE:

Voici un exemple utilisant <enter> et <leave> comme @bryanoakley l'a suggéré avec un niveau supérieur (avec overridedirect défini sur true). Utilisez le hover_timer classe pour une utilisation facile. Cela nécessite le widget et le texte d'aide (avec un argument de délai facultatif - 0,5 s par défaut) et peut être facilement appelé simplement en lançant la classe puis en l'annulant.

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
Minion Jim