布局管理器
所有的 tkinter 组件都包含专用的几何管理方法,这些方法是用来组织和管理整个父配件区中子配件的布局的。tkinter 提供了截然不同的三种几何管理类:pack
、grid
和 place
。
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" ) |
注: 以上选项中可以看出 expand
、fill
和 side
是相互影响的。
tkinter 模块提供了一系列大写值,其等价于字符型小写值,即 tkinter.YES == "yes"
。
注意
>>> import tkinter
>>> (tkinter.N, tkinter.NE)
('n', 'ne')
>>> (tkinter.TOP, tkinter.LEFT)
('top', 'left')
根据上面的案例,我们可以看出 tkinter 提供了一些变量用来简化字符串的操作
设置控件方位
可以通过修改 pack()
方法的 side
参数,side
参数可以设置 LEFT
、RIGHT
、TOP
和 TOTTOM
四个方位,默认的设置是 side=tkinter.TOP
。
例如可以修改为左对齐
say_hello.pack(side=tk.LEFT)
如果你不想按钮挨着"墙角",可以通过设置 pack()
方法的 padx
和 pady
参数自定义按钮的偏移位置:
say_hello.pack(side=tk.LEFT, padx=10, pady=10)
按钮既然可以设置前景色,那一定也能设置背景色吧?没错,bg
参数就是 background
背景色的缩写:
say_hello = tk.Button(frame, text='打招呼', bg="black", fg='blue')
多组件依次排列
多组件布局(从左往右):默认布局是从上往下。
我们常常会遇到的一个情况是将一个组件放到一个容器组件中,并填充整个父组件。下面生成一个 Listbox
组件并将它填充到 root
窗口中:
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()
fill 填充内容
fill
选项是告诉窗口管理器该组件将填充整个分配给它的空间,BOTH
表示同时横向和纵向扩展,X
表示横向,Y
表示纵向;expand
选项是告诉窗口管理器将父组件的额外空间也填满。
默认情况下,pack
是将添加的组件依次纵向排列:
# 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)
横向排列
如果想要组件横向挨个儿排列,可以使用 side
选项:
# 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)
注意
在同一个组件内,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
分布给组件的位置称为网格)的尺寸,因为管理器会自动计算。
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
选项来修改这一特性。该选项可以使用的值有 E
、W
、S
、N
(EWSN
分别表示东西南北,即上北下南左西右东)以及它们的组合。因此,可以通过 sticky=W
使得 Label
左对齐:
"""设置表格布局组件内的布局方位 """
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)
有时候可能需要用几个网格来放置一个组件,可以做到吗?当然可以,只需要指定 rowspan
和 columnspan
就可以实现跨行和跨列的功能:
跨行和跨列布局
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()
grid 布局方法
函数名 | 描述 |
---|---|
slaves() | 以列表方式返回本组件的所有子组件对象。 |
propagate(boolean) | 设置为 True 表示父组件的几何大小由子组件决定(默认值),反之则无关。 |
info() | 返回 pack 提供的选项所对应得值。 |
forget() | Unpack 组件,将组件隐藏并且忽略原有设置,对象依旧存在,可以用 pack(option, …) ,将其显示。 |
grid_remove () |
绝对布局(place)
通常情况下不建议使用 place
布局管理器,因为对比起 pack
和 grid
,place
要做更多的工作。不过纯在即合理,place
在一些特殊的情况下可以发挥妙用。请看下面的例子。
使用 place
,可以将子组件显示在父组件的正中间:
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()
在某种情况下,或许你希望一个组件可以覆盖另一个组件,那么 place
又可以派上用场了。下面例子演示用 Button
覆盖 Label
组件:
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
覆盖组件
不难看出,relx
和 rely
选项指定的是相对于父组件的位置,范围是00~1.0,因此0.5表示位于正中间。那么 relwidth
和 relheight
选项则是指定相对于父组件的尺寸:
相对位置和相对尺寸
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
的尺寸均会跟着同步。
案例-登录页面
编写下面内容的布局