当 ttk.Checkbutton 小部件的状态发生变化时,如何编写后续操作?

How to program follow-up actions when the state of a ttk.Checkbutton widget changes?

我注意到 ttk.Checkbuttons 小部件中的行为。也就是说,任何对此小部件应用的绑定或事件处理程序总是在执行小部件的 command 选项 method/function 之前发生。

意义:

  1. 鉴于ttk.Checkbuttons状态的改变是由command选项method/function执行的,ttk.Checkbuttons['variable']访问的值通过事件处理程序将始终处于旧状态,而不是小部件的 command 选项 method/function.

    定义的新状态
  2. 在事件处理程序中更改 ttk.Checkbuttons['variable'] 的值会扰乱小部件的 command 选项 method/function 的性能。因此,ttk.Checkbuttons['variable'] 应该在小部件的 command 选项 method/function.

    中设置

问题:

鉴于上述情况,那么将事件处理程序绑定到 ttk.Checkbuttons 小部件的目的是什么?

事件处理程序在事件发生时执行,例如当 <ButtonRelease-1> 出现在 ttk.Checkbutton 小部件上时。如果我想根据小部件的状态设计后续操作,我无法通过事件处理程序来实现,因为小部件 instate 尚未更新。解决方法是假设在事件处理程序中获得的小部件 instatevariable 与报告的 instate 或 variable.get() 值相反。然而,这样的做法似乎很冒昧。

当状态发生变化时,我应该如何使用 ttk.Checkbutton 的事件处理程序来编写后续操作?或者我应该不使用它而只使用widgetscommand选项method/function根据widget的状态设计后续动作?

Given the above, so what is the purpose of binding event handlers to a ttk.Checkbuttons widget?

这样您就可以定义自己的行为来覆盖默认行为。

How do should I use event handlers for a ttk.Checkbutton to program follow-up actions when there is a state change?

一种解决方案是使用事件处理程序。相反,在关联变量上设置跟踪。使用变量跟踪,您的回调函数将在设置变量后调用,并且每次值更改时都会调用,即使更改是由事件以外的其他因素完成的。

cb_var = tk.StringVar(value="off")
cb = ttk.Checkbutton(root, variable=cb_var, onvalue="on", offvalue="off", text="Ready?")
cb_var.trace_add('write', callback)

另一个解决方案是在 class 的绑定标签之后创建一个自定义绑定标签。

示例:

cb_var = tk.StringVar(value="off")
cb = ttk.Checkbutton(root, variable=cb_var, onvalue="on", offvalue="off", text="Ready?")
tag = f'custom_{cb}'
cb.bindtags((cb, 'TCheckbutton', '.', tag, 'all'))
cb.bind_class(tag, "<ButtonRelease-1>", callback)

在上述情况下,在小部件 class 上的默认绑定处理该事件后,将在 <ButtonRelease-1> 按钮上调用回调。如果这样做,您还应该以类似的方式添加到空格键的绑定,因为您还可以使用空格键设置复选按钮的值。

有关 bindtags 的另一个示例以及更多关于它们如何工作的讨论,请参阅 this answer to the question How to bind self events in Tkinter Text widget after it will binded by Text widget?. Also, see this answer to the question Basic query regarding bindtags in tkinter

以下是我使用 .trace_add() 方法和使用 自定义绑定标签后的发现和结论 class 共享。希望对tkinter用户有所帮助


Instead, set a trace on the associated variable. With a variable trace, your callback function will be called after the variable has been set, and will be called every time the value changes even when the change is done by something other than an event.

我发现了以下内容:

  1. .trace_add()方法执行的回调总是在ttk.Checkbutton小部件的命令选项method/function.
  2. 之前
  3. 此外,如果ttk.Checkbutton的变量值在ttk.Checkbuttonwidget的命令选项method/function执行过程中发生变化,.trace_add()的回调方法将在小部件的命令选项 method/function 的中间执行。这意味着,.trace_add() 方法的回调可以在一个完整的事件流中执行多次。

为了说明以上几点,下面是我写的一个测试代码的打印语句。测试代码包含 4 个 ttk.Checkbutton 小部件,其控制变量值分别为 B1B2B3B4,当打开时和 '0' 值关闭。单击时,ttk.Checkbutton 将在打开和关闭状态之间切换。但是,必须始终至少打开一个 ttk.Checkbutton。最初,这些按钮都处于打开状态。接下来,依次单击这些按钮以关闭。语句 ### Callback ####.trace_add() 方法回调的打印输出。从 ### Command Trigger 0 ###### Command Trigger 1 ### 的打印输出出现在 ttk.Checkbutton 小部件的 command 选项的 method/function 的开始和结束处。

打印报表:

# B1 ttk.Checkbutton clicked on
#++ Callback ++++  Button variable values = ['0', 'B2', 'B3', 'B4']  3
### Command Trigger 0 ### Button variable values = ('0', 'B2', 'B3', 'B4') 3
cb=.!App.!checkbutton   text=B1   variable=B1 Checkbutton variable
nchosen != 0
### Command Trigger 1 ### Button variable values = ('0', 'B2', 'B3', 'B4') 3

# B2 ttk.Checkbutton clicked on
#++ Callback ++++  Button variable values = ['0', '0', 'B3', 'B4']  2
### Command Trigger 0 ### Button variable values = ('0', '0', 'B3', 'B4') 2
cb=.!App.!checkbutton2   text=B2   variable=B2 Checkbutton variable
nchosen != 0
### Command Trigger 1 ### Button variable values = ('0', '0', 'B3', 'B4') 2

# B3 ttk.Checkbutton clicked on
#++ Callback ++++  Button variable values = ['0', '0', '0', 'B4']  1
### Command Trigger 0 ### Button variable values = ('0', '0', '0', 'B4') 1
cb=.!App.!checkbutton3   text=B3   variable=B3 Checkbutton variable
nchosen != 0
### Command Trigger 1 ### Button variable values = ('0', '0', '0', 'B4') 1

# B4 ttk.Checkbutton clicked on
#++ Callback ++++  Button variable values = ['0', '0', '0', '0']  0
### Command Trigger 0 ### Button variable values = ('0', '0', '0', '0') 0
cb=.!App.!checkbutton4   text=B4   variable=B4 Checkbutton variable
nchosen == 0

#++ Callback ++++  Button variable values = ['0', '0', '0', 'B4']  1
### Command Trigger 1 ### Button variable values = ('0', '0', '0', 'B4') 1

意义:

因为 .trace_add() 方法的回调总是先于 ttk.Checkbutton 小部件的 command 选项的 method/function 执行,并且可以在 method/function 执行期间发生=111=] 在每个事件流中 ttk.Checkbutton 小部件的 command 选项,.trace_add() 方法的回调内容必须预见到这种情况。


Another solution is to create a custom binding tag that comes after the binding tag of the class.

..., the callback will be called on the button after that event has been processed by the default binding on the widget class.

我认为术语 回调 应该称为 事件处理程序。在任何情况下,这种安排的打印输出语句如下所示。 这表明事件处理程序将始终在 ttk.Checkbutton 小部件的命令选项 method/function 完全执行后执行, 与使用 [=12= 的流程不同] 方法,ttk.Checkbutton 小部件的命令选项 method/function 的执行永远不会中断。

打印报表:

# B1 ttk.Checkbutton clicked on
### Command Trigger 0 ### Button variable values = ('0', 'B2', 'B3', 'B4') 3
cb=.!App.!checkbutton   text=B1   variable=jpg variable
nchosen != 0
### Command Trigger 1 ### Button variable values = ('0', 'B2', 'B3', 'B4') 3
#&& run_event_handler &&&&  Button variable values = ('0', 'B2', 'B3', 'B4') 3

# B2 ttk.Checkbutton clicked on
### Command Trigger 0 ### Button variable values = ('0', '0', 'B3', 'B4') 2
cb=.!App.!checkbutton2   text=B2   variable=B2 variable
nchosen != 0
### Command Trigger 1 ### Button variable values = ('0', '0', 'B3', 'B4') 2
#&& run_event_handler &&&&  Button variable values = ('0', '0', 'B3', 'B4') 2

# B3 ttk.Checkbutton clicked on
### Command Trigger 0 ### Button variable values = ('0', '0', '0', 'B4') 1
cb=.!App.!checkbutton3   text=B3   variable=B3 variable
nchosen != 0
### Command Trigger 1 ### Button variable values = ('0', '0', '0', 'B4') 1
#&& run_event_handler &&&&  Button variable values = ('0', '0', '0', 'B4') 1

# B4 ttk.Checkbutton clicked on
### Command Trigger 0 ### Button variable values = ('0', '0', '0', '0') 0
cb=.!App.!checkbutton4   text=B4   variable=tif variable
nchosen == 0
### Command Trigger 1 ### Button variable values = ('0', '0', '0', 'B4') 1
#&& run_event_handler &&&&  Button variable values = ('0', '0', '0', 'B4') 1

结论:

  1. 我的需要是在执行后续流程之前完全配置 ttk.Checkbutton 小部件。因此,使用小部件的 command 选项 method/function 首先配置小部件,并使用小部件绑定标签后的自定义绑定标签 class 执行后续过程被发现是最好的方法。
  2. 在小部件发生事件后,观察了以下方法和处理程序的执行顺序:
    1. .bind() 方法的事件处理程序
    2. method/function 小部件的 command 选项
    3. .bind_class() 方法的事件处理程序具有自定义绑定标记,该标记位于小部件 class 的绑定标记之后。
  3. .trace_add()方法的回调总是发生在第2项执行之前。此外,它也可能发生在第2项和第3项执行过程中。