I noticed a behaviour in the ttk.Checkbuttons
widget. That is, any applied bindings or event handlers to this widget always occurs before the widget's command
option method/function is executed.
Significance:
Given that the change in the state of the
ttk.Checkbuttons
is performed by thecommand
option method/function, the value of thettk.Checkbuttons['variable']
accessed through an event handler will always be of the old state and not the new state that is defined by the widget'scommand
option method/function.Changing the value of
ttk.Checkbuttons['variable']
in an event handler will mess up the performance of the widget'scommand
option method/function. Hence,ttk.Checkbuttons['variable']
should be set in the widget'scommand
option method/function.
Question:
Given the above, so what is the purpose of binding event handlers to a ttk.Checkbuttons
widget?
Event handlers are executed when an event occurs, e.g. when <ButtonRelease-1>
has occurred at the ttk.Checkbutton
widget. If I want to design a follow-up action based on the state of the widget, I can't do that via event handler as the widget instate
has not been updated yet. A workaround is to assume the widget instate
and variable
obtained in an event handler is the opposite of the reported instate
or variable.get() values. However, such an approach seems presumptuous.
How do should I use event handlers for a ttk.Checkbutton
to program follow-up actions when there is a state change? Or should I not use it and only use the widgets command
option method/function to design a follow-up action based on the state of the widget?
CodePudding user response:
Given the above, so what is the purpose of binding event handlers to a ttk.Checkbuttons widget?
So that you can define your own behavior that overrides the default behavior.
How do should I use event handlers for a ttk.Checkbutton to program follow-up actions when there is a state change?
One solution is to not use event handlers. 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.
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)
Another solution is to create a custom binding tag that comes after the binding tag of the class.
Example:
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)
With the above, the callback will be called on the <ButtonRelease-1>
button after that event has been processed by the default binding on the widget class. If you do this, you should also add a binding to the spacebar in a similar fashion since you can also set the value of a checkbutton with the spacebar.
For another example of bindtags with a bit more of a discussion about how they work, see 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.