单选按钮悬停错误,无法通过阻止垃圾收集 tkinter 来解决

radiobutton hovering bug that cannot be solved by preventing it from garbage collection tkinter

我正在浏览一些代码并遇到了这个,当将鼠标悬停在单选按钮上时,Checkbutton 被选中,我尝试保留对变量的引用(不需要,因为已经是全局的),但它失败了。

代码如下:

from tkinter import *

root = Tk()

text_colors = ['1','2','3','4','5','6','7','8']
rads = []
for i in range(4):
    for j in range(2):
        col = text_colors.pop()
        val = IntVar()
        root.garbage_saver = val
        rads.append(Radiobutton(root,text=col,fg='red',variable=val))
        rads[-1].grid(row=i,column=j)

root.mainloop()

此错误可在我的 Windows 10、Python 3.9

上重现

这是没有错误的代码:

from tkinter import *

root = Tk()

text_colors = ['1', '2', '3', '4', '5', '6', '7', '8']
rads = []

val = IntVar(root)
val.set(-1)

for i in range(4):
    for j in range(2):
        col = text_colors.pop()
        rbutton = Radiobutton(root, text=col, fg="red", variable=val, value=i*2+j)
        rbutton.grid(row=i, column=j)
        rads.append(rbutton)

root.mainloop()

基本上,您会用最后一个 IntVar 对象覆盖 root.garbage_savervar 变量,因此它会破坏前一个对象。该行为是由 tkinter 在垃圾收集时取消设置变量引起的。

tkinter.__init__.py:

class Variable:
    """Class to define value holders for e.g. buttons.

    Subclasses StringVar, IntVar, DoubleVar, BooleanVar are specializations
    that constrain the type of the value returned from get()."""
    _default = ""
    _tk = None
    _tclCommands = None
    def __del__(self):
        """Unset the variable in Tcl."""
        if self._tk is None:
            return
        if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
            self._tk.globalunsetvar(self._name)
        if self._tclCommands is not None:
            for name in self._tclCommands:
                #print '- Tkinter: deleted command', name
                self._tk.deletecommand(name)
            self._tclCommands = None

注意 tkinter 调用 self._tk.globalunsetvar(self._name) 告诉 tcl/tk 取消设置变量。

Tcl/Tk docs: "The widget sets the alternate state whenever the linked -variable is unset."