在 PySide6 中,布局管理器(Layout)是实现界面自适应、控件合理排布的核心,而尺寸策略(Size Policy)则决定了控件在布局中如何响应尺寸变化。二者结合,就可以构建健壮、自适应、可维护的图形界面,从根本上解决手动低效率布局和频繁计算几何尺寸的痛点。
在上一节容器类部件的学习中,有一段代码:
python
import sys
from PySide6.QtWidgets import QApplication, QWidget, QPushButton
# 定义一个父容器类,作为主窗口
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QWidget 基础容器示例")
self.setStyleSheet("background-color: green;") # 设置容器的显示样式,方便观察
self.resize(300, 200)
# 创建 QWidget 作为子容器
son_container = QWidget(self)
son_container.setGeometry(10, 10, 280, 180) # 设置子容器的几何特征,位置为 (10, 10),大小为 (280, 180)
son_container.setStyleSheet("background-color: red") # 设置容器的显示样式,方便观察
button = QPushButton("子容器的按钮", son_container) # 创建按钮,作为子容器的子控件
button.setStyleSheet("background-color: gray") # 设置按钮的显示样式
button.setGeometry(40, 50, 200, 40) # 设置按钮的几何特征
if __name__ == "__main__":
app = QApplication(sys.argv) # 创建 QApplication 对象
main_window = MainWindow() # 创建 MainWindow 实例对象
main_window.show() # 显示窗口
sys.exit(app.exec()) # 退出应用程序
运行后显示如下:

在上面的代码中,使用setGeometry()指定了子部件在父容器中的位置,如果再增加一个新的子部件在父容器中,可能就要重新计算子部件的几何参数;或者动态修改父容器的尺寸,比如,手动拖动父容器的窗口大小,子部件的几何参数并不会动态地随父容器改变:

现在使用布局管理器,并合理设置尺寸策略,重写以上代码:
python
import sys
from PySide6 import QtWidgets
from PySide6.QtWidgets import (QApplication, QWidget, QPushButton,
QVBoxLayout, QHBoxLayout)
from PySide6.QtCore import Qt
# 定义一个父容器类,作为主窗口
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("布局管理器+尺寸策略示例")
self.setStyleSheet("background-color: green;") # 父容器背景色
self.resize(300, 200)
# ========== 1. 父容器的主布局(控制子容器的位置/大小) ==========
main_layout = QVBoxLayout(self) # 父容器绑定垂直布局
# 设置父布局边距(对应原代码子容器的10px边距)
main_layout.setContentsMargins(10, 10, 10, 10)
# ========== 2. 创建按钮并设置尺寸策略(固定大小) ==========
button = QPushButton("子容器的按钮")
button.setStyleSheet("background-color: gray")
# 核心:设置按钮尺寸策略为 Fixed(固定大小)
button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
# 设置按钮固定尺寸(对应原代码的200x40)
button.setFixedSize(200, 40)
# ========== 3. 将按钮添加到布局(实现居中) ==========
# 添加拉伸项:按钮左右/上下的空白自动填充,实现居中
son_container = QWidget()
son_container.setStyleSheet("background-color: red") # 子容器背景色
son_layout = QVBoxLayout(son_container)
son_layout.addStretch() # 上方拉伸
son_layout.addWidget(button, alignment=Qt.AlignCenter) # 水平+垂直居中
son_layout.addStretch() # 下方拉伸
# ========== 4. 将子容器添加到父布局 ==========
main_layout.addWidget(son_container)
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
# 窗口缩放时,按钮仍保持固定大小+居中
sys.exit(app.exec())

修改后的代码,当手动改变父容器的尺寸,子容器自动随之改变尺寸,保持了固定的四边距,而子容器中的按钮则会随时居中显示并保持固定大小。
下面将详细介绍布局管理器和尺寸策略的应用。
布局管理器
布局管理器,存在的意义远不止 "排列"和"布局",它的本质是将控件的位置 / 尺寸计算逻辑从开发者转移到 Qt 框架,核心价值体现在 "自适应" 和 "自动化":
1. 自动适配窗口缩放与分辨率
手动布局(如 setGeometry(x, y, w, h) 或 move())的控件位置 / 尺寸是固定值,窗口缩放、屏幕分辨率变化(如从 1080p 到 4K)、系统缩放(如 150% DPI)时,界面会出现:
- 控件重叠、截断;
- 空白区域过多;
- 控件超出窗口可视范围。
而布局管理器会实时重新计算所有控件的位置和尺寸,确保:
- 控件始终在可视范围内;
- 空间分配合理(如拉伸控件占满空白);
- 界面比例与内容适配。
一、核心概念
1. 布局管理器的作用
- 自动调整控件的位置和大小,适配窗口缩放、不同分辨率屏幕
- 避免手动设置控件坐标(setGeometry)导致的界面错乱
- 支持控件的动态添加 / 移除,保持布局一致性
python
# ❶ 手动布局(反例):窗口缩放后按钮位置固定,界面错乱
import sys
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout
class BadExample(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("手动布局(反例)")
self.resize(300, 200)
# 固定坐标和尺寸,窗口缩放后无变化
btn1 = QPushButton("按钮1", self)
btn1.setGeometry(20, 20, 80, 30)
btn2 = QPushButton("按钮2", self)
btn2.setGeometry(120, 20, 80, 30)
# ❷ 布局管理器(正例):窗口缩放后按钮自动拉伸/对齐
class GoodExample(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("布局管理器(正例)")
self.resize(300, 200)
layout = QHBoxLayout(self)
layout.addWidget(QPushButton("按钮1"))
layout.addWidget(QPushButton("按钮2"))
if __name__ == "__main__":
app = QApplication(sys.argv)
win1 = BadExample() # 缩放窗口看效果
win2 = GoodExample() # 缩放窗口,按钮自动适配
win1.show()
win1.move(500,500)
win2.show()
win2.move(900,500)
sys.exit(app.exec())

- 常用布局管理器
PySide6 提供 4 种核心布局,均继承自 QLayout,需通过 setLayout() 绑定到父控件(如 QWidget/QMainWindow)。
1. QHBoxLayout (水平布局)
将控件沿水平方向排列(从左到右)。
python
import sys
from PySide6.QtWidgets import (QApplication, QWidget, QHBoxLayout,
QPushButton, QLabel)
class HBoxExample(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("水平布局示例")
layout = QHBoxLayout(self) # 创建水平布局并绑定到当前窗口
# 添加控件
layout.addWidget(QLabel("标签1"))
layout.addWidget(QPushButton("按钮1"))
layout.addWidget(QPushButton("按钮2"))
# 布局间距/边距设置
layout.setSpacing(10) # 控件之间的间距
layout.setContentsMargins(20, 20, 20, 20) # 布局边缘与父控件的边距(左、上、右、下)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = HBoxExample()
win.show()
sys.exit(app.exec())

2. QVBoxLayout (垂直布局)
将控件沿垂直方向排列(从上到下),用法与水平布局完全一致,仅排列方向不同:
python
layout = QVBoxLayout(self)
替换上述示例中的布局即可

3. QGridLayout (网格布局)
按行 / 列的网格形式排列控件,支持跨行列,是最灵活的布局:
python
import sys
from PySide6.QtWidgets import QGridLayout, QLabel, QLineEdit, QPushButton, QApplication, QWidget
class GridExample(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("网格布局示例")
layout = QGridLayout(self)
# 添加控件:addWidget(控件, 行, 列, 跨行数, 跨列数)
layout.addWidget(QLabel("用户名:"), 0, 0)
layout.addWidget(QLineEdit(), 0, 1)
layout.addWidget(QLabel("密码:"), 1, 0)
layout.addWidget(QLineEdit(), 1, 1)
# 按钮跨2列
layout.addWidget(QPushButton("登录"), 2, 0, 1, 2)
# 设置列拉伸因子(第1列拉伸优先级更高)
layout.setColumnStretch(0, 1)
layout.setColumnStretch(1, 3)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = GridExample()
win.show()
sys.exit(app.exec())

4. QFormLayout (表单布局)
专为标签 + 输入框的表单场景设计,自动对齐:
python
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QWidget, QFormLayout, QLineEdit, QSpinBox, QApplication
class FormExample(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("表单布局示例")
layout = QFormLayout(self)
# 添加行:addRow(标签, 输入控件)
layout.addRow("姓名", QLineEdit())
layout.addRow("年龄", QSpinBox())
layout.addRow("邮箱", QLineEdit())
# 设置对齐方式
layout.setLabelAlignment(Qt.AlignRight) # 标签右对齐
if __name__ == "__main__":
app = QApplication(sys.argv)
win = FormExample()
win.show()
sys.exit(app.exec())

5. 布局嵌套
实际开发中需嵌套布局实现复杂界面:
python
import sys
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QGridLayout, QPushButton, \
QApplication
class NestedLayout(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("布局嵌套示例")
# 主布局(垂直)
main_layout = QVBoxLayout(self)
# 顶部水平布局
top_layout = QHBoxLayout()
top_layout.addWidget(QLabel("标题:"))
top_layout.addWidget(QLineEdit())
main_layout.addLayout(top_layout) # 布局添加到布局
# 中间网格布局
grid_layout = QGridLayout()
grid_layout.addWidget(QPushButton("按钮1"), 0, 0)
grid_layout.addWidget(QPushButton("按钮2"), 0, 1)
main_layout.addLayout(grid_layout)
# 底部按钮(水平居中)
bottom_layout = QHBoxLayout()
bottom_layout.addStretch() # 左侧拉伸
bottom_layout.addWidget(QPushButton("确定"))
bottom_layout.addWidget(QPushButton("取消"))
bottom_layout.addStretch() # 右侧拉伸
main_layout.addLayout(bottom_layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = NestedLayout()
win.show()
sys.exit(app.exec())

尺寸策略
QSizePolicy 控制控件在布局中的尺寸行为,每个控件默认有尺寸策略,可通过 setSizePolicy() 自定义。
1. 核心参数
尺寸策略包含水平策略 和垂直策略,可选值:
| 策略值 | 含义 |
|---|---|
QSizePolicy.Fixed |
固定尺寸,仅使用 sizeHint(),不缩放 |
QSizePolicy.Minimum |
最小尺寸为 sizeHint(),可放大(但不主动拉伸) |
QSizePolicy.Maximum |
最大尺寸为 sizeHint(),可缩小 |
QSizePolicy.Preferred |
首选尺寸 sizeHint(),可放大 / 缩小(默认值) |
QSizePolicy.Expanding |
优先拉伸,尽可能占满可用空间 |
QSizePolicy.MinimumExpanding |
最小尺寸 sizeHint(),优先拉伸 |
QSizePolicy.Ignored |
忽略 sizeHint(),完全由布局控制 |
2. 几个 关键方法
python
# 获取/设置尺寸策略
policy = widget.sizePolicy()
widget.setSizePolicy(horizontal_policy, vertical_policy)
# 设置最小/最大/固定尺寸
widget.setMinimumSize(width, height)
widget.setMaximumSize(width, height)
widget.setFixedSize(width, height) # 等价于同时设置最小/最大
# 获取首选尺寸(控件自身推荐的尺寸)
hint = widget.sizeHint()
3. 应用实例
python
import sys
from PySide6.QtWidgets import QWidget, QHBoxLayout, QPushButton, QSizePolicy, QApplication
class SizePolicyExample(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("尺寸策略示例")
layout = QHBoxLayout(self)
# 按钮1:固定尺寸
btn1 = QPushButton("Fixed")
btn1.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
btn1.setFixedSize(80, 40)
layout.addWidget(btn1)
# 按钮2:优先拉伸(Expanding)
btn2 = QPushButton("Expanding")
btn2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
layout.addWidget(btn2)
# 按钮3:仅最小尺寸,可放大但不主动拉伸
btn3 = QPushButton("Minimum")
btn3.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
layout.addWidget(btn3)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = SizePolicyExample()
win.resize(400, 100)
win.show()
sys.exit(app.exec())

4. 拉伸因子(Stretch Factor)
布局的 setStretch() 方法可设置控件 / 布局的拉伸优先级:
python
layout = QHBoxLayout(self)
btn1 = QPushButton("拉伸因子1")
btn2 = QPushButton("拉伸因子2")
layout.addWidget(btn1)
layout.addWidget(btn2)
# 设置拉伸因子:btn2的拉伸优先级是btn1的2倍
layout.setStretch(0, 1) # 索引0的控件(btn1)拉伸因子1
layout.setStretch(1, 2) # 索引1的控件(btn2)拉伸因子2
5. 空白拉伸(addStretch)
addStretch() 向布局中添加空白拉伸项,用于控件对齐:
python
layout = QHBoxLayout(self)
# 控件左对齐
layout.addWidget(QPushButton("左对齐"))
layout.addStretch() # 右侧空白拉伸
# 控件右对齐
layout.addStretch()
layout.addWidget(QPushButton("右对齐"))
# 控件居中
layout.addStretch()
layout.addWidget(QPushButton("居中"))
layout.addStretch()
常见问题与最佳实践
1. 常见坑点
- QMainWindow 布局:前面的demo都是用QWidget做例子的,实际的编程中,主程序窗口往往是QMainWindow,QMainWindow需先设置 centralWidget,再给 centralWidget 设布局:
python
from PySide6.QtWidgets import QWidget, QVBoxLayout, QMainWindow
main_window = QMainWindow()
central = QWidget()
main_window.setCentralWidget(central)
layout = QVBoxLayout(central) # 布局绑定到centralWidget
- 控件重复添加:一个控件只能属于一个布局,重复添加会导致布局错乱
- 尺寸策略冲突:同时设置了几种尺寸策略,比如:setFixedSize 和 Expanding 策略,Fixed 优先级更高
2. 最佳实践
- 优先使用布局管理器,避免手动设置坐标
- 合理嵌套布局,单层布局最好不超过 8 个控件
- 用 addStretch 实现控件对齐,而非固定间距
- 对关键控件设置最小尺寸,避免缩放过小导致内容截断
- 复杂界面拆分多个子控件(QWidget),每个子控件独立布局
- 根据需求灵活设置尺寸策略
通过灵活组合布局管理器和尺寸策略,可实现适配不同屏幕、支持缩放的专业级 Qt 界面。
下一节将要学习QMainWindow的知识。