Tkinter 教程 - 布局管理

Jinku Hu 2023年1月30日 2018年2月27日
  1. Tkinter pack 布局方法
  2. Tkinter grid 布局方法
  3. Tkinter place 方法
Tkinter 教程 - 布局管理

我们在前面的几节中介绍了 Tkinter 的几种控件类型,比如标签按钮,下拉条等。在介绍这些控件的同时,也多少的提到了如果在程序窗口中来布局这些小控件,这也是你将要在这一节里所学到的重点-Tkinter 的布局管理方法。

Tkinter 有三种布局方法,packgrid 以及 place 方法。我们会对它们一一进行介绍。

Tkinter pack 布局方法

pack 按照字面理解,就是打包的意思,它将刚创建的控件通过打包的方法来放置到窗口中。我们在 Tkinter 标签的学习章节中,第一次使用这种布局方法,并且列出了 pack 的所有选项。

我们通过几个例子来看我们如何通过 pack 以及它的选项来布局 Tkinter 的控件。

Tkinter pack 布局-相对位置

import tkinter as tk    
    
app = tk.Tk()
app.geometry('300x200')

buttonW = tk.Button(app, text="West", width=15)
buttonW.pack(side='left')

buttonE = tk.Button(app, text="East", width=15)
buttonE.pack(side='right')

app.mainloop()

程序运行后,你得到的是这样的一个窗口界面。

Tkinter pack 布局方法的 side 相对位置

可以看到,按钮 buttonWest 紧贴着窗口左侧,按钮 buttonEast 紧贴着窗口右侧。你可以试着缩放下窗口的大小,你会发现,它们仍然会紧贴在窗口两侧,相对位置不会改变。

buttonW.pack(side='left')

side 有四个选项,topbottomleftright。它会将控件放在窗口的 side 侧。就像所举的例子中,buttonW 放在窗口的左侧,因为 side='left',而 buttonE 放在窗口的右侧,因为其 side=right

现在问题来了,如果两个控件,它们具有相同的 side 属性,那它们又是怎么布局的呢?

试着自己回答吧,或者运行下以下的代码来实际操作下。

import tkinter as tk
    
    
app = tk.Tk()
app.geometry('300x200')

buttonW = tk.Button(app, text="West", width=15)
buttonW.pack(side='left')

buttonE1 = tk.Button(app, text="East 1", width=15)
buttonE1.pack(side='right')

buttonE2 = tk.Button(app, text="East 2", width=15)
buttonE2.pack(side='right')

app.mainloop()

Tkinter pack 增加控件内部外部 padding

有些情况下,你需要在控件的内部或者外部增加一些 padding,使得控件之间以及控件文字跟控件边界间不是那么的拥挤。这时候,你需要 ipadxipady 以及 ipadxipadx 选项的配置。

import tkinter as tk
    
app = tk.Tk()
app.geometry('300x200')

buttonW = tk.Button(app, text="West")
buttonW.pack(side='left', ipadx=20, padx=30)

buttonE = tk.Button(app, text="East")
buttonE.pack(side='right', ipadx=20, padx=30)

app.mainloop()

Tkinter pack 布局方法增加控件 padding

两个按钮都增加了内部 20 个单位和外部 30 个单位的 x 方向上的 padding,这里 ipadpad 的单位是像素,而不是一个字符的宽度。

Tkinter pack 布局在 x,y 方向上的填充

下面的代码实现的功能就是控件的尺寸能够自动的填充至同窗口等宽或者等高,而且当你缩放窗口的时候,控件的尺寸也能够自动随着窗口的大小而变化。

import tkinter as tk
    
app = tk.Tk()
app.geometry('300x200')

buttonX = tk.Button(app, text="Fill X", bg="red", height=5)
buttonX.pack(fill='x')

buttonY = tk.Button(app, text="Fill Y", bg="green", width=10)
buttonY.pack(side='left', fill='y')

app.mainloop()

Tkinter pack 布局方法在 X,Y 方向上填充

butonX.pack(fill='x') 意味着控件 buttonX 的宽度会填充满整个窗口的宽度,同样的,如果 fill='y'就将控件的高度延伸至填满整个窗口的高度,fill='both'就是在 X 和 Y 方向的结合,会自动在宽度和高度方向上填满整个窗口。

Tkinter pack 布局 expand 选项-自动展开控件

上面 fill= 选项是如果将窗口拖放时候,自动将控件在 xy 方向上填充。跟它类似的一个需求就是假如一个控件有很多的内容,比如说一个列表项,如果把它的内容自动全部展示出来呢?

import tkinter as tk
import calendar    
    
app = tk.Tk()

buttonX = tk.Button(app, text="Label ", bg="blue", height=5)
buttonX.pack(fill='x')

listboxA = tk.Listbox(app, width=10)
listboxA.pack(fill='both', expand=1)

for i in range(1,13):
    listboxA.insert(tk.END, calendar.month_name[i])
    
app.mainloop()

Tkinter pack 布局方法在 expand 选项开启

expand=True or 1 时,列表框就会铺开列表中所有的元素,比如例子中,显示了从 JanuaryDecember

假如 expand 选项没有选中的话,那么列表框就默认的只显示了前十个元素,后续的元素需要你选中列表框后通过鼠标或者方向键移动才能够显示出来。

listboxA.pack(fill='both', expand=0)

通过 expand=0 禁止了列表框自动的铺开所有元素。

Tkinter pack 布局方法在 expand 选项关闭禁止

Tkinter grid 布局方法

Tkinter grid 是另外一种,也是最重要的一种窗口内控件布局的方法,假如要在三种布局方法里面只学习一种的话,那非 grid 方法莫属。

grid 经常用在用在对话框中,你可以按照网格的坐标位置来安放控件,这样能够得到稳定的控件相对位置。

同以上的举例方法不一样,我们下面的例子会创建一个相对复杂的界面,尽可能的能够用到所有的 grid 选项,然后在后续的说明中再详细的进行解释。

import tkinter as tk

app = tk.Tk() 

labelWidth = tk.Label(app,
                    text = "Width Ratio")
labelWidth.grid(column=0, row=0, ipadx=5, pady=5, sticky=tk.W+tk.N)

labelHeight = tk.Label(app,
                    text = "Height Ratio")
labelHeight.grid(column=0, row=1, ipadx=5, pady=5, sticky=tk.W+tk.S)

entryWidth = tk.Entry(app, width=20)
entryHeight = tk.Entry(app, width=20)

entryWidth.grid(column=1, row=0, padx=10, pady=5, sticky=tk.N)
entryHeight.grid(column=1, row=1, padx=10, pady=5, sticky=tk.S)

resultButton = tk.Button(app, text = 'Get Result')
resultButton.grid(column=0, row=2, pady=10, sticky=tk.W)

logo = tk.PhotoImage(file='python.gif')
labelLogo = tk.Label(app, image=logo)

labelLogo.grid(row=0, column=2, columnspan=2, rowspan=2,
               sticky=tk.W+tk.E+tk.N+tk.S, padx=5, pady=5)

app.mainloop()

Tkinter grid 布局方法

Tkinter grid columnrow 选项

labelWidth.grid(column=0, row=0, ipadx=5, pady=5, sticky=tk.W+tk.N)

在网格布局中,每一个控件都要放在固定的单元格中,每个单元格的坐标由行列也就是 columnrow 来确定。

labelWidth 控件就放在坐标为 (0, 0) 的单元格中,坐标是以窗口的左上角为坐标系的原点。

ipadxipadypadx 以及 pady 同前面介绍的 pack 布局方法中的同名选项意义是一样的,是控件在内部和外部的填充间距。

Tkinter grid sticky 选项

sticky 的作用是当所产生的单元格大小比控件本身要大的时候,如何进行扩展。

sticky 选项 意义
W 左对齐
E 右对齐
N 上对齐
S 下对齐

sticky 的默认选项是居中,也就是 W+E+N+S

Tkinter columnspanrowspan 选项

labelLogo.grid(row=0, column=2, columnspan=2, rowspan=2,
               sticky=tk.W+tk.E+tk.N+tk.S, padx=5, pady=5)

在 logo 控件中,单元格的坐标是 (column=2, row=0),又因为 logo 的尺寸比较大,我们让它占据 2x2 个单元格的大小。columnspan=2rowspan=2 就是表示控件以所设定的坐标为起点,在 XY 方向上都有两个单元格大小的跨度。

Tkinter place 方法

Tkinter place 是将控件安放在窗口中的固定或者相对的位置上。我们还是同样的采用刚才类似的思路,先列出例子和图形界面来,然后再来解释具体的选项。

import tkinter as tk

app = tk.Tk()
app.geometry('300x300') 

labelA = tk.Label(app, text = "Label (0, 0)", fg="blue", bg="#FF0")
labelB = tk.Label(app, text = "Label (20, 20)", fg="green", bg="#300")
labelC = tk.Label(app, text = "Label (40, 50)", fg="black", bg="#f03")
labelD = tk.Label(app, text = "Label (0.5, 0.5)", fg="orange", bg="#0ff")

labelA.place(x=0, y=0)
labelB.place(x=20, y=20)
labelC.place(x=40, y=50)
labelD.place(relx=0.5, rely=0.5)

app.mainloop()

Tkinter Place 布局方法

Tkinter place 绝对位置

labelA.place(x=0, y=0)
labelB.place(x=20, y=20)

placex=y= 选项确定了控件的绝对位置,它们的单位是像素。比如 lableB.place(x=20, y=20) 的意思就是将控件放置在坐标为 (20, 20) 的位置上。

Tkinter place 相对位置

绝对位置的弊端即使假如当窗口缩放的时候,假如窗口中还有其他的控件是用相对位置来布局的,那么使用绝对位置布局的控件和其他的控件可能就会有重叠,这是你不希望看到的。

place 方法中,也有相对位置的方法,这就是

labelD.place(relx=0.5, rely=0.5)

其中,relxrely 的数值是 0.0~1.0。它们代表了控件位置和窗口大小的相对大小关系。

比如 relx=0.5, rely=0.5 的意思即使控件位置就在窗口水平尺寸的一半,也在窗口垂直尺寸的一半。

relx=1.0 的位置是窗口的右边框,rely=1.0 的位置是窗口的下边框。

Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn