关于 python:Tkinter 参数通过私有属性传递给处理程序 – 可以吗?

Tkinter argument passing to handler via private attributes - is this OK?

我已经看到了几种将附加参数传递给以不同方式使用package函数的 Tkinter 事件处理程序的解决方案。但是对于在事件中传递一些额外静态数据的简单情况,这样做有什么缺点:

widget.my_private_attribute = my_private_data

并在事件处理程序中从事件中恢复数据:

private_data = event.widget.my_private_attribute

我已经尝试过了,它可以工作,但它没有出现在我见过的任何建议的解决方案中,所以我担心存在一些我不知道的缺点。

这是一个代码示例。重新评论"私人"的含义,我想这是误导。我的意思是"由我组成,不是标准的 tk 属性之一"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
:
:
def onClickPosition(event):
    print"you clicked on", event.widget.grid_position
    if event.widget.cget("bg") =="red":
        event.widget.config(bg="yellow")
    else:
        event.widget.config(bg="red")

root = tk.Tk()

buttonList = []

for i in range(16):
    for j in range(16):
        square = tk.PhotoImage(file="small_square_30x30pix.gif")
        l = tk.Label(root, image=square, borderwidth=0, bg="yellow")
        l.save_image = square
        l.grid_position=(i,j)
        l.bind("<1>", onClickPosition)
        l.grid(row=i, column=j)
        buttonList.append(l)
:
:

"grid_position"是我所指的数据。

我在以下位置看到了解决方案的几种变体:

http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html

但我的做法似乎更简单。


如果值在任何有意义的意义上"属于"小部件,那么是的,不仅将其作为属性附加是安全的,而且是惯用的。

文档中有很多例子。

一方面,每个使用子类化 Frame 的"标准 Tkinter OO 习惯用法"在 __init__ 方法或其他地方添加属性的示例都在做你正在做的事情。您没有子类的事实无关紧要。这是同一个想法。

如果您不购买,请参阅 Tkinter Book 的 PhotoImage 页面:

Note: When a PhotoImage object is garbage-collected by Python (e.g. when you return from a function which stored an image in a local variable), the image is cleared even if it’s being displayed by a Tkinter widget.

To avoid this, the program must keep an extra reference to the image object. A simple way to do this is to assign the image to a widget attribute, like this:

1
2
3
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()

label 不是 label 的子类,它有另一个名为 image 的属性,它只是一个 label,我们只是动态添加了另一个属性,这很好。

这只是我在浏览几秒钟后发现的第一个例子。

但也可以将值"部分"放入回调本身,就像 TigerhawkT3 的回答一样。*

* 事实上,我相信这是让 Guido 相信 Python 3.x 仍然需要 lambdapartial 的论点之一。

那么,您如何决定使用哪一个?这是一个判断电话。以下是我的想法:

  • 该值自然是小部件的成员,还是那种延伸?
  • 您是否已经在使用小部件子类?
  • 您是否有多个回调都需要访问相同的值?
  • 是否可以想象该值还有其他用途(例如,在调试输出中),或者这没有任何意义?

如果这些都没有帮助,那么您最终需要一些默认策略来处理密切判断调用。我喜欢两种方式都写,然后看看哪个看起来更易读,或者想象一下向刚刚注册帮助我维护代码的新手解释这两种方式。如果我仍然无法决定,我在交互式解释器中输入 random.random() < 0.5。 :)


代替:

1
2
3
4
5
6
7
l.grid_position=(i,j)
l.bind("<1>", onClickPosition)

...

def onClickPosition(event):
    print"you clicked on", event.widget.grid_position

尝试:

1
2
3
4
5
6
l.bind("<1>", lambda event, i=i, j=j: onClickPosition(event, i, j))

...

def onClickPosition(event, i, j):
    print"you clicked on", (i,j)

lambda 定义了一个内联匿名函数,允许我们向 onClickPosition() 发送更多参数。将 i=i, j=j 放入定义中会使这些变量在定义函数时而不是在调用函数时解析 - lambda event, i, j 将始终产生 (15, 15).

的点击位置