tkinter 布局管理

1 布局管理器

布局管理器是负责管理各组件的大小和位置的。此外,当用户调整窗口大小后,布局管理器还会自动调整窗口中各组件的大小和位置。

1.1 Pack 布局管理器

使用 Pack 布局管理时,向容器中添加组件时,这些组件会依次向后排名,排列方向可以是水平的,也可以是垂直的。Pack 布局的用法示例如下,下面这段代码向窗口中添加三个 Label 组件。

  1. anchor:当可用空间大于组件所需求的大小时,该选项决定组件被放置在容器的何处。支持的选项有:N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左上)、NE(东北,右上)、SW(西南,左下)、SE(东南,右下)、CENTER(中,默认值)等这些值。
  2. expand:该 bool 值指定当父容器增大时是否拉伸组件。
  3. fill:设置组件是否沿水平或垂直方向填充。支持四个值:NONE、X、Y、BOTH,其中 NONE 表示不填充,BOTH 表示沿着两个方向填充。
  4. ipadx:指定组件在 x 方向(水平)上的内部留白(padding)。
  5. ipady:指定组件在 y 方向(垂直)上的内部留白(padding)。
  6. padx:指定组件在 x 方向(水平)上与其他组件的间距。
  7. pady:指定组件在 y 方向(垂直)上与其他组件的间距。
  8. 件的添加位置,可设置为 TOP、BOTTOM、LEFT 或 RIGHT 四个值的其中之一。

当做出来的界面比较复杂时,就需要使用多个容器(Frame)分开布局,然后再将 Frame 添加到窗口中。示例如下:

from tkinter import *
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
 
    def initWidgets(self):
        # 创建第一个容器
        fm1 = Frame(self.master)
        # 该容器放在左边
        fm1.pack(side=LEFT, fill=BOTH, expand=YES)
        # 向 fm1 中添加三个按钮
        # 设置按钮从顶部开始排列,且按钮只能在水平(X)方向上填充
        Button(fm1, text='第一个').pack(side=TOP, fill=X, expand=YES)
        Button(fm1, text='第二个').pack(side=TOP, fill=X, expand=YES)
        Button(fm1, text='第三个').pack(side=TOP, fill=X, expand=YES)
        # 创造第二个容器
        fm2 = Frame(self.master)
        # 该容器放在左边排列,就会挨着 fm1
        fm2.pack(side=LEFT, padx=10, expand=YES)
        # 向 fm2 中添加三个按钮
        # 设置按钮从右边开始排列
        Button(fm2, text='第一个').pack(side=RIGHT, fill=Y, expand=YES)
        Button(fm2, text='第二个').pack(side=RIGHT, fill=Y, expand=YES)
        Button(fm2, text='第三个').pack(side=RIGHT, fill=Y, expand=YES)
        # 创建第三个容器
        fm3 = Frame(self.master)
        # 该容器放在右边排列,就公挨着 fm1
        fm3.pack(side=RIGHT, padx=10, fill=BOTH, expand=YES)
        # 向 fm3 中添加三个按钮
        # 设置按钮从底部开始排列,且按钮只能在垂直(Y)方向上填充
        Button(fm3, text='第一个').pack(side=BOTTOM, fill=Y, expand=YES)
        Button(fm3, text='第二个').pack(side=BOTTOM, fill=Y, expand=YES)
        Button(fm3, text='第三个').pack(side=BOTTOM, fill=Y, expand=YES)

root = Tk()     # 创建顶层窗口
root.title('Pack布局')
display = App(root)
root.mainloop()

在上面代码中,创建了三个 Frame 容器,其中第一个 Frame 容器内包含三个从顶部(TOP)开始排列的按钮,这意味着这三个按钮会从上到下依次排列,且这三个按钮能在水平(X)方向上填充;第二个 Frame 容器内包含三个从右边(RIGHT)开始排列的按钮,这意味着这三个按钮会从右向左依次排列;第三个 Frame 容器内包含三个从底部(BOTTOM)开始排列的按钮,这意味着这三个按钮会从下到上依次排列,且这个三个按钮能在重复(Y)方向上填充。运行结果,如图2示。

图2 pack 布局

从图2可以看到,fm1 的三个按钮是从上到下,并且可以在水平方向上填充;fm3 的三个按钮是从下到上,并且可以在垂直方向上填充。但是 fm2 的三个按钮虽然设置了 “fill=Y, expand=YES”,但是却不能在垂直方向上填充,在创建 fm2 这个容器时的代码是:

fm2.pack(side=LEFT, padx=10, expand=YES)

这说明 fm2 本身不在任何方向上填充,因此 fm2 内的三个按钮都不能填充。如果希望 fm2 空的三个按钮也能在垂直方向上填充,可将 fm2 的 pack() 方法改为如下代码:

fm2.pack(side=LEFT, padx=10, fill=BOTH, expand=YES)

1.2 Grid 布局管理器

Tkinter 后来引入的 Grid 布局简单易用,管理组件也很方便。Grid 把组件空间分解成一个网格进行维护,按照行、列的方式排列组件,组件位置由其所在的行号和列号决定。行号相同而列号不同的几个组件会被依次上下排列,列号相同而行号不同的几个组件会依次左右排列。
在多数场景下,Grid 是最好用的布局方式。Grid 布局的过程就是为各个组件指定行号和列号的过程,不需要为每个网格都指定大小,Grid 布局会自动为它们设置合适的大小。
容器调用组件的 grid() 方法就进行 Grid 布局,在调用 grid() 方法时可传入多个选项,该方法支持的 ipadx、ipady、padx、pady 与pack() 方法的这些选项相同。grid() 方法还额外增加了下面这些方法:

  1. column:指定将组件放入哪列。第一列的索引为0.
  2. columnspan:指定组件横跨多少列。
  3. row:指定组件放入哪行。第一行的索引为0.
  4. rowspan:指定组件横跨多少行。 (5)、sticky:有点类似于 pack() 方法的 anchor 选项,同样支持 N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左上)、NE(东北,右上)、SW(西南,左下)、SE(东南,右下)、CENTER(中,默认值)等这些值
from tkinter import *
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
 
    def initWidgets(self):
        # 创建一个输入组件
        e = Entry(relief=SUNKEN, font=('Courier New', 24), width=25)
        # 对该输入组件使用 pack 布局,放在容器(或者窗口)顶部
        e.pack(side=TOP, pady=10)
        p = Frame(self.master)
        p.pack(side=TOP)
        # 定义字符串元组
        names = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                 '+', '-', '*', '/', '.', '=')
        # 遍历字符串元组
        for i in range(len(names)):
            # 创建 Button,将 Button 组件放入 p 容器中
            b = Button(p, text=names[i], font=('Verdana', 20), width=6)
            b.grid(row=i // 4, column=i % 4)
 
root = Tk()
root.title("Grid布局")
App(root)
root.mainloop()

1.3 Place 布局管理器

Place 布局也叫“绝对布局”,要求程序员显式指定每个组件的绝对位置相对于其他组件的位置。要使用 Place 布局,只要调用相应组件的 place() 方法即可。该方法支持的选项如下:

  1. x:指定组件的 X 坐标。x 为0代表位于最左边。
  2. y:指定组件的 Y 坐标。y 为0代表位于最右边。
  3. relx:指定组件的 X 坐标,以父容器总宽度为单位 1,该值应该在 0.0~1.0之间,其中0.0位于窗口最左边,1.0位于窗口最右边,0.5 位于窗口中间。
  4. rely:指定组件的 Y 坐标,以父容器总高度为 1,该值应该在 0.0~1.0 之间,其中 0.0位于窗口最上边,1.0 位于窗口最下边,0.5位于窗口中间。
  5. width:指定组件的宽度,以 pixel 为单位。
  6. height:指定组件的高度,以 pixel 为单位。
  7. relwidth:指定组件的宽度,以父容器总宽度为单位 1,该值应该在 0.0~1.0 之间,其中 1.0 代表整个窗口宽度,0.5代表窗口的一半宽度。
  8. relheight:指定组件的高度,以父容器总高度为单位 1,该值应该在 0.1~1.0 之间,其中 1.0 代表整个窗口高度,0.5 代表窗口的一半高度。
  9. bordermode:该属性支持 “inside”或“outside” 属性值,用于指定当设置组件的宽度、高度时是否计算该组件的边框宽度。

当使用 Place 布局管理容器中的组件时,需要设置组件的 x、y 或 relx、rely 选项,Tkinter 容器内的坐标系统的原点(0, 0)在左上角,其中 X 轴向右延伸,Y 轴向下延伸。如果通过 x、y 指定坐标,单位就是 pixel(像素);如果通过 relx、rely 指定坐标,则以整个父容器的宽度、高度为1。下面代码使用 Place 进行布局,并动态计算各 Label 的大小和位置,并通过 place() 方法设置各 Label 的大小和位置。代码如下:

from tkinter import *
import random
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
 
    def initWidgets(self):
        # 定义字符串元组
        books = ('Python 入门', 'Python 初级', 'Python 进阶', 'Python 高级', 'Python 核心')
        for i in range(len(books)):
            # 生成三个随机数
            ct = [random.randrange(256) for _ in range(3)]
            grayness = int(round(0.299*ct[0] + 0.587*ct[1] + 0.114*ct[2]))
            # 将元组中的三个随机数格式化成十六进制数,转换成颜色格式
            bg_color = "#%02x%02x%02x" % tuple(ct)
            # 创建 Label,设置背景色和前景色
            lb = Label(root, text=books[i], fg='White' if grayness < 125 else 'Black',
                       bg=bg_color)
            # 使用 place() 设置该 Label 的大小和位置
            lb.place(x = 20, y = 36 + i*36, width=180, height=30)
root = Tk()
root.title('Place 布局')
# 设置窗口的大小和位置
# width * height + x_offset + y_offset
root.geometry("250x250+30+30")
App(root)
root.mainloop()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270