为啥使用 StringVar 而不是 IntVar 时 Tkinter 的单选按钮都开始选中?

Posted

技术标签:

【中文标题】为啥使用 StringVar 而不是 IntVar 时 Tkinter 的单选按钮都开始选中?【英文标题】:Why do Tkinter's Radio Buttons all Start Selected When Using StringVar but not IntVar?为什么使用 StringVar 而不是 IntVar 时 Tkinter 的单选按钮都开始选中? 【发布时间】:2017-04-02 18:09:37 【问题描述】:

这是一些示例代码,它创建了 4 个 Radiobuttons,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

编辑:澄清一下,这些按钮在被点击后仍然可以正常工作。

【问题讨论】:

FWIW,我在 Ubuntu 14.04 上看到了类似的行为。 str 按钮都被选中,但都是灰色的。 @Robᵩ 感谢您的信息,现在我知道它不是有线窗口的东西! 相关:mail.python.org/pipermail/tutor/2009-October/072105.html 从该线程中,解决方法是self.v2.set(None) 【参考方案1】:

在第二组单选按钮的情况下,它们以“三态模式”呈现。

根据official documentation1

如果变量的值与 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 的一个薄包装,这个文档是小部件行为方式的明确答案。

【讨论】:

如果您提供指向报价来源的链接,这将对我有所帮助。 但是如何将值设置为'',没办法? @CoolCloud 我不明白这个问题。可以正常设置为空字符串。 @CoolCloud:是的。如果您不想要这种效果,但希望合法值是空字符串,则需要将 tristatevalue 设置为其他值。 @martineau:我很抱歉,我误解了。是的,它们的行为不同,因为 ttk 小部件没有tristatevalue。当您将tristatevalue 排除在等式之外时,单选按钮的行为与所有未选中的按钮相同。【参考方案2】:

感谢@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)

【讨论】:

以上是关于为啥使用 StringVar 而不是 IntVar 时 Tkinter 的单选按钮都开始选中?的主要内容,如果未能解决你的问题,请参考以下文章

OR-Tools MIP Solver - 根据 int 定义目标,而不是 IntVar

Tkinter IntVar 返回 PY_VAR0 而不是值

随笔Java 基于Redis分布式锁

Java中几种常用数据类型之间转换的方法

Tkinter,label内容随多选框变化

Tkinter StringVar 获取所选选项的索引