Python tkinter(GUI编程)模块最完整讲解(上)
Posted Python zzy Tk+Pg
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python tkinter(GUI编程)模块最完整讲解(上)相关的知识,希望对你有一定的参考价值。
提示:下滑文章左侧可以查看目录!
1 走进tkinter世界
1.1 认识tkinter
tkinter是一个GUI开发模块,是Tcl/Tk语言在Python上的接口,可以在大部分操作系统上运行。tkinter非常的简单而且好用。tkinter模块是自带的Python模块,如果在安装Python的时候勾选了Tcl/Tk这个选项,那么使用tkinter不会有任何问题。
导入模块非常简单,但是Python3和Python2略有不同,Python3是这样的:
import tkinter
本文的示例以Python3为准,而Python2是这样的:
import Tkinter #Tkinter开头的t是大写的
不过tkinter这个名字非常长,所以我们通常习惯这么导入:
import tkinter as tk
from tkinter import *
如果导入时候就出现了错误,提示找不到_tkinter这一模块,或者调用里面的方法时出现版本错误提示,可能是因为安装时不到位,没有勾选Tk/Tcl这一选项。在安装包中选择Modify,更改Python的安装即可
接下来让我们了解一下自己tkinter的版本:
import tkinter
print(tkinter.TkVersion)
最好是使用8.5 Version以上的tkinter,功能比较全面一些。
1.2 tkinter的坐标系与颜色格式
坐标系
组件的排放,鼠标事件等功能都少不了坐标。tkinter的坐标系和数学上习惯用的坐标系略有不同,和pygame的坐标系是一样的。
以左上角为起点,x轴向右延伸,y轴向下延伸。在窗口中,容器的左上角是(0, 0),不包括窗口的标题栏和菜单栏。
颜色
当在tkinter中设置颜色时,可以用两种表示颜色的方式:一种是颜色的名称,比如"green", "brown";另一种是颜色的十六进制形式,比如"#00ffff"。遗憾的是,tkinter不支持颜色RGB元组形式,不过可以把它转换成十六进制形式。
这种十六进制形式相当于:"#"+R的十六进制+G的十六进制+B的十六进制。比如(255, 255, 255)是纯白,转换成十六进制形式就变成了#ffffff。
tkinter也有一种特殊的颜色名称,叫做SystemButtonFace,是一种浅灰色,是组件的默认背景颜色。
1.3 创建根窗口
根窗口是最主要的一个窗口,根窗口最好只有一个,因为一个Tk就是一个新的Tcl/Tk解释器,解释器并不需要太多。
根窗口使用tkinter中的Tk方法创建。在窗口中,我们可以添加各种各样的控件,也称组件(widget),比如按钮、文本输入框等,我们将在后期介绍。窗口也可以有一些子窗口。当父窗口关闭后,所有的子窗口会跟着关闭,但是子窗口关闭,父窗口不会关闭。
from tkinter import *
root = Tk()
mainloop()
这一段代码创建一个窗口,并且循环显示这个窗口。mainloop方法,可以让窗口循环显示,否则运行时窗口一闪就没了。一定不要忘记mainloop!mainloop也可以用while True: root.update()这一段代替,不过mainloop更加常用一些。mainloop也可以作为窗口的一个方法,即root.mainloop()。
这段代码创建了一个独立的窗口,默认标题叫tk,你可以在下面的任务栏找到它。同样,你也可以自由拖拽它的位置,改变窗口的大小。也可以把它关闭、最小化、最大化。
Tk(screenName=None, baseName=None, className='Tk', useTk=1, sync=0, use=None)
Tk有一个参数叫做className,允许你改变窗口标题。但是这样改变标题有一个bug,就是窗口标题的首字母会自动小写,因此不推荐你这么做,而应使用title方法。Tk的参数并不常用,但有一些比较基础常用的方法,更多的方法,请参见2.2.14。
方法 | 使用方法 |
title(string=None) | 设置窗口的标题,同时返回窗口标题 |
geometry(newGeometry=None) | 设置窗口的尺寸大小,同时返回当前窗口尺寸 |
iconbitmap(bitmap=None) | 设置窗口的图标,需指定图标文件(*.ico)的位置 |
resizable(width=None, height=None) | 设定是否能够改变窗口的宽和高尺寸 |
destroy() | 销毁窗口,也就是把窗口关掉 |
下面看一个示例,演示了tk中一些常用的窗口操作:
from tkinter import *
root = Tk()
root.title("我的窗口")
root.iconbitmap("my_icon.ico")
root.geometry("500x500")
root.resizable(False, False)
mainloop()
可以看出,窗口被设置了标题"我的窗口",图标也变成了自定义的图标。由于resizable的设定,这个窗口无法改变大小,只能保持在500x500。
下面着重讲一下geometry方法。这个方法不仅可以设置窗口的尺寸,也可以设置窗口在电脑屏幕上的位置。给定参数的格式是:widthxheight+x+y。root.geometry("300x100+20+50")代表的就是把root窗口设置为300x100的尺寸,与屏幕最左边相隔20像素,与屏幕最上方相隔50像素。可以只设置窗口的尺寸,即widthxheight;也可以只设置窗口的位置,即+x+y。geometry还有一些用法,在讲Wm类的时候会介绍。
窗口有一个默认的背景颜色,同样也是大多数tk组件的颜色。这个颜色是一种浅灰色,在tk内部称作SystemButtonFace,只能在tk中使用,其他模块是不支持这个颜色的。如果要改变窗口的背景,可以使用窗口的config方法,也可以写作configure方法。大部分控件都支持这个方法,用来定义控件后改变它的属性。
from tkinter import *
root = Tk()
root.config(bg="blue")
root.mainloop()
一个纯蓝色的窗口就出现了。
1.4 组件
tkinter支持很多组件,可以帮助你完成一些功能。组件根据坐标被排列在容器(container)中,窗口的界面是该窗口中最大的容器。
tkinter的组件有:
- Label:标签控件,用来在窗口上显示文本和图片
- Message:消息控件,用来显示多行文本,与Label功能类似
- Button:按钮控件,用户可以点击按钮,点击事件将会传递给设置的回调函数
- Entry:文本输入框控件,用户可以输入文字,但只能在一行输入
- Text:多行文本输入框控件,用户可以输入多行文字,自由换行
- Canvas:画布控件,可以在上面显示基本图形、文字、图片
- Frame:框架控件,作为一个小容器,相当于给组件分组。
- LabelFrame:文字框架控件,和Frame不同的是,框架外面多了文本提示
- Menu:菜单控件,在窗口上显示菜单,或定义弹出式菜单。
- Menubutton:菜单按钮控件,是Button的样子,点击后弹出一个菜单。
- Checkbutton:多选按钮,用户可以勾选或取消勾选。
- Radiobutton:单选按钮,用户可以在同类的Radiobutton中选择一个,无法取消勾选
- Listbox:列表框组件,可以显示一个字符串的列表
- Scrollbar:滚动条控件,用来添加一个滚动条控制滚动
- Scale:尺度条控件,用来添加一个数字滑块,用户可以滑动调整数值。
- Spinbox:数字选值框控件,用户既可以输入数字,也可以按调节按钮调整数值。
- OptionMenu:选项菜单,用户可以从下拉菜单中选择一个值,但是不能自己输入。
- PanedWindow:分栏容器控件,和Frame类似,但是有更多的功能设定,比如用户可以调节大小
- Toplevel:上层窗口控件,可以定义某个窗口的子窗口。
tkinter还有一些子模块,如ttk,messagebox,colorchooser,filedialog等。
ttk中有一些扩展组件,里面有一些和主模块一样的控件,但是样子要不同。ttk有一个最大的特点,组件的字体、颜色等功能不能直接修改,而是要用ttk.Style形式修改,后期会讲述。而tkinter主模块中可以直接指定组件的颜色、字体等样式。所以,如果在from tkinter import *后继续导入from tkinter.ttk import *,就会覆盖tkinter.ttk与tkinter主模块中相同的组件,要改变字体和颜色只能使用Style的形式。这一点千万不能弄错。
tkinter.ttk的扩展组件有:
- Combobox:组合选择框控件,用户可以自己在输入框中输入内容,也可以在下拉列表中选择。
- Notebook:笔记本控件,添加多个Frame选项卡,用户可以在不同选项卡之间切换。
- Progressbar:进度条控件,显示一个加载时的进度条
- Separator:分割线控件,显示一条垂直或水平的分割线。
- Treeview:树状图控件,显示一个表格或是树状图。
- Sizegrip:尺寸调整控件,显示一个调整窗口尺寸的按钮。
组件都有一个参数,用来定义这个组件的父容器,大多数组件的类也都有一些共同的参数,这些参数以**kw的形式传递,如:
参数名称 | 作用 |
master | 组件的父容器,一般必选 |
name | 组件在Tcl/Tk内部的名称 |
bg background | 改变组件的背景(ttk没有) |
fg foreground | 改变组件的前景色,一般是文本颜色 |
width | 组件的宽,单位是像素(在文本输入类组件中,单位是字符数量) |
height | 组件的高,单位是像素(在文本输入类组件中,单位是字符数量) |
cursor | 鼠标放上组件的光标样式 |
relief | 组件边框样式 |
state | 组件状态,可设置为normal(普通样式),disabled(禁用),active(激活),readonly(只读)。其中normal和disabled所有组件都支持,而active,readonly只有部分组件支持。 |
takefocus | 组件是否能获取焦点 |
bd borderwidth | 组件边框的宽度 |
activebackground | 组件激活时的背景色 |
activeforeground | 组件激活时的前景色 |
disabledforeground | 组件禁用时的前景色 |
disabledbackground | 组件禁用时的背景色 |
highlightcolor | 高亮(组件获得焦点)时的边框颜色 |
highlightthickness | 高亮边框宽度 |
exportselection | 这个是所有含有输入功能的组件的共有参数,表示选中的内容是否可以被Misc.selection_get方法检测到,参见后文对Misc类的介绍 |
这意味着,你可以这么定义一个组件:Label(master=root, bg="blue")。组件的类也有一个cnf参数,传给这个参数一个字典,也可以达到定义组件的效果。上面也可以写作:Label(cnf="master":root, "bg":"blue)。
组件也有一些共同的方法:
方法名称 | 作用 |
after(ms, func=None) | 等待ms毫秒后执行一次func |
bind(sequence=None, func=None) | 绑定事件,检测到事件后调用func |
unbind(sequence) | 解除绑定事件 |
update() | 刷新组件,一般不需要手动调用 |
cget(key) | 返回关键字参数key的值,如:root.cget("bg")返回root的背景色 |
configure(**kw) | 也写作config,重新改变组件的关键字参数设置 |
destroy() | 销毁组件 |
focus_set() | 设置输入焦点 |
2 tkinter主模块
tkinter有一系列的子模块,这里介绍主模块内一些方法的使用方式。
2.1 Label
本节中你将了解tk中最常用也是最简单的组件:标签。
参考资料:Python Tkinter 标签控件(Label) | 菜鸟教程
Label(master=None, cnf=, **kw)
参数名称 | 作用 |
text | 显示的文本 |
font | 文本的字体 |
image | 显示的图片 |
bitmap | 显示的位图,和image只能指定一个 |
textvariable | 绑定的文本变量 |
compound | 当文本和图片同时显示时,图片位于文本的方位,可以是top, bottom, left, right, center,如:设置为top表示图片位于文本上方 |
padx | 标签内容与左右的间距 |
pady | 标签内容与上下的间距 |
anchor | 文本靠标签哪个方向显示,可以是n,s,w,e,ne,nw,sw,se,center,即北、南、西、东、东北、西北、西南、东南、中间,默认靠中间显示 |
justify | 文本的对齐方式,可以是left, right, center,默认是center |
wraplength | 自动换行字符数量,到达数量后文本会自动换一行显示 |
创建Label
from tkinter import *
root = Tk()
root.geometry("200x200")
lab = Label(root, text="Hello, Tkinter!")
lab.pack()
mainloop()
创建Label的时候,先要指定master,即摆放组件的父容器。所有组件都需要这样,不然tk可能不清楚你要排放在哪个容器上面。text指定显示的文本。这样就定义好了一个Label对象,赋值给lab。lab.pack()的意思是,将定义的Label摆放到父容器上面。所有的组件都需要在定义后摆放到窗口上,不然组件显示不了。至于排放位置的设置,之后会讲述到。
运行效果如下:
relief参数
relief参数设定组件的样式,大多数组件都支持relief参数。
from tkinter import *
root = Tk()
root.geometry("200x200")
lab = Label(root, text="Hello, Tkinter!", relief="sunken")
lab.pack()
mainloop()
运行效果:
relief参数指定了组件边框的样式,一共有6种relief,分别是flat, groove, raised, ridge, solid, sunken。Label的默认relief是flat。这6种relief的效果如下:
cursor参数
参考资料:【Python cursor指针】——Python Tkinter Cursor鼠标指针属性值
cursor参数指定鼠标移动到组件上时,光标的样子。光标样式有很多,这里不再赘述,可以参考下面这个示例,它会显示光标所有的样式。鼠标放在对应Label上,会显示出当前光标样式。
cursorList = ['arrow', 'xterm', 'watch', 'hand2', 'question_arrow', 'sb_h_double_arrow', 'sb_v_double_arrow', 'fleur',
'crosshair', 'based_arrow_down', 'based_arrow_up', 'boat', 'bogosity', 'top_left_corner',
'top_right_corner', 'bottom_left_corner', 'bottom_right_corner', 'top_side', 'bottom_side', 'top_tee',
'bottom_tee', 'box_spiral', 'center_ptr', 'circle', 'clock', 'coffee_mug', 'cross', 'cross_reverse',
'diamond_cross', 'dot', 'dotbox', 'double_arrow', 'top_left_arrow', 'draft_small', 'draft_large',
'left_ptr', 'right_ptr', 'draped_box', 'exchange', 'gobbler', 'gumby', 'hand1', 'heart', 'icon',
'iron_cross', 'left_side', 'right_side', 'left_tee', 'right_tee', 'leftbutton', 'middlebutton',
'rightbutton', 'll_angle', 'lr_angle', 'man', 'mouse', 'pencil', 'pirate', 'plus', 'rtl_logo', 'sailboat',
'sb_left_arrow', 'sb_right_arrow', 'sb_up_arrow', 'sb_down_arrow', 'shuttle', 'sizing', 'spider',
'spraycan', 'star', 'target', 'tcross', 'trek', 'ul_angle', 'umbrella', 'ur_angle', 'X_cursor']
#所有的光标样式
from tkinter import *
root = Tk()
for i in range(len(cursorList)):
cursor = cursorList[i]
Label(text=cursor, cursor=cursor, relief="groove").grid(
column=i // 20, row=i % 20, sticky="we") #后面会讲到grid,是一种排放组件方式
root.mainloop()
介绍一下常见的光标样式。不同的主题和系统可能有所不同。
arrow | |
xterm | |
watch | |
hand2 | |
question_arrow | |
sb_h_double_arrow | |
sb_v_double_arrow | |
fleur | |
crosshair |
font参数
font参数指定文本的字体,大多数带有文本的组件都支持这个参数。font参数指定字体的样式、大小、以及是否有加粗下划线等特殊样式。font参数可以是tkinter.font.Font对象,也可以只给一个字体名称或是字体大小数值,或是给一个元组。
lab = Label(root, text="Hello!", font=("黑体", 20)) #字体为黑体,大小20;顺序不能颠倒
lab = Label(root, text="Hello!", font=20) #只设置大小20
lab = Label(root, text="Hello!", font="黑体") #只设置字体为黑体
字体的元组后面还可以加上字体的特殊样式,一共有bold(加粗), italic(斜体),underline(下划线),overstrike(删除线)几种。可以叠加设置。
Label(root, text="加粗", font=("黑体", 20, "bold")).pack()
Label(root, text="斜体", font=("黑体", 20, "italic")).pack()
Label(root, text="下划线", font=("黑体", 20, "underline")).pack()
Label(root, text="删除线", font=("黑体", 20, "overstrike")).pack()
Label(root, text="叠加使用", font=("黑体", 20, "bold", "italic", "underline", "overstrike")).pack()
运行效果:
bitmap参数
bitmap参数指定添加位图,即内置图标,有error, info, hourglass, questhead, question, warning, gray12, gray25, gray50, gray75。下面的示例列举了所有bitmap:
from tkinter import *
root = Tk()
bitmaps = ["error", "info", "hourglass", "questhead", "question", "warning",
"gray12", "gray25", "gray50", "gray75"]
for bitmap in bitmaps:
Label(root, text=bitmap, bitmap=bitmap, compound="left").pack()
mainloop()
compound的意思是:图片置于文字的位置,上面的参数解释有。
运行效果:
image参数
image参数在Label中添加图片。这个图片是一个tk.PhotoImage对象,支持的格式只有*.gif, *.ppm, *.pgm,较新版的tk支持显示*.png。注意:直接在图片文件后面改后缀不会改变图片本身的文件类型!更需要强调的一点是,图片对象必须要赋值给一个全局变量,或者是类的实例变量之类的,保证不会被Python机制回收,否则图片无法正确显示。
image = tkinter.PhotoImage(file="图片名称")
这段代码建立了一个tk图片对象,现在需要把它传递给Label。
image = PhotoImage(file="monster.gif")
Label(root, image=image, text="It's a monster.", compound="top").pack()
运行效果:
但是,如果想要显示更多的图片文件格式,比如*.jpg该怎么办呢?这时候需要使用pillow工具。这是一个第三方模块,需要用pip安装:pip install pillow,导入时import PIL。
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.geometry("200x200")
image = ImageTk.PhotoImage(Image.open("monster.png"))
Label(root, image=image, text="It's a monster.", compound="top").pack()
mainloop()
运行效果:
这样,我们用PIL工具成功显示了png图片(不过版本较新的Tk本来也可以显示png图片)。也需要注意,在使用tkinter.PhotoImage的时候,需要指定file="文件名"这个关键字参数,但使用PIL工具则不需要指定file关键字参数。
tkinter也有一些内部的图片,可以通过字符串传递给image。
包括:"::tk::icons::error","::tk::icons::information","::tk::icons::question","::tk::icons::warning"。
它们的效果如下:
from tkinter import *
root = Tk()
for image in ["::tk::icons::error",
"::tk::icons::information",
"::tk::icons::question",
"::tk::icons::warning"]:
Label(root, text=image, image=image, compound="top").pack()
mainloop()
这些名字为什么这么复杂呢?其实,::是Tcl语言命名空间的表示方式,这里不多讲。
textvariable参数
tkinter提供了一些变量对象,可以绑定到组件,可以设置它们的值。绑定的组件会随着设置而刷新。这些对象有StringVar()文本变量,IntVar()整数变量,DoubleVar()浮点数变量,BooleanVar()布尔值变量。
建立一个tkinter变量的方法是:
var = tkinter.StringVar()
然后就可以设置变量的值:
var.set(value)
也可以获取变量的值:
sth = var.get()
StringVar中可以设置文字,IntVar可以设置整数,BooleanVar可以设置True和False,等等。
Label绑定textvariable,只需要添加一个参数textvariable=var,后期如果想要更改Label中的内容,可以执行var.set(value)来更改。当然也可以使用lab.config(text=value)这样的方式。
2.2 Button
Button即按钮,可以绑定一个回调函数,用户点击时将会执行这个函数。
参考资料:Python Tkinter 按钮组件 | 菜鸟教程
Button(master=None, cnf=, **kw)
很多tk组件都有共通性,比如带有文本的组件,大多都有text, font, textvariable这些参数。Button的参数和Label基本类似,也支持text, image, bitmap等功能。Button也有relief,按钮默认的relief是raised。与Label最不同的是,它还可以绑定一个点击事件。
参数名称 | 作用 |
command | 点击按钮时运行(是一个方法) |
repeatdelay | 延迟多少毫秒(1000ms=1s)后进行按钮的持续触发 |
repeatinterval | 按钮持续触发的间隔时长(毫秒) |
overrelief | 鼠标经过时按钮的relief样式 |
常用方法:
方法 | 作用 |
invoke | 调用Button的command(disabled无效) |
flash | 使Button闪烁几次(在normal和active几次切换) |
创建Button
下面我们就来创建一个Button,它可以绑定一个回调函数,点击时在屏幕上打印"你点了一下按钮"。
from tkinter import *
root = Tk()
def callback():
print("你点了一下按钮")
button = Button(root, text="按钮", command=callback)
button.pack()
mainloop()
运行后点击按钮,可以看到如下输出。按钮可以多次点击。
activeforeground和activebackground参数
如果鼠标长按按钮,那么按钮不会被触发,而是松开鼠标后触发。这时候,按钮处于一种激活状态。我们可以设置激活时按钮的前景和背景颜色:
Button(root, text="按钮", activeforeground="blue", activebackground="yellow")
如上面这段代码,把激活时的前景色设为blue(蓝色),背景色则设为yellow(黄色)。 点击时呈现这样的效果:
repeatdelay和repeatinterval参数
这两个参数可以用于按钮的持续触发。用户长按在按钮上,经过repeatdelay毫秒的延迟后,按钮将会重复触发,每次触发的间隔是repeatinterval毫秒。
from tkinter import *
root = Tk()
root.geometry("200x200")
def addnum():
num = int(b.cget("text")) #获取组件的参数选项
b.config(text=str(num + 1))
b = Button(root, text="0", command=addnum,
repeatdelay=1000, repeatinterval=500)
b.pack()
mainloop()
效果:当用户按在按钮上面不动时,经过repeatdelay毫秒(1秒)后,按钮的command每间隔repeatinterval毫秒(0.5秒)就执行一次。
禁用按钮
所有组件都有state参数,表示组件的状态。一共有三个状态:normal, disabled, active。默认的state是normal,此时用户可以点击按钮。而处于disabled禁用的按钮,用户将无法点击。active则是激活状态。
Button(root, text="按钮", state="disabled")
处于禁用状态的按钮,无法点击:
处于激活状态的按钮如果不进行设置是看不出来的,但是设置了activebackground和activeforeground,就可以看出按钮处于激活状态。但是点击松开之后,激活状态就会取消(变成normal状态)。
根据这个原理,我们可以做出点击一次就禁用的按钮:
from tkinter import *
root = Tk()
def disable():
button.config(state="disabled")
button = Button(root, text="点击禁用", command=disable)
button.pack()
mainloop()
如果你忘了config的用法,这里再强调一次 :用于改变组件原本设定的参数,这个方法非常常用。
运行后点击一次按钮,按钮的状态由normal改为disabled,无法点击第二次。
点击后>>>
2.3 布局管理(pack,grid,place)
上面已经提过,组件需要在创建后,摆放到屏幕上。一共有三种摆放的方式:pack, grid, place。注意:在同一个容器中,只能使用一种布局方式,要么组件都用pack,要么都用grid,要么都用place。接下来介绍一下这三个方法的参数:
pack
pack适用于简单的布局。
- side:组件靠哪个方向排放,可以是"top", "bottom", "left", "right",分别是上下左右,默认是"top"。
- anchor:当排放组件的可用空间要多于所需空间时,组件靠哪个方向排放,可选项是八个方位和中心(n, s, w, e, nw, ne, sw, se, center)。默认是"nw"。
from tkinter import *
root = Tk()
root.geometry("200x200")
w1 = Button(root, text="多余空间靠左")
w1.pack(anchor="w")
root.mainloop()
- expand:组件适应窗口。如设置为True,当窗口中有别的可用空间时,将会自动把组件居中摆放,并且拖拽后仍然适应窗口大小。默认为False。
from tkinter import *
root = Tk()
root.geometry("200x80")
w1 = Button(root, text="W1")
w1.pack(expand=True)
w2 = Button(root, text="W2")
w2.pack(expand=True)
root.mainloop()
拖拽窗口后>>>
如果不设置expand,则变成这样,组件不会自动适应窗口大小:
拖拽窗口后>>>
- fill:组件的填充,可选项有"x", "y", "both", "none",默认为"none"。分别表示:x方向填充,y方向填充,两个方向都填充,无填充。这将根据参数指定填充组件可用空间,一般和expand一起使用,以保证可用空间足够。(下面都设置了expand=True,窗口未拖拽时尺寸200x80)
参数设置 | 未拖拽时效果 | 拖拽后效果 |
fill="x" | ||
fill="y" | ||
fill="both" | ||
fill="none" |
- padx和pady:分别表示组件与外部容器在x轴和y轴的间隔。可以只提供一个数字,表示左右间隔或上下间隔,也可以提供一个两个项的元组表示左右间隔或上下间隔。不一定要一起设置。
from tkinter import *
root = Tk()
w1 = Button(root, text="Hello")
w1.pack(padx=50, pady=30) #左右间隔50,上下间隔30
root.mainloop()
from tkinter import *
root = Tk()
w1 = Button(root, text="Hello")
w1.pack(padx=(50, 40), pady=(30, 60)) #左间隔50,右间隔40,上间隔30,下间隔60
root.mainloop()
- ipadx和ipady:表示内部与组件边框的间隔,与padx,pady使用方法类似。
from tkinter import *
root = Tk()
root.geometry("400x200")
w1 = Button(root, text="Hello")
w1.pack(ipadx=40, ipady=40)
root.mainloop()
grid
pack布局方式适合于简单的布局,在同一容器内,只能进行上下左右四方向的布局。而grid可以实现网格布局,根据行和列指定组件的位置。grid布局和pack一样都支持padx, pady, ipadx, ipady这几个参数。
- row和column:分别指定组件排列的行和列,row和column指定以0为起点,第一行就是row=0。
from tkinter import *
root = Tk()
root.geometry("400x200")
Button(root, text="0行0列").grid(row=0, column=0)
Button(root, text="1行0列").grid(row=1, column=0)
Button(root, text="0行1列").grid(row=0, column=1)
Button(root, text="1行1列").grid(row=1, column=1)
root.mainloop()
可以看见,实现了整齐的布局。如果此时把column设置为一个很大的数字,比如999,但是容器上的组件只有1列,那么并不会把组件排放到999列,而是排放在2列的位置。
- rowspan和columnspan:rowspan表示组件占几行的大小,columnspan表示组件占几列的大小。
from tkinter import *
root = Tk()
root.geometry("400x200")
Button(root, text="0行0列").grid(row=0, column=0)
Button(root, text="1行0列").grid(row=1, column=0)
Button(root, text="0行1列(占两行)").grid(row=0, column=1, rowspan=2)
root.mainloop()
from tkinter import *
root = Tk()
root.geometry("400x200")
Button(root, text="0行0列").grid(row=0, column=0)
Button(root, text="0行1列").grid(row=0, column=1)
Button(root, text="1行0列(占两列)").grid(row=1, column=0, columnspan=2)
root.mainloop()
如果在第二段代码的基础上,不加columnspan的设置,结果就会变成这样:
- sticky:在pack布局中和anchor类似,表示组件的方位,同时也可以达到fill的功能。可以提供八个方位+center,来指定组件在可用空间中的排列位置。
from tkinter import *
root = Tk()
root.geometry("400x200")
Button(root, text="Helloooo").grid(row=0, column=0)
Button(root, text="Hiiiiii").grid(row=0, column=1)
Button(root, text="Hello").grid(row=1, column=0, sticky="e")
root.mainloop()
也可以设置组件填充排放。x轴填充表示为"ew",y轴填充表示"ns",xy轴both填充设置为"nwse"
也可以设置填充时,同时靠某个方向排放。需要提供三个参数。比如x方向填充时同时靠北(n)排放,可以设置为sticky="ewn"。
place
place布局适用于更加复杂的,需要准确摆放组件的容器。这种布局不是很常用,因为使用比较麻烦,需要提供xy坐标以及组件的width和height(以像素为单位),place布局不支持padx……几个参数。
- x和y:组件在x轴和y轴上的位置,单位为像素。如果不清楚tkinter坐标系,可以翻回去看一下,左上角为(0, 0)。
- anchor:组件的锚选项,可选有八个方位,以及center。意思是:组件anchor位置的坐标设置为x,y。如:anchor="n"的时候,如果x=100, y=100,那么组件的最上面的中间的那个点的位置就是(100, 100)。下面是几个例子:
- width和height:指定组件排放时的宽和高。
- relx和rely:组件在x轴或y轴相对于整个容器的位置,是一个0-1之间的浮点数,表示组件位于整个容器的位置。比如想要把组件的x设在容器30%的位置,则可以把位置设为0.3.如果想要把组件居中,就设置relx=0.5, rely=0.5, anchor="center"。
- relwidth和relheight:指定组件相对于容器的宽与高,是一个0-1之间的浮点数。组件宽是容器宽的50%,则可以把relwidth设置为0.5.
更改组件映射
规范地说,将组件排放布局到容器上,这个过程叫做映射(map)。widget.pack/grid/place()是映射一个组件,自然也有取消映射(Unmap)的方法。
这个方法是布局方法后面加上_forget,pack布局取消映射方法是pack_forget,grid则是grid_forget,place是place_forget。
调用后,相当于隐藏了这个组件。如果还想映射的话,再次调用一下pack/grid/place即可。
将组件布局,也有办法更改布局给的参数。pack_configure, grid_configure, place_configure,可以更改布局时给定的参数。
布局管理
综合使用布局管理,可以美化界面。布局的时候,组件和组件最好都空开一定距离,这样更加美观(pack和grid可以设置padx和pady)。不要把组件挤在一处,不要让窗口长宽比过大。同样,也不要让窗口大小超出屏幕大小,影响用户操作。
同一容器中只能使用一种布局方式,这就带来了一定麻烦和局限性。所以接下来将介绍Frame组件,使用它可以使布局管理更加方便。
2.4 Frame
Frame是框架的意思,让你在容器中能够创建一个子容器。使用Frame,可以对组件编组,也可以使你能够在一个窗口中综合使用不同的布局方式。比如,在窗口中使用pack布局,在窗口上的Frame中使用grid布局,这是允许的。
参考资料:Python Tkinter 框架控件(Frame) | 菜鸟教程
Frame(master=None, cnf=, **kw)
Frame没有别的参数,只有几个基本参数,如relief, cursor, highlightcolor等。使用也很简单。
创建Frame
from tkinter import *
root = Tk()
root.geometry("200x200")
fr = Frame(root)
fr.pack()
Button(fr, text="button in frame").pack()
Button(fr, text="button2 in frame").pack()
mainloop()
看上去,和没有Frame也没有什么区别,我们可以给Frame加上边框(设置relief参数)。注意,设置Frame的时候必须要更改它的边框宽度,即bd(borderwidth)。
fr = Frame(root, relief="solid", bd=2)
Frame的作用
Frame中可以添加容器中能添加的任何组件,甚至可以嵌套Frame。那么,使用Frame的意义是什么呢?可以参考下面几个作用:
- 方便组件的排放:如果想要在窗口顶部横向摆放几个组件,在下面再摆一个组件,使用grid固然可以,但就比较麻烦,行列不容易调整。这时候可以加上Frame,在窗口中摆一个Frame,下面摆一个组件。再在Frame中横向摆三个组件。这样使用pack布局就能轻松完成。
from tkinter import *
root = Tk()
root.geometry("200x200")
fr = Frame(root)
fr.pack(padx=5, pady=5)
Button(fr, text="1").pack(side="left")
Button(fr, text="2").pack(side="left")
Button(fr, text="3").pack(side="left")
Button(root, text="OK").pack(pady=5)
mainloop()
- 方便取消映射或销毁组件:如果一个窗口中插入了大量组件,想要把其中一部分隐藏,就需要调用很多个forget,显得很麻烦 。但如果把这些需要隐藏的组件放进一个Frame,到时候只需要隐藏这个Frame,就可以把所有的组件一起隐藏掉了。
from tkinter import *
root = Tk()
root.geometry("200x200")
fr = Frame(root)
fr.pack(padx=5, pady=5)
Button(fr, text="1").pack(side="left")
Button(fr, text="2").pack(side="left")
Button(fr, text="3").pack(side="left")
Button(root, text="隐藏", command=fr.pack_forget).pack(pady=5)
mainloop()
点击隐藏按钮>>>
再比如,想要删除一部分组件,然后换成另外一部分组件,也可以使用Frame。只需要把这些组件放进一个Frame,然后遍历Frame的子组件,对组件挨个销毁即可。
容器的winfo_children方法返回一个列表,包含了容器所有的子组件。destroy方法销毁一个组件,组件方法介绍时提到过。
for widget in frame.winfo_children():
widget.destroy() #逐个销毁frame的子组件
这样可以销毁Frame中的所有组件。
2.5 LabelFrame
这个组件与Frame类似,但是可以在左上方显示一段文本或是一个组件。
LabelFrame(master=None, cnf=, **kw)
参数名称 | 作用 |
text | 显示的文本 |
font | 文本的字体 |
labelanchor | 文本位于Frame的方位 |
labelwidget | 用一个组件替代显示的文本 |
创建LabelFrame
from tkinter import *
root = Tk()
root.geometry("200x200")
fr = LabelFrame(root, text="LabelFrame")
fr.pack(fill="both", padx=4)
Button(fr, text="1").pack()
Button(fr, text="2").pack()
mainloop()
labelanchor参数
labelanchor参数设置文本的位置,可选有八个方位,但不包括center,默认是nw。下面是两个示例。
labelwidget参数
如果你不想要LabelFrame的上面显示一段文字,也可以把它替换为别的组件,比如Button。这个组件的master不影响,只要在同一父容器中就行。
from tkinter import *
root = Tk()
root.geometry("200x200")
fr = LabelFrame(root, text="LabelFrame", labelwidget=Button(root, text="按钮"))
fr.pack(fill="both", padx=4)
Button(fr, text="1").pack()
Button(fr, text="2").pack()
mainloop()
2.6 Entry
Entry是一个文本框组件,用户可以在里面输入文本。
参考资料:Python ---(六)Tkinter窗口组件:Entry_近视的脚踏实地的博客-CSDN博客
Entry(master=None, cnf=, **kw)
参数名称 | 作用 |
font | 输入文本字体 |
show | 输入文本被显示为什么字符 |
selectbackground | 选中文字的背景色 |
selectforeground | 选中文字颜色 |
insertborderwidth | 光标边框宽度(指定时光标样式为raised) |
insertontime | 光标闪烁时,处于“亮”状态的时长(毫秒) |
insertofftime | 光标闪烁时,处于“灭”状态的时长(毫秒) |
insertwidth | 光标的宽度 |
selectborderwidth | 选中文字的背景边框宽度 |
textvariable | 绑定的StringVar,同步Entry输入的内容 |
readonlybackground | 文本框处于readonly状态下的背景颜色 |
xscrollcommand | x方向滚动条(后面介绍) |
validate | 验证输入合法性的条件 |
vcmd validatecommand | 判断输入合法性的回调函数,或者是一个包含回调和所需参数的元组 |
invcmd invalidcommand | 输入不合法时执行的回调函数 |
常用方法:
方法名称 | 作用 |
get() | 获取文本框的值 |
delete(first, last=None) | 删除文本框中从索引first到last的内容 |
insert(index, s) | 在文本框中插入文本,index是索引,s是插入内容 |
select_range(start, end) | 选中从start到end的文本 |
icursor(index) | 将光标移动到索引处 |
创建Entry
from tkinter import *
root = Tk()
root.geometry("200x200")
ent = Entry(root)
ent.pack()
mainloop()
出现了一个输入框,可以在里面自由输入内容。
show参数
指定show参数,可以使输入里面的内容显示为一个字符,常用于密码输入。下面的示例,将所有的输入内容显示为*号。
from tkinter import *
root = Tk()
root.geometry("200x200")
Label(root, text="Pwd: ").pack()
ent = Entry(root, show="*")
ent.pack()
mainloop()
无论输入什么内容,都是*号显示。
get方法
get方法获取文本框的值,如下示例:
from tkinter import *
root = Tk()
root.geometry("200x200")
ent = Entry(root)
ent.pack()
def printget():
print(ent.get())
Button(root, text="获取输入", command=printget).pack()
mainloop()
点击按钮,将会输出文本框中的值。
insert和delete方法
insert方法可以插入一段内容,需要指定一个插入的位置和插入的内容。
插入位置可以是是字符的索引,比如0代表在第一个字符前面插入。也可以是一些特殊的值,比如"end"(在结尾处插入), "insert"(在光标闪烁处插入)
delete方法则用于删除一段内容,需要指定删除的开始和结束位置。位置的设定和insert一样。
tkinter还有一些类也有这两个方法,索引的用法也都基本一样。
readonly状态
Entry可以设置为state="readonly",也就是只读状态。处于readonly的Entry不能被输入,但是用户可以选中Entry里面插入的内容,当然也可以复制。如果是disabled状态,用户不但不能输入,而且不能选中里面的内容。
输入验证
validate, validatecommand, invalidcommand这三个参数用于输入验证。输入验证,也就是判断用户在Entry里面输入的内容是否符合要求。
validate参数是输入的条件,也就是在什么情况下,开启输入验证的功能。可以有"focus", "focusin", "focusout", "key", "all", "none"这几个可选。
参数值 | 解释 |
focus | 当组件获得或失去焦点时验证 |
focusin | 当组件获得输入焦点(光标闪烁)时验证 |
focusout | 当组件失去输入焦点时验证 |
key | 当输入内容更改时验证,如果验证为False,输入内容不会被插入文本框 |
all | 当上述任何一种情况出现时验证 |
none | 不进行验证(默认值) |
validatecommand是验证的函数,invalidcommand是验证失败时执行的方法。验证条件成立时,会调用validatecommand方法,这个方法要有一个True或False的返回值。如果是True,则验证通过;如果是False,则验证不通过,将会执行invalidcommand方法。
下面是一个示例,只有输入数字才会通过验证。
from tkinter import *
root = Tk()
root.geometry("200x200")
def vld():
if e.get().isdigit():
print("数字,符合要求")
return True
else:
print("不是数字,不符合要求")
return False
def wrong():
print("输入不符合要求,调用invalidcommand")
e = Entry(root, validate="focusout", validatecommand=vld, invalidcommand=wrong)
e.pack()
mainloop()
运行效果:当输入一个数字,然后把焦点转移(激活另外一个可输入窗口)时,打印“数字,符合要求”。当输入的不是数字时,打印“不是数字,不符合要求”和"输入不符合要求,调用invalidcommand"两句。
validatecommand参数还可以给一个元组(callback, v1, v2, v3, ...),包含执行的方法和你希望Entry传递给方法的参数。
参数选项 | 解释 |
%d | 传递一个操作代码:0表示删除操作,1表示插入操作,-1表示textvariable内容被程序更改或组件失去/获得焦点 |
%i | 传递用户插入或删除内容的索引位置,如果是失去/获得焦点或textvariable内容被程序更改,传递-1 |
%P | 传递文本框最新的输入内容 |
%s | 传递调用验证函数前,文本框上一次的输入内容 |
%S | 传递文本框输入或删除的内容 |
%v | 传递当前validate参数的值 |
%V | 传递调用验证函数的原因,是"focusin"(获得焦点), "focusout"(失去焦点), "key"(输入或删除文本框内容), "forced"(textvariable被程序修改) |
%W | 组件名称(Tcl内部名称) |
比如,把validatecommand设为(callback, "%P", "%s"),那么在调用callback的时候会传递两个参数,一个是文本框最新的输入内容,一个是文本框上一次的输入内容。
需要说明的是,使用validatecommand传递元组的时候,不能直接传递普通的函数,需要注册为Tcl函数才能使用输入验证。注册方法是:
tcl_cmd = root.register(cmd)
下面的一个示例,演示了validatecommand传参。
from tkinter import *
root = Tk()
root.geometry("200x200")
def vld(s):
print("输入或删除了", s)
return True
vld = root.register(vld) #注册为Tcl函数
e = Entry(root, validate="key", validatecommand=(vld, "%S"))
e.pack()
mainloop()
实例:密码验证系统
下面的一个实例中,用户需输入一串正确的密码,否则无法通过验证。
以上是关于Python tkinter(GUI编程)模块最完整讲解(上)的主要内容,如果未能解决你的问题,请参考以下文章