众所周知,Python提供很多种图形界面(GUI)设计的库,其中最常见的库之一是tkinter模块,它是 Python 的标准 Tk GUI 工具包的接口[1]。
Tkinter库给用户提供了各种各样的组件,网站[2]介绍了这些组件的用法。但复选框(Checkbutton)组件[3]介绍的内容较少,所以本文对该组件的介绍做一些补充。
checkbox = ttk.Checkbutton(container,
text='<checkbox label>',
command=check_changed,
variable=checkbox_var,
onvalue='<value_when_checked>',
offvalue='<value_when_unchecked>')
网页[3]中使用了以上代码介绍Checkbutton, 即复选框的参数。其中text是复选框的标记文字,command是该复选框被点按后触发的函数,variable是该复选框的值绑定的变量,onvalue和offvalue是在用户在变量中不愿用0、1而是愿意用其它值来代表复选框的开关状态时使用[3]。
本文在三个方面对Checkbutton的介绍进行一些补充。
一、事件绑定
很多时候,为了让触发函数的方式更为灵活,我们会建议用事件(event)绑定(bind)代替组件中的command。在这里设计一个程序,把Checkbutton的变量值和一个标签(Label)的显示同步,让Checkbutton被点击时,通过事件,把新的Checkbutton的变量值更新给标签。
程序如下:
from tkinter import *
from tkinter import ttk
from time import sleep
def checkChange(log):
labl1['text'] = checkVar.get()
root = Tk()
root.geometry('333x333')
root.title('check box window')
frm = ttk.Frame(root)
frm.pack()
checkVar = BooleanVar()
chB1 = ttk.Checkbutton(frm, text="Check this please", variable=checkVar)
chB1.bind('<ButtonRelease>', checkChange)
chB1.pack()
labl1 = ttk.Label(frm, text=checkVar.get())
labl1.pack()
root.mainloop()
在这里,名为chB1的Checkbutton组件的<ButtonRelease>事件和checkChange绑定,表明鼠标按下该复选框,并松开后,checkChange函数会被触发。关于事件名的意思,见[4]。
以上程序的运行效果如下:
显然,该Label显示的值和checkVar(即该复选框的对应变量)的值最初相同,之后都是相反的。造成这个不合理的现象的原因在于,Checkbutton组件通过事件处理程序访问的值将始终是旧状态[5]。换句话说,当Checkbutton被点击后,其绑定的事件是在Checkbutton值改变前就产生的,所以在绑定的事件触发的函数运行时,Checkbutton值尚未改变,所以checkVar.get()读出的值是点击Checkbutton之前的值。当程序运行完后,Checkbutton值才改变。
为了证实这一点,现在在checkChange函数里增加一行sleep(1),让函数运行时等待一秒。
def checkChange(log):
sleep(1)
labl1['text'] = checkVar.get()
效果如下:
可以看出,当复选框被点击后,等了一秒钟,复选框里的勾才发生变化。这说明复选框被点击时,先执行被绑定的事件的函数,完成后才更新对应的变量数值。
所以,Checkbutton组件并不适合用这种事件绑定的做法更新其它组件的内容显示[5]。
二、变量追踪
用command触发函数,使复选框被按下时,将复选框对应的变量值同步给其它组件,是可行的。
from tkinter import *
from tkinter import ttk
from time import sleep
def checkChange():
labl1['text'] = checkVar.get()
root = Tk()
root.geometry('333x333')
root.title('check box window')
frm = ttk.Frame(root)
frm.pack()
checkVar = BooleanVar()
chB1 = ttk.Checkbutton(frm, text="Check this please", variable=checkVar, command=checkChange)
chB1.pack()
labl1 = ttk.Label(frm, text=checkVar.get())
labl1.pack()
root.mainloop()
但除了这种方法外,还有另一种方法,就是用变量追踪(trace)功能,追踪Checkbutton组件的变量的值的变化。当值变化时,更新其它组件。
Tkinter的变量有追踪(trace)的功能,可以注册一些追踪器,使得当变量值发生变化或其它操作时,触发一些函数。详细介绍见[6]。简单地说,trace_add添加一个追踪器,即添加一个在变量上发生一些操作时触发的函数;trace_remove删除追踪器;trace_info列出变量目前已注册的追踪器。
from tkinter import *
from tkinter import ttk
from time import sleep
def checkChange(a,b,c):
sleep(1)
labl1['text'] = checkVar.get()
print(f"a={a},b={b},c={c}")
print(checkVar.trace_info())
root = Tk()
root.geometry('333x333')
root.title('check box window')
frm = ttk.Frame(root)
frm.pack()
checkVar = BooleanVar()
chB1 = ttk.Checkbutton(frm, text="Check this please", variable=checkVar)
checkVar.trace_add('write', checkChange)
chB1.pack()
labl1 = ttk.Label(frm, text=checkVar.get())
labl1.pack()
root.mainloop()
程序里的这句checkVar.trace_add('write', checkChange)就是把“在checkVar值变化时触发checkChange”这个追踪器添加到checkVar上。
效果如下:
注意在追踪器触发时,会传递三个参数给checkChange函数。所以在这个函数中,添加了打印这三个参数的功能。于此同时,也添加打印trace_info的功能。每次点击复选框后,打印内容如下:
a=PY_VAR7,b=,c=write
[(('write',), '2480373178944checkChange')]
所以它传递了追踪器的代号以及触发的条件(这里是‘write’)。
另外trace_info是一个包含该变量注册的所有追踪器的列表。
三、初始状态
有的用户希望实现当程序启动时,复选框默认处于被选中的状态。这是可行的,只要其对应的变量的初始值为onvalue即可,这里onvalue=1。
checkVar = BooleanVar(value=1)
用这句代码初始化checkVar即可。
效果如下:
四、小结
如果想让Checkbutton的值和其它组件显示的内容同步,或绑定,用事件绑定不是一个好的方法。但除了用command外,也可以用追踪器(trace)。另外,通过设置变量初始值,可以定义Checkbutton的初始状态。
参考资料
[1]Python GUI 编程(Tkinter) | 菜鸟教程
[5]python - 当 ttk.Checkbutton 小部件的状态发生变化时,如何编写后续操作?_Stack Overflow中文网