Python grid布局的简介与细节进阶 (3)

前两节我们:

  1. 介绍了tkinter库(Python标准GUI库)grid布局的基本概念
  2. 通过一个样例程序,讲解了grid布局与frame部件的配合使用技巧

接下来,我们继续演示grid布局相关的一些有用设置。

1. Frame的宽高设置以及grid_propagate()方法

1.1 Frame的宽高设置

假如说,我们想让主界面稍微大一些,就算285像素宽210像素高吧。看起来,实现这一点只需要修改一下frm_root的属性即可:

1
frm_root = tk.Frame(width=285, height=210)

但程序执行起来的结果和没有设置前毫无变化。主窗口的长宽与我们指定的值并不一致。这是为什么呢?

1.2 grid_propagate()设置

这是因为frame等容器类部件有一个grid_propagate的设置,其缺省设置为1(enabled):

  • 当该设置enabled时(widget.grid_propagate(1)):frame的大小将随其中容纳的所有部件的大小而变化,即frame的大小保证刚好能装下所有部件,frame部件本身的长宽设置不再起作用;
  • 当该设置disabled时(widget.grid_propagate(0)): frame的大小遵从其自身的长宽设置,不再考虑内部容纳部件的大小。

因此,我们只需disable掉frm_root的grid_propagate设置即可:

1
2
frm_root = tk.Frame(width=285, height=210)
frm_root.grid_propagate(0)

通过运行结果可见,新的主界面确实比原来大了:

修改了大小及grid_propagate的主界面

但现在所有部件还都紧紧地挤在一起,堆在主界面的右上角。看来通过修改frame大小,并不能很好地改善我们的布局。

下面,我们通过grid()方法的pad(外部间距)与ipad(内部间距)设置,以及部件的pad(内部间距)属性来进一步调节各个部件的位置。

2. grid()方法的pad/ipad设置与部件的pad属性

2.1 pad与ipad简介

无论是grid()方法的pad/ipad设置,还是部件的pad属性,都是用来调整部件间距的。

这里我们借用HTML/CSS中的margin(外部间距)/padding(内部间距)来理解一下:

HTML & CSS - Design and Build Websites中关于Border、Margin和Padding的图示说明

不用过多解释,这幅图已经很好地解释了什么是margin(外部间距),什么是padding(内部间距)。

上图摘自Jon Duckett所著HTML & CSS - Design and Build Websites的第13章 Boxes。顺便强力推荐一波,这本书不仅将HTML/CSS相关概念讲得非常清晰有条理,而且其排版更是干净、漂亮,宛如一尊精致的工艺品(下面是两段样张):

HTML & CSS - Design and Build Websites样页1

HTML & CSS - Design and Build Websites样页2

2.2 grid()方法的pad/ipad设置

grid()方法中的pad分为padx、pady,分别用以设置控件外部的横向间距与纵向间距,对应上述HTML/CSS中的margin;ipad同样分为ipadx、ipady,用以设置控件内部的横向间距与纵向间距,对应上述HTML/CSS中的padding。

使用方法为:

1
widget.grid(padx=5, pady=10, ipadx=15, ipady=20)

如果左右边距或上下边距需要设置为不一致的值,我们可以将一个包含两个值的tuple传给相关属性,如:

1
widget.grid(padx=(5, 10), pady=10, ipadx=15, ipady=20)

2.3 部件的pad属性

部件的pad属性同样分为padx、pady,分别用以设置控件内部的横向间距与纵向间距,对应HTML/CSS中的padding。
使用方法为(以Label为例):

1
lbl_tags = tkinter.Label(frm_root, padx=5, pady=10)

注意:

  1. 部件的pad属性不支持tuple赋值,即不支持左右边距或上下边距不一致
  2. 部件没有ipad属性

2.4 样例程序,以及grid()方法pad/ipad设置与部件pad属性的配合使用

我们用最简单的界面(主界面上只有一个Label)来演示相关效果。同时为了看清楚内外间距,我们特意把Label的border描出来了。

2.4.1 原始程序

1
2
3
4
5
6
7
8
9
10
11
12
import tkinter as tk
# main window
frm_root = tk.Frame()
frm_root.grid()
# tags row - label
lbl_tags = tk.Label(frm_root, borderwidth=1, relief="solid",
text="foobarfoobarfoobarfoobar")
lbl_tags.grid(row=0, column=0)
frm_root.mainloop()

运行结果:

未设置任何pad/ipad选项时的小程序界面

2.4.2 设置Label的内边距

我们只需要改创建lbl_tags对象的语句:

1
2
lbl_tags = tk.Label(frm_root, borderwidth=1, relief="solid", padx=15, pady=15,
text="foobarfoobarfoobarfoobar")

运行结果:

设置Label的pad属性时的小程序界面

2.4.3 设置Label的外边距

由于部件自身没有ipad属性,因此我们需要设置其grid()方法中的pad设置:

1
lbl_tags.grid(row=0, column=0, padx=15, pady=15)

运行结果:

设置Label的pad属性以及其grid()方法中pad设置后的小程序界面

2.4.3 内外边距配合使用

一般来说,我们:

  • 通过部件本身的pad属性,设置内部边距;
  • 通过部件grid()方法的pad设置,设置其外部边距

理论上,部件的外部边距也可以被视为其所在Frame的内部边距,但由于grid()方法的ipad设置在某些场景下会出现问题,所以并不推荐这么使用。

举一个例子,我们按照这个思路改写一下程序:

1
2
3
4
5
6
7
8
9
10
11
12
import tkinter as tk
# main window
frm_root = tk.Frame()
frm_root.grid(ipadx=15, ipady=15)
# tags row - label
lbl_tags = tk.Label(frm_root, borderwidth=1, relief="solid", padx=15, pady=15,
text="foobarfoobarfoobarfoobar")
lbl_tags.grid(row=0, column=0)
frm_root.mainloop()

其运行结果就比较诡异了:

设置Frame的grid()方法中ipad后的小程序界面

这里tkinter似乎知道要去设置frame的内部边距(即label的外部边距),但是却阴差阳错设置错了。所以,一般来说,没有特殊需要,我们不使用grid()方法的ipad设置。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器