Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
238 views
in Technique[技术] by (71.8m points)

python - 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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...