AZLord
AZLord

Reputation: 3

Creating a drop down menu item selection Python

I am trying to create a drop down selection for a personal program, that auto updates the name being shown based on the selected item.

The thing is, regardless of what is selected or what the default value should be, it always auto changes the selected name to the last option the user can choose and it will not change if another option is selected.

import tkinter
from tkinter import *


def changename(name=str):
    selectedText.set(name)
    mb.grid(row=1,column=1)
def startmainloop(var):
    var.mainloop()



root = Tk()

label0=Label(root,text=".....")
label0.grid(row=0,columnspan=2)

selectedText=StringVar()
selectedText.set("Select Race")
mb=Menubutton(root, textvariable=selectedText,relief=GROOVE)
label1=Label(root, text="RACE")
label1.grid(row=1,column=0)
mb.grid()
mb.menu=Menu(mb, tearoff=0)

mb.menu.add_command(label="Argonian", command=changename("Argonian"))
mb.menu.add_command(label="Khajiit", command=changename("Khajiit"))

mb["menu"]=mb.menu

startmainloop(root)

In the image that would be above, the default that it should be showing is "Select Race". A drop down menu appears after clicking on Select Race with the two command options "Argonian" and "Khajiit". After clicking on the option "Argonian", the "Select Race" label of the menu should change to "Argonian". The option "Khajiit" though seems to be overloading the default and won't change regardless of the user selecting Argonian.

Upvotes: 0

Views: 5955

Answers (1)

martineau
martineau

Reputation: 123463

When you specify the argument value this way command=changename("Khajiit") it calls the function right then and its return value (None) is passed as the argument value. This is a very common Tkinter programming mistake.

Do something like this instead:

mb.menu.add_command(label="Argonian", command=lambda: changename("Argonian"))
mb.menu.add_command(label="Khajiit", command=lambda: changename("Khajiit"))

This will make the values passed as the command argument anonymous functions that call your handler function with the right argument.

An arguably better, as in more generic and less repetitive approach, would be to modify the changename() function so it retrieved the label of the command itself, then you would only need to specify the string once.

In other words, do something like this:

def changename(index):
    name = mb.menu.entrycget(index, "label")
    selectedText.set(name)
    mb.grid(row=1, column=1)

Which would allow you to just pass the index of the item:

mb.menu.add_command(label="Argonian", command=lambda: changename(0))
mb.menu.add_command(label="Khajiit", command=lambda: changename(1))

To avoid needing to explicitly count and hardcode the index of each command, this could be done in a for loop:

for i, label in enumerate(["Argonian", "Khajiit"]):
    mb.menu.add_command(label=label, command=lambda: changename(i))

Upvotes: 1

Related Questions