ttk Menu wont unpost

痞子三分冷 提交于 2021-01-28 08:37:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!