- 1、图形化界面设计的基本理解
- 当前流行的计算机桌面应用程序大多数为图形化用户界面(Graphic User Interface,GUI),即通过鼠标对菜单、按钮等图形化元素触发指令,并从标签、对话框等图型化显示容器中获取人机对话信息。Python自带了tkinter 模块,实质上是一种流行的面向对象的GUI工具包 TK 的Python编程接口,提供了快速便利地创建GUI应用程序的方法。其图像化编程的基本步骤通常包括:
- 导入 tkinter 模块
- 创建 GUI 根窗体
- 添加人机交互控件并编写相应的函数。
- 在主事件循环中等待用户触发事件响应。
- 2、窗体控件布局
- 2.1、根窗体是图像化应用程序的根控制器,是tkinter的底层控件的实例。当导入tkinter模块后,调用 Tk()方法可初始化一个根窗体实例 root ,用 title() 方法可设置其标题文字,用geometry()方法可以设置窗体的大小(以像素为单位)。将其置于主循环中,除非用户关闭,否则程序始终处于运行状态。执行该程序,一个窗体就呈现出来了。在这个主循环的根窗体中,可持续呈现中的其他可视化控件实例,监测事件的发生并执行相应的处理程序。下面是根窗体呈现示例:
from tkinter import * root= Tk() root.title('我的第一个Python窗体') root.geometry('240x240') # 这里的乘号不是 * ,而是小写英文字母 x root.mainloop()
2.2、tkinter 常用控件
- 常用控件:常用的10 多种,如下:
控件 | 名称 | 作用 |
Button | 按钮 | 单击触发事件 |
Canvas | 画布 | 绘制图形或绘制特殊控件 |
Checkbutton | 复选框 | 多项选择 |
Entry | 输入框 | 接收单行文本输入 |
Frame | 框架 | 用于控件分组 |
Label | 标签 | 单行文本显示 |
Lisbox | 列表框 | 显示文本列表 |
Menu | 菜单 | 创建菜单命令 |
Message | 消息 | 多行文本标签,与Label 用法类似 |
Radiobutton | 单选按钮 | 从互斥的多个选项中做单项选择 |
Scale | 滑块 | 默认垂直方向,鼠标拖动改变数值形成可视化交互 |
Scrollbar | 滑动条 | 默认垂直方向,课鼠标拖动改变数值,可与 Text、Lisbox、Canvas等控件配合移动可视化空间 |
Text | 文本框 | 接收或输出显示多行文本 |
Toplevel | 新建窗体容器 | 在顶层创建新窗体 |
- 控件的共同属性:在窗体上呈现的可视化控件,通常包括尺寸、颜色、字体、相对位置、浮雕样式、图标样式和悬停光标形状等共同属性。不同的控件由于形状和功能不同,又有其特征属性。在初始化根窗体和根窗体主循环之间,可实例化窗体控件,并设置其属性。父容器可为根窗体或其他容器控件实例。常见的控件共同属性如下表:
属性 | 说明 | 取值 |
anchor | 文本起始位置 | CENTER(默认),E,S,W,N,NE,SE,SW,NW |
bg | 背景色 | 无 |
bd | 加粗(默认 2 像素) | 无 |
bitmap | 黑白二值图标 | 网上查找 |
cursor | 鼠标悬停光标 | 网上查找 |
font | 字体 | 无 |
fg | 前景色 | 无 |
height | 高(文本控件的单位为行,不是像素) | 无 |
image | 显示图像 | 无 |
justify | 多行文本的对其方式 | CENTER(默认),LEFT,RIGHT,TOP,BOTTOM |
padx | 水平扩展像素 | 无 |
pady | 垂直扩展像素 | 无 |
relief | 3D浮雕样式 | FLAT,RAISED,SUNKEN,GROOVE,RIDGE |
state | 控件实例状态是否可用 | NORMAL(默认),DISABLED |
width | 宽(文本控件的单位为行,不是像素) | 无 |
- 标签及常见属性示例:
from tkinter import * root = Tk() lb = Label(root,text='我是第一个标签',\ bg='#d3fbfb',\ fg='red',\ font=('华文新魏',32),\ width=20,\ height=2,\ relief=SUNKEN) lb.pack() root.mainloop()
- 其中,标签实例lb 在父容器root中实例化,具有代码中所示的text(文本)、bg(背景色)、fg(前景色)、font(字体)、width(宽,默认以字符为单位)、height(高,默认以字符为单位)和 relief(浮雕样式)等一系列属性。
在实例化控件时,实例的属性可以“属性=属性值”的形式枚举列出,不区分先后次序。例如:“ text='我是第一个标签' ”显示标签的文本内容,“bg='#d3fbfb'”设置背景色为十六进制数RGB色 #d3fbfb等等。属性值通常用文本形式表示。
当然如果这个控件实例只需要一次性呈现,也可以不必命名,直接实例化并布局呈现出来,例如:
Label(root,text='我是第一个标签',font='华文新魏').pack()
属性 relief 为控件呈现出来的3D浮雕样式,有 FLAT(平的)、RAISED(凸起的)、SUNKEN(凹陷的)、GROOVE(沟槽状边缘)和 RIDGE(脊状边缘) 5种。
2.2、控件布局
控件的布局通常有pack()
、grid()
和 place()
三种方法。
- 2.2.1、pack()方法:是一种简单的布局方法,如果不加参数的默认方式,将按布局语句的先后,以最小占用空间的方式自上而下地排列控件实例,并且保持控件本身的最小尺寸。如下的例子:
- 用pack() 方法不加参数排列标签。为看清楚各控件所占用的空间大小,文本用了不同长度的中英文,并设置relief=GROOVE的凹陷边缘属性。如下所示:
from tkinter import * root = Tk() lbred = Label(root,text="Red",fg="Red",relief=GROOVE) lbred.pack() lbgreen = Label(root,text="绿色",fg="green",relief=GROOVE) lbgreen.pack() lbblue = Label(root,text="蓝",fg="blue",relief=GROOVE) lbblue.pack() root.mainloop()
- 使用pack()方法可设置 fill、side 等属性参数。其中,参数fill 可取值:fill=X,fill=Y或fill=BOTH,分别表示允许控件向水平方向、垂直方向或二维伸展填充未被占用控件。参数 side 可取值:side=TOP(默认),side=LEFT,side=RIGHT,side=BOTTOM,分别表示本控件实例的布局相对于下一个控件实例的方位。如下例子:
from tkinter import * root = Tk() lbred = Label(root,text="Red",fg="Red",relief=GROOVE) lbred.pack() lbgreen = Label(root,text="绿色",fg="green",relief=GROOVE) lbgreen.pack(side=RIGHT) lbblue = Label(root,text="蓝",fg="blue",relief=GROOVE) lbblue.pack(fill=X) root.mainloop()
- 2.2.2、grid()方法:是基于网格的布局。先虚拟一个二维表格,再在该表格中布局控件实例。由于在虚拟表格的单元中所布局的控件实例大小不一,单元格也没有固定或均一的大小,因此其仅用于布局的定位。pack()方法与grid()方法不能混合使用。grid()方法常用布局参数如下:
- column: 控件实例的起始列,最左边为第0列。
- columnspan: 控件实例所跨越的列数,默认为1列。
- ipadx,ipady: 控件实例所呈现区域内部的像素数,用来设置控件实例的大小。
- padx,pady: 控件实例所占据空间像素数,用来设置实例所在单元格的大小。
- row: 控件实例的起始行,最上面为第0行。
- rowspan: 控件实例的起始行数,默认为1行。
- 看下面的例子:用grid()方法排列标签,设想有一个3x4的表格,起始行、列序号均为0.将标签lbred 至于第2列第0行;将标签lbgreen置于第0列第1行;将标签lbblue置于第1列起跨2列第2行,占20像素宽。
from tkinter import * root = Tk() lbred = Label(root,text="Red",fg="Red",relief=GROOVE) lbred.grid(column=2,row=0) lbgreen = Label(root,text="绿色",fg="green",relief=GROOVE) lbgreen.grid(column=0,row=1) lbblue = Label(root,text="蓝",fg="blue",relief=GROOVE) lbblue.grid(column=1,columnspan=2,ipadx=20,row=2) root.mainloop()
- 2.2.3、place()方法:根据控件实例在父容器中的绝对或相对位置参数进行布局。其常用布局参数如下:
- x,y:控件实例在根窗体中水平和垂直方向上的其实位置(单位为像素)。注意,根窗体左上角为0,0,水平向右,垂直向下为正方向。
- relx,rely:控件实例在根窗体中水平和垂直方向上起始布局的相对位置。即相对于根窗体宽和高的比例位置,取值在
0.0~1.0
之间。 - height,width:控件实例本身的高度和宽度(单位为像素)。
- relheight,relwidth:控件实例相对于根窗体的高度和宽度比例,取值在
0.0~1.0
之间。 - 利用place()方法配合relx,rely和relheight,relwidth参数所得的到的界面可自适应根窗体尺寸的大小。place()方法与grid()方法可以混合使用。如下例子:利用place()方法排列消息(多行标签)。
from tkinter import * root = Tk() root.geometry('320x240') msg1 = Message(root,text='''我的水平起始位置相对窗体 0.2,垂直起始位置为绝对位置 80 像素,我的高度是窗体高度的0.4,宽度是200像素''',relief=GROOVE) msg1.place(relx=0.2,y=80,relheight=0.4,width=200) root.mainloop()
- 3、tkinter常见控件的特征属性
- 3.1、文本输入和输出相关控件:文本的输入与输出控件通常包括:标签(Label)、消息(Message)、输入框(Entry)、文本框(Text)。他们除了前述共同属性外,都具有一些特征属性和功能。
- 标签(Label)和 消息(Message):除了单行与多行的不同外,属性和用法基本一致,用于呈现文本信息。值得注意的是:属性text通常用于实例在第一次呈现时的固定文本,而如果需要在程序执行后发生变化,则可以使用下列方法之一实现:1、用控件实例的configure()方法来改变属性text的值,可使显示的文本发生变化;2、先定义一个tkinter的内部类型变量var=StringVar() 的值也可以使显示文本发生变化。看下面的一个例子:制作一个电子时钟,用root的after()方法每隔1秒time模块以获取系统当前时间,并在标签中显示出来。
- 方法一:利用configure()方法或config()来实现文本变化。
import tkinter import time def gettime(): timestr = time.strftime("%H:%M:%S") # 获取当前的时间并转化为字符串 lb.configure(text=timestr) # 重新设置标签文本 root.after(1000,gettime) # 每隔1s调用函数 gettime 自身获取时间 root = tkinter.Tk() root.title('时钟') lb = tkinter.Label(root,text='',fg='blue',font=("黑体",80)) lb.pack() gettime() root.mainloop()
- 方法二:利用textvariable变量属性来实现文本变化。
import tkinter import time def gettime(): var.set(time.strftime("%H:%M:%S")) # 获取当前时间 root.after(1000,gettime) # 每隔1s调用函数 gettime 自身获取时间 root = tkinter.Tk() root.title('时钟') var=tkinter.StringVar() lb = tkinter.Label(root,textvariable=var,fg='blue',font=("黑体",80)) lb.pack() gettime() root.mainloop()
- 文本框(Text):
文本框的常用方法如下:
方法 | 功能 |
delete(起始位置,[,终止位置]) | 删除指定区域文本 |
get(起始位置,[,终止位置]) | 获取指定区域文本 |
insert(位置,[,字符串]...) | 将文本插入到指定位置 |
see(位置) | 在指定位置是否可见文本,返回布尔值 |
index(标记) | 返回标记所在的行和列 |
mark_names() | 返回所有标记名称 |
mark_set(标记,位置) | 在指定位置设置标记 |
mark_unset(标记) | 去除标记 |
- 上表位置的取值可为整数,浮点数或END(末尾),例如0.0表示第0列第0行
如下一个例子:每隔1秒获取一次当前日期的时间,并写入文本框中,如下:本例中调用 datetime.now()获取当前日期时间,用insert()方法每次从文本框txt的尾部(END)开始追加文本。
from tkinter import * import time import datetime def gettime(): s=str(datetime.datetime.now())+'\n' txt.insert(END,s) root.after(1000,gettime) # 每隔1s调用函数 gettime 自身获取时间 root=Tk() root.geometry('320x240') txt=Text(root) txt.pack() gettime() root.mainloop()
- 输入框(Entry):通常作为功能比较单一的接收单行文本输入的控件,虽然也有许多对其中文本进行操作的方法,但通常用的只有取值方法get()和用于删除文本的delete(起始位置,终止位置),例如:清空输入框为delete(0,END)。
- 3.2、按钮(Button):主要是为响应鼠标单击事件触发运行程序所设的,故其除控件共有属性外,属性command是最为重要的属性。通常,将按钮要触发执行的程序以函数形式预先定义,然后可以用一下两种方法调用函数。Button按钮的状态有:
'normal','active','disabled'
- 直接调用函数。参数表达式为“command=函数名”,注意函数名后面不要加括号,也不能传递参数。如下面的command=run1:
- 利用匿名函数调用函数和传递参数。参数的表达式为“command=lambda”:函数名(参数列表)。例如下面的:"command=lambda:run2(inp1.get(),inp2.get())"。
- 看下面的例子:1.从两个输入框去的输入文本后转为浮点数值进行加法运算,要求每次单击按钮产生的算是结果以文本的形式追加到文本框中,将原输入框清空。2.按钮方法一不传参数调用函数run1()实现,按钮“方法二”用lambda调用函数run2(x,y)同时传递参数实现。
from tkinter import * def run1(): a = float(inp1.get()) b = float(inp2.get()) s = '%0.2f+%0.2f=%0.2f\n' % (a, b, a + b) txt.insert(END, s) # 追加显示运算结果 inp1.delete(0, END) # 清空输入 inp2.delete(0, END) # 清空输入 def run2(x, y): a = float(x) b = float(y) s = '%0.2f+%0.2f=%0.2f\n' % (a, b, a + b) txt.insert(END, s) # 追加显示运算结果 inp1.delete(0, END) # 清空输入 inp2.delete(0, END) # 清空输入 root = Tk() root.geometry('460x240') root.title('简单加法器') lb1 = Label(root, text='请输入两个数,按下面两个按钮之一进行加法计算') lb1.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.1) inp1 = Entry(root) inp1.place(relx=0.1, rely=0.2, relwidth=0.3, relheight=0.1) inp2 = Entry(root) inp2.place(relx=0.6, rely=0.2, relwidth=0.3, relheight=0.1) # 方法-直接调用 run1() btn1 = Button(root, text='方法一', command=run1) btn1.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1) # 方法二利用 lambda 传参数调用run2() btn2 = Button(root, text='方法二', command=lambda: run2(inp1.get(), inp2.get())) btn2.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1) # 在窗体垂直自上而下位置60%处起,布局相对窗体高度40%高的文本框 txt = Text(root) txt.place(rely=0.6, relheight=0.4) root.mainloop()