PyQt入门(5)-尺寸策略和布局(下)

目录

一、QGridLayout

二、QFormLayout

三、QStackedLayout


一、QGridLayout

QGridLayout将可用空间划分成行和列,然后把widget放入正确的单元格中。

每列都有一个最小宽度和一个拉伸因子。列的最小宽度用setColumnMinimumWidth()设置,列的最小宽度也是该列中每个小部件的最小宽度。拉伸因子是使用setColumnStretch()设置的,它确定列在最小宽度之上将获得多少可用空间。

QGridLayout添加widget的函数是:

addWidget(self, a0: QWidget,
    row: int,
    column: int,
    rowSpan: int,
    columnSpan: int,
    alignment: Union[Qt.Alignment, Qt.AlignmentFlag] = Qt.Alignment())
row: 行标,从0开始
column:列标,从0开始
rowSpan:占几行
columnSpan:占几列

下图是官网文档的一个示例图片,显示了一个具有五列三行网格的对话框片段(网格显示为红色边框):

此例中的第0、2和4列由QLabel、QLineEdit和QListBox组成。第1列和第3列是由setColumnMinimumWidth()构成的占位符。第0行由三个QLabel对象、第1行由三个QLineEdit对象和第2行由三个QListBox对象组成。我们使用占位符列(1和3)来获得列之间适当的空间量。

请注意,列和行的宽度和高度并不相等。如果希望两列具有相同的宽度,则必须将它们的最小宽度和拉伸因子设置为相同。您可以使用setColumnMinimumWidth()和setColumnStretch()来完成此操作。

下面我们会首先用QGridLayout重写上一章的计算器界面,然后再用代码实现这个官网的图片。

计算器界面代码:

class GridWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        # 窗口标题
        self.setWindowTitle('计算器')
        # 窗口大小
        self.resize(300, 400)
        self.setCentralWidget(QWidget())

        # 把按钮的文字都设置成20px
        self.setStyleSheet("""QPushButton{font-size:20px;}""")

        # 主界面layout是一个QVBoxLayout
        self.main_layout = QVBoxLayout()

        # 用一个QLabel展示计算结果
        self.l_result = QLabel('0')
        self.l_result.setStyleSheet("""QLabel{background-color: red; font-size:40px;}""")
        # 设置QLabel的文件靠右垂直居中
        self.l_result.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        # 把l_result添加到主界面layout,拉伸因子是1
        self.main_layout.addWidget(self.l_result, stretch=1)

        # 按钮区域layout
        self.operation_layout = QGridLayout()
        self.operation_layout.setSpacing(0)
        # 设置layout内控件整体的外边距(可以理解成layout的内边距)
        self.operation_layout.setContentsMargins(0, 0, 0, 0)

        self.btn_init = QPushButton("AC")
        self.btn_back = QPushButton("X")
        self.btn_remainder = QPushButton("%")
        self.btn_div = QPushButton("÷")
        self.operation_layout.addWidget(self.btn_init, 0, 0, 1, 1, alignment=Qt.AlignLeft)
        self.operation_layout.addWidget(self.btn_back, 0, 1, 1, 1)
        self.operation_layout.addWidget(self.btn_remainder, 0, 2, 1, 1)
        self.operation_layout.addWidget(self.btn_div, 0, 3, 1, 1)

        self.btn_7 = QPushButton("7")
        self.btn_8 = QPushButton("8")
        self.btn_9 = QPushButton("9")
        self.btn_mul = QPushButton("x")
        self.operation_layout.addWidget(self.btn_7, 1, 0, 1, 1)
        self.operation_layout.addWidget(self.btn_8, 1, 1, 1, 1)
        self.operation_layout.addWidget(self.btn_9, 1, 2, 1, 1)
        self.operation_layout.addWidget(self.btn_mul, 1, 3, 1, 1)

        self.btn_4 = QPushButton("4")
        self.btn_5 = QPushButton("5")
        self.btn_6 = QPushButton("6")
        self.btn_sub = QPushButton("-")
        self.operation_layout.addWidget(self.btn_4, 2, 0, 1, 1)
        self.operation_layout.addWidget(self.btn_5, 2, 1, 1, 1)
        self.operation_layout.addWidget(self.btn_6, 2, 2, 1, 1)
        self.operation_layout.addWidget(self.btn_sub, 2, 3, 1, 1)

        self.btn_1 = QPushButton("1")
        self.btn_2 = QPushButton("2")
        self.btn_3 = QPushButton("3")
        self.btn_add = QPushButton("+")
        self.operation_layout.addWidget(self.btn_1, 3, 0, 1, 1)
        self.operation_layout.addWidget(self.btn_2, 3, 1, 1, 1)
        self.operation_layout.addWidget(self.btn_3, 3, 2, 1, 1)
        self.operation_layout.addWidget(self.btn_add, 3, 3, 1, 1)

        self.btn_zero = QPushButton("0")
        self.btn_point = QPushButton(".")
        self.btn_equal = QPushButton("=")
        # 按钮0占两列
        self.operation_layout.addWidget(self.btn_zero, 4, 0, 1, 2)
        # 由于按钮0占用了两列,按钮.的位置就在第三列(下标2)了
        self.operation_layout.addWidget(self.btn_point, 4, 2, 1, 1)
        self.operation_layout.addWidget(self.btn_equal, 4, 3, 1, 1)

        # 按钮区域layout的拉伸因子是5
        self.main_layout.addLayout(self.operation_layout, stretch=5)

        self.centralWidget().setLayout(self.main_layout)
        self.bat_set_policy()

    def bat_set_policy(self):
        for field in self.__dir__():
            if field.startswith("btn_"):
                # QPushButton的vPolicy默认是Fixed,要改成可拉伸的策略
                getattr(self, field).setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

官网图片代码:

class WebsiteWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('官网示例界面')
        self.resize(500, 100)
        self.setCentralWidget(QWidget())

        self.main_layout = QGridLayout()

        self.main_layout.addWidget(QLabel("Font"), 0, 0, 1, 1)
        self.main_layout.addWidget(QLineEdit("Times"), 1, 0, 1, 1)
        self.lw1 = QListWidget()
        self.lw1.addItems(['Times', 'Helvetica', 'Courier', 'Palatino', 'Gill sans', 'Test1', 'Test2'])
        self.lw1.setCurrentRow(0)
        self.main_layout.addWidget(self.lw1, 2, 0, 1, 1)

        self.main_layout.addWidget(QLabel("Font style"), 0, 2, 1, 1)
        self.main_layout.addWidget(QLineEdit("Roman"), 1, 2, 1, 1)
        self.lw2 = QListWidget()
        self.lw2.addItems(['Roman', 'Italic', 'Oblique'])
        self.lw2.setCurrentRow(0)
        self.main_layout.addWidget(self.lw2, 2, 2, 1, 1)

        self.main_layout.addWidget(QLabel("Size"), 0, 4, 1, 1)
        self.main_layout.addWidget(QLineEdit("10"), 1, 4, 1, 1)
        self.lw3 = QListWidget()
        self.lw3.addItems(['8', '10', '12', '14', '16', '18', '20', '22', '24', '26', '28', '30'])
        self.lw3.setCurrentRow(0)
        self.main_layout.addWidget(self.lw3, 2, 4, 1, 1)

        # 占位的列
        self.main_layout.setColumnMinimumWidth(1, 40)
        self.main_layout.setColumnMinimumWidth(3, 20)

        self.centralWidget().setLayout(self.main_layout)

二、QFormLayout

QFormLayout是一个便捷的布局,它以一个两列的表单形式来排列它的子widget,左列是标签,右列是Input类型的widget。

传统上,这样的两列形式布局使用QGridLayout也可以完美实现的。QFormLayout是一种更高级别的替代方案,具有以下优势:

  • 遵守不同平台的外观指南

        比如:macOS上标签会右对其,windows上标签通常是左对齐

  • 支持包装长行

        所谓包装长行就是在宽度不足时,把标签显示在字段的上面。

        QFormLayot有个字段是RowWrapPolicy,它指明了行包装策略:

class RowWrapPolicy(int):
     DontWrapRows = ... # type: QFormLayout.RowWrapPolicy
     WrapLongRows = ... # type: QFormLayout.RowWrapPolicy
     WrapAllRows = ... # type: QFormLayout.RowWrapPolicy

        对于Qt扩展样式,默认为WrapLongRows;对于其他样式,默认为DontWrapRows。通过setRowWrapPolicy()可设置:

self.main_layout = QFormLayout()
self.main_layout.setRowWrapPolicy(QFormLayout.RowWrapPolicy.WrapLongRows)

  • 便捷的API来创建 标签-字段 对

        下面我们实现一下官网文档上的示例:

class FormWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('提交表单')
        self.resize(300, 100)
        self.setCentralWidget(QWidget())

        self.main_layout = QFormLayout()
        self.main_layout.addRow('Name:', QLineEdit("Gandalf"))
        self.main_layout.addRow('Email Address:', QLineEdit("gg@troll.cn"))
        self.spinbox = QSpinBox()
        self.spinbox.setMinimum(1)
        self.spinbox.setMaximum(100)
        self.main_layout.addRow('Age:', self.spinbox)

        self.centralWidget().setLayout(self.main_layout)

可以看到,对于表单需求的界面,用QFormLayout实现起来是很便捷的。 

三、QStackedLayout

QStackedLayout可以用来创建一个类似于QTabWidget提供的那种用户界面,在同一时刻只有一个页面能显示出来。在QStackedLayout之上还构建了一个方便的QStackedWidget类(这个在后面讲控件部分再说)。

QStackedLayout没有为用户提供切换页面的内在方式(类似于选项卡那种)。通常是通过存储QStackedLayout页面标题的QComboBox或QListWidget配合QStackedLayout提供的setCurrentIndex()来完成页面切换。

子widgets都保存在一个内部列表中,addWidget()把widget添加到列表的末尾,insertWidget()可以把widget添加到指定的索引处。

使用示例:

"""
通常每个子窗口都有自己的业务逻辑,这里这里定义了三个独立的子窗口
"""
class SubWindow1(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(QPushButton("这是一个按钮"))
        self.setLayout(self.main_layout)


class SubWindow2(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(QLabel("这是一个静态文本"))
        self.setLayout(self.main_layout)


class SubWindow3(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(QLineEdit("这是一个输入框"))
        self.setLayout(self.main_layout)


class StackedWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowTitle('栈布局')
        self.resize(500, 300)
        self.setCentralWidget(QWidget())
        self.main_layout = QHBoxLayout()

        self.left_list_layout = QVBoxLayout()
        self.switch_handles = QListWidget()
        self.switch_handles.addItems(['QPushButton', 'QLabel', 'QLineEdit'])
        self.switch_handles.setCurrentRow(0)
        self.left_list_layout.addWidget(self.switch_handles)
        self.main_layout.addLayout(self.left_list_layout, stretch=1)

        self.right_stacked_layout = QStackedLayout()
        # QStackedLayout添加子widget(窗口)
        self.right_stacked_layout.addWidget(SubWindow1(self))
        self.right_stacked_layout.addWidget(SubWindow2(self))
        self.right_stacked_layout.addWidget(SubWindow3(self))
        # QStackedLayout使用setCurrentIndex()来切换当前显示的窗口
        self.right_stacked_layout.setCurrentIndex(0)
       
        # 把QListWidget的currentRowChanged信号连接到QStackedLayout的setCurrentIndex,实现点击QListWidget的选项时自动切换QStackedLayout
        self.switch_handles.currentRowChanged.connect(self.right_stacked_layout.setCurrentIndex)
        self.main_layout.addLayout(self.right_stacked_layout, stretch=4)

        self.centralWidget().setLayout(self.main_layout)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值