tkinter04_网格几何管理器

本文聚焦Tk网格几何管理器,介绍其在几何管理方面的应用。阐述了按列和行布置小部件、跨越多个单元格、单元格内布局、处理调整大小、填充等功能,还提及查询和更改网格选项、内部填充、忘记和删除等附加功能,以及嵌套布局的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:https://tkdocs.com/tutorial/grid.html

网格几何管理器

我们将暂停讨论不同的小部件(在屏幕上放置什么),而是专注于几何管理(将这些小部件放在哪里)。我们在“Tk 概念”一章中介绍了几何管理的一般概念。在这里,我们专注于一个特定的几何管理器:grid.

正如我们所见,grid让您可以按列和行布置小部件。如果您熟悉使用 HTML 表格进行布局,那么在这里您会感到宾至如归。本章说明了可以调整网格的各种方法,以便为您提供用户界面所需的所有控件。

Grid 是 Tk 中可用的几种几何管理器之一,但其强大的功能、灵活性和易用性使其成为最佳选择。它的约束模型非常适合当今依赖于小部件对齐的布局。Tk 中还有其他几何管理器:pack也非常强大,但更难使用和理解,同时place让您可以完全控制每个元素的定位。甚至我们稍后将探讨的窗格窗口、笔记本、画布和文本等小部件也可以充当几何管理器。

值得注意的是,Tk 于 1996 年首次引入grid,在 Tk 流行几年后,它花了一段时间才流行起来。在此之前,开发人员一直习惯于pack进行基于约束的几何管理。当grid出来的时候,许多开发商不在使用pack,你还是会在许多Tk的程序和文档中发现它。虽然pack在技术上没有任何问题,但算法的行为通常难以理解。更重要的是,由于小部件的打包顺序在确定布局方面很重要,因此修改现有布局可能会更加困难。

Grid 具有 pack 的所有功能,产生更好的布局(水平和垂直对齐小部件),并且更易于学习和使用。正因为如此,我们认为网格在大多数情况下是大多数开发人员的正确选择。

grid参考文档提供了对 grid、其行为和所有选项的详尽描述。

列和行

在网格中,小部件被分配了一个column和一个row。这些指示每个小部件相对于其他小部件的位置。同一列中的所有小部件位于彼此上方或下方。同一行中的那些将位于彼此的左侧或右侧。

列号和行号必须是正整数(即 0, 1, 2, …)。您不必从 0 开始,并且可以在列号和行号中留出空隙(例如,第 1、2、10、11、12、20、21 列)。如果您打算稍后在用户界面中间添加更多小部件,这将非常有用。

每列的宽度将根据列中包含的小部件的宽度而变化。每行的高度也是如此。这意味着在勾勒出您的用户界面并将其分为行和列时,您无需担心每列或每行的宽度相等。

跨越多个单元格

小部件可以在网格中占据多个单元格;为此,我们将在网格化小部件时使用columnspanrowspan选项。这些类似于 HTML 表格的“colspan”和“rowspan”属性。

以下是创建具有多个小部件的用户界面的示例,其中一些小部件占用多个单元格。
在这里插入图片描述

网格化多个小部件。

from tkinter import *
from tkinter import ttk

root = Tk()

content = ttk.Frame(root)
frame = ttk.Frame(content, borderwidth=5, relief="ridge", width=200, height=100)
namelbl = ttk.Label(content, text="Name")
name = ttk.Entry(content)

onevar = BooleanVar(value=True)
twovar = BooleanVar(value=False)
threevar = BooleanVar(value=True)

one = ttk.Checkbutton(content, text="One", variable=onevar, onvalue=True)
two = ttk.Checkbutton(content, text="Two", variable=twovar, onvalue=True)
three = ttk.Checkbutton(content, text="Three", variable=threevar, onvalue=True)
ok = ttk.Button(content, text="Okay")
cancel = ttk.Button(content, text="Cancel")

content.grid(column=0, row=0)
frame.grid(column=0, row=0, columnspan=3, rowspan=2)
namelbl.grid(column=3, row=0, columnspan=2)
name.grid(column=3, row=1, columnspan=2)
one.grid(column=0, row=3)
two.grid(column=1, row=3)
three.grid(column=2, row=3)
ok.grid(column=3, row=3)
cancel.grid(column=4, row=3)

root.mainloop()

单元格内的布局

因为列的宽度(和行的高度)取决于添加到其中的所有小部件,所以至少某些小部件的宽度或高度可能小于所在单元格分配的宽度或高度. 那么问题就变成了,它到底应该放在单元格中的哪个位置?

默认情况下,如果单元格比其中包含的小部件大,则小部件将在其中水平和垂直居中。父级的背景将显示在它周围的空白区域。在下图中,右上角的小部件小于它所在的单元格。母版的(白色)背景填充了单元格的其余部分。

截屏
单元格内的布局和“粘性(sticky)”选项。

sticky选项可以更改此默认行为。它的值是一个由 0 个或多个罗盘方向nsew组成的列表,指定小部件应该“附着”到单元格的哪些边缘。例如,值n(north) 会将小部件附着在顶部,底部就有一些额外的垂直空间;小部件仍将水平居中。值nw(西北部)指部件将被粘贴到左上角,在底部和右侧有额外的空间。

在Tkinter中,你可以指定它为包含NSE,和W值得列表,在本书中我们倾向于使用列表格式。

指定两个相对的边缘,例如we(west, Eastern) 意味着小部件将被拉伸。在这种情况下,它将被附着在单元格的左右边缘。因此,小部件将比其“理想”尺寸更宽。

如果您希望小部件扩展以填满整个单元格,请使用stickynsew (北、南、东、西)对其进行网格化,这意味着它将粘在每一边。上图中的左下角小部件就是这样。

大多数小部件都有anchor此选项,可以控制它们的显示方式。例如,标签小部件具有控制标签文本在小部件边界内的位置的选项。上图中左下角标签使用默认锚点(w,即左侧,垂直居中)。

如果您无法按照您希望的方式排列东西,请首先确保您知道小部件有多大。正如我们在前一章中讨论的 label小部件,更改小部件的背景或边框会有所帮助。

处理调整大小

如果您尝试调整示例的大小,您会注意到根本没有任何移动,如下所示。
截屏
调整窗口大小。

即使您查看了下面的内容并将额外的sticky选项添加到我们的示例中,您仍然会看到相同的显示。如果单元格的行或列确实需要调整大小,如果调整单元格的大小似乎sticky应该告诉 Tk如何反应,但实际上,如果有额外的空间可用,行或列不一定就应该调整大小。让我们解决这个问题。

网格中的每一列和每一行都有一个weight选项。这告诉grid如果母版中有额外的空间要填充,列或行应该增长多少。默认情况下,每列或每行的权重为 0,这意味着它不会扩展以填充任何额外空间。

为了调整用户界面的大小,我们需要为要扩展的列和行指定一个正权重。您必须为至少一列和一行提供权重。这时应使用gridcolumnconfigurerowconfigure方法完成。这个权重是相对的。如果两列具有相同的权重,它们将以相同的速率扩展。在我们的示例中,我们将最左边的三列(持有复选按钮)的权重设为 3,将最右边两列的权重设为 1。对于右列每增长一个像素,左列将增长三个像素。因此,随着窗口变大,大部分额外空间将位于左侧。
在这里插入图片描述
添加权重后调整窗口大小。

columnconfigurerowconfigure方法都有minsize网格选项,它指定列或行可以缩小到的最小尺寸。

填充

通常,每一列或每一行都彼此相邻,因此小部件将彼此相邻。这有时是您想要的(想想列表框及其滚动条),但通常您希望小部件之间有一些空间。在 Tk 中,这称为填充,您可以通过多种方式添加它。

我们实际上已经看到了一种方法,那就是使用小部件自己的选项来添加它周围的额外空间。并非所有小部件都具有此功能,但框架具有此功能;这很有用,因为框架最常用作网格其他小部件的母版。框架的padding 选项允许您在框架内指定一些额外的填充,无论是四个边中的每一个边的数值相同,还是每个边的数值都不同。

第二种方法是在添加小部件时使用padxpady网格选项。如您所料,padx在左侧和右侧放置了一些额外的空间,同时在pady 顶部和底部添加了额外的空间。该选项的单个值在左侧和右侧(或顶部和底部)放置相同的填充,而双值列表允许您在左侧和右侧(或顶部和底部)放置不同的数值。请注意,此额外填充位于包含小部件的网格单元内。

如果您想在整行或整列周围添加填充,columnconfigurerowconfigure 方法接受一个pad选项,该选项将为您完成此操作。

让我们在示例中添加额外的sticky、调整大小和填充行为(以粗体显示)。

from tkinter import *
from tkinter import ttk

root = Tk()

content = ttk.Frame(root, padding=(3,3,12,12))
frame = ttk.Frame(content, borderwidth=5, relief="ridge", width=200, height=100)
namelbl = ttk.Label(content, text="Name")
name = ttk.Entry(content)

onevar = BooleanVar()
twovar = BooleanVar()
threevar = BooleanVar()

onevar.set(True)
twovar.set(False)
threevar.set(True)

one = ttk.Checkbutton(content, text="One", variable=onevar, onvalue=True)
two = ttk.Checkbutton(content, text="Two", variable=twovar, onvalue=True)
three = ttk.Checkbutton(content, text="Three", variable=threevar, onvalue=True)
ok = ttk.Button(content, text="Okay")
cancel = ttk.Button(content, text="Cancel")

content.grid(column=0, row=0, sticky=(N, S, E, W))
frame.grid(column=0, row=0, columnspan=3, rowspan=2, sticky=(N, S, E, W))
namelbl.grid(column=3, row=0, columnspan=2, sticky=(N, W), padx=5)
name.grid(column=3, row=1, columnspan=2, sticky=(N,E,W), pady=5, padx=5)
one.grid(column=0, row=3)
two.grid(column=1, row=3)
three.grid(column=2, row=3)
ok.grid(column=3, row=3)
cancel.grid(column=4, row=3)

root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
content.columnconfigure(0, weight=3)
content.columnconfigure(1, weight=3)
content.columnconfigure(2, weight=3)
content.columnconfigure(3, weight=1)
content.columnconfigure(4, weight=1)
content.rowconfigure(1, weight=1)

root.mainloop()

这看起来更好。玩这个例子来感受一下调整大小的行为。
在这里插入图片描述
网格示例,处理单元格内布局和调整大小。

附加网格功能

如果你看一下grid参考文档,你会看到对网格可以做很多事情。这里有一些更有用的。

查询和更改网格选项

就像小部件本身一样,很容易查看各种网格选项或更改它们。在您第一次为小部件设置网格时设置选项当然很方便,但您可以随时更改它们。

slaves方法将告诉您已在母版中网格化的所有小部件,或者可选择仅在特定列或行中的那些小部件。info方法将返回小部件的所有网格选项及其值的列表。最后,configure方法允许您更改小部件上的一个或多个网格选项。

在此交互式会话中进行说明:

>>> content.grid_slaves()
[<tkinter.ttk.Button object .!frame.!button2>, <tkinter.ttk.Button object .!frame.!button>,
<tkinter.ttk.Checkbutton object .!frame.!checkbutton3>, <tkinter.ttk.Checkbutton object .!frame.!checkbutton2>,
<tkinter.ttk.Checkbutton object .!frame.!checkbutton>, <tkinter.ttk.Entry object .!frame.!entry>, 
<tkinter.ttk.Label object .!frame.!label>, <tkinter.ttk.Frame object .!frame.!frame>]
>>> for w in content.grid_slaves(): print(w)
...
.!frame.!button2
.!frame.!button
.!frame.!checkbutton3
.!frame.!checkbutton2
.!frame.!checkbutton
.!frame.!entry
.!frame.!label
.!frame.!frame
>>> for w in content.grid_slaves(row=3): print(w)
...
.!frame.!button2
.!frame.!button
.!frame.!checkbutton3
.!frame.!checkbutton2
.!frame.!checkbutton
>>> for w in content.grid_slaves(column=0): print(w)
...
.!frame.!checkbutton
.!frame.!frame
>>> namelbl.grid_info()
{'in': <tkinter.ttk.Frame object .!frame>, 'column': 3, 'row': 0, 'columnspan': 2, 'rowspan': 1, 
'ipadx': 0, 'ipady': 0, 'padx': 5, 'pady': 0, 'sticky': 'nw'}
>>> namelbl.grid_configure(sticky=(E,W))
>>> namelbl.grid_info()
{'in': <tkinter.ttk.Frame object .!frame>, 'column': 3, 'row': 0, 'columnspan': 2, 'rowspan': 1, 
'ipadx': 0, 'ipady': 0, 'padx': 5, 'pady': 0, 'sticky': 'ew'}

内部填充

您看到了padxpady网格选项如何在小部件外部添加额外空间。还有一种较少使用的填充类型,称为“内部填充”,网格选项ipadxipady.

差异可能很微妙。假设您有一个 20x20 的框架,并指定每边 5 个像素的正常(外部)填充。框架将从几何管理器请求一个 20x20 的矩形(它的自然大小)。通常,这就是它会被授予的,所以它会得到一个 20x20 的矩形框,周围有 5 像素的边框。

使用内部填充,几何管理器将在确定其自然大小时有效地向小部件添加额外的填充,就好像小部件请求了 30x30 矩形一样。如果框架居中,或者连接到单个边或角(使用sticky),我们最终会得到一个 20x20 的框架,周围有额外的空间。然而,如果该帧被设置为伸展(即,sticky的值wensnwes),它将填补额外的空间,从而产生30×帧,没有边界。

忘记和删除

grid的forget方法从它们当前所在的网格中删除从属部件。它需要一个或多个从属部件的列表作为参数。这不会完全破坏小部件,而是将其从屏幕上移除,就好像它一开始没有被网格化一样。您可以稍后再次对其进行网格化,但您最初分配的任何网格选项都将丢失。

remove网格的方法是一样的,只是网格选项如果你grid以后再用会记住。

嵌套布局

随着您的用户界面变得越来越复杂,您用来组织所有小部件的网格也会变得越来越复杂。这会使更改和维护您的程序变得非常困难。

幸运的是,您不必使用单个网格来管理整个用户界面。如果您的用户界面中有一个区域与其他区域相当独立,请创建一个新框架来容纳该区域,并在该框架内的区域中对小部件进行网格化。例如,如果您正在构建具有多个调色板、工具栏等的图形编辑器,那么这些区域中的每一个都可能是放入自己框架的候选区域。

理论上,这些框架,每个都有自己的网格,可以嵌套任意深度,但在实践中,这通常不会超过几个层次。这对模块化程序有很大帮助。例如,如果您有一个绘图工具调色板,则可以在单独的函数或类中创建整个内容。它将负责创建所有组件小部件、将它们网格化、设置事件绑定等。该调色板中的事物如何工作的细节可以包含在这段代码中。从主程序的角度来看,它需要知道的只是包含调色板的单帧小部件。

我们的示例仅显示了这一点,其中将内容框架网格化到主窗口中,然后将所有其他小部件网格化到内容框架中。

随着您自己的程序变得越来越大,您可能会遇到这样的情况:更改界面一部分的布局需要对另一部分的布局进行代码更改。这可能是重新考虑您的使用方式grid以及将组件拆分为单独的框架是否会有所帮助的线索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔚蓝慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值