Python TKinter 下拉菜单问题

Posted

技术标签:

【中文标题】Python TKinter 下拉菜单问题【英文标题】:Python TKinter dropdown menu issue 【发布时间】:2012-12-08 03:57:04 【问题描述】:

在下面的代码中,我遇到了self.dmenu1.bind("<Button-1>", self.branches) 行的问题,如果有人能给我指明正确的方向,我将不胜感激。

我希望在下拉菜单中选择一个选项,它会更改其下方列表框中的排序。 然而实际发生的情况是,在我做出选择之后,我必须再单击一次下拉框,然后排序才会生效。

这不是用户期望下拉菜单起作用的方式。我已经发布了完整的代码,你可以看到我对这一切都很陌生,但这是一个很好的学习挑战:)

提前感谢您的帮助。 问候,

from tkinter import *

ALL = N+S+W+E

users = ['Fred Asus','Tom Yahoo','Jessy Samsung','Jermain Sony','Nikki Nikon',
        'Ian IBM','Elena Google','Rob Braun','Tammy Tonika','James Intel',
        'Murphy Richards','Daniel Denon']

branchlst = 138:'Driving - St Albans', 170:'Brighton', 271:'Driving - Birmingham',
           330:'Leeds', 680:'Edinburgh'

class Application(Frame):

    def __init__(self, master=None):
        #initiate the primary window.
        Frame.__init__(self, master)
        self.master.rowconfigure(0, weight=1)
        self.master.columnconfigure(0, weight=1)

        self.rowconfigure(0, weight=0)
        self.rowconfigure(1, weight=0)
        self.rowconfigure(2, weight=3)
        self.columnconfigure(0, weight=0)
        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=1)

        self.grid(sticky=ALL)
        self.frameset()

    def frameset(self):
        #define and setup frames with columns and rows for widgets
        #Colours added to framesets to help designing layout. delete them
        self.Frame1 = Frame(self)   # D
        self.Frame2 = Frame(self, bg='blue')  # E
        self.Frame3 = Frame(self)              # L
        self.Frame4 = Frame(self, bg='blue')  # E
        self.Frame5 = Frame(self) # T
        self.Frame6 = Frame(self) # E colours

        self.Frame1.rowconfigure(0,weight=0)
        self.Frame2.rowconfigure(0,weight=0)
        self.Frame3.rowconfigure(0,weight=1)
        self.Frame4.rowconfigure(0,weight=1)
        self.Frame5.rowconfigure(0,weight=1)
        self.Frame6.rowconfigure(0,weight=1)

        self.Frame1.columnconfigure(0,weight=0)
        self.Frame2.columnconfigure(0,weight=0)
        self.Frame3.columnconfigure(0,weight=1)
        self.Frame4.columnconfigure(0,weight=1)
        self.Frame5.columnconfigure(0,weight=1)
        self.Frame6.columnconfigure(0,weight=1)

        self.Frame1.grid(row=0, column=0, rowspan=1, columnspan=1, sticky=ALL)
        self.Frame2.grid(row=0, column=1, columnspan=2, sticky=ALL)
        self.Frame3.grid(row=1, column=0, rowspan=2, sticky=ALL)
        self.Frame4.grid(row=1, column=1, columnspan=2, sticky=ALL)
        self.Frame5.grid(row=2, column=1, rowspan=1, columnspan=1, sticky=ALL)
        self.Frame6.grid(row=2, column=2, sticky=ALL)


        label4a = Label(self.Frame4, text='table1', bg='orange')
        label4b = Label(self.Frame4, text='table2', bg='yellow')
        label4a.pack(side=LEFT)
        label4b.pack(side=RIGHT)

        self.objects()

    def objects(self):
        var = StringVar()
        var.set('Name')
        self.dmenu1 = OptionMenu(self.Frame1, var,'Costcode','Name')
        self.dmenu1.pack(side=TOP, fill=BOTH)
        self.dmenu1.bind("<Button-1>", self.branches)

        self.f3ListBox = Listbox(self.Frame3, selectmode='single')
        #self.branches()
        self.f3ListBox.grid(sticky=ALL)
        self.f3ListBox.bind("<Button-3>", self.f1handler1)

        f5ListBox = Listbox(self.Frame5, selectmode='single')
        n = 0
        for item in users:
            f5ListBox.insert(n,item)
            n += 1
        f5ListBox.grid(sticky=ALL)

        f6ListBox = Listbox(self.Frame6, selectmode='single')
        f6ListBox.insert(1,'S123456') # DELETE
        f6ListBox.insert(2,'S313414') # DELETE
        f6ListBox.insert(3,'S573343') # DELETE
        f6ListBox.grid(sticky=ALL)    


    def f1handler1(self, event):
        """Creates a popup menu for the alternative mouse button.
        Edit this to add more options to that popup"""
        select = lambda: self.f3ListBox.delete(ACTIVE)
        popup = Menu(self, tearoff=0)
        popup.add_command(label='Quit',command=self.quit)
        popup.add_command(label='delete',command=select) #add more of these for more options


        try:
            popup.post(event.x_root, event.y_root)
        except:
            pass
    def branches(self, event):
        self.f3ListBox.delete(0,END)
        n = 0
        if self.dmenu1.cget('text') == 'Costcode':
            cc = sorted(list(branchlst.keys()))
            for item in cc:
                self.f3ListBox.insert(n,str(item)+' '+branchlst[item])
                n += 1
        elif self.dmenu1.cget('text') == 'Name':
            bb = sorted(list(branchlst.values()))
            for item in bb:
                for name,val in branchlst.items():
                    if item == val:
                        self.f3ListBox.insert(n,item+' '+str(name))

root = Tk()
app = Application(master=root)
app.mainloop()

【问题讨论】:

【参考方案1】:

StringVar 类有一个trace 方法,它允许您将回调函数附加到它。当变量值发生变化时,该函数将被调用。

在您的代码中,将此行添加到 objects 方法中的 var.set('Name') 行下方。

var.trace('w', self.branches)

这将导致每当var 更改时调用self.branches。它将使用三个参数调用,因此您需要将分支的定义更改为:

def branches(self, name, index, mode):

您还应该删除 self.dmenu1.bind("&lt;Button-1&gt;", self.branches) 行,因为它现在是多余的。

【讨论】:

Kevin,您的回复在实施后确实效果很好,我现在发现了 var.trace 方法,谢谢。我觉得@mmgp 提供了一个更适合我和我现在理解水平的答案。感谢你的回复。非常感谢和信息丰富。 @Zenettii 我会给你一个免费的提示:尽可能坚持使用 Python。我的意思是这个变量跟踪实际上是由 Tcl 完成的,而不是由 Python 完成的。由于两种语言之间的根本差异,Python 和 Tcl 之间的桥梁给了关于数据类型的各种惊喜。如果您可以在 Python 中使用 Tkinter 时远离 Tcl 变量,那就去做吧。【参考方案2】:

我更喜欢理解问题并解决问题的途径,所以让我们来看看吧。在您的代码中,您有 self.dmenu1.bind("&lt;Button-1&gt;", self.branches)

你有没有问过自己这个事件是什么时候触发的?当您单击OptionMenu 时会触发它。这意味着当前选项将是使用的选项。因此,假设选项“a”处于活动状态并且您更改为选项“b”。此选择更改不会触发 Button-1 事件,但是当您再次单击 OptionMenu 时,它将触发,然后小部件将“b”作为当前选项。

您在代码中的实际内容是:

self.dmenu1 = OptionMenu(self.Frame1, var,'Costcode','Name',
                         command=self.branches)

并且前面提到的绑定可以安全地消除。只要在您的OptionMenu 上进行选择,刚刚添加的command 选项就会调用某个函数。除了此更改之外,您可能还希望在程序启动时填充它下面的列表框。为此,请在定义 self.f3ListBox 后调用 self.branches(None)

【讨论】:

谢谢你。这对我的理解帮助很大。我的印象是绑定是必需的,您好心回答了我想知道的另一个问题,self.branches(None),我没有意识到 None 可以在这里工作。非常感谢,吸取了教训:) 还有其他地方/情况需要绑定,但这次不是。继续你的 Tkinter 开发,你会发现这些其他情况:)

以上是关于Python TKinter 下拉菜单问题的主要内容,如果未能解决你的问题,请参考以下文章

Python3 tkinter基础 Menu add_checkbutton 多选的下拉菜单

Python3 tkinter基础 Menu add_radiobutton 单选的下拉菜单

你如何在 Tkinter 中制作下拉菜单?

python tkinter 如何做一个如下所示的下拉菜单

Python3 Tkinter基础 Menubutton 设置一个按钮 点击按钮出现下拉菜单

Python3 Tkinter基础 Menu 点击下拉菜单中的一项 这项前面出现对勾(不可以多选) add_radiobutton