一、QFormLayout的介绍(官翻)
QFormLayout是一个方便的布局类,它以两列的形式布局其子元素。左列由标签组成,右列由“字段”小部件(行编辑器、数字显示框等)组成。
传统上,这种两栏布局是通过使用QGridLayout实现的。QFormLayout是一种更高级别的替代方案,具有以下优点:
- 遵守不同平台的外观和感觉指南。
例如,macOS Aqua和KDE指南规定标签应右对齐,而Windows和GNOME应用程序通常使用左对齐。
- 支持包裹长行。
对于小显示器的设备,QFormLayout可以设置为换行长行,甚至可以设置为换行所有行。
- 用于创建标签-字段对的便捷API。
addRow() 重载接受一个 QString 和一个 QWidget *,在幕后创建一个 QLabel 并自动设置其好友。然后我们可以这样写代码:
formLayout = QFormLayout(self)
formLayout.addRow("Name:", nameLineEdit)
formLayout.addRow("Email:", emailLineEdit)
formLayout.addRow("Age:", ageSpinBox)
与下面的代码进行比较,使用QGridLayout编写:
gridLayout = QGridLayout(self)
nameLabel = QLabel("Name:")
nameLabel.setBuddy(nameLineEdit)
emailLabel = QLabel("Name:")
emailLabel.setBuddy(emailLineEdit)
ageLabel = QLabel(("Name:")
ageLabel.setBuddy(ageSpinBox)
gridLayout.addWidget(nameLabel, 0, 0)
gridLayout.addWidget(nameLineEdit, 0, 1)
gridLayout.addWidget(emailLabel, 1, 0)
gridLayout.addWidget(emailLineEdit, 1, 1)
gridLayout.addWidget(ageLabel, 2, 0)
gridLayout.addWidget(ageSpinBox, 2, 1)
下表显示了不同样式的默认外观。
通过调用 setLabelAlignment() 、 setFormAlignment() 、 setFieldGrowthPolicy() 和 setRowWrapPolicy() ,也可以单独覆盖表单样式。例如,要在所有平台上模拟QMacStyle的表单布局外观,但标签左对齐,你可以这样写:
formLayout.setRowWrapPolicy(QFormLayout.DontWrapRows)
formLayout.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint)
formLayout.setFormAlignment(Qt.AlignHCenter | Qt.AlignTop)
formLayout.setLabelAlignment(Qt.AlignLeft)
二、QFormLayout的相关方法
1.QFormLayout的创建及插入行列操作方法
API函数 | 参数说明 | 返回值 | 功能作用 |
QFormLayout(self, parent) | parent:QWidget | None | 创建一个表格布局实例 |
addRow(self,lab,field) | lab:QWidget field:QWidget或QLayout | None | 按照给定格式内容中的控件和数据项添加行 |
addRow(self,labText,field) | labText:str field:QWidget或QLayout | None | 按照给定格式内容中的控件和数据项添加行(默认第一个对象为QLabel) |
addRow(self,layout) | layout:QLayout | None | 添加行,此行为布局项,占两列 |
addRow(self,widget) | layout:QWidget | None | 添加行,此行为控件,占两列 |
insertRow(self,row,lab,field) | row:int lab:QWidget field:QWidget或QLayout | None | 在指定索引行,按照给定格式内容中的控件和数据项添加行 |
insertRow (self, row,labText,field) | row:int labText:str field:QWidget或QLayout | None | 在指定索引行,按照给定格式内容中的控件和数据项添加行(默认第一个对象为QLabel) |
insertRow (self,row,layout) | row:int layout:QLayout | None | 在指定索引行,添加行,此行为布局项,占两列 |
insertRow(self,row,widget) | row:int layout:QWidget | None | 在指定索引行,添加行,此行为控件,占两列 |
setWidget (self,row,role,layout) | row:int role:QFormLayout.Itemrole layout:QLayout | None | 在指定索引行,置入标志项为指定布局 |
setLayout(self,row,role,widget) | row:int role:QFormLayout.Itemrole layout:QWidget | None | 在指定索引行,置入标志项为指定控件 |
QFormLayout布局简化了添加流程,默认为两列控件,左列为标签,右列为字段控件。故,上述方法中的lab参数即指代标签对象,右侧的field则指代字段控件对象。如果相关方法直接填入字符串,则将其默认为标签QLabel对象。如addRow()方法和insertRow()方法。
但是,要说明的是,并不是左列只能添加标签对象,两列可以添加任意继承自QWidget的子类。
上述方法中role参数是一个枚举类:
枚举类 | 枚举常量 | 枚举值 | 功能描述 |
QFormLayout | LabelRole | 0 | 标签小部件标志 |
FieldRole | 1 | 数据项小部件标志 | |
SpanningRole | 2 | 跨列的标签或数据项小部件标 |
role参数各枚举值的含义也很简单,LabelRole就是指左列,Filed指右列,而SpanningRole指的是跨列的项。
from PySide6.QtWidgets import QApplication,QWidget,QPushButton,QLineEdit,QLabel,\
QCalendarWidget,QDateEdit,QTextEdit,QHBoxLayout,QFormLayout
from PySide6.QtCore import Qt,QRect
import sys
class MyWindow(QWidget):
def __init__(self):
super().__init__()
# 创建表单布局及布局内控件
lay = QFormLayout(self)
btn1 = QPushButton()
date_edit = QDateEdit()
line_edit = QLineEdit()
calendar_1 = QCalendarWidget()
# 创建水平子布局及子布局内的控件
child_lay = QHBoxLayout()
btn_insert = QPushButton("插入行")
btn_set = QPushButton("修改行")
# 添加控件至水平子布局
child_lay.addWidget(btn_insert)
child_lay.addWidget(btn_set)
# 将控件与子布局以添加行的方式置入布局内
lay.addRow(btn1,date_edit)
lay.addRow("姓名:",line_edit)
lay.addRow(calendar_1)
lay.addRow(child_lay)
# 创建插入和替换控件
lab = QLabel("年龄:")
line_edit_2 = QLineEdit()
self.set_lab = QLabel("个人简历")
self.text_edit = QTextEdit()
# 点击按钮在第1行插入行
btn_insert.clicked.connect(lambda :lay.insertRow(1,lab,line_edit_2))
# 点击按钮将标签置入第2行的标签位
btn_set.clicked.connect(self.set_row)
def set_row(self):
self.layout().setWidget(2, QFormLayout.ItemRole.LabelRole, self.set_lab)
#self.layout().setWidget(2, QFormLayout.ItemRole.FieldRole,self.text_edit)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec())
运行结果:
使用setLayout()和setWidget()方法时,置入的位置不能被占据,否则界面会出现问题且控制台报错。需要同replace的相关方法,或者删除已占位的控件后再设置。
2.QFormLayout的删除行列操作
API函数 | 参数说明 | 返回值 | 功能作用 |
removeRow(self, row: int) | row:int | None | 移除行并删除控件 |
removeRow(self, widget) | widget:QWidget | None | 移除行并删除控件 |
removeRow(self, layout) | layout:QLayout | None | 移除行并删除控件 |
takeRow(self, row: int) | row:int | QFormLayout.TakeRowResult | 移除行但不删除控件 |
takeRow(self, widget) | widget:QWidget | QFormLayout.TakeRowResult | 移除行但不删除控件 |
takeRow (self, layout) | layout:QLayout | QFormLayout.TakeRowResult | 移除行但不删除控件 |
setRowVisible(self, row, on) | row:int on:bool | None | 设置行隐藏,Ture显示,Flase隐藏 |
setRowVisible(self, widget, on) | widget:QWidget on:bool | None | 设置行隐藏,Ture显示,Flase隐藏 |
setRowVisible(self, layout, on) | layout:QLayout on:bool | None | 设置行隐藏,Ture显示,Flase隐藏 |
isRowVisible(self, row) | row:int | bool | 查看行是否隐藏 |
isRowVisible(self, row) | widget:QWidget | bool | 查看行是否隐藏 |
isRowVisible(self, row) | layout:QLayout | bool | 查看行是否隐藏 |
count(self) | None | int | 获取控件总数 |
rowCount(self) | None | int | 获取行总数 |
相关方法都很简单,不做过多说明。有一点需要注意,QFormLayout的removeRow()方法和继承自QLayout的removewidget()是不一样的,romoveRow()方法直接删除了控件,而removewidget()只是将控件从布局中移除。setRowVisiable方法与控件的hide()方法一致,但与QGridLayout不同的是,此处在控件的尺寸策略中设置控件隐藏占位,是无效的。
3.QFormLayout的其他相关方法
API函数 | 参数说明 | 返回值 | 功能作用 |
getLayoutPosition(self, layout) | layout: QLayout | tuple(int, QFormLayout.ItemRole ) | 获取子布局位置及数据项信息的元组 |
getWidgetPosition(self, widget) | widget: QWidget | tuple(int, QFormLayout. ItemRole | 获取子布局位置及数据项信息的元组 |
setFormAlignment(self, alignment) | alignment: Qt.AlignmentFlag | None | 设置表单的对其方式 |
formAlignment(self) | None | Qt.AlignmentFlag | 返回表单的对其方式 |
setLabelAlignment(self, alignment) | alignment: Qt.AlignmentFlag | None | 设置标签的对其方式 |
labelAlignment(self) | None | Qt.AlignmentFlag | 返回标签的对其方式 |
labelForField(self, field) | field: QWidget | QWidget | 通过右侧子布局获取左侧标签项 |
labelForField(self, field) | field: QLayout | QWidget | 通过右侧控件获取左侧标签项 |
setSpacing(self, arg__1) | arg_1:int | None | 设置表单布局的水平和竖向间距 |
spacing(self) | None | int | 获取表单布局的水平和竖向间距 |
setHorizontalSpacing(self, spacing) | spacing:int | None | 设置水平间距 |
horizontalSpacing(self) | None | spacing:int | 获取水平间距 |
setVerticalSpacing(self, spacing) | spacing:int | None | 设置竖向间距 |
verticalSpacing(self) | None | spacing:int | 获取竖向间距 |
setRowWrapPolicy(self, policy) | policy: QFormLayout. RowWrapPolicy | None | 设置行包装策略 |
rowWrapPolicy(self) | None | QFormLayout. RowWrapPolicy | 获取行包装策略 |
setFieldGrowthPolicy(self, policy) | policy: QFormLayout .FieldGrowthPolicy | None | 设置数据项增长策略 |
fieldGrowthPolicy(self) | None | QFormLayout. RowWrapPolicy | 获取数据项增长策略 |
这里重点说一下相关尺寸策略的问题,主要涉及行和字段控件部分的尺寸策略。如setRowWrapPolicy(self, policy),他的参数不是QSizePolicy类,而是QFormLayout的一个枚举类。
枚举常量 | 枚举值 | 功能描述 | |
QFormLayout.RowWrapPolicy | DontWrapRows | 0 | 数据项始终位于其标签旁边。 (即使父控件挤压至最小,他还是会在一行) |
WrapLongRows | 1 | 标签具有足够的水平空间以适合最宽的标签,其余空间则分配给数据项。如果数据项对应的最小大小大于可用空间,则该字段将换行到下一行。 (如果父控件挤压至最小,字段控件会挪至下一行) | |
WrapAllRows | 2 | 字段始终布置在其标签下方。 | |
QFormLayout.FieldGrowthPolicy | FieldsStayAtSizeHint | 0 | 字段永远不会超出其有效大小提示。 (不会随父控件大小变化而变化) |
ExpandingFieldsGrow | 1 | 水平尺寸策略为“扩展”或“最小扩展”的字段将增长以填充可用空间。 其他字段将不会超出其有效大小提示。 (只有策略为Expanding和MinimumExpanding才增长) | |
AllNonFixedFieldsGrow | 2 | 具有允许其增长的大小策略的所有字段都将增长以填充可用空间。 (除Maximum,Fixed以外的策略都能增长 |
这个枚举类的值相当于方便的为我们提供了lab项和field项的尺寸策略的设置,而不需要我们单独对控件进行尺寸策略的设置。但另一方面,使用setRowWrapPolicy(self, policy)和setFieldGrowthPolicy(self, policy)方法就统一了一行或者字段列的整体尺寸策略,对于有不同需要的控件就进行了限制。
同时,对于扩展至存在于水平方向,竖向还是需要通过控件的尺寸策略进行设置。
from PySide6.QtWidgets import QApplication,QWidget,QPushButton,QLineEdit,\
QCalendarWidget,QLabel,QDateEdit,QHBoxLayout,QFormLayout
from PySide6.QtCore import Qt
import sys
class MyWindow(QWidget):
def __init__(self):
super().__init__()
# 创建总表单布局设置给父窗口
all_lay = QFormLayout(self)
# 创建子表单布局和相关控件
lay = QFormLayout()
lab = QLabel("日期信息:")
date_edit = QDateEdit()
line_edit = QLineEdit()
calendar_1 = QCalendarWidget()
# 创建水平子布局及子布局内的控件
child_lay = QHBoxLayout()
btn2 = QPushButton("设置表单右对齐")
btn3 = QPushButton("设置标签右对齐")
# 添加控件至水平子布局
child_lay.addWidget(btn2)
child_lay.addWidget(btn3)
# 将控件与子布局以添加行的方式置入布局内
lay.addRow(lab,date_edit)
lay.addRow("年龄:",line_edit)
# 将lay、calendar_1和child_lay添加至总布局中
all_lay.addRow(lay)
all_lay.addRow(calendar_1)
all_lay.addRow(child_lay)
# 设置控件的水平间距和竖向间距
all_lay.setHorizontalSpacing(10)
all_lay.setVerticalSpacing(20)
# 设置lay布局中行的包装策略
lay.setRowWrapPolicy(QFormLayout.RowWrapPolicy.WrapAllRows)
# 设置lay布局中的数据项的增长策略
lay.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
# 获取calendar_1的布局位置及数据项信息
print(all_lay.getWidgetPosition(calendar_1))
btn2.clicked.connect(lambda :lay.setFormAlignment(Qt.AlignmentFlag.AlignRight))
btn3.clicked.connect(lambda :lay.setLabelAlignment(Qt.AlignmentFlag.AlignRight))
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec())
运行结果:
标签:控件,QFormLayout,管理器,self,None,---,lay,row From: https://blog.csdn.net/love_songming/article/details/143270650