Tkinter optionmenu won't allow me to pass the frame of the object I want to update depending on the choice made

Question:

I have a list of frames that each have an optionmenu with the same list of choices. When a choice is made in that specific optionmenu, I’ve only been able to get the last entry widget to change, not the corresponding one. In other widgets I’ve been able to use something like “lambda F=F:function(args)” but that isn’t working here.

I’ve tried a trace on the variable in the option menu, I’ve tried wrapper functions, I’ve tried every combination of a lambda in the command section of the optionmenu widget. Most approaches create errors, some, like the one attached, modify the bottom frame/entry but not the correct corresponding one.

This doesn’t seem like it should be too hard. If the option for the top frame selected is “Continuous” or “Discrete”, the entry next to it should be ‘normal’ state with “?..?” in the box, if it is categorical, it should change to be ‘disabled’ with no contents. I could do this easily if I could somehow pass the Frame dictionary key to the “updateOnChange” function, but I can’t, it only allows a single argument to be passed and that is the string value of mType[F].

from tkinter import *

def updateOnChange(type):
    print(type)
    if type.upper()=='CATEGORICAL':
        rangeEntry[F].delete(0,END)
        rangeEntry[F].config(state='disabled')
        print("runCat")
    else:
        rangeEntry[F].config(state='normal')
        rangeEntry[F].delete(0,END)
        rangeEntry[F].insert(0,'?..?')
        print("runCont")

mType={}
frame={}
om={}
rangeEntry={}
root=Tk()
Frames=['FrameOne','FrameTwo']
miningTypes=['Continuous','Categorical','Discrete']
for F in Frames:
    mType[F]=StringVar(root)
    if F=='FrameOne':
        mType[F].set("Continuous")
    else:
        mType[F].set("Categorical")
    frame[F]=Frame(root,borderwidth=3,relief=SUNKEN)
    frame[F].pack(side=TOP,fill=X)
    rangeEntry[F]=Entry(frame[F],width=20,font=("Arial",12))
    om[F]=OptionMenu(frame[F],mType[F],*miningTypes,command=updateOnChange)
    om[F].pack(side=LEFT)
    rangeEntry[F].pack(side=LEFT)
mainloop()


``
Asked By: jbokusky

||

Answers:

Your updateOnChange function hard-coded the entry to be changed as rangeEntry[F], which points to the last Entry widget created in your for loop. To properly associate each entry, you should pass the widget as a parameter:

def updateOnChange(type, entry):
    if type.upper()=='CATEGORICAL':
        entry.delete(0,END)
        entry.config(state='disabled')
        print("runCat")
    else:
        entry.config(state='normal')
        entry.delete(0,END)
        entry.insert(0,'?..?')
        print("runCont")

And then pass the parameter in your command:

om[F]= OptionMenu(frame[F],mType[F],*miningTypes,command=lambda e, i=rangeEntry[F]: updateOnChange(e, i))
Answered By: Henry Yik
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.