发布时间:2023-02-06 文章分类:编程知识 投稿人:王小丽 字号: 默认 | | 超大 打印

以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/B1hH5Qzd2RkAiiUId1tLWw

本文大概 2874 个字,阅读需花 10 分钟
内容不多,但也花了一些精力
如要交流,欢迎关注我然后评论区留言
谢谢你的点赞收藏分享

进入正文之前先说一件小事,本公众号已改名为【ENG八戒】,原名是【englyf】。改名的理由是什么?以后会告诉朋友们的!

另外文末有福利彩蛋,毕竟今天是元宵节!

这篇文章属于系列文章《Python 内置界面开发框架 Tkinter入门篇》的第三篇,上接《Python 内置界面开发框架 Tkinter入门篇乙》,欢迎关注我的微信公众号「ENG八戒」查看这个系列相关文章。

界面布局

关于 Tkinter 框架的 GUI 布局,其实官方没有提供对应的图形化工具可用,但是网上有一些开源的小工具可以使用。这里不打算介绍这些小工具的使用,而是直接用框架提供的几何图形管理器来布局,比如上面提到过的 pack() 就是其中一种。这里提到的几何图形管理器也就是其它框架里常说的布局管理器。

Tkinter 框架提供的布局管理器有:pack、grid、place 三种。每一个控件只可以使用一种布局管理器,不同控件使用的布局管理器可以不一样。

pack

形象点说, pack 就是把控件包装在一个矩形区域,这个区域大小足够放置控件,而且默认置中。pack 是最简单的布局管理器,也称之为包装布局。

直接试一试用 pack 来布局三个静态标签 Label,默认设置(pack() 传入参数为空)

import tkinter as tk
window = tk.Tk()
lbl_1 = tk.Label(
    master=window,
    text="label 1",
    fg="black",
    bg="red",
    width=10,
    height=5
    )
lbl_1.pack()
lbl_2 = tk.Label(
    master=window,
    text="label 2",
    fg="black",
    bg="yellow",
    width=10,
    height=5
    )
lbl_2.pack()
lbl_3 = tk.Label(
    master=window,
    text="label 3",
    fg="black",
    bg="blue",
    width=10,
    height=5
    )
lbl_3.pack()
window.mainloop()

看看显示效果

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

可以看到,默认 pack 会在父窗口 window 中垂直方向按顺序包装排列这三个静态标签 Label。

那么,如果我需要让这几个标签水平排列呢?可以这样子改

lbl_1.pack(side=tk.LEFT)
...
lbl_2.pack(side=tk.LEFT)
...
lbl_3.pack(side=tk.LEFT)

看看显示效果

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

pack(side=tk.TOP) 和默认设置等价。

简单汇总介绍一下其它的参数

参数 赋值 说明
after 控件 widget 将此控件包装在控件 widget 后边
anchor NSEW (or subset) 根据方向定位此控件,NSEW 表示北南东西四个方向
before 控件 widget 将此控件包装在控件 widget 前边
expand bool 类型值 跟着父控件一起伸缩
fill NONE、X、Y、BOTH 选择当控件伸缩时按照哪个方向填充
ipadx amount 在x方向添加内部填充
ipady amount 在y方向添加内部填充
padx amount 在x方向添加填充
pady amount 在y方向添加填充
side TOP、BOTTOM、LEFT、RIGHT 把控件往哪边添加

很多时候开发界面都需要让里边的控件跟随窗口自动拉伸大小,来看一下上面的代码应该怎么改

lbl_1.pack(fill=tk.BOTH, expand=tk.TRUE)
...
lbl_2.pack(fill=tk.BOTH, expand=tk.TRUE)
...
lbl_3.pack(fill=tk.BOTH, expand=tk.TRUE)

启动的时候,还没有拉伸窗口

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

然后拉伸看看

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

grid

如名字表述,grid 会把父窗口划分成行列,然后根据调用时传入参数 row,column 确定把控件放置在对应的行列中。grid 也称之为格子布局。

还是以静态标签为例,创建 3x3 的矩阵标签。为了凸显各个标签的边界,这里还需要添加 Frame 控件,每个标签放置于单独的 Frame 中。

import tkinter as tk
window = tk.Tk()
for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(
            master=frame,
            text=f"Row {i}\nColumn {j}"
            )
        label.pack()
window.mainloop()

可以看到,上面这个例子布局时,只有 Frame 才需要应用 grid 管理器,因为每个标签和 Frame 一一对应,所以标签不需要重复应用格子布局。

看看显示效果

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

简单汇总介绍一下其它的参数

参数 赋值 说明
column 列序号 指定放置的列,从0开始
columnspan 列数 放置的控件横跨多少列
ipadx amount 在 x 方向添加内部填充
ipady amount 在 y 方向添加内部填充
padx amount 在 x 方向添加外部填充
pady amount 在 y 方向添加外部填充
row 行序号 指定放置的行,从0开始
rowspan number 放置的控件横跨多少行
sticky NSEW 如果单元格比较大,那么控件的指定边界将贴着单元格。NSEW分别对应顶部、底部、右边、左边边界

细心的朋友会发现,一旦拉伸上面的那个矩阵标签窗口,窗口界面就会露出部分底面,矩阵标签没有跟随一起拉伸。

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

这样明显和我们的预期不符合,这个问题怎么解决呢?

可以调用父窗口的 columnconfigure() 和 rowconfigure() 方法配置各列和行的伸缩比。这两个方法都有三个输入参数,看下面的表格

参数 说明
index 序号,指定特定的行或列,可以是单个行列序号值,也可以是代表多个行或列的列表
weight 伸缩权重
minsize 最小宽度值

现在来看看怎么改,才能让矩阵标签跟随父窗口一起拉伸?

import tkinter as tk
window = tk.Tk()
for i in range(3):
    window.rowconfigure(i, weight=1)
    window.columnconfigure(i, weight=1)
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(
            master=frame,
            text=f"Row {i}\nColumn {j}"
            )
        label.pack()
window.mainloop()

看看拉伸之后的显示效果

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

perfect!另外需要提一下,grid 具有 pack 能做的所有功能,但是使用的形式更简单,因此应该作为更优先的布局管理器。

place

place 用于对控件精确定位的场合。使用的时候需要传入参数 x 和 y 分别用于指定控件的放置位置坐标值 x 和 y,传入的 x 和 y 是基于父控件的左上角为原点的坐标系,单位是像素点。

大多数界面应用里,控件都不需要精确的定位。但是某些,比如地图应用里,就的确需要对元素的精确定位了。

下面举个栗子,在窗口里不同位置放置各一个标签

import tkinter as tk
window = tk.Tk()
label1 = tk.Label(
    master=window,
    text="place (0, 0)",
    bg="yellow"
    )
label1.place(x=0, y=0)
label2 = tk.Label(
    master=window,
    text="place (40, 40)",
    bg="blue"
    )
label2.place(x=40, y=40)
window.mainloop()

看看上面代码的显示效果

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

说回来,place 的参数xy单位是像素,那么在不同的系统下,字体类型和大小都是不同的,那么被放置的控件就有可能超出窗口边界。因此 place 真的不常用,关于 place 进一步的信息就不再展开了。

交互

上面介绍的内容都仅限于 Tkinter 界面的可视化设计,那么现在是时候介绍一下Tkinter 界面和用户的互动了。

比如,Tkinter 界面对于事件的响应是怎么发生的?

一般,在 Tkinter 中通过预先绑定事件和响应处理函数,每当事件发生时,主窗口的 mainloop 就会收到事件,然后根据绑定信息,查找到响应处理函数并调用,来达到交互的效果。绑定的通用方法是调用各个控件的 bind()。

比如,下面我们来实现一个简单的键盘响应互动,每次按键按下时就把对应的按键打印出来

import tkinter as tk
def handle_keypress(event):
    print(event.char)
window = tk.Tk()
window.bind("<Key>", handle_keypress)
window.mainloop()

上面的代码没有添加额外的控件,除了有个空白的主窗口。其中,对主窗口 window 绑定了按键事件 Key 和处理函数 handle_keypress。

调用 bind() 最少要求输入两个参数,一个是形式为 "<event_name>" 的事件,另一个是事件处理函数。

定义处理函数 handle_keypress 时,唯一做的事情是把传入的事件字符打印到标准终端。

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

每当按下键盘的按键,命令行终端就会输出对应的按键。

但是,对于按钮 Button 来说,点击事件的触发处理可以不需要使用 bind(),仅需要在实例化控件 Button 时,传入处理函数给 command 参数即可,初始化过程会自动绑定 click 事件的响应。

上代码看看

import tkinter as tk
def handle_keypress():
    print("clicked")
window = tk.Tk()
button = tk.Button(
    master=window,
    text="click me!",
    command=handle_keypress
    )
button.pack()
window.mainloop()

运行程序

Python 内置界面开发框架 Tkinter入门篇 丙(文末有福利彩蛋,今天可是元宵节)

用鼠标点击一下界面上的按钮,发现终端会输出字符串 clicked

由于篇幅受限,本系列教程还未完结,下一篇《Python 内置界面开发框架 Tkinter入门篇 丁》将在本公众号稍后推送,如果你对此教程有兴趣或者想和我一起交流更多精彩内容,欢迎关注我的微信公众号【ENG八戒】,等着你哦!

《元宵彩蛋》

闹元宵了,《八戒陪你一起闹元宵2023》