这节课我们来学习 Tkinter 的布局管理器,那什么是布局管理器呢?说白了,就是用于管理你的组件如何排列。Tkinter 提供了 3 大布局管理器:pack、grid 和 place。

  • pack 是按添加顺序排列组件
  • grid 是按行/列形式排列组件
  • place 则允许程序员指定组件的大小和位置

(一)pack 管理器

对比 grid 管理器,pack 更适用于少量组件的排列,但它在使用上更加简单(就像我们前边所有的例子中,展示一个组件我们一般都直接使用 .pack(),多简单~)。如果你需要创建相对复杂的布局结构,那么建议是使用多个框架(Frame)结构构成,或者使用 grid 管理器实现。

注意:不要在同一个父组件中混合使用 pack 和 grid,因为 Tkinter 会很认真地在那儿计算到底先使用那个布局管理器......以至于你等了半个小时,Tkinter 还在那儿纠结不出结果!

我们来给大家举个例子:

我们常常会遇到一个情况就是 将一个组件放进另一个组件里,并且要求是填充另一个组件,那我们要怎么做呢?

我们先用 pack 来演示:

import tkinter as tk

root = tk.Tk()

listbox = tk.Listbox(root)
listbox.pack(fill = "both", expand = True)

for i in range(10):
    listbox.insert("end", str(i))

root.mainloop()

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

刚才我们说了,pack 是按照组件的添加顺序依次排列的,那么它依次是纵向排列呢,还是横向排列呢?我们做一个演示:

import tkinter as tk

root = tk.Tk()

tk.Label(root, text="Red", bg="red", fg="white").pack(fill="x")
tk.Label(root, text="Green", bg="green", fg="black").pack(fill="x")
tk.Label(root, text="Blue", bg="blue", fg="white").pack(fill="x")

root.mainloop()

使用python grid显示图像为什么只能显示一个 python plt grid_ide

我们可以看到,是纵向填充的。如果想要组件横向挨个儿排放,你可以使用 side 选项:

import tkinter as tk

root = tk.Tk()

tk.Label(root, text="Red", bg="red", fg="white").pack(side = "left")
tk.Label(root, text="Green", bg="green", fg="black").pack(side = "left")
tk.Label(root, text="Blue", bg="blue", fg="white").pack(side = "left")

root.mainloop()

使用python grid显示图像为什么只能显示一个 python plt grid_tkinter_02

(二)grid 管理器

grid 管理器可以说是 Tkinter 这三个布局管理器中最灵活多变的。如果你只希望学习使用一个布局管理器,那么 grid 绝对是首选。当你在设计对话框的时候,使用 gird 尤其便捷。如果你此前一直在用 pack 构造窗口布局,那么学习完 grid 你会悔恨当初为啥不早学它。使用一个 grid 就可以简单的实现你用很多个框架和 pack 搭建起来的效果。

注意:不要在同一个父组件中混合使用 pack 和 grid,因为 Tkinter 会很认真地在那儿计算到底先使用那个布局管理器......以至于你等了半个小时,Tkinter 还在那儿纠结不出结果!

我们还是举例说明:

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

import tkinter as tk
 
root = tk.Tk()
 
# column 默认值是 0
tk.Label(root, text="用户名").grid(row=0)
tk.Label(root, text="密码").grid(row=1)
 
tk.Entry(root).grid(row=0, column=1)
tk.Entry(root, show="*").grid(row=1, column=1)
 
root.mainloop()

使用python grid显示图像为什么只能显示一个 python plt grid_tkinter_03

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

tk.Label(root, text="用户名").grid(row=0, sticky="w")
tk.Label(root, text="密码").grid(row=1, sticky="w")

使用python grid显示图像为什么只能显示一个 python plt grid_布局管理器_04

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

import tkinter as tk
 
root = tk.Tk()
 
# column 默认值是 0
tk.Label(root, text="用户名").grid(row=0, sticky="w")
tk.Label(root, text="密码").grid(row=1, sticky="w")
 
tk.Entry(root).grid(row=0, column=1)
tk.Entry(root, show="*").grid(row=1, column=1)
 
photo = tk.PhotoImage(file="logo.gif")
tk.Label(root, image=photo).grid(row=0, column=2, rowspan=2, padx=5, pady=5) #跨两行
 
tk.Button(text="提交", width=10).grid(row=2, columnspan=3, pady=5)  #跨3列
 
root.mainloop()

使用python grid显示图像为什么只能显示一个 python plt grid_ide_05

(二)place 管理器

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

比如 我想 :将子组件显示在父组件的正中间:

import tkinter as tk

root = tk.Tk()

def callback():
    print("正中靶心")

tk.Button(root, text="点我", command=callback).place(relx=0.5, rely=0.5, anchor="center")

root.mainloop()

relx 和 rely 就是相对于 父组件的水平位置 和 垂直位置; anchor="center" 表示居中显示。

使用python grid显示图像为什么只能显示一个 python plt grid_Python_06

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

import tkinter as tk

root = tk.Tk()

photo = tk.PhotoImage(file = "Python.gif")
tk.Label(root, image = photo).pack()

def callback():
    print("正中靶心")

tk.Button(root, text="点我", command=callback).place(relx=0.5, rely=0.5, anchor="center")

root.mainloop()

使用python grid显示图像为什么只能显示一个 python plt grid_Python_07

relx 和 rely 选项指定的是相对于父组件的位置,范围是 00 ~ 1.0,因此 0.5 表示位于正中间。那么 relwidth 和 relheight 选项则是指定相对于父组件的尺寸:

import tkinter as tk

root = tk.Tk()

tk.Label(root, bg="red").place(relx=0.5, rely=0.5, relheight=0.75, relwidth=0.75, anchor="center")
tk.Label(root, bg="yellow").place(relx=0.5, rely=0.5, relheight=0.5, relwidth=0.5, anchor="center")
tk.Label(root, bg="green").place(relx=0.5, rely=0.5, relheight=0.25, relwidth=0.25, anchor="center")

root.mainloop()

使用python grid显示图像为什么只能显示一个 python plt grid_布局管理器_08

上图无论你如何拉伸改变窗口,三个 Label 的尺寸均会跟着改变(神同步~)。x 和 y 选项用于设置偏移(像素),如果同时设置 relx(rely)和 x(y),那 place 将优先计算 relx 和 rely,然后再实现 x 和 y 指定的偏移值。