为什么使用StringVar时,Tkinter的单选按钮都会默认选中,而使用IntVar时则不会?

16

以下是一些示例代码,它创建了4个Radiobutton,其中2个使用int,另外2个使用str

from tkinter import *

class test:
    def __init__(self):
        wind = Tk()
        frame1 = Frame(wind)
        frame1.pack()
        self.v1 = IntVar()
        self.v2 = StringVar()

        int1 = Radiobutton(frame1, text = 'int1', variable = self.v1, value = 1,
                           command = self.ipress)
        int2 = Radiobutton(frame1, text = 'int2', variable = self.v1, value = 2,
                           command = self.ipress)

        str1 = Radiobutton(frame1, text = 'str1', variable = self.v2, value = '1',
                           command = self.spress)
        str2 = Radiobutton(frame1, text = 'str2', variable = self.v2, value = '2',
                           command = self.spress)

        int1.grid(row = 1, column = 1)
        int2.grid(row = 1, column = 2)

        str1.grid(row = 2, column = 1)
        str2.grid(row = 2, column = 2)

        str1.deselect() #this didn't fix it
        str2.deselect()

    def ipress(self):
        print('int'+str(self.v1.get()))
    def spress(self):
        print('str'+self.v2.get())

test()

运行后,我有一个看起来像这样的框:。出于某种原因,str 的开始被选中,而 int 的则没有。这是为什么?有解决方法吗?我知道我可以通过使用数字值然后将它们转换为字符串来解决问题,但我想先了解为什么会发生这种情况。Windows 10

编辑:为了澄清,按钮在被点击后仍然正常工作。


就我所知,我在Ubuntu 14.04上看到了类似的行为。 str按钮都被选中,但都变灰了。 - Robᵩ
@Robᵩ 感谢您提供的信息,现在我知道这不是一些奇怪的Windows问题了! - Whud
相关链接:https://mail.python.org/pipermail/tutor/2009-October/072105.html - Robᵩ
从那个线程中,一个解决方法是 self.v2.set(None) - Robᵩ
2个回答

20

在第二组单选按钮中,它们处于“三态模式”下。

根据官方文档1

如果变量的值与tristateValue匹配,则单选按钮将使用三态模式绘制。此模式用于指示混合或多个值。

说明

tristatevalue的默认值是空字符串,StringVar的默认值也是空字符串。对于第二组单选按钮,变量值和tristatevalue相同,因此您会看到“三态模式”。

对于IntVar,变量的默认值为零。tristatevalue仍然是空字符串。这两个值不同,所以小部件不会出现“三态模式”。

要证明这一点,请将第一组单选按钮的tristatevalue设置为零,使其与相关变量的默认值匹配,您将看到它们的外观会变成与第二组相同。

int1 = Radiobutton(..., tristatevalue=0)
int2 = Radiobutton(..., tristatevalue=0)

同样地,您可以将第二组的tristatevalue设置为默认值以外的某个值,以使其看起来像第一组:

str1 = Radiobutton(..., tristatevalue="x")
str2 = Radiobutton(..., tristatevalue="x")

解决方案

处理单选按钮最佳实践是始终确保默认值对应于单选按钮的一个值,除非您确实希望使用“三态模式”。

在您的情况下,您应该将变量初始化为单选按钮中的一个值:

self.v1 = IntVar(value=1)
self.v2 = StringVar(value="1")

...或在您创建它们之后,通过set进行设置:

self.v1.set(1)
self.v2.set("1")

1这个链接指向tcl/tk的手册。Tkinter只是tcl/tk的一个薄包装器,而这份文档是关于小部件行为的权威答案。


1该链接指向tcl/tk的文档页面。Tkinter仅是tcl/tk的一个轻量级封装,本文档是有关小部件行为的权威参考。


1
@CoolCloud:是的。如果你不想要这个效果,但是你想让一个合法的值为空字符串,你需要将tristatevalue设置为其他值。 - Bryan Oakley
谢谢您的澄清,我希望它可以像那样被总结成一两个句子。我的困惑部分是由于 ttk.Radiobutton 在这方面的工作方式不同。同时感谢您提供的用户体验提示,始终保持默认选择(我可以将其设置为“无”)。 - martineau
@martineau: “我自己的困惑部分是由于ttk.Radiobutton在这方面的工作方式不同。” - 这不是真的。它们的工作方式是相同的。 - Bryan Oakley
Bryan:如果您按原样运行问题中的代码tkinter GUI only partially updating until mouse is moved(在我的非OSX系统上),两个ttk.Radiobutton都不会被初始选中。但是,如果您将它们都更改为“plain” tk.RadioButton,则两者都会被选中。这就是我所说的两者行为不同的意思。 - martineau
1
@martineau:我道歉,我误解了。是的,它们的行为不同,因为ttk小部件没有tristatevalue。当你把tristatevalue排除在外时,单选按钮在所有未选择的情况下的行为相同。 - Bryan Oakley
显示剩余6条评论

0

感谢@Bryan Oakley的详细解释。

为了简化起见,一开始将选中“Sam”单选按钮。

self.name = tk.StringVar(value='Sam')  ## is like self.name.set('Sam'), will preset the value as "Sam" 
name_rad1 = tk.Radiobutton(frame, text='Sam', variable=self.name, value='Sam', command=None)
name_rad1.pack(side=tk.LEFT)

开始时没有单选按钮被选中

self.name = tk.StringVar(value=' ')  ## the value will be " ", when you print(self.name.get())
name_rad1 = tk.Radiobutton(frame, text='Sam', variable=self.name, value='Sam', command=None)
name_rad1.pack(side=tk.LEFT)

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