Python使用子类时tkinter的grid布局放置机制是如何工作的?
Tkinter类结构下Grid布局逻辑问题说明
问题描述
下述代码可正常运行,需要解释布局行为的成因,核心疑问为:实例化各框架类放置组件时,在外层App类中给子框架指定的grid位置完全不影响GUI实际布局——哪怕将所有子框架都设置在
row=0位置,它们依然会分行显示,只有修改各个子类内部的grid放置参数才会改变布局。原本预期类内部的grid布局是放置在App类的网格单元格中,需要明确该理解的偏差点。
问题复现代码如下:
from tkinter import ttk class App(tk.Tk): def __init__(self): super().__init__() self.geometry('400x400') self.rowconfigure(0, uniform=True, weight=1) self.rowconfigure(1, uniform=True, weight=1) self.rowconfigure(2, uniform=True, weight=1) self.header_frame = HeaderFrame(self) self.header_frame.grid(column=0,row=0) self.login_frame = LoginFrame(self) self.login_frame.grid(column=0,row=1) self.button_frame = ButtonFrame(self) self.button_frame.grid(column=0,row=2) class HeaderFrame(ttk.Frame): def __init__(self, parent): super().__init__() self.parent = parent self.columnconfigure(0, uniform=True, weight=1) self.canvas = tk.Canvas(self, bg='black') self.canvas.grid(column=0,row=0) class loginFrame(ttk.Frame): def __init__(self, parent): super().__init__() self.parent=parent self.columnconfigure(0, uniform=True, weight=1) self.entryBox = ttk.Entry(self, width=33) self.entryBox.grid(column0,row1) class ButtonFrame(ttk.Frame): def __init__(self, parent): super().__init__() self.parent=parent self.columnconfigure(0, uniform=True, weight=1) self.btn = ttk.Button(self, text='test') self.btn.grid(column=0,row=2) if __name__ == '__main__': App().mainloop()
注:上述贴出的代码存在几处笔误,实际运行时需修正:补充import tkinter as tk导入、统一loginFrame/LoginFrame类名大小写、修正entryBox.grid处缺失的等号为column=0, row=1
核心成因
你的理解偏差本质是混淆了Tkinter Grid布局的独立网格域规则,叠加代码写法问题导致布局表现和预期不符,具体原因分三点:
- 每个容器(主窗口、Frame)都拥有完全独立的私有网格坐标系,互相不干扰:你在App类中配置的3行网格、给子Frame设置的
row/column参数,仅控制Frame本身在App主窗口网格中的位置,和Frame内部放置的组件没有任何关系;而每个Frame内部给组件设置的row/column参数,是在Frame自身的局部网格里生效,行号列号都是从0开始的局部计数,根本不会和外层App的网格行号对应。 - 你观察到的“所有Frame设为row=0依然分行”是视觉错觉:默认情况下Frame没有背景色/边框,且你调用
grid()放置Frame时没有添加sticky参数,也没有给App的第0列配置weight,Frame不会拉伸填满分配给它的网格单元格,只会根据内部内容的尺寸收缩到最小大小。你肉眼能看到的所有可视元素(Canvas、输入框、按钮)都是Frame内部的组件,Frame本身是透明的,所以你感知不到Frame本身的位置重叠,只会看到三个Frame里的内容因为各自收缩、没有拉伸填充,看起来像依次往下排列。给三个Frame分别设置红、绿、蓝不同背景色,就能直接看到三个Frame实际重叠在同一单元格位置。这也是你觉得“只有修改子类内部grid参数才会改变布局”的直接原因——你能看到的本来就只有内部组件的位置变化。 - 自定义Frame的初始化写法不规范:所有Frame子类调用
super().__init__()时没有传入parent参数,虽然因为Tkinter默认会把无master的组件挂到第一个创建的Tk实例上,不会导致组件脱离主窗口,但这种写法很容易在多窗口场景下出现组件挂载到错误父容器的问题,规范写法应为super().__init__(parent)。
预期布局实现方法
如果要实现三个Frame分别占App窗口三分之一高度的初始预期布局,需要补充三处配置:
- 给App的第0列添加配置:
self.columnconfigure(0, weight=1) - 给每个Frame的grid调用添加
sticky='nsew'参数,让Frame拉伸填满分配到的网格单元格 - 给每个Frame内部需要拉伸填充的组件也添加对应
sticky参数,按需配置Frame内部对应行/列的weight
内容的提问来源于stack exchange,提问作者Tyler Willke
相关产品推荐
相关产品推荐

