简体   繁体   English

Python TKinter下拉菜单问题

[英]Python TKinter dropdown menu issue

In the below code I am having trouble with the line self.dmenu1.bind("<Button-1>", self.branches) , and I'd be really grateful if someone can please set me in the right direction. 在下面的代码中我遇到了self.dmenu1.bind("<Button-1>", self.branches) ,如果有人可以请我指出正确的方向,我真的很感激。

I'm expecting to select the an option in the dropdown menu and it changes the sorting inside the Listbox below it. 我希望在下拉菜单中选择一个选项,它会更改它下面的列表框内的排序。
However what is actually happening, is that after I make my selection, then I have to click the drop down box one more time before the sorting takes effect. 然而实际发生的是,在我做出选择之后,我必须再次单击下拉框,然后排序生效。

This is not how users would expect the dropdown menu to work. 这不是用户期望下拉菜单工作的方式。 I've posted the full code, as you can see I'm new to it all, but it's a nice challenge to learn :) 我已经发布了完整的代码,因为你可以看到我对这一切都很陌生,但学习这是一个很好的挑战:)

Thanks in advance for your help. 在此先感谢您的帮助。
Regards, 问候,

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

I prefer the route of understanding the problem and solving it, so let us go through it. 我更喜欢理解问题和解决问题的路线,所以让我们来看看。 In your code you have self.dmenu1.bind("<Button-1>", self.branches) . 在你的代码中你有self.dmenu1.bind("<Button-1>", self.branches)

Did you ask yourself when is this event actually fired ? 你有没有问自己这个事件什么时候被解雇了? It is fired when you click on the OptionMenu . 单击OptionMenu时会触发它。 This means that the current option will be the one used. 这意味着当前选项将是使用的选项。 So, suppose option "a" was active and you changed to option "b". 因此,假设选项“a”处于活动状态,您更改为选项“b”。 This selection change doesn't fire a Button-1 event, but when you click on your OptionMenu again it will fire and then the widget will have "b" as the current option. 此选择更改不会触发Button-1事件,但是当您再次单击OptionMenu时,它将触发,然后窗口小部件将具有“b”作为当前选项。

What you actually in your code is: 你的代码实际上是:

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

and the earlier mentioned binding can be safely eliminated. 并且可以安全地消除前面提到的绑定。 The just added command option will call a certain function whenever a selection is made on your OptionMenu . 只要在OptionMenu上进行选择,刚添加的command选项就会调用某个函数。 Besides this change, you probably also want to populate the listbox bellow it when the program starts. 除了这个更改,您可能还希望在程序启动时填充列表框。 For that, call self.branches(None) after you have defined self.f3ListBox . 为此,在定义self.f3ListBox之后调用self.branches(None)

The StringVar class has a trace method, which allows you to attach a callback function to it. StringVar类有一个trace方法,允许您将回调函数附加到它。 The function will be called when the variable changes value. 变量更改值时将调用该函数。

In your code, add this line just below the var.set('Name') line in the objects method. 在您的代码中,在objects方法的var.set('Name')行下面添加此行。

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

This will cause self.branches to be called whenever var changes. 这会导致每当var发生变化时都会调用self.branches It will be called with three arguments, so you'll need to change branches' definition to: 它将使用三个参数调用,因此您需要将分支的定义更改为:

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

You should also delete the self.dmenu1.bind("<Button-1>", self.branches) line, as it is now redundant. 您还应该删除self.dmenu1.bind("<Button-1>", self.branches)行,因为它现在是多余的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM