python是编程语言,如果要实现GUI窗体设计,还需要单独的GUI模块。 其余的编程语言里貌似除了微软系列产品外,其他的都得依赖于GUI设计软件。例如Java需要Swing工具包,go语言需要GTK等。对于Python而言,如果要开发窗体应用程序,除了借助于django或flask等与HTML前端技术结合外,同时也有一些GUI工具包可以使用,例如tkinter、wxpython,还可以使用pyQT来完成。
本篇以tkinter内置模块为例,介绍一下python窗体设计过程和方法。同时将以开发一个爬虫模块窗体实战为例,帮助大家快速上手实践。
(1)tkinter基本用法
tkinter是python内置的一个GUI开发模块,使用的时候直接使用import方法就可以导入该模块。
import tkinter
# 或者 from tkinter import *
如果有习惯读取该模块源代码的话,可以直接进入python安装目录下的Lib文件夹里,下面就是tkinter模块目录,打开后发现有如下py文件,功能简介如下:
直接使用idle就可以打开这些py文件,当然读取源代码来理解还是比较痛苦的,还是上手实践来得快。
在spyder开发页面内或者IDLE内输入如下代码,启动第一个hello窗口:
import tkinter #导入tkiner模块
root=tkinter.Tk() #实例化一个窗体对象
root.wm_title('hello,python') #设置该窗体的标题为hello,python
root.geometry('300x200') #设置窗体大小为宽300,高200,中间用键盘上的x表示
root.mainloop() #窗体对象运行,消息循环
运行后就会弹出第一个窗体hello:
这样我们就设计了一个窗体了。窗体也是一个二维平面容器,容器的意思就是里面可以放置其他的东西,对于窗体而言,可以依附在上的就是UI控件了。UI控件也就是窗体上那些基本元素,如标签、按钮、文本输入框、单选框、多选框、列表选择框、图片容器、表格等,英文名叫widgets。
如下窗体里就包括标签、按钮、输入框、列表选择框:
很显然,标签主要用于文本内容显示,如上面图片中的姓名、身高等信息;按钮就用于点击后形成触发事件,执行下一步的动作;输入框用于信息的输入,提供给使用者来进行输入;列表框、单选框、多选框属于同一类型,通过选择一种或者多种来准备下一步的事件。
对于python如果使用tkinter模块,上述图片中这些控件需要通过代码编写来实现,而不是如VB或者VC那样提供控件库,在窗体上拖拽即可快速实现布局。所以在实现控件的布局方面还是有一定挑战的,除了要使用代码生成这些控件外,还得使用代码来实现布局,也就是这些控件的摆放位置和顺序。好在是记住规律,多实践也能搞定。
(2)tkinter基本控件的绘制
下面我们来使用代码生成常用的控件,同时还需要使用布局方法。
绘制Label标签控件: Label(root)
标签是窗体中最基本的控件,用于清楚标注窗体各个组成部分的内容。在tkinter中采用Label函数来在窗体中设计标签,基本用法为:
label=Label( rootWindow) -- 括号中的参数表示在rootWindow窗体上绘制Label标签,返回一个标签对象。
标签的基本属性包括:text -- 标签内容 ; font -- 字体属性 ; fg -- 字体颜色 ; bg -- 背景颜色 ; image -- 背景图片。配置这些基本属性时,可以与绘制标签一起,也可以后续配置。如果与创建标签同时配置属性,写法为:
label=Label(rootWindow,text = 'hello', fg= 'red' )
也可以将创建过程与配置属性过程分离,即:
label=Label(rootWindow)
label['text']='hello'
有了控件后,如何摆放布局也是需要设计的。在tkinter中有三种布局方法,pack、grid、place。
其中pack方法为自适应布局方法,即根据控件大小和顺序来自动实现布局,默认让一个控件独立占一行并居中显示,也可以加入参数调整位置,其基本用法为:
ui.pack() #ui为控件名
或者:
ui1.pack(side='LEFT') #设定ui显示在窗体左侧,这时右侧也需要有一个控件
ui2.pack(side='RIGHT')
grid方法为网格布局,即基于设定将窗体分割为相等大小的单元格,用行和列位置来布局控件,其基本用法为:
ui.grid(row=m,column=n) #m,n为设定控件所在的行数和列数位置
place方法为空间布局,可以自由设定控件元素的起始x和y坐标:
ui.place(x=100,y=100) #设定该控件左上角坐标为x:100,y:100
下面进入第一个示例事件,即在窗体上绘制一个标签,并设置基本属性和布局方式:
from tkinter import *
root=Tk()
root.wm_title('hello,python')
root.geometry('300x200')
#在窗体root上添加label标签
label=Label(root) #调用Label绘制函数,root参数为根窗体对象,即在root窗体上绘制label控件
label['text']='welcome to the first GUI program using python!' #设置text属性,即显示内容
label['font']=14 #设置font属性,包括字体大小、字体类型等
lable['fg']='red' #设置fg前景颜色,这里就是字体颜色
label.pack() #使用pack方法实现空间的自动布局
root.mainloop() #窗体消息循环,运行窗体对象
上述代码中使用Label函数绘制了一个标签,设置了标签的显示内容和字体大小。该标签是依附于root窗体中的,所以Label函数里窗体对象为root。标签内容显示使用其text属性key配置,字体大小使用font属性。创建了标签,还需要设置其显示的位置。这里先使用pack方法来实现空间的默认自动布局。运行该代码后,可以看到创建的标签自动居中显示:
绘制Button按钮控件: Button(root)
接下来采用相同的结构来创建一个按钮,按钮是使用频率最高的控件类型。有了按钮,就可以设置当点击按钮时将会执行的事件,也即响应事件。
对于按钮,其基本属性包括:
text--按钮显示文本;command--点击响应事件 ; fg-- 前景字体颜色 ; bg--背景颜色
如下代码:
from tkinter import *
root=Tk()
root.wm_title('hello,python')
root.geometry('300x200')
#在窗体root上添加label标签
label=Label(root)
label['text']='welcome to the first GUI program using python!'
label['font']=14 #标签字体大小
label['fg']='red' #标签文本颜色为红色
label.pack() #标签pack方法布局
#在窗体root上添加Button按钮,同时给定点击事件函数
def callback(): #定义回调函数,当点击按钮时,设置标签对象的文本颜色为蓝色
label['fg']='blue' #将label标签对象的文本设置为蓝色
btn=Button(root) #在root窗体上绘制一个Button按钮对象
btn['text']='点击我,标签字体颜色将变成蓝色哦' #按钮上的文本内容
btn['font']=14 #设置文本大小为14号字体
btn['fg']='white' #设置fg属性,按钮文本颜色为白色
btn['bg']='blue' #设置bg属性,按钮背景颜色为蓝色
btn['command']=callback #定义点击按钮时响应事件为callback函数
btn.pack() #设置按钮对象在窗体上的位置
root.mainloop() #运行窗体对象
代码中注解的还是比较详细,在按钮这块我们分了两部分:第一块为定义的按钮点击回调函数,也就是响应事件,使用command属性配置;第二块为绘制按钮及其布局,这里依然使用pack方法布局。注意的是按钮的回调函数需要写在绘制按钮代码块之前。后面我们将采用类的写法,可以将绘制和回调函数封装起来。执行上述代码,效果如下:
绘制文本框Entry: Entry(root)
上面介绍了绘制标签和按钮的基本方法,接下来我们换个例子引入文本输入框Entry对象控件。在许多软件或者app当访问时都需要用户注册或登录,其中控件类型就包括标签、输入框和按钮。基本布局如下:
如果要实现上述窗体设计,需要两个标签、两个文本输入框和两个按钮。
在tkinter中,输入框绘制采用Entry方法,将其依附于root窗体对象,即Entry(root)绘制一个输入框对象,所以使用: entry1= Entry(root) 生成输入框对象entry1,然后再配置entry1的必要属性,最后再进行entry1的布局。
对于输入框而言,输入文本内容后如何取得其中的值肯定是必须考虑的。tkinter中我们可以直接使用输入框对象entry1的get方法获得,即: input_str=entry1.get()。如果想清除文本框的输入,可以使用entry1对象的delete方法,使用格式为: entry1.delete(0,n),式中的n为结束的字符位置,0为输入框中的起始字符。如果要全部清除,直接将n替换为END即可。即:entry1.delete(0,END), 为整个清除。
由于需要多个控件合理并排排列,因此这里选用了另外一种布局方式,就是grid方法,见名知意,grid就是网格。网格就会使用行和列来布局,在使用时就必须给定行和列的位置,即:grid(row=m,column=n),这里的m和n为单元格所在的行和列的位置。tkinter会自动根据当前窗体大小,依据设定的行和列总数将窗体分割为设定的网格,然后依据网格控件的布局来完成内容和组件的显示。
结合前面介绍的绘制标签和绘制按钮的方法,我们可以先采用顺序方式组织代码完成这个登录界面的开发:
from tkinter import *
root=Tk()
#1.绘制两个标签说明及两个文本输入框
#采用grid布局,可以使标签与文本输入框并排显示,
#row=0,column=0,表示第一行第一格,这里放置第一个用户名标签
#row=0,column=1,表示第一行第二格,这里放置第一个文本输入框
#row=1,column=0,表示第二行第一格,这里放置第二个用户密码标签
#row=1,column=1,表示第二行第二格,这里放置第二个文本输入框
label=Label(root)
label['text']='用户名'
label['font']=14
label.grid(row=0,column=0)
entry_name=Entry(root) #Entry为输入框控件绘制方法,在root窗体上绘制
entry_name.grid(row=0,column=1)
label=Label(root)
label['text']='用户密码'
label['font']=14
label.grid(row=1,column=0)
entry_pwd=Entry(root) #Entry为输入框控件绘制方法,在root窗体上绘制
entry_pwd.grid(row=1,column=1)
#定义点击按钮响应函数
def toLogin(): #点击登录按钮时的响应事件
name=entry_name.get() #获取用户名右侧文本输入框的输入值,采用get方法
pwd=entry_pwd.get() #获取用户密码右侧文本输入框的输入值,采用get方法
if name=='admin' and pwd=='admin': #用户名和密码验证
print('welcome to my app, dear'+name)
else:
print('your information is error!')
def toReset(): #点击重置按钮时的响应事件
entry_name.delete(0,END) #清空输入框内容使用delete方法,参数为0,END,表示整个输入框都清空
entry_pwd.delete(0,END) #清空密码输入框内容
#绘制点击登录和重置按钮
btn_log=Button(root)
btn_log['text']='登录'
btn_log['font']=14
btn_log['fg']='yellow'
btn_log['bg']='green'
btn_log['padx']=15
btn_log['command']=toLogin #登录按钮的响应函数为toLogin
btn_log.grid(row=2,column=0) #表示第三行第一格,这里放置第1个登录按钮
btn_reset=Button(root)
btn_reset['text']='重填'
btn_reset['font']=14
btn_reset['padx']=15
btn_reset['fg']='white'
btn_reset['bg']='green'
btn_reset['command']=toReset #重置按钮的响应函数为toReset
btn_reset.grid(row=2,column=1) #表示第三行第二格,这里放置第2个重填按钮
root.mainloop() #运行窗体对象
执行上述代码,可以获得如下效果:
这个效果没有设计图那样美观,但基本实现了当初的设计布局。但我们再回过去看上述实现代码时按顺序组织下来会觉得比较混乱,案例中还只是非常简单控件组合,如果有更多的控件,这样组织代码肯定不容易理解。因此还需要使用类的编写方法将代码进行封装,便于阅读和理解。上述案例代码可以改写如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 23 19:57:56 2019
@author: caojianhua
"""
from tkinter import *
class myWindow():
#定义构造函数,绘制窗体及控件
def __init__(self,master=None):
self.master=master
self.master.wm_title('hello,python')
self.master.geometry('300x200')
self.createWidgets()
#定义绘制控件函数
def createWidgets(self):
label=Label(self.master)
label['text']='用户名'
label['font']=14
label.grid(row=0,column=0)
entry_name=Entry(self.master)
entry_name.grid(row=0,column=1)
label=Label(self.master)
label['text']='用户密码'
label['font']=14
label.grid(row=1,column=0)
entry_pwd=Entry(self.master)
entry_pwd.grid(row=1,column=1)
btn_log=Button(self.master)
btn_log['text']='登录'
btn_log['font']=14
btn_log['fg']='yellow'
btn_log['bg']='green'
btn_log['padx']=15
btn_log['command']=self.toLogin
btn_log.grid(row=2,column=0)
btn_reset=Button(self.master)
btn_reset['text']='重填'
btn_reset['font']=14
btn_reset['padx']=15
btn_reset['fg']='white'
btn_reset['bg']='green'
btn_reset['command']=self.toReset
btn_reset.grid(row=2,column=1)
#定义点击按钮回调函数
def toLogin(self):
name=entry_name.get() #获取文本输入框的get方法
pwd=entry_pwd.get()
if name=='admin' and pwd=='admin':
print('welcome to my app, dear'+name)
else:
print('your information is error!')
def toReset(self):
entry_name.delete(0,END) #清空输入框内容使用delete方法
entry_pwd.delete(0,END)
#实例化一个窗体及控件组合
root=Tk()
UserLogWindow=myWindow(root)
root.mainloop()
选择框在窗体设计中也很常见,选择框类型包括单选、多选和列表选择。选择框是给定选项值,选中时获取选项的值为后续操作做准备。这三种类型基本处理方法一样,所以放在一起来进行实践体验。
绘制单选框Radiobutton
Radiobutton单选框:与普通按钮不同,单选框需要在选择后直接获取设置的内容选项,如点击A选择,就获得A选项的值,不过也有其他类型的触发响应。其创建方法与前述控件思路一致,使用Radiobutton函数,将单选框依附于窗体即可,如下:
radioChoice=Radiobutton(rootWindow)
radioChoce.pack()
然后其属性包括text--选项显示内容,value -- 选项自带的值,variable -- 选项变量参数,command -- 选中时响应事件。其中variable参数较为特殊,可以在外部设定值与其关联,然后在响应事件中通过该参数获取单选按钮的value值。
variable主要用于传参和绑定变量。主要参数有:variable
, textvariable
, onvalue
, offvalue
, value
。他是双向绑定的,也就是说如果该变量发生变化,随之绑定的控件也会变化,与他保持一致。常用的variable变量有:
x = StringVar() 保存一个 string 类型变量, 默认值为""
x = IntVar() 保存一个整型变量, 默认值为0
x = DoubleVar() 保存一个浮点型变量,默认值为0.0
x = BooleanVar() 保存一个布尔型变量,返回值为0表示假,1表示真
操作时主要包括两种:
设置他的值,用set()方法,即:x.set()
得到他的值,用get()方法,即:x.get()
下面通过代码实践来理解:
from tkinter import *
root=Tk()
root.geometry('300x400')
root.wm_title('hello,python')
label=Label(root,text='下列哪门编程语言是你最喜欢的?请选择:')
label.pack()
#准备好单选选择项,一般使用列表来设置
choiceList=[('python','A'),('C++','B'),('VB','C'),('GO','D')]
#使用tkinter中的StringVar类设定选项所在的值,将其与单选框中的variable绑定关联,当选择某一项时,
#可以使用get方法来获得该项的value值。
v=StringVar() #观察在单选按钮属性中variable属性值也为v,这两者进行关联绑定
v.set('A') #将单选按钮默认选择value为A的项,这里为第一项
#定义单选按钮回调函数
def callback():
choice=v.get() #获得选择项中的value值
label1['text']="您的选项为: " + choice #将label1的text属性设置为选项的值
label1['fg']='red' #设置label1的文本颜色为红色
#采用循环来设定单选按钮
for item,value in choiceList:
radioChoice=Radiobutton(root)
radioChoice['text']=item #设定单选按钮后面的内容
radioChoice['variable']=v #设定单选按钮变量v
radioChoice['value']=value #设定单选按钮自带的值
radioChoice['command']=callback #设定单选按钮回调函数
radioChoice.pack() #采用pack方法布局
#绘制一个标签用于显示选中的项
label1=Label(root)
label1['text']="您的选项为: "
label1.pack()
root.mainloop() #窗体消息循环
当运行代码时效果如图:
绘制多选框Checkbutton
复选框绘制方法与单选框思路是一样的,即使用Checkbutton类:
checkbox=Checkbutton(rootWindow)
其属性设置与单选框也基本类似,不过此时复选框里的variable参数需要各个选项不一致,也就是单独设置variable,比较麻烦。
如下实践代码:
from tkinter import *
root=Tk()
root.geometry('300x400')
root.wm_title('hello,python')
label=Label(root,text='下列哪几门编程语言是你最喜欢的?请选择:')
label.pack()
#准备好多项选择项,使用列表来设置
choiceList=[('python','A'),('C++','B'),('VB','C'),('GO','D')]
#使用tkinter中的BooleanVar类设定选项所在的值,将其与复选框中的variable绑定关联,当选择某一项时,
#可以使用get方法来获得该项的value值。这里由于每一项都有选与不选两种状态,因此如果要获得状态值,
#需要对每一个选项单独设置variable值,如下v0,v1,v2,v3等:
v0=BooleanVar()
v1=BooleanVar()
v2=BooleanVar()
v3=BooleanVar()
def callback():
choice=''
if v0.get()==True:
choice+=choiceList[0][1]
if v1.get()==True:
choice+=choiceList[1][1]
if v2.get()==True:
choice+=choiceList[2][1]
if v3.get()==True:
choice+=choiceList[3][1]
label1['text']=choice
label1['fg']='red'
#多项选择创建及布局
choice1=Checkbutton(root,text=choiceList[0][0],variable=v0,command=callback)
choice2=Checkbutton(root,text=choiceList[1][0],variable=v1,command=callback)
choice3=Checkbutton(root,text=choiceList[2][0],variable=v2,command=callback)
choice4=Checkbutton(root,text=choiceList[3][0],variable=v3,command=callback)
choice1.pack()
choice2.pack()
choice3.pack()
choice4.pack()
#显示选中内容
label1=Label(root)
label1.pack()
root.mainloop()
运行后效果如图:
列表选择框Listbox绘制
列表选择框类似于单选框,绘制时使用Listbox类,并依附于一个窗体上:
listchoice=Listbox(rootWindow)
在创建了列表选项框后,使用listvariable对列表内容赋值,并可设置选择模式selectmode,单选为browse或single,多选为multiple。选择到某一项后,还需要添加事件响应,因此对整个列表选项框使用bind方法,绑定事件类型ListboxSelect及对应的响应。如下代码实践:
from tkinter import *
root=Tk()
root.geometry('300x400')
root.wm_title('hello,python')
label=Label(root,text='编程语言选择:')
label.place(x=10,y=10) #使用place来布局
#准备好多项选择项,使用列表来设置
choiceList=['python','php','java','go']
#使用tkinter中的StringVar类设定选项所在的值,将其与列表框中的listvariable绑定关联
list1=StringVar(value=choiceList) #定义列表项
def show_msg(*args): #定义响应函数
indexs = choice1.curselection() #定义列表项所在的索引,返回索引列表
index = int(indexs[0]) #取得当前项的索引号
label1['text']="您的选项为: " +str(index) #设置当前索引号为label1标签内容
#列表选择创建及布局
choice1=Listbox(root,listvariable=list1,height=len(choiceList),selectmode='browse')
choice1.place(x=120,y=10)
choice1.bind("<<ListboxSelect>>",show_msg) #绑定选择对应的响应函数
#显示选中内容
label1=Label(root)
label1.place(x=10,y=100)
root.mainloop()
执行后效果如图:
组合选择框Combobox绘制
Combobox控件属于ttk模块类中的元素,因此在绘制组合选择框时需要导入ttk模块,然后使用ttk.Combobox(rootWindow)方法来完成。即:
cmb=ttk.Combobox(rootWindow)
其属性主要包括: value--选择列表内容。添加事件响应时使用bind函数,对整个组合框对象设置ComboboxSelected动作。如果要获得选择的内容,可以使用cmb对象的.get方法。如下代码实践:
from tkinter import *
#导入ttk模块,Combobox控件属于ttk模块的对象
from tkinter import ttk
root=Tk()
root.geometry('300x400')
root.wm_title('hello,python')
label=Label(root,text='编程语言选择:')
label.place(x=10,y=10)
#定义选择响应函数
def callback(*args):
text=choice1.get()
label1['text']="您的选项为: " +text
#组合框创建及布局
choice1=ttk.Combobox(root)
choice1['value']=['python','php','java','go']
choice1.place(x=120,y=10)
choice1.bind("<<ComboboxSelected>>",callback)
#显示选中内容
label1=Label(root)
label1['text']="您的选项为: "
label1.place(x=10,y=40)
root.mainloop()
执行后效果如图:
(3)菜单类控件设计
通常对于一个窗体应用程序而言,菜单是非常重要的构成部分。通过选择菜单,来完成对应的操作。因此在开发这类窗体时,主要关心菜单及其子菜单如何生成、菜单的点击事件响应。至于菜单的布局,默认会放在窗体的顶端,不过也可以通过后续布局来修改。例如python自带的IDLE:
主菜单创建Menu
在窗体上绘制菜单时,使用Menu类,并将创建的菜单实例与窗体进行绑定。即:
mainMenu=Menu(root) #指定父级窗口
root(menu=mainMenu) #在父级窗口里设定menu属性
有了主菜单对象后,就可以使用add_command方法将菜单标签内容及响应函数添加到主菜单中。 add_command 中的参数常用的有 label 属性,用来指定的 是菜单项的名称,command 属性用来指定被点击的时候调用的方法, acceletor 属性指定的是快捷键,underline 属性是否拥有下划线。
例如创建如IDLE主菜单代码:
from tkinter import *
root=Tk()
root.wm_title('hello,python')
root.geometry('400x200')
#主菜单内容列表
mainMenuList=['File','Edit','Format','Run','Options','Window','Help']
#在root窗体中创建菜单,返回menubar对象
menubar=Menu(root)
#给每个主菜单添加标签内容
for item in mainMenuList:
menubar.add_command(label=item)
#配置父级root窗体的menu属性
root['menu']=menubar
root.mainloop()
执行后效果如下:
效果与IDLE基本一致。
子菜单创建
接下来我们添加一下子菜单。一般情况下主菜单都仅仅是标签内容显示,实际执行菜单多放置在子菜单内。添加子菜单时也需要指定子菜单的父级控件,例如要是Help菜单下创建子菜单,那Help菜单就是其子菜单的父级控件。在子菜单中需要给定点击响应函数,一般情况下点击响应函数都会不一样,因此在实际开发代码的时候需要单独给定,如下代码实践:
from tkinter import *
root=Tk()
root.wm_title('hello,python')
root.geometry('400x200')
#创建root窗体里的菜单对象,同时添加主菜单内容
menubar=Menu(root)
menubar.add_cascade(label='File')
menubar.add_cascade(label='Edit')
menubar.add_cascade(label='Format')
menubar.add_cascade(label='Run')
menubar.add_cascade(label='Options')
menubar.add_cascade(label='Window')
#定义help子菜单回调函数
def toAbout():
label['text']='当前选中的子菜单为:'+helpMenu[0]
def toHelp():
label['text']='当前选中的子菜单为:'+helpMenu[1]
def toDoc():
label['text']='当前选中的子菜单为:'+helpMenu[2]
def toTurtle():
label['text']='当前选中的子菜单为:'+helpMenu[3]
#构建help主菜单及其子菜单内容,例如构建help菜单及其子菜单
helpMenu=['About IDLE','IDLE Help','Python Docs F1','Turtle Demo']
hmenu=Menu(menubar)
hmenu.add_command(label=helpMenu[0],command=toAbout)
hmenu.add_command(label=helpMenu[1],command=toHelp)
hmenu.add_command(label=helpMenu[2],command=toDoc)
hmenu.add_command(label=helpMenu[3],command=toTurtle)
#构建help主菜单,同时添加子菜单
menubar.add_cascade(label='Help',menu=hmenu)
root['menu']=menubar
#在窗体内增加一个标签,显示当前选中的菜单内容
label=Label(root)
label.pack()
#主窗体循环,启动显示
root.mainloop()
上面的代码仅仅对help主菜单添加了子菜单,同时给每个子菜单添加了点击事件响应,效果如下:
如果要完成类似IDLE窗体,还差一个文本输入框和右侧的滚动侧边栏。我们试着添加一下,代码如下:
from tkinter import *
root=Tk()
root.wm_title('hello,python')
root.geometry('400x200')
#创建root窗体里的菜单对象,同时添加主菜单内容
menubar=Menu(root)
menubar.add_cascade(label='File')
menubar.add_cascade(label='Edit')
menubar.add_cascade(label='Format')
menubar.add_cascade(label='Run')
menubar.add_cascade(label='Options')
menubar.add_cascade(label='Window')
#定义help子菜单回调函数
def toAbout():
label['text']='当前选中的子菜单为:'+helpMenu[0]
def toHelp():
label['text']='当前选中的子菜单为:'+helpMenu[1]
def toDoc():
label['text']='当前选中的子菜单为:'+helpMenu[2]
def toTurtle():
label['text']='当前选中的子菜单为:'+helpMenu[3]
#构建help主菜单及其子菜单内容,例如构建help菜单及其子菜单
helpMenu=['About IDLE','IDLE Help','Python Docs F1','Turtle Demo']
hmenu=Menu(menubar)
hmenu.add_command(label=helpMenu[0],command=toAbout)
hmenu.add_command(label=helpMenu[1],command=toHelp)
hmenu.add_command(label=helpMenu[2],command=toDoc)
hmenu.add_command(label=helpMenu[3],command=toTurtle)
#构建help主菜单,同时添加子菜单
menubar.add_cascade(label='Help',menu=hmenu)
root['menu']=menubar
#在窗体内增加一个文本框,右侧为滚动侧边栏,同时将滚动栏与文本框内容绑定显示
TextArea=Text(root)
TextArea.pack(side='left',fill=Y)
scrollbtn=Scrollbar(root)
scrollbtn.pack(side='right',fill=Y)
TextArea.config(yscrollcommand=scrollbtn.set)
scrollbtn.config(command=TextArea.yview)
#主窗体循环,启动显示
root.mainloop()
再来运行,效果如图:
(4)Frame控件
Frame就是框架容器,可以在里面添加各种控件,其实root根窗体就是一个大的Frame。在tkinter里有单独的Frame类,使用Frame类方法就可以创建一个frame,然后设置其布局方式。
我们先来进行实践,而后解释:
from tkinter import *
root=Tk()
root.wm_title('hello,python')
root.geometry('400x200')
root.resizable(0,0)
label=Label(root,text='frame框架的例子').pack()
#左侧新建一个frame容器,里面放置单选框
frm1=Frame(root,width=100,height=200,bg='#ddd')
frm1.pack(side='left',fill=Y)
label=Label(frm1,text='请选择',bg='#ddd',font=15)
label.place(x=5,y=5)
#准备好单选内容列表
choiceList=[('米老鼠',1),('唐老鸭',2),('猪八戒',3),('孙悟空',4)]
#引入IntVar类,存储全局的v变量值
v=IntVar()
v.set(1) #设置v值为1,即默认选择为第一项
#单选时回调函数
def callback():
value=v.get()
if value==1:
label2['text']="唐老鸭,即唐纳德。(英文名称:Donald Fauntleroy Duck)是
迪士尼所创的经典卡通萌星之一,官方生日是1934年6月9日,
首次出现在《聪明的小母鸡》里。它有橙黄色的嘴、脚和蹼,
身上穿的是方特罗伊水手装,但没有穿裤子"
elif value ==2:
label2['text']="米奇老鼠(英文名称:Mickey Mouse),迪士尼代表人物形象,是一只
有着圆滚滚的大脑袋,圆滚滚的大耳朵,梨形的身体与像橡胶
软管一样柔软,没有明显的关节,可以自由拉伸仿佛没有骨骼
的四肢的小老鼠。"
elif value ==3:
label2['text']=" 孙悟空是中国著名的神话人物之一,出自四大名著之《西游记》。祖籍
东胜神州,由开天辟地以来的仙石孕育而生,因带领群猴进入
水帘洞而成为众猴之王,尊为 “美猴王”。"
elif value ==4:
label2['text']= "猪八戒是明朝小说家吴承恩所作《西游记》中登场的虚拟角色。又名猪
刚鬣,法号悟能(观音取),浑名八戒(唐僧取),是唐僧的
二徒弟,会天罡数的三十六般变化,所持武器为太上老君所造
、玉皇大帝亲赐的上宝沁金钯(俗称九齿钉耙)"
#在左侧frame中设置单选项
for item,value in choiceList:
radioChoice=Radiobutton(frm1,bg='#ddd',font=(14))
radioChoice['text']=item
radioChoice['variable']=v
radioChoice['value']=value
radioChoice['command']=callback
radioChoice.place(x=5,y=10+value*25)
#右侧新建一个frame容器,里面放置标签
frm2=Frame(root,width=298,height=150,bg='lightgreen',relief='groove')
frm2.pack(side='left',fill=Y)
label2=Label(frm2,font=16,anchor="w", justify="left",wraplength=300,bg='lightgreen')
label2.place(x=1,y=1)
运行后效果如图:
我在上述案例中布局顺序为:先布局一个标签,内容为“frame框架的例子", 采用pack方法布局,让其占满第一行并居中显示;然后布局两个frame,一个在左侧,一个在右侧,左侧frame中构建单选列表,右侧frame中构建一个label标签,用于左侧frame中单选框选择时,其text属性为关联的内容。
从代码看来,创建frame的方式为:
fm=Frame(rootWindow) #创建frame
fm.pack() #实现窗体布局方式
创建了frame后,就可以将其作为一个父级容器,在里面可以绘制其他控件,如绘制一个label标签,就可以使用如下代码:
label=Label(fm) #以frame为父级容器创建label标签
label.place(x=0,y=0) #建议采用place方式布局更为精确
(5)Canvas控件
与Frame类似,Canvas也是一种框架容器,可以在里面添加各种控件,不过canvas从字面意思是画布,所以主要使用canvas容器在上面进行图形绘制。这里的图形包括直线、椭圆、多边形、图片等多种类型,主要使用方式为:
canvas=Canvas(rootWindow,width=500,height=300) #在父级容器中创建一个画布
canvas.pack() #对画布进行布局位置设定
有了画布,就可以在上面绘制多种图形了,主要使用方式如下:
canvas.create_line(0,0,100,100,fill='blue') #绘制一个直线,给定起始和终点坐标,fill表示填充颜色
canvas.create_polygon(0,0,100,100,200,130) #绘制一个polygon,需要至少三个点坐标
canvas.create_oval(20,20,40,40) #绘制一个椭圆
canvas.create_rectangle(10,10,300,250) #绘制多边形
下面我以上述frame布局案例为基础,修改一下代码,左侧frame仍为单选项,设置为绘制图形的类型,右侧frame中创建一个画布,然后根据左侧的选择在上面绘制不同的图形。
from tkinter import *
import time #导入time时间包
import random #导入随机模块
root=Tk()
root.wm_title('hello,python')
root.geometry('400x200')
root.resizable(0,0)
label=Label(root,text='canvas的例子').pack()
#左侧新建一个frame容器,里面放置单选框
frm1=Frame(root,width=100,height=200,bg='#ddd')
frm1.pack(side='left',fill=Y)
label=Label(frm1,text='请选择类型',bg='#ddd',font=15)
label.place(x=5,y=5)
#准备好单选内容列表
choiceList=[('画直线',1),('画多边形',2),('画椭圆',3),('动画示例',4)]
#引入IntVar类,存储全局的v变量值
v=IntVar()
v.set(1) #设置v值为1,即默认选择为第一项
#单选时回调函数
def callback():
value=v.get()
if value==1:
canvas.delete(ALL) #清除画布
canvas.create_line(1,1,120,120,fill='red') #画直线
elif value ==2:
canvas.delete(ALL)
canvas.create_polygon(10,10,120,120,200,80,fill='blue') #画polygon,并充填
elif value ==3:
canvas.delete(ALL)
canvas.create_oval(50,50,70,70,fill='purple') #画一个圆
elif value ==4:
canvas.delete(ALL)
canvas.create_oval(50,50,70,70,fill='red') #画一个圆,当画布的位置设定时间循环移动时就可以形成动画
for i in range(0,60): #设定一共循环60次
dx=random.randint(-15,15) #x方向随机变化取值
dy=random.randint(-13,13) #y方向随机变化取值
canvas.move(1,dx,dy) #按设定的x和y变动方向移动画布
frm2.update() #移动后frm2容器刷新
time.sleep(0.25) #间隔0.25秒后进入下一次循环
#在左侧frame中设置单选项
for item,value in choiceList:
radioChoice=Radiobutton(frm1,bg='#ddd',font=(14))
radioChoice['text']=item
radioChoice['variable']=v
radioChoice['value']=value
radioChoice['command']=callback
radioChoice.place(x=5,y=10+value*25)
#右侧新建一个frame容器,里面放置画布
frm2=Frame(root,width=300,height=200,bg='lightgreen',relief='groove')
frm2.pack(side='left',fill=Y)
canvas=Canvas(frm2,width=298,height=198) #创建一个画布
canvas.place(x=1,y=1) #画布以父级容器为参考,左上角坐标为1,1
执行代码,效果如图:
当点击动画示例时,可以得到如下效果:
https://www.zhihu.com/video/1182073111012433920
由此可以使用tkinter及画布组合来设计很多好玩的小游戏呢。
至此本文就tkinter的窗体设计开发方面进行了主要功能的介绍,还有一些如开篇介绍的颜色选择器、文件选择器、ttk、tix等模块都未涉及,后续再进行爬虫软件开发时再来详细介绍,有兴趣的同学也可以自行查询tkinter文档来实际体验。