Python tkinter(GUI编程)模块最完整讲解(上)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

提示下滑文章左侧可以查看目录

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方法可以让窗口循环显示否则运行时窗口一闪就没了。一定不要忘记mainloopmainloop也可以用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和columnspanrowspan表示组件占几行的大小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)的方法。

这个方法是布局方法后面加上_forgetpack布局取消映射方法是pack_forgetgrid则是grid_forgetplace是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状态下的背景颜色
xscrollcommandx方向滚动条后面介绍
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()

实例密码验证系统

下面的一个实例中用户需输入一串正确的密码否则无法通过验证。

from tkinter import *

correct_pwd = "0123456789" #正确的密码

root = Tk()
root.title("密码验证系统")

def ok():
    if pwd.get() == correct_pwd:
        print("验证通过")
        root.destroy()

    else:
        print("验证失败")
        
pwd = Entry(root, show="*")
pwd.pack()

Button(root, text="OK", command=ok).pack()

root.mainloop()

 

2.7 事件绑定

组件的bind方法可以使组件绑定一个事件和一个回调函数事件有按下某个键点击组件等一般是由用户引发的。事件会被传递给回调函数然后执行函数。

Widget.bind(sequence=None, func=None)

sequence是事件序列func是检测到事件的回调函数。

参考资料Tkinter 事件绑定

按键名称

此处只选取一些常用的按键更多按键名请参考keysyms manual page - Tk Built-In Commands

按键名keysym   按键码keycode       代表的按键

Alt_L                64                左边的Alt按键
Alt_R                113               右边的Alt按键
BackSpace            22                BackSpace退格按键
Cancel               110               break按键
Caps_Lock            66                CapsLock大写字母锁定按键
Control_L            37                左边的Control
Control_R            109               右边的Control
Delete               107               Delete按键
Down                 104               ↓按键
End                  103               End按键
Escape               9                 Esc按键
Execute              111               SysReq按键
F1                   67                F1按键
F2                   68                F2按键
F3                   69                F3按键
F4                   70                F4按键
F5                   71                F5按键
F6                   72                F6按键
F7                   73                F7按键
F8                   74                F8按键
F9                   75                F9按键
F10                  76                F10按键
F11                  77                F11按键
F12                  96                F12按键
Home                 97                Home按键
Insert               106               Insert按键
Left                 100               ←按键
Linefeed             54                LinefeedCtrl + J
KP_0                 72                小键盘数字0
KP_1                 73                小键盘数字1
KP_2                 74                小键盘数字2
KP_3                 75                小键盘数字3
KP_4                 76                小键盘数字4
KP_5                 77                小键盘数字5
KP_6                 78                小键盘数字6
KP_7                 79                小键盘数字7
KP_8                 80                小键盘数字8
KP_9                 81                小键盘数字9
KP_Add               86                小键盘的+按键
KP_Begin             84                小键盘的中间按键5
KP_Decimal           91                小键盘的点按键.
KP_Delete            91                小键盘的删除键
KP_Divide            112               小键盘的/按键
KP_Down              88                小键盘的↓按键
KP_End               87                小键盘的End按键
KP_Enter             108               小键盘的Enter按键
KP_Home              79                小键盘的Home按键
KP_Insert            90                小键盘的Insert按键
KP_Left              83                小键盘的←按键
KP_Mutiply           63                小键盘的*按键
KP_Next              89                小键盘的PageDown按键
KP_Prior             81                小键盘的PageUp按键
KP_Right             85                小键盘的→按键
KP_Subtract          82                小键盘的-按键
KP_Up                80                小键盘的↑按键
Next                 105               PageDown按键
Num_Lock             77                NumLock数字锁定按键
Pause                110               Pause暂停按键
Print                111               PrintScrn打印屏幕按键
Prior                99                PageUp按键
Return               36                Enter回车按键
Right                102               →按键
Scroll_Lock          78                ScrollLock按键
Shift_L              50                左边的Shift按键
Shift_R              62                右边的Shift按键 
Tab                  23                Tab制表按键
Up                   98                ↑按键

sequence

事件序列遵从一定的格式如果不合理将会报错

<Modifier...-Type-Detail>

外面由<>尖括号括起来中间由-减号隔开。 modifier是条件表示只有当modifier成立的时候才会执行函数可以有多个type是主要事件的类型当type有detail的时候可以省略type有时候可能弄混Detail是事件类型的附带描述有的事件类型需要Detail但有的不需要。

这可能有些难懂下面将详细解析。

<Type-Detail>

type是事件的类型下面是type的名称

type触发条件detail

Button

ButtonPress

用户点击鼠标1:鼠标左键;2:鼠标中键;3:鼠标右键;4:滑轮向上滚动(Linux);5:滑轮向下滚动(Linux)
ButtonRelease鼠标按键释放1:鼠标左键;2:鼠标中键;3:鼠标右键;4:滑轮向上滚动(Linux);5:滑轮向下滚动(Linux)
Active组件被激活
Deactivate组件失去激活
Enter光标进入组件范围(不是按下回车键)
Leave光标离开组件范围

Key

KeyPress

用户按下按键可以指定具体的按键名参见前面的按键表
KeyRelease用户释放按键可以指定具体的按键名参见前面的按键表
Map组件被映射
Unmap组件取消映射
FocusIn组件获得焦点
FocusOut组件失去焦点
Configure组件尺寸被调节或拖拽
Destroy组件被销毁
Expose窗口或组件的某部分不再被覆盖
Motion光标在组件内移动
MouseWheel鼠标滚动(Windows和MacLinux应为Button-4、5)
Visibility窗口在屏幕中可见比如还原最小化、窗口由隐藏变为显示时触发

下面是一个示例演示了bind方法。

from tkinter import *

def callback(event):
    Label(root, text="你点了一下").pack()

root = Tk()
root.geometry("400x200")
root.title("点击窗口")
root.bind("<Button-1>", callback)

mainloop()

 点击几次窗口>>>

事件序列中Button是type意思是点击鼠标后面的1是detail表示鼠标左键。同样如果想要用鼠标中键可以改为Button-2右键可以改为Button-3。如果不指定detail只是Button那么三个鼠标键点击都会被检测到。

callback是回调函数它必须带有一个参数让bind方法传递。当检测到Button-1事件的时候bind方法会将一个Event对象传递给callback函数的第一个参数让函数中能够处理检测到事件的信息。下面就来介绍Event对象。

Event

tkinter.Event返回一个Event对象。Event对象在bind绑定时会传递给回调函数。大多数Event是所有事件类型可共用的但有一些不是。下面是Event的属性。

属性解释仅限事件类型
widget发生事件的组件
serial事件序列号
type事件类型
time发生事件的时间
x鼠标在窗口中的x位置
y鼠标在窗口中的y位置
x_root鼠标在整个屏幕上的x位置Button, ButtonRelease, Key, KeyRelease, Motion
y_root鼠标在整个屏幕上的y位置Button, ButtonRelease, Key, KeyRelease, Motion
num鼠标按下的键Button, ButtonRelease
focus窗口是否有焦点Enter, Leave
width窗口的宽度Configure, Expose
height窗口的高度Configure, Expose
keycode按键代码上面的按键表中有Key, KeyRelease
char按键的字符Key, KeyRelease
keysym按键名称Key, KeyRelease
keysym_num按键名称的数字形式Key, KeyRelease
state事件状态数字Button, ButtonRelease, Key, KeyRelease, Enter, Leave, Motion
state事件状态字符串Visibility
delta滚轮滚动信息MouseWheel

下面是一个用法示例。 

from tkinter import *

def callback(event):
    print("事件类型", event.type)
    print("点击了鼠标键", event.num)
    print("事件发生在组件", event.widget)

root = Tk()
root.geometry("400x200")
root.title("点击窗口")
root.bind("<Button>", callback)

mainloop()

 运行后点击几次窗口

<Modifier-Type-Detail>

Modefier可以指定一些条件条件成立的时候才会捕获事件。

Modefier可以是以下内容

modefier成立条件
Alt用户按着Alt键
Control用户按着Ctrl键
Shift用户按着Shift键

Button1

B1

用户按着鼠标左键

Button2

B2

用户按着鼠标中键

Button3

B3

用户按着鼠标右键

Button4

B4

用户将鼠标滚轮向上滚动(Linux)

Button5

B5

用户将鼠标滚轮向下滚动(Linux)
Double后面的事件类型被连续两次触发常用于双击鼠标左键(<Double-Button-1>)
Triple后面的事件类型被连续三次触发

比如想要达到检测组合键的效果按下Ctrl+S的时候执行某些操作即可表示为<Control-S>和<Control-s>bind区分大小写会区分大写字母和小写字母这里的Control是一个modefier是可选的不写也是一个合理的事件。

这个Control要和Key事件类型的Control_L和Control_R区分开来。如果要单独检测一个Ctrl键就必须要用Control_L和Control_R而不能使用条件因为事件类型才是必选的。

再比如检测双击鼠标和三击鼠标事件分别是<Double-Button>和<Triple-Button>。

bind_all方法

bind_all方法可以在一个窗口中绑定所有的子组件。比如想要把一个窗口里面的所有组件都绑定<Button-1>那么就可以用root.bind_all("<Button-1>", callback)。这样就不需要很麻烦地一个一个组件地绑定。

如果只是bind_all窗口里面的一个组件那么整个窗口的组件也会被绑定。

虚拟事件

tkinter中同样可以定义自己的事件也就是虚拟事件事件格式略有不同表示为<<event>>event是事件名用两层尖括号括起来。当窗口检测到虚拟事件的时候会执行绑定的回调函数并传递一个event。

既然是自定义事件那么触发方式也需要自定。event_generate方法可以向组件发送一个事件。这个事件可以是自定义事件也可以不是自定义事件而是<Button>那种自带的事件类型。

Widget.event_generate(sequence, **kw)

sequence是事件的名称**kw是一些参数表示传递的event对象里面的属性比如可以设置x=1,y=1等。不过这些参数和event的属性略有不同只有部分和Event的属性一样的才会能够调用所以建议你尽量不要设置**kw以免出现错误。

下面就来自定义一个事件这个自定义事件如果bind没有收到不会报错。

from tkinter import *

def generate_click_event():
    root.event_generate("<<MyClickEvent>>")

root = Tk()
root.geometry("200x200")

Button(root, text="点击", command=generate_click_event).pack()
root.bind("<<MyClickEvent>>", lambda event:print("点了一下"))

mainloop()

 点击按钮>>>

首先窗口绑定了一个<<MyClickEvent>>事件双尖括号代表这是一个自定义的事件。当这个事件被捕获执行后面的lambda event:print("点了一下")。然后当我们点击按钮的时候会执行command()root组件上生成了一个事件<<MyClickEvent>>这个事件随即被bind捕获然后调用后面的print("点了一下")。

注如果不了解匿名函数lambda的用法请参考https://blog.csdn.net/PY0312/article/details/88956795

bindtags方法

事件处理有先后顺序这些顺序可以获取或进行修改。组件的bindtags方法会返回一个列表包含事件处理的顺序。

Widget.bindtags(tagList=None)

以Button为例一个没有任何别的设置的Button会返回这样一个元组

('.!button', 'Button', '.', 'all')

这个元组中第一个是这个Button的Tcl内部名称代表button.bind绑定的事件。第二个是组件的名称代表这个Button从定义起就绑定的事件比如点击时会执行command点击时按钮会下陷。第三个是Button的父容器的Tcl内部名称代表父容器绑定的事件。最后一个是窗口中bind_all的绑定事件。

如果给定bindtags方法tagList参数可以改变事件的执行顺序这个tagList形式仿照bindtags返回的形式。比如只需要执行绑定的事件而不需要执行其他事件那么就设为button.bindtags(".!button")。

注不要认为所有的Button组件的Tcl内部名称都是.!button。设置组件的name参数可以设定组件的名称。而调用组件时写作的内部名称比较复杂是父容器+组件名称这种写法。可以通过str(widget)返回组件的内部名称。

通过这样我们还可以将其他组件的bind方法作用到这个组件上。

from tkinter import *

root = Tk()

b1 = Button(root, text="b1", command=lambda:print("command b1"))
b1.pack()
b1.bind("<Button-1>", lambda x:print("bind b1"))

b2 = Button(root, text="b2", command=lambda:print("command b2"))
b2.pack()
b2.bind("<Button-1>", lambda x:print("bind b2"))

b1.bindtags((str(b1), str(b2), "Button"))
mainloop()

设置b1的bindtags中包含了b1的bind事件和b2的bind事件。点击b1按钮的时候不仅会执行b1.bind的回调函数也会执行b2.bind的回调函数打印bind b1和bind b2.

如果想要在一个事件执行之后不再执行后续的事件可以在这个事件触发的回调函数中返回"break"字符串。这被解释器捕获后将不会执行后面的事件。

from tkinter import *

root = Tk()

e = Entry()
e.pack()

def rb(x):
    print("rb")
    return "break" #不执行后续事件

e.bind("<Key>", rb)
root.bind("<Key>", lambda x:print("root bind"))

mainloop()

如上面这段代码在Entry中输入文本会打印"rb"但是输入的内容不会插入到Entry中也不会打印root bind。由于Entry组件默认的bindtags顺序是Entry.bind在前Entry的默认定义事件和父容器的定义在后。所以当Entry.bind的回调函数返回了"break"后面的事件都不会再执行了。

unbind方法

unbind方法将某个事件绑定解除。

Widget.unbind(sequence)

from tkinter import *

root = Tk()

lab = Label(root, text="Label")
lab.pack()

lab.bind("<Button-1>", lambda x:print("Hello"))
lab.unbind("<Button-1>")

mainloop()

这段代码给label绑定了一个事件但接着又解除绑定了所以点击后组件不会有反应。

关于事件绑定的实例

下面提供3个示例帮助大家更好地了解事件的用法。

  • 示例1
from tkinter import *

root = Tk()
root.title("获取鼠标位置")
root.geometry("200x200")

def change_label(event):
    mouse_pos = (event.x_root, event.y_root) #鼠标在屏幕上的位置
    lab.place(anchor="nw", x=event.x, y=event.y) #调整lab的位置
    lab.config(text=str(mouse_pos)) #更改label的text为鼠标位置
    
lab = Label(root, text="请移动你的鼠标")
lab.place(anchor="center", x=100, y=100)

root.bind("<Motion>", change_label)

mainloop()

运行效果鼠标在窗口上滑动的时候label会显示为鼠标在屏幕上的位置并且跟随鼠标移动。 

  • 示例2
from tkinter import *

root = Tk()
root.title("获取按键")
root.geometry("200x200")

def get_key(event):
    lab.config(text=event.keysym)
    
lab = Label(root, text="请按键")
lab.pack()

root.bind("<Key>", get_key)

mainloop()

运行效果在键盘上按下按键会显示出按键的名称。 

  • 示例3
from tkinter import *

root = Tk()
root.title("连续事件检测工具")
root.geometry("200x200")

def get_key(event, num):
    lab.config(text="按了"+str(num)+"次"+event.keysym)
    
lab = Label(root, text="请按键")
lab.pack()

root.bind("<Key>", lambda event:get_key(event, 1))
root.bind("<Double-Key>", lambda event:get_key(event, 2))
root.bind("<Triple-Key>", lambda event:get_key(event, 3))

mainloop()

运行效果在键盘上按键或连续按键会显示出键名和按键数量。 

2.8 Variable(tkinter变量)

tkinter中所有的var有StringVar()文本变量对象IntVar()整数变量对象DoubleVar()浮点数变量对象BooleanVar()布尔值变量对象可以绑定到组件。它们都是继承tkinter.Variable用法都是一样的。这几个之前略有提及过下面详细介绍它们。

Variable(master=None, value=None, name=None)

参数作用
master指定variable的父组件一般不用这个参数
value指定variable的值
name指定variable在Tcl中的名称一般不用这个参数

下面的代码创建一个Variable以StringVar为例。

var = StringVar(value="value")

这段代码定义一个StringVar并设置它的值。

var.get()

这段代码设置var的值。

var.set("value")
var.initialize("value") #两种写法都可以

trace_add方法

trace_add方法用于监测variable的变化后面绑定一个回调函数。当检测到变化发生时执行后面的函数。

Variable.trace_add(mode, callback)

这种监测有3种模式write, read, unset。write是指写入当变量内容出现改动时调用函数常用于Entry组件中用户输入追踪。read是指读取当读取变量信息(var.get())的时候执行函数。unset是指变量删除当执行Variable的__del__方法或是del var的时候执行函数。

下面就让我们根据这个原理做一个文本框在里面输入的时候窗口标题同步改动

from tkinter import *

root = Tk()
root.geometry("200x200")

var = StringVar()
var.trace_add("write", lambda *args:root.title(var.get()))
    
e = Entry(root, textvariable=var)
e.pack()

mainloop()

用户在Entry里面输入的时候textvariable的值会被设为用户输入的内容。运行效果如下

输入>>>

那么定义回调的函数时为什么要提供一个*args呢这是因为trace_add会向函数传递3个参数马上就会介绍到这3个参数。

下一个示例演示了r模式。用户点击读取按钮会输出读取内容和“读取成功”的提示。

from tkinter import *

root = Tk()
root.geometry("200x200")

var = StringVar()
var.trace_add("read", lambda *args:print("读取成功"))
    
e = Entry(root, textvariable=var)
e.pack()

b = Button(root, text="Read", command=lambda:print(var.get()))
b.pack()

mainloop()

 

可以看到执行var.get()的时候就打印了读取成功。

注刚开始还多打印了一次读取成功是因为Entry组件在指定textvariable的时候会读取var的值然后把Entry内容设置为这个值导致trace被触发。如果想要避免这个问题把trace_add放到Entry定义后面即可。

那么再试一下unset模式

from tkinter import *

root = Tk()
root.geometry("200x200")

var = StringVar()
var.trace_add("unset", lambda *args:print("变量被unset"))
    
e = Entry(root, textvariable=var)
e.pack()
    
b = Button(root, text="Del", command=var.__del__)
b.pack()

mainloop()

 

如果需要在var被读取或写入的时候执行函数也可以把mode参数设定为一个元组如("write", "read")

variable也有一个方法叫做trace_variable也作trace。有很多教程讲解的是这个方法。tk文档指出这个方法使用了现已弃用的Tcl/Tk方法有可能后期会移除所以建议不要使用这个方法而是使用trace_add。

trace传递给回调函数的参数

trace_add方法一共会传递3个参数。第一个参数是var的名称(name)可以在variable的参数里面设置第二个参数是列表的索引如果var是列表第三个参数是设置的trace的模式。这三个参数中只有第三个可能有点用处。

trace_remove方法

如果想要删除追踪可以使用trace_remove方法。

Widget.trace_remove(mode, cbname)

mode是模式也就是要删除的trace的模式。上面讲到的trace_add方法会返回一个标识就是cbname。比如定义了一个追踪cbn = widget.trace("write", callback)后期想要删除这个trace就要调用widget.trace_remove("write", cbn)。

trace_info方法

trace_info方法返回该variable所有的追踪信息。由于比较少用就不多做解释了。

2.9 Toplevel

Toplevel可以用来创建一个新的窗口它必须继承一个窗口可以是Tk也可以是Toplevel。

Toplevel(master=None)

创建Toplevel

from tkinter import *

root = Tk()

top = Toplevel(root)

mainloop()

运行后可以看到两个窗口一个是Tk一个是Toplevel。当Tk关闭时Toplevel也会关闭。

 

Toplevel的父窗口也是Toplevel这也是支持的。另外Toplevel方法和Tk方法一样。

但是Toplevel并不需要调用mainloop只需要调用根窗口的mainloop方法即可。

下一篇Python tkinter(GUI编程)模块全解中_Python zzy的博客-CSDN博客

如果你在开发Python tkinter程序时遇到了问题或是有文章内容的建议可以私信联系我感谢支持

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: python