布局管理
布局
,按照一定规则,将子控件放入父控件
- 手动布局;绝对布局move & resize & resizeEvent
-
布局管理器,实现
快速布局
,是控件的定位策略。
布局步骤
- 创建布局对象(没有父控件)
from PyQt5.QtWidgets import QLayout, QBoxLayout, QHBoxLayout, QVBoxLayout, QGridLayout
def set_ui(self):
# 1.实例化布局对象
hb = QHBoxLayout()
# 2. 布局格式
hb.setContentsMargins(10, 20, 30, 40) # 外边距
hb.setSpacing(100) # 子控件间距
# hb.setAlignment(Qt.AlignmentFlag.AlignCenter)
# 3. 添加子控件, 创建子控件没有父控件
l1 = QLabel("part1")
l1.setStyleSheet("background-color: lightblue;")
l2 = QLabel("part2")
l2.setStyleSheet("background-color: pink;")
hb.addWidget(l1)
hb.addSpacing(100) # 添加空白,控制布局
hb.insertSpacing(0, 100)
hb.addWidget(l2)
# 替换子控件
hb.replaceWidget(l2, l3) # 能实现效果就不用隐藏l2
l2.hide() # 隐藏并没有释放内存
# 移除控件
hb.removeWidget(l3)
l3.hide()
# 添加子布局
vb = QVBoxLayout()
...
vb.addLayout(hb)
vb.addWidget(l4)
# 布局方向
hb.setDirection(hb.direction() + 1)
# 4. 父控件(QWidget对象) 添加布局,然后将布局中的子控件自动加入该父控件
self.setLayout(hb)
self.setLayoutDirection(Qt.LayoutDirection.RightToLeft)
- 设置布局格式
-
布局对象添加子控件,
创建子控件,不需父控件
- 父控件设置布局格式
QHBoxLayout & QVBoxLayout
伸缩因子
# 伸缩窗口时
hb.addWidget(l1, 1) # 占1份
hb.addWidget(l2, 3) # 占3份
hb.addStretch(4) # 4份的空白
hb.addWidget(l3, 1) # 占1份
# 单独为一个子控件设置伸缩因子
hb.setStetchFactor(l1, 10)
QFormLayout
表单布局,实现如下的布局:
def set_ui(self):
# 1.实例化布局对象
fl = QFormLayout() #
# 2. 设置样式
fl.setContentsMargins(10, 10, 10, 10)
fl.setSpacing(5) # 子控件之间的距离
# 3. 添加子控件
# 用户名 密码
username = QLabel("用户名:") # 不用父控件
ule = QLineEdit()
pwd = QLabel("密码:")
pwd_le = QLineEdit()
# 性别选择
sex_label = QLabel("性别:")
male = QRadioButton("男")
male.clicked.connect(lambda :print("选择的性别: 男"))
female = QRadioButton("女")
hb = QHBoxLayout()
hb.addWidget(male)
hb.addWidget(female)
# 登录按钮
login_btn = QPushButton("登录")
# 添加行
fl.addRow(username, ule) # widget, widget
fl.addRow(pwd, pwd_le)
fl.addRow(sex_label, hb) # widget, layout
fl.addRow(login_btn)
# fl.addRow("用户名(&n)", ule) # 添加一行,并设置快捷键关联 alt + n
# 4. 父控件 设置布局
self.setLayout(fl)
插入行
formLayout.insertRow(idx, ‘爱好’, xxx)
若索引超出范围,则在最后一行插入
获取控件位置
formLayout.getWidgetPosition(username)
formLayout.getLayoutPosition(hb)
formLayout.rowCount()
设置控件
formLayout.setWidget(0, QFormLayout.LabelRole, username)
formLayout.setWidget(0, QFormLayout.FieldRole, ule)
若一行被占用,只能设置label
删除一行
fl.removeRow(0) 输入索引,删除一行并释放子控件
fl.takeRow(2) 删除一行,未释放
fl.labelForField(ule) 获取label控件
设置行标签的策略
fl.setRowWrapPolicy(QFormLayout.RowWrapPolicy.WrapAllRows) 输入框在label的下面
# 表单的对齐
fl.setFormAlignment(Qt.AlignmentFlag.AlignCenter)
# label的对齐
fl.setLabelAlignment(Qt.AlignmentFlag.AlignRight)
fl.setVerticalSpacing(50) # 垂直方向 行间距
fl.setHorizontalSpacing(40) # 水平方向 控件间距
QGridLayout
网格布局
gl = QGridLayout()
# 第一行
gl.addWidget(l1, 0, 0)
gl.addWidget(l2, 0, 1)
# 第二行
gl.addWidget(l3, 1, 0, 3, 3) # 占3行3列
gl.addLayout(hb, 2, 0)
# 获取控件位置
gl.getItemPosition(idx)
gl.itemAtPosition(1, 2).widget()
# 最小列宽、行高
gl.setColumnMinimumWidth(col, val)
gl.setRowMinimumHeight(row, val)
# 拉伸系数
gl.setColumnStretch(0, 2) # 第0列 占2份
gl.setColumnStretch(1, 1) # 第一列 占1份
gl.setRowStretch(3, 1) # 第三行 占1份
# 水平控件间的距离
gl.setHorizontalSpacing(40)
gl.setVerticalSpacing(30)
gl.setSpacing(30)
# 行数、列数
print(gl.rowCount())
print(gl.columnCount())
# 父控件 展示后的 网格布局的单元格区域
window.show()
gl = window.layout()
print(gl.cellRect(0, 0))
QStackedLayout
栈布局,实现几个页面依次切换。
def set_ui(self):
# 1. 实例化
sl = QStackedLayout()
# 2. 设置样式
sl.setContentsMargins(10, 10, 10, 10)
sl.setSpacing(10)
# 3. 父控件设置样式
self.setLayout(sl)
# 4. 添加子控件
label1 = QLabel("part1")
label1.resize(self.geometry().size())
label1.setStyleSheet("background-color: lightblue;")
label2 = QLabel("part2")
label2.resize(self.geometry().size())
label2.setStyleSheet("background-color: pink;")
label3 = QLabel("part3")
label3.resize(self.geometry().size())
label3.setStyleSheet("background-color: gray;")
label4 = QLabel("part4")
label4.resize(self.geometry().size())
label4.setStyleSheet("background-color: cyan;")
sl.addWidget(label1) # 返回其索引
sl.addWidget(label2)
sl.insertWidget(0, label3) # 当前展示的子控件 仍为label1 并且其索引+1
# 顺序展示每个控件
timer = QTimer(sl)
timer.timeout.connect(lambda : sl.setCurrentIndex((sl.currentIndex() + 1)%sl.count()))
timer.start(1000)
# 其他
sl.widget(idx) # 获取控件
sl.setCurrentWidget(w) # 展示当前控件
sl.setStackingMode(QStackedLayout.StackingMode.StackAll) # 所有的子控件都展示,只是有时被遮挡
# 信号
sl.currentChanged.connect(lambda idx: print("当前展示:", idx))
sl.widgetRemoved.connect(lambda idx: print("移除控件:", idx))
sl.removeWidget(w) # 移除后,下面的控件会展示
控件尺寸QSizePolicy
在布局过程中,每个子控件都会有
建议大小
,无布局时无效。
# 自定义类
class MyLabel(QLabel):
# 布局的建议尺寸
def sizeHint(self):
return QSize(200, 100)
def minimumSizeHint(self): # 最小的建议尺寸
return QSize(100, 50)
# 在后续的缩放过程中, 宽度、高度 会参照建议尺寸
布局,不会把子控件设置为比最小建议尺寸还小
,子控件的SizePolicy会告诉布局系统,该如何拉伸、收缩。
# 实例化
label = QLabel("测试拉伸")
# 设置拉伸策略 水平、垂直方向
label.setSizePolicy(QSizePolicy.Policy.fixed, QSizePolicy.Policy.Expanding)
# 设置固定大小
label.setFixedSize(100, 50) # 布局无法拉伸、缩小
QSizePolicy.Fixed,固定为建议尺寸;
QSizePolicy.Minimum,以建议尺寸为最小尺寸;
QSizePolicy.Maximum,以建议尺寸为最大尺寸;
QSizePolicy.Preferred, 可以伸展、收缩
QSizePolicy.Expanding, 可以伸展、收缩,尽可能多的去获取额外空间。
QSizePolicy.Ignored, 忽略建议尺寸