在使用 tkinter 时遇到问题,这是我正在做的一个简单示例
import tkinter as tk
from tkinter import ttk
from typing import Any, Callable, Mapping, NamedTuple, Type
values = {
'eat': ('something', 'nice'),
'dirt': ('something', 'else'),
'kid': ('something'), '': tuple()
}
class MyTuple(NamedTuple):
widget: Type[tk.Widget]
config: Mapping[str, Any] = {}
postcommand: Callable[[ttk.Combobox], None] = None
class Reg():
def __init__(self, var):
self.var = var
self.widgets = (
MyTuple(ttk.Label),
MyTuple(ttk.Combobox, {'values': tuple(values.keys()), 'textvariable': self.var}),
MyTuple(ttk.Combobox, postcommand=self.cb),
MyTuple(ttk.Combobox, postcommand=self.other_cb),
)
def display(self, frame: tk.Tk):
for w in self.widgets:
widget = w.widget(frame, **w.config)
widget.pack()
if not w.postcommand is None:
widget['postcommand'] = lambda widget=widget : w.postcommand(widget)
def cb(self, combobox: ttk.Combobox):
combobox['values'] = values[self.var.get()]
def other_cb(self, combobox: ttk.Combobox):
combobox['values'] = tuple(values.keys())
class GUI(tk.Tk):
def __init__(self):
super().__init__()
v = tk.StringVar()
reg = Reg(v)
reg.display(self)
_ = GUI()
_.mainloop()
运行此程序时,似乎 w 被覆盖并且为所有组合框设置了相同的后置命令。我尝试将显示更改为如下所示:
from copy import deepcopy
...
def display(self, frame: tk.Tk):
for w in self.widgets:
widget = w.widget(frame, **w.config)
widget.pack()
if not w.postcommand is None:
command = deepcopy(w.postcommand)
widget['postcommand'] = lambda widget=widget : command(widget)
我得到错误
TypeError: cannot pickle '_tkinter.tkapp' object
.
我可以使用 deepcopy 的替代方法吗?我所能找到的只是一种覆盖 pickle 函数的方法,但我无法弄清楚如何在这里应用它,因为我什至无法弄清楚什么 tkinter 对象是 deepcopy 的一部分。
或者,有没有办法解决 w 的作用域问题?
(使用 python 3.8.10)
调用
w.postcommand
的 lambda 没有捕获 w
的正确值。如果将循环中的 lambda 更改为此会发生什么?
widget['postcommand'] = lambda widget=widget, w=w: w.postcommand(widget)
进一步阅读:lambda 函数闭包捕获什么?