'Tkinter radio button initialization bug
If I put a radio button in a function and draw them; the first time they are drawn you cannot hover over them without making them look like they are all selected.
The same code out of a function does not exhibit this behaviour.
from Tkinter import *
def App(master):
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron = 1 )
b.pack(side = LEFT)
if __name__ == '__main__':
master = Tk()
App(master)
#The following code chunk is the same as that in App()
#------------------------
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to Not look selected as expected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron = 1 )
b.pack(side = LEFT)
#------------------------
mainloop()
Once you have made a selection this does not happen again. Am I doing something wrong? Is there a workaround, because my code has to be in a function!
This is the second elementary bug I have found in Tkinter. Is there something better for Python GUI development?
ps: I'm using python 2.7
Solution 1:[1]
The place where you store the variable object (StringVar, v, in your case) must persist so that this odd behavior wont show up. My guess is we're seeing this behavior because v, goes out of scope, something is going wrong. Aside from using a global, I can't think of a way to do this from a function.
Broken code:
from Tkinter import *
def App(master):
v = StringVar()
v.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
App(master)
mainloop()
Example Fix:
from Tkinter import *
def App(master, radio_var):
radio_var.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=radio_var, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
radio_var = StringVar()
App(master, radio_var)
mainloop()
Consider that if you have more than one variable that needs to persist you can pass in a list or dictionary of variables.
Also, just in case "has to be in a function" is a homework assignment requirement, consider wrapping the code in a class. I'm not a tk expert, but that would seem the preferred manner of organizing your code.
Example fix 2:
from Tkinter import *
class App(object):
def __init__(self, master):
self.radio_var = StringVar()
self.radio_var.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=self.radio_var, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
app = App(master)
mainloop()
Notice a minor change in
app = App(master)
This is required so that the App instance is not garbage collected prematurely. This would effectively pull the self.radio_var out of scope and we're back at square one.
Solution 2:[2]
Try this, it works for me.
v = 0 # this is global variable
def some_function():
global v
v = IntVar()
v.set(0)
rb1 = Radiobutton (parent, variable = v, value = 0)
rb1.pack()
rb2 = Radiobutton (parent, variable = v, value = 1)
rb2.pack()
Make your radio buttons and then you get your radio buttons as they should.
Solution 3:[3]
I know that passed a lot of time, but I've tried all the strategies showed here and none of them work with me. What worked for me was simple "rewrite" the event handler for mouse motion event. It's not a perfect solution because I print some kind of garbage to the terminal, but well, this is not a problem in my particular case.
server_name = IntVar()
server_name.set(1)
server_name_rb_1 = Radiobutton(container_3, text="Server", variable=server_name, value=1)
server_name_rb_1.select()
server_name_rb_1.pack()
server_name_rb_2 = Radiobutton(container_3, text="Local", variable=server_name, value=2)
server_name_rb_2.deselect()
server_name_rb_2.pack()
server_name_rb_2.bind('<Motion>',lambda e: print(str(server_name.get())) )
P.S.: You don't need to rewrite all functions just rewrite one of them must be enough.
Solution 4:[4]
i was able to fix it by simply adding state=NORMAL, to all the radio buttons. i appreciate that this is the default behavior but it looks like that tkinter kind of doesn't remember this.
Solution 5:[5]
I found a solution that it actually works fine so maybe it can be helpful for other desperate people like me.
First thing, I created separate radioButtons so that I can rapidly access to the variable names; finally, I binded the mouse "Leave" event, of the unselected widget, to "break" (doing this I undid the normal behaviour of the widget aka when the mouse is over the radio button now it does nothing). As to now, I'm not having any problems and it works just fine. Below, an example code:
...
self.__var__ = IntVar()
self.__var__.set(2)
unselected_btn = Radiobutton(self.__frame__,
text="One", padx=20,
variable=self.__var__,
value=1
)
unselected_btn.grid(row=0, column=1, sticky=E)
selected_btn = Radiobutton(self.__frame__,
text="Two",
padx=20,
variable=self.__var__,
value=2
)
selected_btn.grid(row=0, column=0, sticky=W)
unselected_btn.bind("<Leave>", lambda e: "break")
...
NOTE: here I used grid, but you can simply change it to pack or place
Solution 6:[6]
I will admit you found quite an odd "glitch". I'm not entirely prepared to call it a glitch seeing as I'm not sure what is causing it. It seemed to be stemming from the variable, argument. A work around would be instead of getting the variable, get the actual text of the widget via the .cget('text') method.
My changes to your App function:
def App(master):
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, value=mode, indicatoron = 1 ) # I omitted the variable argument, which seemed to be the root of your troubles.
b.pack(side = LEFT)
b.deselect() # manually deselects each instance of the Radiobutton.
b.select() # manually selects a default instance of the Radiobutton.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | Lukasavicus |
| Solution 4 | |
| Solution 5 | |
| Solution 6 | rectangletangle |
