如何在 Python 中的 Tkinter 小部件上使用 Tcl/Tk 绑定功能?

how to use Tcl/Tk bind function on Tkinter's widgets in Python?

虽然 Tkinter 源自 Tcl/Tk,但不如 Tcl/Tk 完整。 Tcl/Tk 绑定函数有一些 tkinter 没有的属性(例如 %d returns 来自事件 https://www.tcl.tk/man/tcl8.4/TkCmd/bind.htm#M24 的详细字段)。
Tcl/Tk 脚本可由 python 中的 "eval" 函数使用,但我不知道如何在 Tcl/Tk 脚本中声明 tkinter 小部件。

那么我如何在 Tkinter 小部件上使用此函数及其属性?

如果您询问如何创建使用 "data" 字段的绑定(即:%d 替换),您将不得不调用一些 tcl 代码来实现。这需要两个步骤:创建调用 python 函数的 tcl 命令,并使用 tcl 将事件绑定到该函数。

首先,让我们创建一个 python 程序,它可以创建一个事件并设置 "data" 字段(假设存在一个名为 root 的全局变量,我们将稍后创建)。调用此函数时,它将生成一个自定义事件,其中数据字段由字符串填充。

def create_custom_event():
    root.event_generate("<<Custom>>", data="Hello, world")

接下来,让我们创建一个程序,在按下按钮时调用该函数

import tkinter as tk
root = tk.Tk()
button = tk.Button(root, text="click me", command=create_custom_event)
button.pack(side="top", padx=20, pady=20)
root.mainloop()

接下来我们需要创建一个在事件生成时调用的函数。我们计划设置 data 字段,因此此函数必须接受一个值,该值是 %d 替换的值。

def callback(detail):
    print("detail: %s" % detail)

因为您想使用 %d 替换,所以我们必须通过 Tcl 创建绑定。但是,tcl 不会自动知道我们的 python 函数,因此我们必须向 tcl 注册该函数。然后只需通过 tcl 接口调用 bind 来设置绑定即可。

然后,第一步是注册回调。我们已经创建了这个函数,我们只需要创建一个调用这个函数的 tcl 函数。幸运的是,tkinter 为我们提供了一种使用 register 命令来做到这一点的方法。你这样使用它:

cmd = root.register(callback)

这需要一个 python 函数(在本例中为 callback),并创建一个将调用该函数的 tcl 命令。 register returns 我们存储在名为 cmd 的变量中的那个 tcl 命令的名称(名称无关紧要)

接下来,我们需要通过Tcl设置一个绑定来调用这个命令。如果我们在实际的 tcl 脚本中执行此操作,命令将如下所示(其中“.”代表根 window):

bind . <<Custom>> {callback %d}

python 等价物是这样的:

root.tk.call("bind", root, "<<Custom>>", cmd + " %d")

请注意 call 的参数与 tcl 参数之间存在 1:1 对应关系。方便的是,tkinter 小部件的默认字符串表示是内部 tcl 名称,因此我们可以在调用中直接使用小部件(尽管迂腐地,也许我们应该使用 str(root))。

把它们放在一起给我们这个,当你点击按钮时它会打印出 "detail: Hello, world":

import tkinter as tk

def callback(detail):
    print("detail: %s" % detail)

def create_custom_event():
    root.event_generate("<<Custom>>", data="Hello, world")

root = tk.Tk()

button = tk.Button(root, text="click me", command=create_custom_event)
button.pack(side="top", padx=20, pady=20)

cmd = root.register(callback)
root.tk.call("bind", root, "<<Custom>>", cmd + " %d")

root.mainloop()