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

0. 背景

使用Python tkinter编写GUI程序有一段时间了,期间也遇到并解决了一些问题。为此,将过程中的心得与某些值得注意的地方总结记录下来,供(未来的)自己及同行参考。

了解Python tkinter基本概念或已经使用过tkinter的读者,会从本系列博文中收获最多;有Python编程基础的读者,也可以籍此文初窥Python GUI编程(特别是grid布局)的简洁。

原以为本文不会太长,没想到拉拉杂杂写下来才发现:要想把相关概念、细节讲清楚,并配上足够的示例图片的话,篇幅就太长了。为了保证读者阅读得舒适,本文只好扩展为一系列博文。

本系列博文包括以下4篇内容:

  1. 介绍tkinter库(Python标准GUI库)grid布局的基本概念;
  2. 通过一个样例程序,讲解grid布局的一些常用设置,包括grid与frame的配合使用,grid_propagate方法;
  3. 继续上面的例子,讲述grid方法padx/pady与ipadx/ipady选项的设置;
  4. 继续上面的例子,讲述grid方法sticky选项的设置;附:1)学习tkinter及grid布局的推荐资源,2)本程序在GitHub上的地址。

本系列博文不包括以下内容:

  1. Python 语法解释;
  2. Python GUI编程的完整介绍。

1. Python tkinter布局简介

布局是GUI程序的重要概念,它决定了屏幕上每一个部件(widget)的位置。
Python标准GUI库tkinter支持三种布局方式:

  • pack - 采用堆砌法来组织部件,程序需指定部件与部件之间的相对位置
  • place - 采用位置法来组织部件,程序需明确指定部件要被显示的坐标
  • grid - 采用表格法来组织部件,系统把屏幕(frame)视为一个二维表格,程序需指定某部件要显示在哪一个单元格中

这三种布局方式实现的功能是一致的1,每一种布局方式都可以让程序员:

  • 管理部件的位置
  • 管理部件与其相关处理的关联注册关系
  • 在屏幕上显示部件

注意:这三种布局方式互不兼容,因此不要在同一窗口中混用!

2. 一般情况下推荐使用grid布局

一般说来,在这三种布局方式中,pack最简单粗旷,place最精细复杂,而grid在这两者之间最为平衡:既不失简洁又足以控制部件出现在你想要的位置上。因此grid布局获得了很多人的推荐:

  • Tkinter 8.5 reference: a GUI for Python提到:

    Although there are three different “geometry managers” in Tkinter, the author strongly prefers the .grid() geometry manager for pretty much everything.

  • The Tkinter Grid Geometry Manager是这么说的:

    The grid manager is the most flexible of the geometry managers in Tkinter. If you don’t want to learn how and when to use all three managers, you should at least make sure to learn this one.

  • Python Course - Layout Managers / Geometry Manager总结到:

    Grid is in many cases the best choice for general use. While pack is sometimes not sufficient for changing details in the layout, place gives you complete control of positioning each element, but this makes it a lot more complex than pack and grid.

既然大家都说好,那么赶紧用起来吧!

3. grid的基本概念与使用

3.1 布局的基本概念

为了便于大家理解,我们先看看最熟悉的EXCEL。EXCEL的工作表虽是一张二维表,但是通过行列的组合,我们可以用其实现复杂的排版:

  • 样例1 - 账单:

EXCEL示例

  • 样例2 - 日历:

EXCEL示例

tkinter grid布局的工作方式与EXCEL一样(或者更精确地说,和HTML的table标签一样):

  • 程序指定某个部件位于哪个单元格中;
  • 出于排版的需要,可以将若干个单元格合并以容纳某个部件(比如上面EXCEL样例1-账单中的实际客户名称就将被放置于21行的C、D、E合并单元格中)

3.2 grid方法简介

grid布局中最重要的内容就是grid()方法。tkinter中所有标准部件都支持grid()法。通过调用grid()方法,每个部件得以获取其布局位置,并在屏幕上显示出来。

grid方法的基本形式为:

1
widget.grid()

3.3 如何指定行列

与HTML的table标签一样,grid布局不需要(也不能)预先指定本屏幕(frame)中到底有多少行多少列。grid随着你的使用而自动增加行列。

  • grid默认你从第0行第0列放置第一个部件(widget):
  • 如果不指定行列,grid将把当前部件放到当前行的下一列中(从第0行第0列开始),这也意味着你一直用widget.grid()放部件,所有部件都会出现在一行中;
  • 同大家的直觉一致,grid的第1列在第0列右侧,第1行在第0行下方,其它行列依此类推;
  • 你可以通过row与column选项告知grid某部件应该被放置到哪一行列:
1
widget.grid(row=1, column=0)
  • 上述语句将widget放置在grid布局的第1行(第0行下方)的第0列(最左侧)

3.4 列宽与行高

grid布局与流行的网页bootstrap布局不一样,其列宽与行高不是固定的:

  • grid某列的列宽是由该列中容纳的最宽部件所决定的;
  • grid某行的高度是由该行中容纳的最高部件所决定的。
  • 程序无法直接指定grid的列宽与行高。

3.5 合并单元格/扩展行列

我们上面说过,合并单元格的目的是为了“排版的需要”;比如上面EXCEL的两个样例演示的那样。后续的博文中,我们会给出实际的例子。这里我们先看看基本代码如何实现:

1
2
3
4
5
6
7
8
# 部件1占据第0行第0、1列(列扩展)
widget1.grid(row=0, column=0, columnspan=2)
# 部件2占据第1、2行第0列(行扩展)
widget2.grid(row=1, column=0, rowspan=2)
# 部件3占据第3、4行第0、1列(行列同时扩展)
widget3.grid(row=3, column=0, columnspan=2, rowspan=2)

用EXCEL来示意,这几个部件的位置应该如下图所示:

用EXCEL示意widget位置

注意:没有widget4与widget5,widget2是没有必要扩展到两行的(为什么?你应该能想明白);当然,widget3也是一样,只不过实例代码与Excel示意图上都省略了其他widget而已。

grid布局的基本概念与使用就先介绍到这里吧,后续博文会通过样例程序继续讲解grid的进阶使用。


尾注


  1. 虽然每一种布局方式都能实现GUI,但不同布局方式的控制方式有所区别,在细微之处无法完全互相替代。

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