GUI的最终选择 Tkinter(四):Entry、Listbox、Scrollbar和Scale组件
Entry组件
Entry组件就是平时所说的输入框。输入框是程序员用到的最多的一个程序,例如在输入账号和密码的时候需要提供两个输入框,用于接收密码的输入框还会有星号将实际输入的内容隐藏起来。
Tkinter组件之间的许多方法和选项之间都是通用的,例如在输入框中用代码添加删除内容,同样也是使用insert()和delete()方法
1 from tkinter import * 2 3 root = Tk() 4 e = Entry(root) 5 e.pack(padx=20,pady=20) 6 e.delete(0,END) 7 e.insert(0,"默认文本。。。") 8 mainloop()
如图所示:
获取输入框里面的内容,可以使用Entry组件的get()方法。当然也可以将一个Thinter的变量(通常是StringVar)挂钩到textvariablex选项,然后通过变量的get()方法获取。
继续看下面的例子,添加一个按钮,当单击按钮时候,获取输入框的内容并打印出来,然后清空输入框。单击“获取信息”按钮,在IDIE中输入框中的内容显示出来。
1 from tkinter import * 2 3 root = Tk() 4 #Tkinter总共提供了三种布局方法:pack(),grid()和place() 5 #grid()方法允许你用表格的形式爱管理组件的位置 6 #row选项代表行,column选项代表列 7 #例如:row=1,column=2表示第二行第三列(0表示第一行) 8 Label(root,text="作品:").grid(row=0) 9 Label(root,text="作者:").grid(row=1) 10 e1 = Entry(root) 11 e2 = Entry(root) 12 e1.grid(row=0,column=1,padx=10,pady=5) 13 e2.grid(row=1,column=1,padx=10,pady=5) 14 def show(): 15 print("作品:《%s》"%e1.get()) 16 print("作者:%s"%e2.get()) 17 e1.delete(0,END) 18 e2.delete(0,END) 19 #如果表格大于组件,那么可以使用sticky选项来设置组件的位置 20 #同样需要使用N,E,S,W以及他们的组合NE,SE,SW,NW来表示方位 21 Button(root,text="获取信息",width=10,command=show).grid(row=3,column=0,sticky=W,padx=10,pady=5) 22 Button(root,text="退出",width=10,command=root.quit).grid(row=3,column=1,sticky=E,padx=10,pady=5) 23 mainloop()
执行结果:
如果你手残的话,肯定会发现单击 “退出”按钮没有反应,这是因为python的IDIE也是使用Tkinter设计的,因此当程序使用IDIE运行的时候,就会出现此类冲突,解决方法:在cmd中运行脚本
如果想设计一个密码输入框,即使用星号(*)代替用户输入的内容,只需要设计show选项即可。
1 from tkinter import * 2 3 root = Tk() 4 Label(root,text="账号:").grid(row=0) 5 Label(root,text="密码:").grid(row=1) 6 v1 = StringVar() 7 v2 = StringVar() 8 e1 = Entry(root,textvariable=v1) 9 e2 = Entry(root,textvariable=v2,show="*") 10 e1.grid(row=0,column=1,padx=10,pady=5) 11 e2.grid(row=1,column=1,padx=10,pady=5) 12 def show(): 13 print("账号:%s"%v1.get()) 14 print("作者:%s"%v2.get()) 15 e1.delete(0,END) 16 e2.delete(0,END) 17 Button(root,text="芝麻开门",width=10,command=show).grid(row=3,column=0,sticky=W,padx=10,pady=5) 18 Button(root,text="退出",width=10,command=root.quit).grid(row=3,column=1,sticky=E,padx=10,pady=5) 19 mainloop()
执行结果:
另外,Entry组件还支持验证输入内容的合法性,例如输入框要求输入的是数字,用户输入了字母就输入“非法”操作了,实现该功能需要通过设置validate,validatecommand和invalidcommand三个选项。
首先是启用验证的“开关”是validate选项,该选项可以设置的值如下表所示:
值 | 含义 |
focus | 当Entry组件获取或失去焦点的时候验证 |
focusin | 当Entry组件获得焦点的时候验证 |
focusout | 当Entry组件失去焦点的时候验证 |
key | 当输入框被编辑的时候验证 |
all | 当出现上面任何一种情况的时候验证 |
none | 当关闭验证功能。默认设置该选项(即不启动验证)。注意,是字符串的‘none’,而非None |
其次是validatecommand选项指定一个验证函数,该函数只能返回True或False表示验证的结果。一般情况下验证函数只需要知道输入框的内容即可。可以通过Entry组件的get()方法获得该字符串。
下面的例子中,在第一个输入框中输入“python3”并通过Tab键将焦点转移到第二个输入框的时候,验证功能被成功触发:
1 from tkinter import * 2 root = Tk() 3 def test(): 4 if e1.get() =="python3": 5 print("正确") 6 return True 7 else: 8 print("错误") 9 e1.delete(0,END) 10 return False 11 v1 = StringVar() 12 e1 = Entry(root,textvariable=v1,validate="focusout",validatecommand=test) 13 e2 = Entry(root) 14 e1.pack(padx=10,pady=10) 15 e2.pack(padx=10,pady=10) 16 mainloop()
执行结果:
最后,invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才会被调用。
在下面的例子中,在第一个输入框中输入的“python3”,并通过Tab键将焦点转移到第二个输入框,validatecommand指定的验证函数被触发并返回False,接着invalidatecommand被触发。
1 from tkinter import * 2 root = Tk() 3 def test(): 4 if e1.get() =="python3": 5 print("正确") 6 return True 7 else: 8 print("错误") 9 e1.delete(0,END) 10 return False 11 def test2(): 12 print("我被调用了。。。") 13 return True 14 v1 = StringVar() 15 # e1 = Entry(root,textvariable=v1,validate="focusout",validatecommand=test) 16 e1 = Entry(root,textvariable=v1,validate="focusout",validatecommand=test,invalidcommand=test2) 17 e2 = Entry(root) 18 e1.pack(padx=10,pady=10) 19 e2.pack(padx=10,pady=10) 20 mainloop()
执行结果:
其实,Tkinter还有个一“隐藏技能”,Tkinter为了验证函数提供 了一些额外的选项,如下表
选项 | 含义 |
%d | 操作代码,0表示删除操作,1表示插入操作,2表示获得、失去焦点或textvariable变量的值被修改 |
%r | 当用户尝试插入或删除操作的时候,该选项表示插入或删除的位置(索引号)如果是由于获得、失去焦点或textvariable变的值被修改而调用验证函数,那么该值是-1 |
%p | 当输入框的值允许改变的时候,该值有效,该值为输入框的最新文本内容 |
%s | 该值为调用验证函数前输入框的文本内容 |
%S | 当插入或删除操作触发验证函数的时候,该值有效选项表示文本被插入和删除的内容 |
%v | 该组件当前的validate选项的值 |
%V | 调用验证函数的原因该值是focusin,focusout,key或forced(textvariable选项指定的变量值被修改)中一个 |
%W | 该组件的名字 |
为了使用这些选项,你也可以这样写:
validatecommand = (f,s1,s2,...)
其中,f是验证函数名,s1,s2等是额外的选项,这些选项会作为参数一次性传递给f函数。在此之前,需要调用register()方法将验证函数包装起来。
1 from tkinter import * 2 root = Tk() 3 v = StringVar() 4 def test(content,reason,name): 5 if content =="python3": 6 print(content,reason,name) 7 return True 8 else: 9 print("错误") 10 print(content,reason,name) 11 return False 12 testCDM = root.register(test) 13 e1 = Entry(root,textvariable=v,validate="focusout",validatecommand=(testCDM,'%P','%v','%W')) 14 e2 = Entry(root) 15 e1.pack(padx=10,pady=10) 16 e2.pack(padx=10,pady=10) 17 mainloop()
执行结果:
下面我们来写一个简单的计算器
1 from tkinter import * 2 3 root = Tk() 4 frame = Frame(root) 5 frame.pack(padx=10,pady=10) 6 v1 = StringVar() 7 v2 = StringVar() 8 v3 = StringVar() 9 def test(content): 10 #注意,这里不能使用e1.get()或者来获取输入的内容 11 #因为validate选项指定"key"的时候,有任何输入操作都会被拦截到这个函数中 12 #也就是说先拦截,只有这个函数返回True,那么输入的内容才会到变量里面 13 #所以要使用%P来获取最新的输入框内容 14 if content.isdigit(): 15 return True 16 else: 17 return False 18 testCDM = root.register(test) 19 Entry(frame,textvariable=v1,width=10,validate='key',validatecommand=(testCDM,'%P')).grid(row=0,column=0) 20 Label(frame,text='+').grid(row=0,column=1) 21 Entry(frame,textvariable=v2,width=10,validate='key',validatecommand=(testCDM,'%P')).grid(row=0,column=2) 22 Label(frame,text='=').grid(row=0,column=3) 23 Entry(frame,textvariable=v3,width=10,validate='key',validatecommand=(testCDM,'%P')).grid(row=0,column=4) 24 def calc(): 25 result = int(v1.get()) + int(v2.get()) 26 v3.set(result) 27 Button(frame,text="计算器",command=calc).grid(row=1,column=2,pady=5) 28 mainloop()
执行结果:
Listbox组件
如果需要提供选择给用户选择,单选可以用Radiobutton组件,多选可以用Checkbutton组件。但是如果提供的选项非常多,例如选择你所在的城市,通过Radiobutton和Checkbutton组件来实现直接导致的结果就是,用户界面存放不了那么多按钮。
这个时候就需要考虑使用Listbox组件了,Listbox组件是以列表形式显示出来,并支持滚动操作,所以对于需要大量选项的情况下会更好用点。
当创建一个Listbox组件的时候,它是空的(里面什么都没有),所以,首先要做的第一个事情就是添加一行或多行文本进去。使用insert()方法添加两个参数:第一个参数是插入的索引号,第二个参数是插入的字符串。索引号通常是项目的序号(第一项的序号是0),当然对于多个项目,应该使用循环。
1 from tkinter import * 2 master = Tk() 3 theLB = Listbox(master,setgrid=True) #创建一个空列表 4 theLB.pack() 5 #向列表中添加数据 6 for item in ["恐龙蛋","鹅蛋","鸭蛋","鸡蛋"]: 7 theLB.insert(END,item) 8 theButton = Button(master,text="删除",command=lambda x=theLB:x.delete(ACTIVE)) 9 theButton.pack() 10 mainloop()
执行结果:
使用delete()方法删除列表中的项目,最常用的操作是删除列表中的所有项目:listbox.delete(0,END)
#跟END一样,这个AXTIVE是一个特殊的索引号,表示当前被选中项目
theButton = Button(master,text="删除",command=lambda x=theLB:x.delete(ACTIVE)) theButton.pack()
最后,这个Listbox组件根据selectmode选项提供了四种不同的选择模式:SINGLE(单选),BROWSE(也是单选,但拖动鼠标或通过方向键可以直接改变选项),MELTIPLE(多选)和EXTENDED(也是多选,但是需要同时按照Shift键或Ctrl键拖动光标实现)。默认的选中模式是BROWSE.
选项增多接踵而来的麻烦也会增多,例如使用Listbox组件默认只能显示10个项目,而有11个怎么办了
1 from tkinter import * 2 master = Tk() 3 theLB = Listbox(master,setgrid=True) #创建一个空列表 4 theLB.pack() 5 #往列表中增加数据 6 for item in range(20): 7 theLB.insert(END,item) 8 mainloop()
执行结果:
共有20个数字,但是只显示了10个,只能利用上下键或者鼠标滚动才能显示下面数字,如果想让下面的数字都显示,第一个方法就是修改height选项
theLB = Listbox(master,setgrid=True,height=14)
执行结果:
可以看到height选项虽然能达到目的,但是少了就不显示,多了就超出屏幕,所以我们可以用第二种方法就是在右边添加一个滚动条。
Scrollbar组件
滚动条是一个独立的组件,但是他平时都是和其他组件配合使用,下面一个例子看下如何使用垂直滚动条。
为了在某个组件上安装垂直滚动条,需要做两件事
1、设置组件的yscrollbarcommand选项Scrollbar组件的set()方法
2、设置Scrollbar组件的command选项为该组件的yview()方法
1 if __name__ == "__main__": 2 from tkinter import * 3 master = Tk() 4 sb = Scrollbar(master) 5 sb.pack(side=RIGHT,fill=Y) 6 lb = Listbox(master,yscrollcommand=sb.set) 7 for i in range(100): 8 lb.insert(END,str(i)) 9 lb.pack() 10 sb.config(command=lb.yview) 11 mainloop()
执行结果:
这是一个互联互通的过程,当用户操作滚动条进行滚动时,滚动条相应滚动并通过Listbox组件的yview()方法滚动列表框里的内容,同样,当列表框中可视范围发送改变的时候,Listbox也会相应的改变。
Scale组件
Scale组件和Scrolbar滚动条组件很相似,都可以滚动,都是条形,但是他们的使用范围是不相同的。Scale组件主要是通过修改选项设置范围以及分辨率(精度)。
当希望用户输入某个范围的数值,使用Scale组件可以很好地替代Entry组件,创建一个指定范围的Scale组件其实非常容易,只要指定from和to两个关键字即可,但是from本身是python的关键字,为了区分我们在后面加下划线from_
1 if __name__ == "__main__": 2 from tkinter import * 3 root = Tk() 4 Scale(root,from_=0,to=20).pack() 5 Scale(root,from_=0,to=200,orient=HORIZONTAL).pack() 6 mainloop()
执行结果:
可以使用get()方法获取当前滑动的位置:
1 if __name__ == "__main__": 2 from tkinter import * 3 root = Tk() 4 s1 = Scale(root, from_=0, to=20) 5 s1.pack() 6 s2 = Scale(root, from_=0, to=200, orient=HORIZONTAL) 7 s2.pack() 8 9 def show(): 10 print(s1.get(),s2.get()) 11 Button(root,text="获取位置",command=show).pack() 12 13 mainloop()
执行结果:
可以通过resolution选项控制分辨率(步长),通过tickinterval选项设置刻度:
1 from tkinter import * 2 root = Tk() 3 Scale(root,from_=0,to=20,tickinterval=5,resolution=5,length=200).pack() 4 Scale(root,from_=0,to=200,tickinterval=10,orient=HORIZONTAL,resolution=10,length=600).pack() 5 mainloop()
执行结果: