python10 - tkinter

python10 - tkinter


Tkinter是Python标准库中的一个GUI(Graphical User Interface,图形用户界面)工具包,其目的是为Python开发者提供快捷创建GUI应用程序的方式。

Tkinter基于Tcl/Tk图形库,允许我们使用Python代码来创建和管理窗口、标签、按钮、复选框、文本框、列表框、滚动条、画布、菜单等多种控件和组件。 Tkinter对多数平台都有良好的支持,而无需安装额外的软件或库。

通过Tkinter编写的GUI程序可以运行在像Windows、Mac OS X和Linux这样广泛流行的操作系统上,并允许用户与程序进行交互操作。 Tkinter提供了简单易懂的API,可以使得初学者也可以迅速学会如何构建Python GUI应用程序

先学习点前置知识:

坐标系 - 组件的排放,鼠标事件等功能都少不了坐标

tkinter的坐标系和数学上习惯用的坐标系略有不同,和pygame的坐标系是一样的。

以左上角为起点,x轴向右延伸,y轴向下延伸。在窗口中,容器的左上角是(0, 0),不包括窗口的标题栏和菜单栏。

在这里插入图片描述

颜色表示

当在tkinter中设置颜色时,可以用两种表示颜色的方式

  • 一种是颜色的名称,比如"green", “brown”;
  • 另一种是颜色的十六进制形式,比如"#00ffff"。

遗憾的是,tkinter不支持颜色RGB元组形式,不过可以把它转换成十六进制形式。

这种十六进制形式相当于:“#” + R的十六进制 + G的十六进制 + B的十六进制。

比如(255, 255, 255)是纯白,转换成十六进制形式就变成了#ffffff。

对于十六进制,可以轻松的通过现有小工具完成取色或者查询

tkinter也有一种特殊的颜色名称,叫做SystemButtonFace,是一种浅灰色,是组件的默认背景颜色。

一:Tkinter创建窗口

1:窗口的创建

import tkinter as tk


def create_window():
    # 创建一个窗口,但是之后这个窗口闪一下就消失了,所以要在用循环反复显示 -> mainloop
    w = tk.Tk()
    w.title("我是第一个tkinter GUI")  # 设置窗口标题,默认的标题时tk
    w.geometry("800x600")  # 设置窗口大小 (宽度x高度)+(x轴+y轴)
    return w


if __name__ == '__main__':
    window = create_window()
    window.mainloop()  # 显示窗口,循环反复显示

2:基本按钮创建和绑定

  1. 通过b = tk.Button(w)创建按钮,并声明放在窗口上
  2. 给按钮取一个名称,b["text"] = "点击我" 可以在第一步中,同时指定按钮名称tk.Button(w text="点击我")
  3. 按钮布局:已经放到窗口里面了,但是放到窗口的哪个位置,东南西北哪个地方,我们就可以用pack()去定位
  4. 创建点击事件的函数,一会绑定之后,点击按钮之后会触发这个事件函数
  5. 绑定事件b.bind(sequence, 绑定的事件)
import tkinter as tk
from tkinter import messagebox


# 定义事件
def button_event(e):
    print("点击了按钮")
    messagebox.showinfo("提示", "我是一个提示框")


def create_window():
    # 创建一个窗口,但是之后这个窗口闪一下就消失了,所以要在用循环反复显示 -> mainloop
    w = tk.Tk()
    w.title("我是第一个tkinter GUI")  # 设置窗口标题
    w.geometry("800x600")  # 设置窗口大小

    # 创建一个按钮
    b = tk.Button(w, text="点击我")  # 创建一个按钮, 将按钮放到窗口w中,并且设置按钮上的文字是"点击我"
    b.pack()  # 布局按钮, 放到窗口的哪个位置,东南西北哪个地方,我们就可以用pack()去定位

    # 点击按钮绑定事件
    b.bind("<Button-1>", button_event)

    return w


if __name__ == '__main__':
    window = create_window()
    window.mainloop()  # 显示窗口,循环反复显示

在这里插入图片描述

二:组件的布局和样式

1:布局-pack

这个布局管理器,要么将组件垂直的排列,要么水平的排列

  • side:组件靠哪个方向排放,可以是"top", “bottom”, “left”, “right”,分别是上下左右,默认是"top"。

在这里插入图片描述

  • anchor:当排放组件的可用空间要多于所需空间时,组件靠哪个方向排放,可选项是八个方位和中心(n, s, w, e, nw, ne, sw, se, center)。默认是"nw(西北,左上方就是)"。

  • expand:组件适应窗口。如设置为True,当窗口中有别的可用空间时,将会自动把组件居中摆放,并且拖拽后仍然适应窗口大小。默认为False

  • fill:组件的填充,可选项有"x", “y”, “both”, “none”,默认为"none"。分别表示:x方向填充,y方向填充,两个方向都填充,无填充。这将根据参数指定填充组件可用空间,一般和expand一起使用,以保证可用空间足够。(下面都设置了expand=True,窗口未拖拽时尺寸200x80)

2:布局-grid

Grid(网格)布局管理器会将控件放置到一个二维的表格里。

主控件被分割成一系列的行和列,表格中的每个单元(cell)都可以放置一个控件。

选项说明
column单元格的列号,从0开始的正整数
columnspan跨列,跨越的列数,正整数
row单元格的行号, 从0开始的正整数
rowspan跨行,跨越的行数,正整数
ipadx,ipady设置子组件之间的间隔,x方向或y方向
默认单位为像素,非浮点数,默认0.0
padx, pady与之并列的组件之间的间隔,x方向或y方向
默认单位为像素,非浮点数,默认0.0
sticky组件紧贴所在的单元格的某一脚,对应于东南西北中以及4个角。
东 = “e”,南=“s”,西=“w”,北=“n”,
“ne”,“se”,“sw”, “nw”;

grid-info => 查看组件默认的参数

import tkinter as tk
from tkinter import messagebox


# 定义事件
def button_event(e):
    print("点击了按钮")
    messagebox.showinfo("提示", "我是一个提示框")


def create_window():
    # 创建一个窗口,但是之后这个窗口闪一下就消失了,所以要在用循环反复显示 -> mainloop
    w = tk.Tk()
    w.title("我是第一个tkinter GUI")  # 设置窗口标题
    w.geometry("800x600")  # 设置窗口大小

    # 创建一个按钮
    b = tk.Button(w, text="点击我")  # 创建一个按钮, 将按钮放到窗口w中,并且设置按钮上的文字是"点击我"
    b.grid()
    '''
    {
        'in': <tkinter.Tk object .>, 
        'column': 0,  所在的列
        'row': 0,     所在的行
        'columnspan': 1,  所跨过的列数
        'rowspan': 1,     所跨过的行数
        'ipadx': 0,   子组件在x轴上的间隔
        'ipady': 0,   子组件在y轴上的间隔
        'padx': 0,    与之并列的组件之间在x轴的间隔
        'pady': 0,    与之并列的组件之间在y轴的间隔
        'sticky': ''  组件紧贴所在的单元格的某一脚,对应于东南西北中以及4个角
    }
    '''
    print(b.grid_info())
    return w


if __name__ == '__main__':
    window = create_window()
    window.mainloop()  # 显示窗口,循环反复显示

3:布局-place

place布局管理器可以通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景

选项说明
x,y组件左上角的绝对坐标(相当于窗口)
relx ,rely组件左上角的坐标(相对于父容器)
width , height组件的宽度和高度
relwidth , relheight组件的宽度和高度(相对于父容器)
anchor对齐方式,左对齐“w”,右对齐“e”,顶对齐“n”,底对齐“s”

4:样式设置

可知设置组件的字体族(tkinter.font),粗细,颜色(bg)等等信息

import tkinter as tk
from tkinter import messagebox
import tkinter.font as font


def create_style_window():
    w = tk.Tk()  # 创建窗口
    w.title("我是第一个tkinter GUI")  # 设置窗口标题
    w.geometry("800x600")  # 设置窗口大小
    # w.configure(bg="red")  # 设置窗口背景颜色
    #  设置字体样式,默认字体样式为Arial
    my_font1 = font.Font(family="华文行楷", size=20, weight="bold")
    my_font2 = font.Font(family="Arial", size=10, weight="normal")

    # bg:背景颜色
    # font:  字体样式
    but1 = tk.Button(w, text="背景色", font=my_font1, bg="LightSkyBlue")
    but1.grid(row=0, column=0)  # 设置按钮位置, 第1行,第1列

    lab1 = tk.Label(w, text="文字颜色", font=my_font2, foreground="Orange")
    lab1.grid(row=0, column=2)  # 设置标签位置第1行,第3列


    return w


if __name__ == '__main__':
    w = create_style_window()
    w.mainloop()  # 放在循环中,这样窗口就会一直显示,

三:基本控件介绍

python提供了15个基础控件,这是他的介绍

控件描述
Button按钮控件;在程序中显示按钮。
Canvas画布控件;显示图形元素如线条或文本
Checkbutton多选框控件;用于在程序中提供多项选择框
Entry输入控件;用于显示简单的文本内容
Frame框架控件;在屏幕上显示一个矩形区域,多用来作为容器
Label标签控件;可以显示文本和位图
Listbox列表框控件;在Listbox窗口小部件是用来显示一个字符串列表给用户
Menubutton菜单按钮控件,用于显示菜单项。
Menu菜单控件;显示菜单栏,下拉菜单和弹出菜单
Message消息控件;用来显示多行文本,与label比较类似
Radiobutton单选按钮控件;显示一个单选的按钮状态
Scale范围控件;显示一个数值刻度,为输出限定范围的数字区间
Scrollbar滚动条控件,当内容超过可视化区域时使用,如列表框。.
Text文本控件;用于显示多行文本
Toplevel容器控件;用来提供一个单独的对话框,和Frame比较类似
Spinbox输入控件;与Entry类似,但是可以指定输入范围值
PanedWindowPanedWindow是一个窗口布局管理的插件,可以包含一个或者多个子控件。
LabelFramelabelframe 是一个简单的容器控件。常用于复杂的窗口布局。
tkMessageBox用于显示你应用程序的消息框。

0:基础架子和通用属性

先搭建一个模板架子,将创建窗口等搭建好

import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        #  界面编写位置
        pass


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

标准属性也就是所有控件的共同属性,如大小,字体和颜色等等。

属性描述
Dimension控件大小;
Color控件颜色;
Font控件字体;
Anchor锚点;
Relief控件样式;
Bitmap位图;
Cursor光标;

relief属性

relief参数指定了组件边框的样式,一共有6种relief

分别是flat, groove, raised, ridge, solid, sunken

cursor属性

cursor参数指定鼠标移动到组件上时,光标的样子

下面的例子可以看到所有的光标样式 -> 鼠标放到对应的标签上时就能显示对应的光标样式

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 *

if __name__ == '__main__':
    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")

    root.mainloop()

下面是最常用的几个:

arrow, xterm, watch, hand2, sb_h_double_arrow, sb_v_double_arrow, fleur

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的意思是:图片置于文字的位置,上面的参数解释有。

1:Label-标签控件

1.1:基本信息参数
参数名称作用
text显示的文本
font文本的字体
image显示的图片
bitmap显示的位图,和image只能指定一个
textvariable绑定的文本变量
1.2:位置调整相关的属性
参数名称作用
compound当文本和图片同时显示时,图片位于文本的方位
可以是top, bottom, left, right, center
如:设置为top表示图片位于文本上方
padx / pady标签内容与左右 / 上下的间距
anchor文本靠标签哪个方向显示
可以是n, s, w, e, ne, nw, sw, se, center(默认)
justify文本的对齐方式
可以是left, right, center(默认)
wraplength自动换行字符数量
到达数量后文本会自动换一行显示
import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        # 创建一个标签控件,并且设置标签的位置(row=0, column=0)
        tk.Label(self.root, text="用户名").grid(row=0, column=0)
        tk.Label(self.root, text="密码").grid(row=1, column=0)
        tk.Label(self.root, text="身份证号码").grid(row=2, column=0)
        pass


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,
1.3:image属性

image参数在Label中添加图片。这个图片是一个tk.PhotoImage对象,支持的格式只有.gif, .ppm, .pgm,较新版的tk支持显示.png

注意:直接在图片文件后面改后缀不会改变图片本身的文件类型!

更需要强调的一点是,要保证储存PhotoImage图片对象变量不会被Python垃圾回收机制删除,这意味着不能设置成局部变量,否则图片无法正确显示。

image = tkinter.PhotoImage(file="图片名称")

这段代码建立了一个tk图片对象,现在需要把它传递给Label。

image = PhotoImage(file="monster.gif")
# compound的意思是:图片置于文字的位置, top就是图片在文字的上方
Label(root, image=image, text="It's a monster.", compound="top").pack()复制

如果要使用其他的格式的图片怎么办,这个时候就要使用PIL库了

pip install pillow
import tkinter as tk
from PIL import Image, ImageTk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        image = ImageTk.PhotoImage(Image.open("./1.png"))
        # 创建一个标签控件,并且设置标签的位置(row=0, column=0)
        tk.Label(self.root, image=image, text="用户名", compound="left").grid(row=0, column=0)
        pass


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

tkinter也有一些内部的图片,可以通过字符串传递给image。

  • ::tk::icons::error - 错误
  • ::tk::icons::information - 信息
  • ::tk::icons::question - 问题
  • ::tk::icons::warning - 警告

2:Button-按钮

可以绑定一个回调函数,用户点击时将会执行这个函数

与Label最不同的是,它还可以绑定一个点击事件。

2.1:个性化参数
参数名称作用
command点击按钮时运行(是一个方法)
repeatdelay延迟多少毫秒(1000ms=1s)后进行按钮的持续触发
repeatinterval按钮持续触发的间隔时长(毫秒)
overrelief鼠标经过时按钮的relief样式

常用方法:

方法作用
invoke()调用Button的command(disabled无效)
flash()使Button闪烁几次(在normal和active几次切换)
from tkinter import *

root = Tk()

def callback():
    print("你点了一下按钮")

# 点击按钮将会触发callback方法
button = Button(root, text="按钮", command=callback)
button.pack()

mainloop()

如果鼠标长按按钮,那么按钮不会被触发,而是松开鼠标后触发。

这时候,按钮处于一种激活状态。我们可以设置激活时按钮的前景和背景颜色:

Button(
    root,  # 挂载的窗口
    text="按钮",  # 按钮上的文字
    activeforeground="blue", # 激活时候的前景色
    activebackground="yellow" # 激活时候的背景色
)
2.2:重复触发

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)) # 改变text, +1操作
    
b = Button(
    root,  # 挂载的窗口
    text="0", # 按钮上的文字
    command=addnum, # 绑定的函数
    repeatdelay=1000, # 长按在按钮上,经过1秒的延迟后,按钮将会重复触发
    repeatinterval=500 # 触发的间隔是500毫秒
)

b.pack() # 按钮布局

mainloop()
2.3:禁用按钮

所有组件都有state参数,表示组件的状态:normal, disabled, active。

默认的state是normal,此时用户可以点击按钮。

而处于disabled禁用的按钮,用户将无法点击。active则是激活状态。

from tkinter import *

root = Tk()

def disable():
    # config -> 用于改变组件原本设定的参数,这个方法非常常用。
    button.config(state="disabled") # 按钮点击一次之后设置为禁用状态
    
button = Button(root, text="点击禁用", command=disable)
button.pack()

mainloop()

3:Entry-输入框

Entry是一个文本框组件,用户可以在里面输入文本。

3.1:常用参数和常用方法
参数名称作用
font输入文本字体
show输入文本被显示为什么字符
selectbackground选中文字的背景色
selectforeground选中文字颜色
insertborderwidth光标边框宽度(指定时光标样式为raised)
insertontime光标闪烁时,处于“亮”状态的时长(毫秒)
insertofftime光标闪烁时,处于“灭”状态的时长(毫秒)
insertwidth光标的宽度
selectborderwidth选中文字的背景边框宽度
textvariable绑定的StringVar,同步Entry输入的内容
readonlybackground文本框处于readonly状态下的背景颜色
xscrollcommandx方向滚动条(后面介绍)
validate验证输入合法性的条件
vcmdvalidatecommand判断输入合法性的回调函数,或者是一个包含回调和所需参数的元组
invcmdinvalidcommand输入不合法时执行的回调函数

常用方法:

方法名称作用
get()获取文本框的值
delete(first, last=None)删除文本框中从索引first到last的内容
insert(index, s)在文本框中插入文本,index是索引,s是插入内容
select_range(start, end)选中从start到end的文本
icursor(index)将光标移动到索引处
3.2:show和state属性
import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        tk.Label(self.root, text="姓名:").grid(row=0, column=0)
        user_name_entry = tk.Entry(self.root)
        user_name_entry.grid(row=0, column=1)

        tk.Label(self.root, text="密码:").grid(row=1, column=0)
        # show属性 -> 将输入的内容全部显示成为星号
        password_entry = tk.Entry(self.root, show="*")
        password_entry.grid(row=1, column=1)

        tk.Label(self.root, text="我是只读的:").grid(row=2, column=0)
        # state属性 -> 如果设置为readonly,则无法修改内容
        read_only_entry = tk.Entry(self.root, state="readonly", width=20)
        read_only_entry.grid(row=2, column=1)

        tk.Button(
            self.root,
            text="登录",
            command=lambda: print(user_name_entry.get(), password_entry.get())
        ).grid(row=4, column=0)


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

🎉 Entry可以设置为state="readonly",也就是只读状态。处于readonly的Entry不能被输入,但是用户可以选中Entry里面插入的内容,当然也可以复制。如果是disabled状态,用户不但不能输入,而且不能选中里面的内容。

3.3:输入信息的验证

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()

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)

改造一下上面的内容,就变成了这样

from tkinter import *

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


def vld(number):
    if number.isdigit():
        print("数字,符合要求")
        return True
    else:
        print("不是数字,不符合要求")
        return False


def wrong():
    print("输入不符合要求,调用invalidcommand")

v = root.register(vld)  # 注册验证函数为Tcl函数
e = Entry(root, validate="focusout", validatecommand=(v, "%P"), invalidcommand=wrong)
e.pack()

mainloop()

4:Frame-框架容器

Frame是框架的意思,让你在容器中能够创建一个子容器。

使用Frame,可以对组件编组,也可以使你能够在一个窗口中综合使用不同的布局方式。

比如,在窗口中使用pack布局,在窗口上的Frame中使用grid布局,这是允许的。

Frame没有别的参数,只有几个基本参数。使用也很简单。

4.1:frame的基本使用
import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        # 创建一个frame
        # 如果指定了frame的边框,必须指定db
        # relief: 边框样式 => flat, raised, sunken, groove, ridge
        frame1 = tk.Frame(self.root, bd=2, relief="groove")  # 创建一个frame, 挂载在根窗口上
        frame1.pack()  # 布局方式
        # ----------- frame中的控件以frame为父控件,所以需要指定父控件为frame --------
        label = tk.Label(frame1, text="Hello World1", bg="red", fg="white", font=("Arial", 16))
        label.grid(row=0, column=0) # frame中的布局
        label = tk.Label(frame1, text="Hello World2", bg="red", fg="white", font=("Arial", 16))
        label.grid(row=0, column=1)
        label = tk.Label(frame1, text="Hello World3", bg="red", fg="white", font=("Arial", 16))
        label.grid(row=0, column=2)

        # 创建一个frame
        # 如果指定了frame的边框,必须指定db
        # relief: 边框样式 => flat, raised, sunken, groove, ridge
        frame2 = tk.Frame(self.root, bd=2, relief="groove")  # 创建一个frame, 挂载在根窗口上
        frame2.pack()
        # ----------- frame中的控件以frame为父控件,所以需要指定父控件为frame --------
        label = tk.Label(frame2, text="Hello World4", bg="red", fg="white", font=("Arial", 16))
        label.grid(row=0, column=0)
        label = tk.Label(frame2, text="Hello World5", bg="red", fg="white", font=("Arial", 16))
        label.grid(row=0, column=1)
        label = tk.Label(frame2, text="Hello World6", bg="red", fg="white", font=("Arial", 16))
        label.grid(row=0, column=2)


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,
4.2:frame的作用

方便组件的排放

如果想要在窗口顶部横向摆放几个组件,在下面再摆一个组件,使用grid固然可以,但就比较麻烦,行列不容易调整。这时候可以加上Frame,在窗口中摆一个Frame,下面摆一个组件。再在Frame中横向摆三个组件。这样使用pack布局就能轻松完成。

方便取消映射或销毁组件

如果一个窗口中插入了大量组件,想要把其中一部分隐藏,就需要调用很多个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")

# 直接隐藏fr
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的子组件

5:其他控件

5.1:弹窗组件messagebox

只要使用了弹窗组件,就导入messagebox包 -> from tkinter import messagebox

import tkinter as tk
from tkinter import messagebox

window = tk.Tk()  # 创建窗口
window.title('entry')
window.geometry('500x500+100+100')  # 设置窗口大小
window.resizable(False, False)  # 禁止窗口缩放

tk.Label(window, text='账号', font=('黑体', 26)).place(x=50, y=100)
tk.Label(window, text='密码', font=('黑体', 26)).place(x=50, y=180)

s1 = tk.StringVar()  # 创建变量, 存储用户名
s2 = tk.StringVar()  # 创建变量, 存储密码

tk.Entry(window, textvariable=s1, font=('黑体', 26)).place(x=150, y=100)
tk.Entry(window, textvariable=s2, font=('黑体', 26)).place(x=150, y=180)


def login():
    print('账号:', s1.get())
    print('密码:', s2.get())
    if s1.get() == 'admin' and s2.get() == '123456':
        messagebox.showinfo(title='提示', message='登录成功')
    else:
        messagebox.showerror(title='提示', message='账号或密码错误')


# 登录按钮
tk.Button(window, text='登录', font=('黑体', 26), command=login).place(x=150, y=250)

window.mainloop()

四种弹窗分别是:

  • messagebox.showinfo(title, message, option) -> 显示信息类的弹窗(蓝色的)
  • messagebox.showwaring(title, message, option) -> 显示警告类的弹窗(黄色的)
  • messagebox.showerror(title, message, option) -> 显示错误类的弹窗(红色的)
  • messagebox.askxxx(title, message, option) -> 询问ok或者取消的弹窗(蓝色的)
def login():
    print('账号:', s1.get())
    print('密码:', s2.get())
    if s1.get() == 'admin' and s2.get() == '123456':
        messagebox.showinfo(title='提示', message='登录成功')
        # messagebox.showerror(title='提示', message='登录成功')
        # messagebox.showwarning(title='提示', message='登录成功')
    else:
        # messagebox.askokcancel(title='提示', message='账号或密码错误')  # 确认 取消
        messagebox.askretrycancel(title='提示', message='账号或密码错误')  # 重试 取消
        # messagebox.askyesno(title='提示', message='账号或密码错误')  # 是 否
        # messagebox.askyesnocancel(title='提示', message='账号或密码错误')  # 是, 否, 取消

对于ask类型的弹窗,可以接收用户点的是那个按钮,然后进一步处理

def login():
    print('账号:', s1.get())
    print('密码:', s2.get())
    if s1.get() == 'admin' and s2.get() == '123456':
        messagebox.showinfo(title='提示', message='登录成功')
        # messagebox.showerror(title='提示', message='登录成功')
        # messagebox.showwarning(title='提示', message='登录成功')
    else:
        ans = messagebox.askokcancel(title='提示', message='账号或密码错误')  # 确认 取消
        if ans:
            print('用户点击了确认')
            login()
        else:
            print('用户点击了取消')
            window.destroy()
5.2:顶层窗口Toplevel

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

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

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

一般用在注册按钮点击之后,弹出的注册窗口就是顶层窗口

from tkinter import Toplevel
import tkinter as tk

window = tk.Tk()
window.title('top level测试')
window.geometry('500x500')

top = Toplevel(window)  # create a top window, father is window
top.title('top level')
top.geometry('200x200')  # set top window properties 

window.mainloop()  # only root window need mainloop, top window don't need
5.3:变量绑定Variable

tkinter中所有的var有:

  • StringVar()文本变量对象
  • IntVar()整数变量对象
  • DoubleVar()浮点数变量对象
  • BooleanVar()布尔值变量对象

他们可以绑定到组件。它们都是继承tkinter.Variable,用法都是一样的

import tkinter as tk

window = tk.Tk()  # 创建窗口
window.title('entry')
window.geometry('500x500+100+100')  # 设置窗口大小
window.resizable(False, False)  # 禁止窗口缩放

tk.Label(window, text='账号', font=('黑体', 26)).place(x=50, y=100)
tk.Label(window, text='密码', font=('黑体', 26)).place(x=50, y=180)

s1 = tk.StringVar()  # 创建变量, 存储用户名
s2 = tk.StringVar()  # 创建变量, 存储密码

tk.Entry(window, textvariable=s1, font=('黑体', 26)).place(x=150, y=100)
tk.Entry(window, textvariable=s2, font=('黑体', 26)).place(x=150, y=180)


def login():
    print('账号:', s1.get())
    print('密码:', s2.get())


# 登录按钮
tk.Button(window, text='登录', font=('黑体', 26), command=login).place(x=150, y=250)

window.mainloop()
5.4:下拉框OptionMenu

OptionMenu是选项菜单,用户可以下拉一个选择菜单,指定OptionMenu的值。

这个组件比较特殊,它继承Menubutton类,有一些必选参数。

比如master参数在其他的组件是可选参数,如果不指定master将会自动继承主Tk窗口。

而OptionMenu必选master, variable参数。

参数作用
master指定父容器
variable指定绑定的variable,设为选项菜单的值
value选项菜单的初始选择值,没什么用,仍需要设置variable的值
*values选项菜单的可选项(不需要再加上value,初始选择值会自动添加到可选项中)
**kwargsOptionMenu继承Menubutton类,在此处指定Menubutton的**kw选项值
import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        #  界面
        label = tk.Label(self.root, text="请选择你的工作城市")
        label.pack()
        var = tk.StringVar() # 创建一个字符串变量,用于下拉框内容的绑定
        var.set("请选择你的工作城市")
        city = tk.OptionMenu(self.root, var, "北京", "上海", "广州", "深圳")
        city.pack() # 布局下拉框


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,
5.5:复选框Checkbutton

用户可以勾选或取消勾选。

Checkbutton可以绑定一个variable,一般是BooleanVar。表示是否选中当前选项?

Checkbutton拥有Button的参数之外,还可以有一些别的参数

参数作用
variable与Checkbutton选择相关的Variable
onvalue多选框选中时variable的值,默认为1
offvalue多选框未选中时variable的值,默认为0
selectcolor选择方框的颜色
selectimage选中时的图片(须指定image参数)
indicatoron是否显示为勾选框样式,默认为True

常用的方法如下:

方法说明
select()选中多选框
deselect()取消选中选框
toggle()切换选框的选中状态(反选选框)
invoke()调用Checkbutton的command(disabled无效)
flash()使Checkbutton闪烁几次(在normal和active几次切换)
import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        #  界面
        bv = tk.BooleanVar()
        cb = tk.Checkbutton(self.root, text="复选", onvalue="1", offvalue="0", variable=bv, command=lambda: print(bv.get()))
        cb.pack()  # pack布局


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,
5.6:单选框Radiobutton

Radiobutton是单选框。多个单选框可以绑定一个variable,这个variable的值是选中的单选框的值

Radiobutton的参数和Checkbutton几乎完全一样,不同的是Radiobutton没有onvalue和offvalue这两个参数,而是由value这个参数代替。value参数的作用是:指定单选框选中时绑定的Var的值

除了没有toggle()方法,其他常用的方法和复选框几乎一致。

import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        tk.Label(self.root, text="选择你最喜欢的编程语言:").pack()
        var = tk.StringVar()
        var.set("A")  # 设置默认值
        tk.Radiobutton(self.root, text="A", variable=var, value="Java").pack()
        tk.Radiobutton(self.root, text="B", variable=var, value="Python").pack()
        tk.Radiobutton(self.root, text="C", variable=var, value="Cpp").pack()


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,
5.7:菜单和子菜单Menu

Menu也就是菜单,菜单一般有两种,一种是窗口上的菜单,一种是弹出式菜单。

在这里插入图片描述

Menu中常用的参数和方法

参数说明
tearoff是否允许用户分离菜单,默认为True
title分离菜单的标题
tearoffcommand用户分离菜单时执行的事件
postcommand菜单被打开时执行的事件
方法说明
add(itemType)添加一个菜单项
insert(index)在index位置插入菜单项
add_command()添加命令菜单项
add_cascade()添加分层菜单项
add_checkbutton()添加多选框菜单项
add_radiobutton()添加单选框菜单项
add_separator()添加菜单分割线
delete(index1,index2)删除位于index1(到index2之间)的菜单项
entrycget(index, option)获取位于index菜单项的option值
entryconfig(index)更改位于index菜单项的参数的值
post(x, y)在(x, y)位置弹出菜单
unpost()取消弹出菜单
invoke(index)执行位于index菜单项的command
type(index)返回位于index菜单项的类型

菜单的创建

from tkinter import *
 
root = Tk()
 
menubar = Menu(root)
root.config(menu=menubar) #把菜单绑定root
 
mainloop()

菜单项的添加 - add()

add方法可以添加菜单项。第一个参数itemType指定菜单项的类型,马上会介绍到。add(itemType)也可以被add_itemType()所替换

对于itemType常有的有:

  • add_command -> 在菜单中添加一个命令菜单项
  • add_cascade -> 菜单可以有层级之分。最上层的菜单就是绑定了root的菜单,下面可以有一些子菜单,继承绑定窗口的父菜单,实现分层效果
  • add_separator -> 可以给菜单添加一条分割线

还可以提供一系列的可选参数:

参数说明
accelerator显示菜单的补充说明标签
label菜单项显示的标签
font标签的字体
bitmap菜单项显示的位图
image菜单项显示的图片
compound图片显示于标签的方位
state菜单项的状态
underline在标签的第几个索引的字符处画下划线,用来绑定Alt快捷键
columnbreak从此菜单项开始另起一列显示
hidemargin菜单项长度适应label长度,默认为True
command点击菜单项时执行的回调函数
menu绑定的分层子菜单(add_cascade)
selectcolor单选或多选按钮菜单的selectcolor
selectimage单选或多选按钮菜单的selectimage
value单选按钮菜单的value
variable单选或多选按钮菜单绑定的variable
onvalue多选按钮菜单的onvalue
offvalue多选按钮菜单的offvalue

add_command(**kw) <=> add("command", **kw)

import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def new(self):
        print("new")

    def open(self):
        print("open")

    def interface(self):
        # 主菜单的创建,挂载到根上 - config
        menu = tk.Menu(self.root)
        self.root.config(menu=menu)  # 配置菜单

        # 子菜单创建和函数绑定 - add_command
        file_menu = tk.Menu(menu)  # 创建菜单项,挂载到菜单栏中
        file_menu.add_command(label='new', command=self.new)  # 菜单项绑定回调函数
        file_menu.add_command(label='open', command=self.open)  # 菜单项绑定回调函数

        # 子菜单挂载到主菜单上 - add_cascade
        menu.add_cascade(label='file', menu=file_menu)

if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

add_cascade(**kw)

import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def new(self):
        print("新建文件")

    def open(self):
        print("打开文件")

    def save(self):
        print("保存文件")

    def quit(self):
        print("退出程序")

    def interface(self):
        # ================= 在这里编写窗口代码 ===================
        menubar = tk.Menu(self.root)  # 创建菜单
        self.root.config(menu=menubar)  # 添加菜单栏 = 刚才创建的菜单
        filemenu = tk.Menu(menubar, tearoff=0) # tearoff禁用分离菜单
        menubar.add_cascade(label="文件", menu=filemenu)
        filemenu.add_command(label="新建", command=self.new)
        filemenu.add_command(label="打开", command=self.open)
        filemenu.add_command(label="保存", command=self.save)
        filemenu.add_separator()
        filemenu.add_command(label="退出", command=self.quit)


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

accelerator参数

accelerator可以对菜单项进行补充。一般这个参数指定的是一个加速键(快捷键)名称,比如Ctrl+N这种。

不过,即使指定了accelerator参数也没有真正的绑定快捷键,需要使用bind来进行绑定。

import tkinter as tk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def new(self):
        print("new")

    def open(self):
        print("open")

    def interface(self):
        # 主菜单的创建,挂载到根上 - config
        menu = tk.Menu(self.root)
        self.root.config(menu=menu)  # 配置菜单

        # 子菜单创建和函数绑定 - add_command
        file_menu = tk.Menu(menu)  # 创建菜单项,挂载到菜单栏中
        file_menu.add_command(label='new', accelerator="ctrl + N", command=self.new)  # 菜单项绑定回调函数
        file_menu.add_command(label='open', accelerator="ctrl + O", command=self.open)  # 菜单项绑定回调函数

        # 快捷键绑定到对应的函数
        self.root.bind("<Control-n>", self.new)
        self.root.bind("<Control-o>", self.open)

        # 子菜单挂载到主菜单上 - add_cascade
        menu.add_cascade(label='file', menu=file_menu)

if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

underline参数

指定一个索引,绑定一个Alt快捷键。指定了underline的菜单项可以快捷触发。

触发方式是:先按下Alt,然后下划线会显示出来,再按下菜单项标签下划线上的字符即可执行这个菜单项。

from tkinter import *
 
root = Tk()
 
menu = Menu(root)
root.config(menu=menu)
 
def new():
    print("New file")

menu.add_command(label="New", underline=0, command=new)
 
mainloop()

弹出菜单

如果要弹出菜单,那么不需要绑定到窗口(root.config(menu=menu)),当然绑定也没关系。

弹出窗口需要使用post方法,需要提供x, y两个参数。

但这两个参数必须是屏幕上的位置,x_root和y_root。

下面的示例:当右击鼠标时在鼠标处弹出菜单。

from tkinter import *
 
root = Tk()
 
menu = Menu(root)

# show事件就是在屏幕的指定位置防止菜单
def show(event):
    menu.post(event.x_root, event.y_root)
    
def cp():
    print("Copy")

# 菜单中添加一个名称为copy的菜单项,绑定的函数时cp
menu.add_command(label="Copy", underline=0, command=cp)

# 鼠标右键绑定show事件
root.bind("<Button-3>", show)
 
mainloop()
5.8:滚动条Scrollbar
5.9:其他不常用控件

四:事件绑定和使用

组件的bind方法可以使组件绑定一个事件和一个回调函数

事件有按下某个键,点击组件等,一般是由用户引发的。事件会被传递给回调函数,然后执行函数。

在这里插入图片描述

# sequence是事件序列,func是检测到事件的回调函数
组件.bind(sequence=None, func=None)

1:按键名称

如果触发的是按键事件的话,就需要先了解对应的按键名称,这里列举出常用的按键名称

按键名(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                Linefeed(Ctrl + 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                ↑按键

2:序列号seq

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

<Modifier...-Type-Detail>

外面由<>尖括号括起来,中间由-减号隔开

  • Modifier -> modifier是条件,表示只有当modifier成立的时候才会执行函数,可以有多个
  • Type -> type是主要事件的类型,当type有detail的时候可以省略type(有时候可能弄混)
  • Detail -> Detail是事件类型的附带描述,有的事件类型需要Detail,但有的不需要
2.1:没有modifier的情况

对于type和detail,直接看下表

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和Mac;Linux应为Button-4、5)
Visibility窗口在屏幕中可见
from tkinter import Toplevel
import tkinter as tk

window = tk.Tk()
window.title('top level测试')
window.geometry('500x500')


# will create a new top window, father window is window
def click_me(e):
    print(e)  # <ButtonPress event num=3 x=23 y=17>
    top = Toplevel(window)
    top.title('top level')
    top.geometry('200x200')


button = tk.Button(window, text="右键点击我")
button.pack()  # pack

# bind event and func
button.bind("<Button-3>", click_me)  # 3 is right button

window.mainloop()  # main window need loop

callback是回调函数,它必须带有一个参数让bind方法传递。

当检测到事件的时候,bind方法会将一个Event对象传递给callback函数的第一个参数,让函数中能够处理检测到事件的信息。

tkinter.Event返回一个Event对象。Event对象在bind绑定时会传递给回调函数。大多数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()
2.2:modifier

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

modefier成立条件说明
Alt用户按着Alt键
Control用户按着Ctrl键
Shift用户按着Shift键
Button1 / B1用户按着鼠标左键
Button2 / B2用户按着鼠标中键
Button3 / B3用户按着鼠标右键
Button4 / B4用户将鼠标滚轮向上滚动(Linux)
Button5 / B5用户将鼠标滚轮向下滚动(Linux)
Double后面的事件类型被连续两次触发,常用于:双击鼠标左键()
Triple后面的事件类型被连续三次触发

3:bind_all

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

这样就不需要很麻烦地一个一个组件地绑定。

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

4:虚拟事件

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

既然是自定义事件,那么触发方式也需要自定。event_generate方法可以向组件发送一个事件。

这个事件可以是自定义事件,也可以不是自定义事件,而是<Button>那种自带的事件类型。

组件名称.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()

5: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的回调函数,

如果想要在一个事件执行之后,不再执行后续的事件,可以在这个事件触发的回调函数中返回"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()

6:unbind

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

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()

五:tk图片对象

tkinter有两种图片对象,一种是PhotoImage,用于导入*.gif, *.ppm, *.pgm格式的文件,新版tk还支持*.png

还有一种是BitmapImage,用于导入*.xbm的位图格式文件。这些图片对象都可以被传递给组件的image参数。这些图片对象都支持以下方法

当然这些都不推荐使用,上面说过推荐使用PIL

import tkinter as tk
from PIL import Image, ImageTk


class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("GUI")
        self.root.geometry("500x200+1100+150")  # 窗口大小 500*200,窗口位置 1100*150
        self.interface()

    def interface(self):
        image = ImageTk.PhotoImage(Image.open("./1.png"))
        # 创建一个标签控件,并且设置标签的位置(row=0, column=0)
        tk.Label(self.root, image=image, text="用户名", compound="left").grid(row=0, column=0)
        pass


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()  # 放在循环中,这样窗口就会一直显示,

六:tk子模块

上面讲述的组件,只需要导入tkinter就能使用。

tkinter还提供了一些子模块,你需要导入它们来使用它们提供的一些拓展的功能。

1:tkinter.constants

这个模块中存储了一些常用变量,在给组件指定一些参数的时候会用到。

比如设置组件的relief参数,可以设为"flat", "groove"等。

constants里面就有两个变量FLAT="flat", GROOVE = "groove"

tkinter主模块中导入了constants模块的所有内容,这意味着你不需要导入这个模块也能在主模块中使用它们。调用了from tkinter import *即可直接使用里面的常量。

2:tkinter.filedialog

filedialog模块中提供了一些询问文件的对话框,比如弹出一个对话框,让你选择一个文件。

下面介绍filedialog的函数。这些函数中,如果用户没有选择任何文件,那么会返回空字符串。

这些函数都可以提供如下关键字参数。

参数作用
defaultextension默认后缀名;如果用户未指定文件名后缀,则默认使用此后缀名
filetypes设置可选的文件类型
initialdir文件选择对话框弹出时,默认位于哪一个文件夹
initialfile文件选择框弹出时,默认选中哪一个文件
parent设置对话框的父窗口
title对话框的标题
multiple是否允许多选,默认为False
方法作用
askopenfilename(**options)返回用户选择打开的文件名称
askopenfilenames(**options)和上面一个函数类似,只不过允许多选
asksaveasfilename(**options)返回用户选择另存为的文件名称
askopenfile(mode="r", **options)选择后open打开
askopenfiles(mode="r", **options)和上面一个函数类似,只不过允许多选
asksaveasfile(mode="w", **options)用户选择另存为的文件名称后, open打开
askdirectory(**options)返回用户选择的文件目录

假设实现一个文件查看器

from tkinter import *
from tkinter.filedialog import askopenfilename


def load():
    filename = askopenfilename(filetypes=[("文本文档", "*.txt")])

    if filename:
        root.title(filename)

        text.config(state="normal")  # 先设置为normal,不然不能进行更改,编辑模式
        text.delete("1.0", "end")  # 清空文本框
        text.insert("1.0", open(filename, encoding="utf-8").read())  # 读取文件内容并插入到text中
        text.config(state="disabled")  # 设置为disabled,不能进行编辑


if __name__ == '__main__':
    root = Tk()
    root.title("Text Viewer")

    menu = Menu(root)
    root.config(menu=menu)
    menu.add_command(label="Load", command=load)

    text = Text(root, state="disabled")
    text.pack()

    root.mainloop()

3:tkinter.colorchooser

tkinter的子模块colorchooser,用于弹出一个对话框,让用户选择一种颜色。

这个模块使用方式非常简单,有一个函数askcolor,执行后即可弹出一个颜色对话框。

用户选择完颜色后,askcolor返回选取的结果,是一个元组

  • 第一项是选取颜色的RGB元组
  • 第二项是选取颜色的十六进制形式"#…"。

如果用户点击了取消,会返回一个两项都是空的元组(None, None)。

from tkinter import *
from tkinter import colorchooser


def choose_color():
    color = colorchooser.askcolor()
    print(color)


root = Tk()
Button(root, text="颜色选择", command=choose_color).pack()

mainloop()
参数作用
initialcolor颜色对话框弹出时,默认显示的颜色
parent颜色对话框的父窗口
title颜色对话框的标题

4:tkinter.ttk

ttk是tkinter的拓展组件库,提供了一些拓展的组件

还有一些组件主模块中已经包含了,它们的样子和原来不相同,显得更加美观

这些组件都继承ttk.Widget类。

from tkinter import *
from tkinter import colorchooser
from tkinter import ttk

def choose_color():
    color = colorchooser.askcolor()
    print(color)


root = Tk()
ttk.Button(root, text="颜色选择", command=choose_color).pack()

mainloop()

组件状态

ttk.Widget增加了一些特殊的方法,用来操作组件的状态。

ttk的状态表示为一个序列,里面包含了多个状态的标志,比如(“disabled”, “focus”)。

下面是ttk可以设置的状态标志。如果在不支持某个状态标志的组件上设置了此标志,那么不会有任何效果。

⚠️ ttk的组件状态和普通tkinter组件的状态不一样!

状态解释
active光标位于组件之上,且组件可激活,将设置此状态
hover光标位于组件之上,将设置此状态
disabled组件处于禁用状态
focus组件获取焦点,将设置此状态
pressed按钮被按下,将设置此状态(限于按钮类组件)
alternate选框的值未被设定时
selected选框处于启用、选中状态,将设置此状态(限于选框类控件)
background组件的父窗口是背景窗口(后台窗口),将设置此状态
readonly组件处于只读状态,禁止用户修改
invalid当组件验证的值无效,将设置此状态(限于Entry等有validate功能的组件)

如果要表示关闭或没有某个状态,可以在状态标志的前面加上"!“符号,如”!active"表示没有active状态。

state方法用于获取当前的状态元组,如果指定参数,也可以添加设置当前的状态。

from tkinter import *
from tkinter.ttk import *
    
root = Tk()
 
b = Button(root, text="点击")
b.pack()
b.state(["disabled"]) #禁用按钮
 
mainloop()

⚠️ 状态中没有normal,如果想要设定成普通的状态,需要通过!符号来关闭。

把上面示例的按钮恢复正常可执行b.state(["!disabled"])

state方法用于添加状态,而不是设置!

比如,设置状态为"disabled"后再执行b.state(["active"])并不代表当前组件状态变为了["active"],而是["disabled", "active"]

instate方法则用于检查组件的状态。

ttk的其他内容

参见:https://docs.python.org/zh-cn/3.10/library/tkinter.ttk.html#

5:tkinter.font

tkinter.font模块中提供了一些关于字体的操作。

首先要介绍的是font.Font类,这个类指定一个类型的字体,这个类的字符串形式可以被直接传递给组件的-font参数。

参数作用
font指定字体样式,可以是一个字体名或者字体元组,如(“宋体”, 12)
name此类字体的名称,指定后可以传递给组件font=“MyFontName”
exists字体是否已经存在于系统,如果设置为True而字体并不存在,则报错

还可以提供下面的关键字参数,这组参数和上面的参数font, exists(name除外)不用重复指定:

参数作用
family字体名称,如"宋体", “黑体”, “Arial”, “Times”
size字体尺寸大小
weight字体的厚度,可以是"normal"(普通), “bold”(加粗)
slant字体的倾斜样式,可以是"roman"(无倾斜), “italic”(倾斜)
underline是否有下划线
overstrike是否有删除线
from tkinter import *
from tkinter import font
        
root = Tk()
f = font.Font(family="黑体",
              size=16,
              slant="italic",
              underline=True)
 
Label(root, text="Tip:这是一段文字", font=f).pack()
 
mainloop()

支持如下的方法

方法作用
copy()将当前字体复制,返回一个Font对象
actual(option=None, displayof=None)返回字体的属性信息
cget(option)返回字体的option属性信息
config(**option)更改字体的选项
measure(text, displayof=None)返回text字符串使用此字体时的文本宽度
metrics(*options, **kw)返回字体的度量信息

除了Font类,font模块还提供了两个方法。

方法作用
families(root=None, displayof=None)返回系统上所有的可用字体名称
names(root=None)返回自定义的字体名称
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值