Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tkinter radio button initialization bug

Tags:

python

tkinter

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

like image 933
user600295 Avatar asked Feb 21 '11 21:02

user600295


3 Answers

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.

like image 185
Jeffrey Martinez Avatar answered Nov 08 '22 14:11

Jeffrey Martinez


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.

like image 26
Dušan Atanacković Avatar answered Nov 08 '22 15:11

Dušan Atanacković


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.

like image 1
Lukasavicus Avatar answered Nov 08 '22 13:11

Lukasavicus