简体   繁体   中英

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.

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

Did you ask yourself when is this event actually fired ? It is fired when you click on the OptionMenu . This means that the current option will be the one used. So, suppose option "a" was active and you changed to option "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.

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

The StringVar class has a trace method, which allows you to attach a callback function to it. 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.

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

This will cause self.branches to be called whenever var changes. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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