问题
I wanted to make a simple pop-up message that shows the name of the options represented as icons when the mouse enters them and hide when it leavs.
This icons are images within buttons, and I have been able to show the message when entering the button using a Menu widget, but when the mouse leaves the button it dose NOT unpost, unless there's a klick.
I tryed deleting the cascade, but the same happens, the difference is that he menu at that moment has no text.
I tryed to .destroy()
the menu object as well, but it does nothing.
from tkinter import *
from tkinter import ttk
from pyautogui import position
Raiz = Tk()
Raiz.title("Mi app")
Ancho = Raiz.winfo_screenwidth()
Alto = Raiz.winfo_screenheight()
Raiz.geometry("{}x{}".format(Ancho, Alto))
Raiz.config(bg="#F4F4F4")
def Despliega(Texto):
global MenuDesplegable
MenuDesplegable = Menu(master=None, tearoff=0, activebackground='#F0F0F0')
MenuDesplegable.add_cascade(label=Texto)
MenuDesplegable.post(position().x, position().y)
def Repliega():
global MenuDesplegable
#MenuDesplegable.delete(0) -- dosen't work
#MenuDesplegable.unpost() -- dosen't work
#MenuDesplegable.destroy() -- dosen't work
Raiz.columnconfigure(0, weight=1)
BarraMenu = Frame(Raiz, bg="light grey", height=50, width="{}".format(Ancho),
bd="4", relief="groove")
BarraMenu.grid(row=0, column=0, sticky="nsew")
I_Abrir = PhotoImage(file="Abrir.png")
B_Abrir = Button(BarraMenu, bg="light grey", image=I_Abrir, bd=0)
B_Abrir.grid(row=0, column=0, padx=10)
B_Abrir.bind('<Enter>', lambda event: Despliega('Abrir'))
B_Abrir.bind('<Leave>', lambda event: Repliega())
I_Nuevo = PhotoImage(file="Nuevo.png")
B_Nuevo = Button(BarraMenu, bg="light grey", image=I_Nuevo, bd=0)
B_Nuevo.grid(row=0, column=1, padx=10)
B_Nuevo.bind('<Enter>', lambda event: Despliega('Nuevo'))
B_Nuevo.bind('<Leave>', lambda event: Repliega())
Raiz.mainloop()
It would be nice if someone understood why it dosen't work as I descrived. Also, if someone knows a way to show the message with a littel delay, please, show it o me.
回答1:
You can create a class that takes a widget and a message as parameters, and then apply to any widget requiring the info.
import tkinter as tk
root = tk.Tk()
class CreateToolTip:
def __init__(self, widget, text='widget info'):
self.waittime = 100 #500 #miliseconds
self.wraplength = 180 #pixels
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.leave)
self.widget.bind("<ButtonPress>", self.leave)
self.id = None
self.tw = None
def enter(self, event=None):
self.schedule()
def leave(self, event=None):
self.unschedule()
self.hidetip()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.showtip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.widget.after_cancel(id)
def showtip(self, event=None):
x = y = 0
x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 40
# creates a toplevel window
self.tw = tk.Toplevel(self.widget)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(self.tw, text=self.text, justify='left',
background="#ffffff", relief='solid', borderwidth=1,
wraplength = self.wraplength)
label.pack(ipadx=1)
def hidetip(self):
tw = self.tw
self.tw= None
if tw:
tw.destroy()
a = tk.Button(root,text="Something")
a.pack()
CreateToolTip(a,"This is something button")
b = tk.Button(root,text="Another")
b.pack()
CreateToolTip(b,"This is another button")
root.mainloop()
回答2:
Aparently Windows does not recognize the unpost command (for more information: https://www.tcl.tk/man/tcl8.6/TkCmd/menu.htm#M45 & https://wiki.tcl-lang.org/page/How+do+you+unpost+a+menu)
The solution that I've come with is the the followin:
def Despliega(Texto):
global MenuDesplegable
MenuDesplegable = Label(master=None, bg="#F4F4F4", text=str(Texto),relief='ridge', bd=3, width=len(Texto)+2)
x = Raiz.winfo_pointerx()
y = Raiz.winfo_pointery()
abs_coord_x = Raiz.winfo_pointerx() - Raiz.winfo_rootx()
abs_coord_y = Raiz.winfo_pointery() - Raiz.winfo_rooty()
MenuDesplegable.place(x = abs_coord_x, y = abs_coord_y)
def Repliega():
global MenuDesplegable
MenuDesplegable.destroy()
This tow methods places a Tkinter.Label
at the mouse position at the time that the coursor gets into one of the buttons, and destoys it when it leves the button.
It works well, but it does rare things when the mouse is placed on top of the Label.
来源:https://stackoverflow.com/questions/57144485/ttk-menu-wont-unpost