GUI:Graphical User Interface图形用户界面
1 简介
Tkinter是Python的默认GUI库, 基于Tk工具包,该工具包最初是为工具命令语言(Tool Command Language,Tcl)设计的。Tk普及后,被移植到很多其他的脚本语言中,包括Perl(Perl/Tk)、Ruby(Ruby/Tk)和Python(Tkinter)。
Python3模块为tkinter。
2 Tkinter和python编程
2.1 添加Tk到应用中
让GUI 程序启动和运行起来需要以下5 个主要步骤:
1.导入tkinter模块。
2.创建一个顶层窗口对象,用于容纳整个GUI应用。
3.在顶层窗口对象之上(或者“其中”)构建所有的GUI 组件(及其功能)。
4.通过底层的应用代码将这些GUI组件连接起来。
5.进入主事件循环
2.2 GUI编程介绍
1、窗口与控件
在GUI编程中,顶层的根窗口对象包含组成GUI应用的所有小窗口对象。它们可能是文字标签、按钮、列表框等。这些独立的GUI组件称为控件。所以当我们说创建一个顶层窗口时,只是表示需要一个地方来摆放所有的控件。
tkinter.Tk()返回的对象通常称为根窗口
2、事件驱动处理
一个GUI应用从开始到结束就是通过整套事件体系来驱动的,事件可以包括按钮按下(及释放)、鼠标移动、敲击回车键等。
当布局管理器排列好所有控件后,GUI应用进入其类似服务器的无限循环。这个循环会一直运行,直到出现GUI事件,进行处理,然后再等待更多的事件去处理
3、布局管理
Tk有3 种布局管理器:
1) Placer:提供控件的大小和摆放位置,然后管理器就会将其摆放好
2) Packer:主要使用,把控件填充到正确的位置,然后对于之后的每个控件,会去寻找剩余的空间进行填充
3) Grid:基于网格坐标,使用Grid来指定GUI控件的放置
2.3 Tk控件
控件 |
描述 |
Button |
与Label类似,但提供额外的功能,如鼠标悬浮、按下、释放以及键盘活动/事件 |
Canvas |
提供绘制形状的功能(线段、椭圆、多边形、矩形),可以包含图像或位图 |
Checkbutton |
一组选框,可以勾选其中的任意个(与HTML的checkbox输入类似) |
Entry |
单行文本框,用于收集键盘输入(与HTML的文本输入类似) |
Frame |
包含其他控件的纯容器 |
Label |
用于包含文本或图像 |
LabelFrame |
标签和框架的组合,拥有额外的标签属性 |
Listbox |
给用户显示一个选项列表来进行选择 |
Menu |
按下Menubutton后弹出的选项列表,用户可以从中选择 |
Menubutton |
用于包含菜单(下拉、级联等) |
Message |
消息。与Label类似,不过可以显示成多行 |
PanedWindow |
一个可以控制其他控件在其中摆放的容器控件 |
Radiobutton |
一组按钮,其中只有一个可以“按下”(与HTML的radio输入类似) |
Scale |
线性“滑块”控件,根据已设定的起始值和终止值,给出当前设定的精确值 |
Scrollbar |
为Text、Canvas、Listbox、Enter等支持的控件提供滚动功能 |
Spinbox |
Entry 和Button 的组合,允许对值进行调整 |
Text |
多行文本框,用于收集(或显示)用户输入的文本(与HTML的textarea类似) |
Toplevel |
与Frame类似,不过它提供了一个单独的窗口容器 |
2.4 Tkinter示例
2.4.1 Label控件
1 import tkinter 2 3 top = tkinter.Tk() #创建了一个顶层窗口 4 5 label = tkinter.Label(top,text='Hello World!') #Label控件,包含Hello World字符串 6 label.pack() #使用Packer来管理和显示控件 7 tkinter.mainloop() #运行GUI应用,tkinter.mainloop()可以让应用进入无限主循环中
运行结果:
2.4.2 Button控件
1 import tkinter 2 3 top = tkinter.Tk() #创建了一个顶层窗口 4 5 #Button控件,当按钮被按下(并且释放)后,整个程序就会退出 6 label = tkinter.Button(top,text='quit',command=top.quit,bg='red',fg='green') 7 label.pack() #使用Packer来管理和显示控件 8 tkinter.mainloop() #运行GUI应用,tkinter.mainloop()可以让应用进入无限主循环中
运行结果:
2.4.3 偏函数应用
1)偏函数
functools.partial
偏函数
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
2)偏函数应用示例
1 from functools import partial as pto #偏函数 2 from tkinter import Tk,Button,X 3 from tkinter.messagebox import showinfo,showwarning,showerror 4 5 WARN = 'warn' 6 CRIT = 'crit' 7 REGU = 'regu' 8 9 SIGNS = { 10 'do not enter':CRIT, 11 'railroad crossing':WARN, 12 '55\nspeed limit':REGU, 13 'wrong way':CRIT, 14 'merging traffic':WARN, 15 'one way':REGU 16 } 17 18 ##各种标志类型按钮的回调函数## 19 critCB = lambda:showerror('Error','Error Button Pressed!') 20 warnCB = lambda:showwarning('Warning','Warning Button Pressed!') 21 infoCB = lambda:showinfo('Info','Info Button Pressed!') 22 23 top = Tk() #顶层窗口 24 top.title('Road Signs') #设置标题 25 Button(top,text='QUIT',command=top.quit,bg='red',fg='white').pack() #设置QUIT按钮,使用Packer来管理和显示控件 26 27 ##偏函数用法## 28 #模板化Button类和根窗口top;调用MyButton时,就会调用Button类(tkinter.Button()创建一个按钮),并将top作为它的第一个参数 29 MyButton = pto(Button,top) 30 #模板化每种标志类型,创建单独的按钮类型 31 CritButton = pto(MyButton,command=critCB,bg='white',fg='red') 32 WarnButton = pto(MyButton,command=warnCB,bg='goldenrod1') 33 ReguButton = pto(MyButton,command=infoCB,bg='white') 34 35 for eachSign in SIGNS: 36 signType = SIGNS[eachSign] 37 ''' 38 expand置1 使能fill属性 39 expand置0 关闭fill属性 40 fill=X 当GUI窗体大小发生变化时,widget在X方向跟随GUI窗体变化 41 fill=Y 当GUI窗体大小发生变化时,widget在Y方向跟随GUI窗体变化 42 fill=BOTH 当GUI窗体大小发生变化时,widget在X、Y两方向跟随GUI窗体变化 43 ''' 44 # title() 方法返回"标题化"的字符串,就是说所有单词都是以大写开始,其余字母均为小写 45 cmd = '%sButton(text=%r%s).pack(fill=X,expand=True)' % (signType.title(),eachSign,'.upper()' if signType == CRIT else '.title()') 46 #eval()将字符串str当成有效的表达式来求值并返回计算结果 47 eval(cmd) 48 49 top.mainloop()
运行结果:
2.4.4 Tkinter示例(目录树遍历)
1 import os 2 from time import sleep 3 from tkinter import * 4 5 class DirList(object): 6 def __init__(self,initdir=None): 7 self.top = Tk() #顶层窗口 8 self.label = Label(self.top,text='Directory Lister V1.1') #Label控件,包含字符串 9 self.label.pack() #使用Packer来管理和显示控件 10 11 self.cwd = StringVar(self.top) #用于保存当前所在的目录名 12 13 #用于显示当前的目录名 14 self.dirl = Label(self.top,fg='blue',font=('Helvetica',12,'bold')) 15 self.dirl.pack() 16 17 #Scrollbar与Listbox控件包含在Frame控件中 18 self.dirfm = Frame(self.top) 19 self.dirsb = Scrollbar(self.dirfm) #文件树超过Listbox的大小能够移动列表 20 #side按扭停靠在窗口的哪个位置,fill填充(x:水平方向,y:竖直方向,both:水平和竖直方向,none:不填充) 21 self.dirsb.pack(side=RIGHT,fill=Y) 22 #列出目录的文件列表,yscrollcommand创建一个垂直滚动条 23 self.dirs = Listbox(self.dirfm,height=15,width=50,yscrollcommand=self.dirsb.set) 24 #绑定操作,这意味着将一个回调函数与按键、鼠标操作或者其他的一些事件连接起来,这里当双击任意条目时,会调用setDirAndGo函数 25 self.dirs.bind('<Double-1>',self.setDirAndGo) 26 self.dirs.pack(side=LEFT,fill=BOTH) 27 self.dirfm.pack() 28 29 #创建一个文本框,可以输入遍历的目录名 30 #Entry是tkinter 用来接收字符串等输入的控件 31 self.dirn = Entry(self.top,width=50,textvariable=self.cwd) 32 #回车绑定 33 self.dirn.bind('<Return>',self.doLS) 34 self.dirn.pack() 35 36 #定义一个按钮框架 37 self.bfm = Frame(self.top) 38 self.clr = Button(self.bfm,text='Clear',command=self.clrDir,activeforeground='white',activebackground='blue') 39 self.ls = Button(self.bfm,text='List Directory',command=self.doLS,activeforeground='white',activebackground='green') 40 self.quit = Button(self.bfm,text='Quit',command=self.top.quit,activeforeground='white',activebackground='red') 41 self.clr.pack(side=LEFT) 42 self.ls.pack(side=LEFT) 43 self.quit.pack(side=LEFT) 44 self.bfm.pack() 45 46 #初始化GUI程序,以当前工作目录作为起始点 47 if initdir: 48 self.cwd.set(os.curdir) #os.curdir:返回当前目录('.') 49 self.doLS() 50 51 ''' 52 清空TK字符串变量cwd,当发生错误时可以回到之前的目录 53 ev默认为None,值是有窗口系统传入的,回调函数中可能会用到,也可能用不到 54 ''' 55 def clrDir(self,ev=None): 56 self.cwd.set('') 57 58 #设置要遍历的目录函数 59 def setDirAndGo(self,ev=None): 60 self.last = self.cwd.get() 61 self.dirs.config(selectbackground='red') #双击时,设置背景色为红色 62 check = self.dirs.get(self.dirs.curselection()) #curselection()返回当前选中项的索引,get()返回制定索引的项值 63 if not check: 64 check = os.curdir 65 self.cwd.set(check) 66 self.doLS() 67 68 def doLS(self,ev=None): 69 error='' 70 tdir = self.cwd.get() 71 if not tdir:tdir = os.curdir #os.curdir:返回当前目录('.') 72 73 if not os.path.exists(tdir): #判断文件是否存在 74 error = tdir + ':no such file' 75 elif not os.path.isdir(tdir): #判断是否是目录 76 error = tdir + ':not a directory' 77 78 if error: 79 self.cwd.set(error) 80 self.top.update() 81 sleep(2) 82 if not (hasattr(self,'last') and self.last):#hasattr判断一个对象里面是否有last属性或者last方法 83 self.last = os.curdir 84 self.cwd.set(self.last) 85 self.dirs.config(selectbackground='LightSkyBlue') 86 self.top.update() 87 return 88 89 self.cwd.set('FETCHING DIRECTORY CONTENTS...') 90 self.top.update() 91 dirlist = os.listdir(tdir) #获取实际文件列表 92 dirlist.sort() 93 os.chdir(tdir) #改变当前工作目录到指定的路径 94 95 self.dirl.config(text=os.getcwd()) #os.getcwd()返回当前进程的工作目录 96 self.dirs.delete(0,END) #删除所有元素 97 #insert()导入listbox中 98 self.dirs.insert(END,os.curdir) 99 self.dirs.insert(END,os.pardir) #os.pardir上级目录 100 for eachFile in dirlist: 101 self.dirs.insert(END,eachFile) 102 self.cwd.set(os.curdir) 103 self.dirs.config(selectbackground='LightSkyBlue') 104 105 def main(): 106 DirList(os.curdir) #os.curdir:返回当前目录('.') 107 mainloop() 108 109 if __name__=='__main__': 110 main()
运行结果:
3 其他GUI
1) Tix(Tk接口扩展)
2) Pmw(Python MegaWidgets Tkinter扩展)
3) wxPython(wxWidgets的Python版本)
4) PyGTK(GTK+的python版本)