Tkinter changing entry state based on radiobutton

Viewed 1278

I have four radio buttons. Underneath these four button is an Entry widget. I am trying to make this Entry widget only become available to type into when the last radio button is selected. The gui is in a class, as you can see in the code below:

class Gui:
    def __init__(self):
        pass

    def draw(self):
        global root

        if not root:
            root  = tk.Tk()
            root.geometry('280x350')

            self.type = tk.StringVar()
            self.type_label = tk.Label(text="Game Mode")
            self.name_entry = tk.Entry()
            self.name_entry.configure(state="disabled")
            self.name_entry.update()
            self.type_entry_one = tk.Radiobutton(text="Garage", value="garage", variable=self.type, command=self.disable_entry(self.name_entry))
            self.type_entry_two = tk.Radiobutton(text="Festival", value="festival", variable=self.type, command=self.disable_entry(self.name_entry))
            self.type_entry_three = tk.Radiobutton(text="Studio", value="studio", variable=self.type, command=self.disable_entry(self.name_entry))
            self.type_entry_four = tk.Radiobutton(text="Rockslam", value="rockslam", variable=self.type, command=self.enable_entry(self.name_entry))
            
         
            self.type_label.pack()
            self.type_entry_one.pack()
            self.type_entry_two.pack()
            self.type_entry_three.pack()
            self.type_entry_four.pack()
            self.name_entry.pack()

            root.mainloop()

    def enable_entry(self, entry):
        entry.configure(state="normal")
        entry.update()

    def disable_entry(self, entry):
        entry.configure(state="disabled")
        entry.update()





if __name__ == '__main__':
    root = None
    gui = Gui()
    gui.draw()

However, the the self.name_entry is always available to type into. What am I doing wrong. If you still don't understand what is happening then please run this code yourself and you will see.

Thank you very much for your time and I look forward to responses.

2 Answers

The only problemS, I see, your facing here is because your not passing in the value "properly" into the function, when you use (..), your calling the function, so to get rid of that use lambda, like:

self.type_entry_one = tk.Radiobutton(text="Garage", value="garage", variable=self.type, command=lambda: self.disable_entry(self.name_entry))
self.type_entry_two = tk.Radiobutton(text="Festival", value="festival", variable=self.type, command=lambda:self.disable_entry(self.name_entry))
self.type_entry_three = tk.Radiobutton(text="Studio", value="studio", variable=self.type, command=lambda:self.disable_entry(self.name_entry))
self.type_entry_four = tk.Radiobutton(text="Rockslam", value="rockslam", variable=self.type, command=lambda:self.enable_entry(self.name_entry))

When using command=lambda:func(arg), this will get executed only when selecting a radiobutton. That is the point of using a radiobutton, right?

Also notice that when the initial code is run, the entire radiobuttons are selected, I think its probably because of tristate values, to get rid of that there are 2 ways I'm aware of:

  1. Changing the declaration of self.type to:
self.type = tk.StringVar(value=' ')
  1. Or, you could also go on adding an extra option to each radiobutton, tristatevalue=' ', like:
self.type_entry_one = tk.Radiobutton(text="Garage",..,tristatevalue=' ')

But make sure to do just one of the above solution. Take a read here about more on tristate values.

Also keep a note that your not passing in any master window to the widgets, its fine as long as your having just one window, when working with multiple windows, it may get confusing for where the widgets should appear.

Also side-note, if this is the complete code, then if nothing is being done on __init__(), its definition can be removed.

You have the right idea about using the RadioButton to enable/disable the entry widget. Mostly it is your class design that is flawed - it is halfway between OO code, and procedural code...

I fixed the class structure and made it a subclass of tk.Tk so it completely encapsulate your GUI. The name_entry is now enabled only when type_entry_four radio button is selected, and disabled otherwise. I've set that last button to be selected at launch, but you can easily change that; it results in the entry being enabled at launch.
Superfluous variable passing through methods was removed, as was the draw method and the calls to it; all widget creation is now conveniently found in GUI.__init__

import tkinter as tk


class Gui(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry('280x350')

        self.select_type = tk.StringVar()
        self.type_label = tk.Label(self, text='Game Mode')
        self.name_entry = tk.Entry(self)
        self.type_entry_one = tk.Radiobutton(self, text='Garage', value='garage', variable=self.select_type, command=self.disable_entry)
        self.type_entry_two = tk.Radiobutton(self, text='Festival', value='festival', variable=self.select_type, command=self.disable_entry)
        self.type_entry_three = tk.Radiobutton(self, text='Studio', value='studio', variable=self.select_type, command=self.disable_entry)
        self.type_entry_four = tk.Radiobutton(self, text='Rockslam', value='rockslam', variable=self.select_type, command=self.enable_entry)

        self.select_type.set('rockslam')   # select the last radiobutton; also enables name_entry
        
        self.type_label.pack()
        self.type_entry_one.pack()
        self.type_entry_two.pack()
        self.type_entry_three.pack()
        self.type_entry_four.pack()
        self.name_entry.pack()

    def enable_entry(self):
        self.name_entry.configure(state='normal')

    def disable_entry(self):
        self.name_entry.configure(state='disabled')


if __name__ == '__main__':
    
    Gui().mainloop()
Related