6、变量类别
6-1 变量类别的基本概念
有些widget控件在执行时会更改内容,例如,文本框(Entry)、选项按钮(Radio button)等。有些我们可以更改他们的内容,例如,标签Label等。如果想要更改它们的内容可以使用这些控件的参数,例如,textvariable、variable,onvalue等。
不过要将Widget控件的参数以变量方式处理时,需要借助tkinter模块内的变量类别(Variable Classes),这个类别有4个子类别,每一个类别其实是一个数据类型的构造方法,我们可以通过这4个子类别的数据类型将它们与Widget控件的相关参数结合。
python
x = IntVar() # 整形变量,默认是0
x = DoubleVar() # 浮点型变量,默认是0.0
x = StringVar() # 字符串变量,默认是""
x = BooleanVar() # 布尔型变量,True是1,False是0
6-2 get()与set()
可以使用get()方法取得变量内容,使用set()方法设置变量内容。
下面程序实例,set()方法的应用。这个程序在执行时若单击Hit按钮可以显示"I like tkinter"字符串
,如果已经显示字符串则改成不显示字符串。这个程序第17行是将标签内容设为变量x,第8行是设置显示标签时的标签内容,第11行则是将标签内容设为空字符串以不显示标签内容。
python
from tkinter import *
def btn_hit():
global msg_on
if msg_on == False:
msg_on = True
x.set("I lie tkinter")
else:
msg_on = False
x.set("")
root = Tk()
root.title("ch6_1")
msg_on = False
x = StringVar()
label = Label(root, textvariable=x,
fg="blue", bg="lightyellow",
font="Verdana 20 bold",
width=25, height=2)
label.pack()
btn = Button(root, text="Click Me", command=btn_hit)
btn.pack()
root.mainloop()
运行结果

在上述实例中利用布尔值msg_on变量判断是否要显示"I lkie tkinter"字符串,如果msg_on是False表示没有显示"I lkie tkinter"字符串,如果msg_on是True表示目前有显示"I lkie tkinter"字符串。当单击Click Me按钮时,会更改msg_on状态,可参考第7行和第10行。同时也由set()方法更改label对象的参数textariable的内容,第8行设置显示"I lkie tkinter"字符串,第11行设置不显示"I lkie tkinter"字符串。
上述程序尽管可以运行,可是并没有使用本节中另一个get方法(),这个方法可以取得Widget控件某参数的变量内容,我们将时殷弘下列程序进行改良。
下面程序重新设计上面程序,取消布尔值msg_on变量,我们可以直接由get()方法获得目前Widget控件参数内容,然后由此内容判断是否显示"I lkie tkinter"字符串。判断方式是如果目前是空字符串zhi'xi则显示"I lkie tkinter"字符串,如果不是空字符串,则改成显示空字符串。
python
from tkinter import *
def btn_hit():
if x.get() == "":
x.set("I lie tkinter")
else:
x.set("")
root = Tk()
root.title("ch6_1")
x = StringVar()
label = Label(root, textvariable=x,
fg="blue", bg="lightyellow",
font="Verdana 20 bold",
width=25, height=2)
label.pack()
btn = Button(root, text="Click Me", command=btn_hit)
btn.pack()
root.mainloop()
执行结果与上面代码一致。
6-3 追踪trace()使用模式w
了解6-2节的变量设置后,我们可以利用变量设置追踪Widget控件,当其内容更改时,让程序执行callback函数。
下面程序设计当Widget控件Entry内容改变时在Python Shell窗口中输出"Entry content changed!"。
python
from tkinter import *
def callback(*args):
print("data chnaged: ", x.get())
root = Tk()
root.title("ch6_3")
x = StringVar()
entry = Entry(root, textvariable=x)
entry.pack(padx=10, pady=5)
x.trace("w", callback) # 若是有更改执行callback
root.mainloop()
运行结果

在看到上述窗口输出时,也可以在Python Shell窗口中看到下列输出。

上面程序的重点是
python
x.trace("w", callback) # w其实是write的缩写
第一个参数是模式,w代表当有执行写入时,就自动去执行callback函数。也可以自行取函数名称,这个动作称为变动追踪。我们可以通过x变量类别追踪Widget控件内容的改变时执行特定动作,本实例是在Python Shell窗口中输出Entry的内容。上述程序的另一个重点,内容如下:
python
def callback(*args):
后续将说明上述"*args"参数的含义。
下面程序扩充上述实例,同时在Entry控件下方建立Label控件,当在Entry中有输入时,同时在下方的Label控件中显示。
python
from tkinter import *
def callback(*args):
xL.set(xE.get())
print("data chnaged: ", xE.get())
root = Tk()
root.title("ch6_3")
xE = StringVar()
entry = Entry(root, textvariable=xE)
entry.pack(padx=10, pady=5)
xE.trace("w", callback) # 若是有更改执行callback
xL = StringVar()
label = Label(root, textvariable=xL)
xL.set("同步显示")
label.pack(padx=10, pady=5)
root.mainloop()
运行结果

6-4 追踪trace()使用模式r
上一节介绍了当Widget控件内容更改时,执行追踪并执行特定函数,其实我们也可以设计当控件内容被读取时,执行追踪并执行特定函数。
下面程序扩展与修改上面的代码,增加一个"读取"按钮,当在Entry中输入数据时Python Shell窗口不显示数据,但是下方的Label将同步显示。主要功能是如果单击了"读取"按钮,系统将发出数据被读取的警告,同时输出所读取的数据。
python
from tkinter import *
def callbackW(*args):
xL.set(xE.get())
def callbackR(*args):
print("Warning: 数据被读取!")
def hit():
print("读取数据: ", xE.get())
root = Tk()
root.title("ch6_5")
xE = StringVar()
entry = Entry(root, textvariable=xE)
entry.pack(padx=10, pady=5)
xE.trace("w", callbackW) # 若是有更改执行callback
xE.trace("r", callbackR) # 若是有被读取执行callback
xL = StringVar()
label = Label(root, textvariable=xL)
xL.set("同步显示")
label.pack(padx=10, pady=5)
btn = Button(root, text="读取", command=hit)
btn.pack(pady=5)
root.mainloop()
运行结果

上述程序的重点是
python
xE.trace("r", callbackR) # r其实是read缩写
上述第一个参数是模式,r代表当执行读取时,就会自动会执行callbackR函数。也可以自行取函数名称,这个动作称为读取追踪。我们可以通过xE变量类别追踪Widget控件内容被读取时执行的特定动作,本实例是在Python Shell窗口输出"Warning: 数据被读取!"和Entry中的内容。
6-5 trace()方法调用的callback方法参数
参考上面的内容
python
def callback(*args):
其实是传递三个参数,分别是tk变量名称、index索引、mode模式。不过目前有关tk变量名称和index索引部分尚未完成实际支持,至于第三个参数则是可以列出是r或w模式。由于我们所设计的程序并不需要传递参数,所以可以直接用"*args"当作参数内容。
下面程序列出trace()方法所调用callback()方法内的参数。
python
from tkinter import *
def callbackW(name, index, mode):
xL.set(xE.get())
print("name = %r, index = %r, mode = %r" % (name, index, mode))
root = Tk()
root.title("ch6_5")
xE = StringVar()
entry = Entry(root, textvariable=xE)
entry.pack(padx=10, pady=5)
xE.trace("w", callbackW) # 若是有更改执行callback
xL = StringVar()
label = Label(root, textvariable=xL)
xL.set("同步显示")
label.pack(padx=10, pady=5)
root.mainloop()
运行结果

6-6 计算器的设计
在4-3节有介绍过简易计算器按钮布局的设计,在5-9节有介绍过eval()方法的用法,本章已经学会了使用变量类别控制标签的输出,其实有这些概念就可以设计简单的计算器了。下面将介绍完整的计算器设计。
下面程序中设计简易的计算器,这个程序中在按钮设计时大量使用Lambda,主要是因为数字按钮与算术表达式按钮使用相同的函数,只是传递的参数不一样,所以用Lambda可以简化设计。
python
from tkinter import *
def calculate():
result = eval(equ.get())
equ.set(equ.get() + "=\n" + str(result))
def show(buttonString):
content = equ.get()
if content == "0":
content = ""
equ.set(content + buttonString)
def backspace(): # 删除最后一个字符
equ.set(equ.get()[:-1])
def clear(): # 清除显示区,重置为0
equ.set("0")
root = Tk()
root.title("计算器")
equ = StringVar()
equ.set("0") # 默认显示0
# 设计显示区
label = Label(root, width=25, height=2, relief="raised", anchor=SE,
textvariable=equ)
label.grid(row=0, column=0, columnspan=4, padx=5, pady=5)
# 清除显示区按钮
clearButton = Button(root, text="C", fg="blue", width=5, command=clear)
clearButton.grid(row=1, column=0)
# 以下是rowl的其它按钮
Button(root, text="DEL", width=5, command=backspace).grid(row=1, column=1)
Button(root, text="%", width=5, command=lambda:show("%")).grid(row=1, column=2)
Button(root, text="/", width=5, command=lambda:show("/")).grid(row=1, column=3)
# 以下是row2的其它按钮
Button(root, text="7", width=5, command=lambda:show("7")).grid(row=2, column=0)
Button(root, text="8", width=5, command=lambda:show("8")).grid(row=2, column=1)
Button(root, text="9", width=5, command=lambda:show("9")).grid(row=2, column=2)
Button(root, text="*", width=5, command=lambda:show("*")).grid(row=2, column=3)
# 以下是row3的其它按钮
Button(root, text="4", width=5, command=lambda:show("4")).grid(row=3, column=0)
Button(root, text="5", width=5, command=lambda:show("5")).grid(row=3, column=1)
Button(root, text="6", width=5, command=lambda:show("6")).grid(row=3, column=2)
Button(root, text="-", width=5, command=lambda:show("-")).grid(row=3, column=3)
# 以下是row4的其它按钮
Button(root, text="1", width=5, command=lambda:show("1")).grid(row=4, column=0)
Button(root, text="2", width=5, command=lambda:show("2")).grid(row=4, column=1)
Button(root, text="3", width=5, command=lambda:show("3")).grid(row=4, column=2)
Button(root, text="+", width=5, command=lambda:show("+")).grid(row=4, column=3)
# 以下是row5的其它按钮
Button(root, text="0", width=12, command=lambda:show("0")).grid(row=5, column=0, columnspan=2)
Button(root, text=".", width=5, command=lambda:show(".")).grid(row=5, column=2)
Button(root, text="=", width=5, bg="yellow", command=lambda:calculate()).grid(row=5, column=3)
root.mainloop()
运行结果
