Search code examples
pythonbuttontkintergetattr

Is it possible to set button state with dynamic name call using getattr and dictionary keys (Python 2.7, Tkinter)?


I'm building a python Tkinter GUI program containing buttons which will be enabled/disabled depending on various situations. For this reason, I want to refer to buttons dynamically rather than using static names. I've built a global dictionary that links button string descriptors to a Boolean value indicating their desired state (populated during widget creation, and dict values altered as required throughout the program), and another dictionary inside a function linking the same string descriptors to the button object names.

Since I wish to change button states dynamically, the object names aren't hardcoded, instead I'm trying to use getattr with a function call returning the object name using the dictionary function described above:

    class WApplication(Frame):
    
        buttonStates={}

...
 
        def buttonDisable(self):
            for i in self.buttonStates:
                #self.Load_Manifest.config(state=DISABLED)   #This line works (i.e., visibly disables the 'Load Manifest' button)
                #print(i+".config(state=DISABLED)")          #Output: Load_Manifest.config(state=DISABLED)
                getattr(self.ObjectDictionary(i),"config(state=DISABLED)")   #AttributeError: Button instance has no attribute 'config(state=DISABLED)'
                
        def buttonEnable(self):   #Similar error encountered as above when debugging this function
            for i in self.buttonStates:
                if self.buttonStates[i]==True:
                    getattr(self.ObjectDictionary(i),"config(state=NORMAL)")
    
...

        def ObjectDictionary(self,string):
            "Function that maps dictionary widget keys to their object titles and returns the mapped object title"
            switcher={
                "Load_Manifest":self.Load_Manifest,
                "Update_Manifest":self.Update_Manifest,
                "Fresh_Manifest":self.Fresh_Manifest,
                "Manifest_Details":self.Manifest_Details
            }
            return switcher.get(string)
            
...
    
        def createWidgets(self):
            self.QUIT = Button(self.master)
            self.QUIT["text"] = "QUIT"
            self.QUIT["fg"]   = "red"
            self.QUIT["command"] =  self.quit
            self.QUIT.place(x=710,y=10,height=50,width=100)
    
            self.Load_Manifest=Button(self.master)
            self.Load_Manifest["text"]="Load Manifest"
            self.Load_Manifest["command"]=self.LoadManifestButton
            self.Load_Manifest.config(state=NORMAL)
            self.Load_Manifest.place(x=10,y=10,height=50,width=110)
            self.buttonStates["Load_Manifest"]=True
            self.buttonActions["Load_Manifest"]=False
    
            self.Update_Manifest=Button(self.master)
            self.Update_Manifest["text"]="Update Manifest"
            self.Update_Manifest["command"]=self.UpdateManifestButton
            self.Update_Manifest.config(state=DISABLED)
            self.Update_Manifest.place(x=130,y=10,height=50,width=120)
            self.buttonStates["Update_Manifest"]=False
            self.buttonActions["Update_Manifest"]=False
            
            self.Fresh_Manifest=Button(self.master)
            self.Fresh_Manifest["text"]="Create New Manifest"
            self.Fresh_Manifest["command"]=self.FreshManifestWindowButton
            self.Fresh_Manifest.config(state=NORMAL)
            self.Fresh_Manifest.place(x=260,y=10,height=50,width=150)
            self.buttonStates["Fresh_Manifest"]=True
            self.buttonActions["Fresh_Manifest"]=False
    
...
    
            self.Manifest_Details=Button(self.master)
            self.Manifest_Details["text"]="Details"
            self.Manifest_Details["command"]=self.ManifestDetailsButton
            self.Manifest_Details.config(state=DISABLED)
            self.Manifest_Details.place(x=10,y=790,height=50,width=100)
            self.buttonStates["Manifest_Details"]=False
            self.buttonActions["Manifest_Details"]=False
    
...

The buttonDisable and buttonEnable functions are called elsewhere in the program in other functions that are run during button presses. I could just stick to hardcoded widget names and put a load of if statements in my buttonEnable function since there may only be four buttons currently, but I plan on expanding the program adding more with additional buttons, and I wish to disable all buttons during an operation to avoid user entry when the program is busy, so therefore dynamic calls are desired.

During the first call of getattr(self.ObjectDictionary(i),"config(state=DISABLED)") an error is raised:

AttributeError: Button instance has no attribute 'config(state=DISABLED)'

But I would have thought .config() is an attribute of a widget object? And the following hardcoded substitute line self.Load_Manifest.config(state=DISABLED) works as intended, which by my understanding of getattr should be equivalent?


Solution

  • I would have thought .config() is an attribute of a widget object?

    Yes, config is an attribute of a widget object. However, the string "config(state=DISABLED)" is not an attribute of a widget object.

    If you want to call the configure method of a widget stored in a dictionary, you do it exactly like you would if the widget was a regular variable. The following example breaks it down into two steps to illustrate the point:

    widget = ObjectDictionary(i)
    widget.configure(state=DISABLED)