Skip to content

布局管理器

所有的 tkinter 组件都包含专用的几何管理方法,这些方法是用来组织和管理整个父配件区中子配件的布局的。tkinter 提供了截然不同的三种几何管理类:packgridplace

pack: 是按添加顺序排列组件。

grid: 是按行/列形式排列组件。

place: 允许程序员指定组件的大小和位置。

顺序布局(pack

pack 几何管理采用块的方式组织配件,在快速生成界面设计中广泛采用,若干组件简单的布局,采用 pack 的代码量最少。pack 几何管理程序根据组件创建生成的顺序将组件添加到父组件中去。通过设置相同的锚点(anchor) 可以将一组配件紧挨一个地方放置,如果不指定任何选项,默认在父窗体中自顶向下添加组件。

pack 方法提供了下列 option 选项,选项可以直接赋值或以字典变量加以修改:

名称描述取值范围
expand当值为 "yes" 时,side 选项无效。组件显示在父配件中心位置;
fill 选项为 "both",则填充父组件的剩余空间。
"yes", 自然数, "no", 0 (默认值为 "no"0
fill填充 x(y) 方向上的空间,当属性 side="top""bottom" 时,填充 x 方向;
当属性 side="left""right" 时,填充 "y" 方向;
expand 选项为 "yes" 时,填充父组件的剩余空间。
"x", "y", "both"(默认值为待选)
ipadx, ipady组件内部在 x(y) 方向上填充的空间大小,默认单位为像素,可选单位为c(厘米)、m(毫米)、i(英寸)、p(打印机的点,即 1/27 英寸),用法为在值后加以上一个后缀既可。非负浮点数(默认值为0.0)
padx, pady组件外部在 x(y) 方向上填充的空间大小,默认单位为像素,可选单位为c(厘米)、m(毫米)、i(英寸)、p(打印机的点,即 1/27 英寸),用法为在值后加以上一个后缀既可。非负浮点数(默认值为0.0)
side定义停靠在父组件的哪一边上。"top", "bottom", "left", "right"(默认为 "top"
before将本组件于所选组建对象之前 pack,类似于先创建本组件再创建选定组件。已经 pack 后的组件对象
after将本组件于所选组建对象之后 pack,类似于先创建选定组件再本组件。已经 pack 后的组件对象
in_将本组件作为所选组建对象的子组件,类似于指定本组件的 master 为选定组件。已经 pack 后的组件对象
anchor对齐方式,左对齐"w",右对齐"e",顶对齐"n",底对齐"s" "n", "s", "w", "e", "nw", "sw", "se", "ne", "center" (默认为"center")

注: 以上选项中可以看出 expandfillside 是相互影响的。

tkinter 模块提供了一系列大写值,其等价于字符型小写值,即 tkinter.YES == "yes"

注意

python
>>> import tkinter
>>> (tkinter.N, tkinter.NE) 
('n', 'ne')
>>> (tkinter.TOP, tkinter.LEFT) 
('top', 'left')

根据上面的案例,我们可以看出 tkinter 提供了一些变量用来简化字符串的操作

设置控件方位

可以通过修改 pack() 方法的 side 参数,side 参数可以设置 LEFTRIGHTTOPTOTTOM 四个方位,默认的设置是 side=tkinter.TOP

例如可以修改为左对齐

python
say_hello.pack(side=tk.LEFT)

如果你不想按钮挨着"墙角",可以通过设置 pack() 方法的 padxpady 参数自定义按钮的偏移位置:

python
say_hello.pack(side=tk.LEFT, padx=10, pady=10)

按钮既然可以设置前景色,那一定也能设置背景色吧?没错,bg 参数就是 background 背景色的缩写:

python
say_hello = tk.Button(frame, text='打招呼', bg="black", fg='blue')

image-20201201142416361

多组件依次排列

多组件布局(从左往右):默认布局是从上往下。

我们常常会遇到的一个情况是将一个组件放到一个容器组件中,并填充整个父组件。下面生成一个 Listbox 组件并将它填充到 root 窗口中:

python
import tkinter as tk

root = tk.Tk()
root.geometry("500x300+100+100")

# 多组件依次排列
tk.Label(root, text="Red", bg="red", fg="white").pack()
tk.Label(root, text="Green", bg="green", fg="black").pack()
tk.Label(root, text="Blue", bg="blue", fg="white").pack()

tk.mainloop()

image-20201201142708343

fill 填充内容

fill 选项是告诉窗口管理器该组件将填充整个分配给它的空间,BOTH 表示同时横向和纵向扩展,X 表示横向,Y 表示纵向;expand 选项是告诉窗口管理器将父组件的额外空间也填满。

默认情况下,pack 是将添加的组件依次纵向排列:

python
# fill 组件里面的内容进行填充
tk.Label(root, text="Red", bg="red", fg="white").pack(anchor=tk.W, fill=tk.X)
tk.Label(root, text="Green", bg="green", fg="black").pack(anchor=tk.W, fill=tk.X)
tk.Label(root, text="Blue", bg="blue", fg="white").pack(anchor=tk.W, fill=tk.X)

image-20201201142852630

横向排列

如果想要组件横向挨个儿排列,可以使用 side 选项:

python
# side 组件与组件之间的对齐方式 会影响后续的布局内容
tk.Label(root, text="Red", bg="red", fg="white").pack(side=tk.LEFT)
tk.Label(root, text="Green", bg="green", fg="black").pack(side=tk.LEFT)
tk.Label(root, text="Blue", bg="blue", fg="white").pack(side=tk.LEFT)

image-20201201143032203

注意

在同一个组件内,pack 布局只能实现一种布局方式,使用 frame 布局组件之后,就没有了这个限制。

pack 布局方法

pack 类提供了下列函数:

函数名描述
slaves()以列表方式返回本组件的所有子组件对象。
propagate(boolean)设置为 True 表示父组件的几何大小由子组件决定(默认值),反之则无关。
info()返回 pack 提供的选项所对应得值。
forget()Unpack 组件,将组件隐藏并且忽略原有设置,对象依旧存在,可以用 pack(option, …),将其显示。
location(x, y)x, y 为以像素为单位的点,函数返回此点是否在单元格中,在哪个单元格中。返回单元格行列坐标,(-1, -1) 表示不在其中。
size()返回组件所包含的单元格,揭示组件大小。

表格布局(grid)

grid 几何管理采用类似表格的结构组织配件,使用起来非常灵活,用其设计对话框和带有滚动条的窗体效果最好。grid 采用行列确定位置,行列交汇处为一个单元格。每一列中,列宽由这一列中最宽的单元格确定。每一行中,行高由这一行中最高的单元格决定。组件并不是充满整个单 元格的,你可以指定单元格中剩余空间的使用。你可以空出这些空间,也可以在水平或竖直或两个方向上填满这些空间。你可以连接若干个单元格为一个更大空间, 这一操作被称作跨越。创建的单元格必须相临。

grid方法提供了下列 option 选项,选项可以直接赋值或以字典变量加以修改:

名称描述取值范围
column组件所置单元格的列号。自然数(起始默认值为0,而后累加)
columnspan从组件所置单元格算起在列方向上的跨度。自然数(起始默认值为0)
ipadx, ipady组件内部在x(y)方向上填充的空间大小,默认单位为像素,可选单位为c(厘米)、m(毫米)、i(英寸)、p(打印机的点,即1/27英寸),用法为在值后加以上一个后缀既可。非负浮点数(默认值为0.0)
padx, pady组件外部在x(y)方向上填充的空间大小,默认单位为像素,可选单位为c(厘米)、m(毫米)、i(英寸)、p(打印机的点,即1/27英寸),用法为在值后加以上一个后缀既可。非负浮点数(默认值为0.0)
row组件所置单元格的行号。自然数(起始默认值为0,而后累加)
rowspan从组件所置单元格算起在行方向上的跨度。自然数(起始默认值为0)
in_将本组件作为所选组建对象的子组件,类似于指定本组件的master为选定组件。已经pack后的组件对象
sticky组件紧靠所在单元格的某一边角。"n", "s", "w", "e", "nw", "sw", "se", "ne", "center"(默认为 "center")

使用 grid 排列组件,只需告诉它你想要将组件放置的位置(行/列,row 选项指定行,cloumn 选项指定列)。此外,你并不用提前指出网格(grid 分布给组件的位置称为网格)的尺寸,因为管理器会自动计算。

python
import tkinter as tk

root = tk.Tk()
root.geometry("500x300+100+100")

# column默认值是0
tk.Entry(root).grid(row=1, column=1)
tk.Entry(root).grid(row=1, column=2)
tk.Entry(root).grid(row=1, column=3)
tk.Entry(root).grid(row=2, column=1)
tk.Entry(root).grid(row=2, column=2)
tk.Entry(root).grid(row=2, column=3)

tk.mainloop()

组件内容的对其方式

默认情况下组件会居中显示在对应的网格里,你可以使用 sticky 选项来修改这一特性。该选项可以使用的值有 EWSNEWSN分别表示东西南北,即上北下南左西右东)以及它们的组合。因此,可以通过 sticky=W 使得 Label 左对齐:

python
"""设置表格布局组件内的布局方位 """
tk.Label(root, text="用户名", padx=10).grid(row=1, column=1, sticky=tk.E)
tk.Entry(root).grid(row=1, column=2)

tk.Label(root, text="密码", padx=10).grid(row=2, column=1, sticky=tk.E)
tk.Entry(root, show="*").grid(row=2, column=2)

tk.Button(root, text='提交').grid(row=3, column=2)

image-20201201145956633

有时候可能需要用几个网格来放置一个组件,可以做到吗?当然可以,只需要指定 rowspancolumnspan 就可以实现跨行和跨列的功能:

跨行和跨列布局

python
import tkinter as tk

root = tk.Tk()

root.geometry("500x300+100+100")

# column默认值是0
tk.Label(root, text="用户名").grid(row=1, column=1, sticky=tk.E)
tk.Entry(root).grid(row=1, column=2)

tk.Label(root, text="密码").grid(row=2, column=1, sticky=tk.E)
tk.Entry(root, show="*").grid(row=2, column=2)

# 实现跨行需求
tk.Button(text="提交", width=20).grid(row=3, column=1, columnspan=2)
tk.mainloop()

image-20201201150105145

grid 布局方法

函数名描述
slaves()以列表方式返回本组件的所有子组件对象。
propagate(boolean)设置为 True 表示父组件的几何大小由子组件决定(默认值),反之则无关。
info()返回 pack 提供的选项所对应得值。
forget()Unpack 组件,将组件隐藏并且忽略原有设置,对象依旧存在,可以用 pack(option, …),将其显示。
grid_remove ()

绝对布局(place)

通常情况下不建议使用 place 布局管理器,因为对比起 packgridplace 要做更多的工作。不过纯在即合理,place 在一些特殊的情况下可以发挥妙用。请看下面的例子。

使用 place ,可以将子组件显示在父组件的正中间:

python
import tkinter as tk

root = tk.Tk()
root.geometry("500x300+100+100")

# relx 和 rely 选项指定的是相对于父组件的位置
# 范围是 00~1.0,因此 0.5 表示位于正中间。
tk.Button(root, text="绝对布局-正中心").place(
    relx=0.5, rely=0.5, anchor=tk.CENTER
)

tk.mainloop()

image-20201201150200788

在某种情况下,或许你希望一个组件可以覆盖另一个组件,那么 place 又可以派上用场了。下面例子演示用 Button 覆盖 Label 组件:

python
import tkinter as tk

root = tk.Tk()
root.geometry("500x300+100+100")

photo = tk.PhotoImage(file="like.png")
tk.Label(root, image=photo).pack()

# 绝对布局的组件可以在其他组件之上
tk.Label(root, text="覆盖组件").place(
    relx=0.5, rely=0.2, anchor=tk.CENTER
)

tk.mainloop()

案例:绝对布局覆盖

利用 place 覆盖组件

不难看出,relxrely 选项指定的是相对于父组件的位置,范围是00~1.0,因此0.5表示位于正中间。那么 relwidthrelheight 选项则是指定相对于父组件的尺寸:

image-20201201150514679

相对位置和相对尺寸

python
import tkinter as tk

root = tk.Tk()
root.geometry("500x300+100+100")
tk.Label(root, bg="red").place(relx=0.5,
                               rely=0.5,
                               relheight=0.75,
                               relwidth=0.75,
                               anchor=tk.CENTER)
tk.Label(root, bg="yellow").place(relx=0.5,
                                  rely=0.5,
                                  relheight=0.5,
                                  relwidth=0.5,
                                  anchor=tk.CENTER)
tk.Label(root, bg="green").place(relx=0.5,
                                 rely=0.5,
                                 relheight=0.25,
                                 relwidth=0.25,
                                 anchor=tk.CENTER)

tk.mainloop()

对于上面的代码,无论你如何拉伸改变窗口,三个 Label 的尺寸均会跟着同步。

案例-登录页面

编写下面内容的布局

image-20201201150105145