Tkinter单选按钮初始化错误

10
如果我在一个函数中放置单选按钮并绘制它们,第一次绘制时,你不能悬停在它们上面而不让它们看起来像是全部被选中了。
同样的代码如果不在函数中展示,则不会表现出这种行为。
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() 

一旦您做出选择,这种情况将不会再次发生。

我做错了什么吗?因为我的代码必须放在一个函数中,是否有解决方法!

这是我在 Tkinter 中发现的第二个基本 bug。是否有更好的 Python GUI 开发工具可选?

备注:我正在使用 Python 2.7 版本。


2
如果您不想使用Tkinter,可以尝试PyQt4wxPythonPyGTK - Artur Gaspar
6个回答

14

您存储变量对象的位置(在您的情况下为StringVar v)必须持续存在,以避免出现这种奇怪的行为。我猜测我们看到这种行为是因为v超出了范围,发生了某些错误。除了使用全局变量外,我想不到其他从函数中实现此操作的方法。

损坏的代码:

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()

修复示例:

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()

如果你有多个需要持久化的变量,可以传入一个列表或字典。同时,如果“必须在函数中”是一项作业要求,请考虑将代码封装在一个类中。我不是tk专家,但这似乎是组织代码的首选方式。

修复示例 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()

注意到一个小变化

app = App(master)

这是必需的,以防止 App 实例被过早垃圾回收。这将有效地将 self.radio_var 从作用域中移除,我们又回到了起点。


4

试试这个,这对我有效。

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()

制作您的单选按钮,然后您将获得应有的单选按钮。

这对我有用,是你未来6年的经验!至少十年了,真不敢相信他们还没有修复这个漏洞。 - Tam

2
我知道时间已经过去了很久,但我尝试了这里展示的所有策略,但它们都对我无效。对我有效的方法是简单地"重新编写"鼠标移动事件的事件处理程序。这不是一个完美的解决方案,因为我会向终端输出某些垃圾信息,但是在我的特定情况下,这不是问题。
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.:您无需重新编写所有函数,只需重写其中一个即可。


0

我找到了一个解决方案,它实际上运行得很好,所以也许对其他像我一样绝望的人有帮助。

首先,我创建了单独的radioButtons,这样我可以快速访问变量名;最后,我将未选中小部件的鼠标“离开”事件绑定到“break”(这样做就取消了小部件的正常行为,即当鼠标悬停在单选按钮上时,它不会执行任何操作)。 到目前为止,我没有遇到任何问题,它工作得很好。 下面是一个示例代码:

...

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")


...

注意:这里我使用了网格,但你可以简单地将其更改为包装或放置


0
我成功地解决了这个问题,只需在所有单选按钮中添加state=NORMAL即可。我知道这是默认行为,但似乎tkinter有时会忘记它。

-1

我承认你发现了一个相当奇怪的“故障”。我不完全准备称之为故障,因为我不确定是什么导致了它。它似乎源自于变量参数。解决方法是,不要获取变量,而是通过.cget('text')方法获取小部件的实际文本。

我对你的App函数所做的更改:

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. 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接