Sub Widget and Sub Layout#

在一个大型 Widget 中, 里面可能包含很多小的 Widget, 这些小的 Widget 都有各自的 Layout. 把逻辑类似的小的 Widget 放在一可复用的类中是一种很好的组织代码的方式. 但是这里有几个很细的问题, 但是会使得最终呈现的效果差别巨大. 这里我们直接把结论放在这里, 如果你已经了解这其中的细节了, 你可以直接参考这里的结论. 而如果你不了解细节, 则建议运行示例代码来查看两者的区别:

  1. 把很多 Widget 组织到一起的时候, 是开一个随便什么 Class, 还是一定要开一个 PySide6.QtWidgets.QWidget 的子类呢?

  2. 你可以用一个 Widget 实例来组织多个子 Widget, 把他们都变成自己的属性既可. 但这个父 Widget 本身有一个 setLayout 的方法. 这里有两种做法:
    1. 在父 Widget 中调用 setLayout 方法, 然后在 Root Widget 中的 Layout 中, 使用 addWidget(父Widget) 将父 Widget 添加进来.

    2. 在父 Widget 仅仅是把 Layout 作为一个属性设置好, 但是不调用 setLayout. 然后在 Root Widget 中的 Layout 中, 使用 addLayout(父Widget.main_layout) 将父 Widget 的 Layout 添加进来.

结论: 这两个问题其实是一个问题. 如果你希望让这些被组织到一起的 Widget 限制在一个隐形的方框容器中, 那么就用 PySide6.QtWidgets.QWidget + root_widget.addWidget() 的方式. 这样会将父 Widget 作为一个有边界的整体, 从 Root Widget 是无法直接触碰到 父 Widget 里面的小 Widget 的. 而如果你只是想增加代码复用, 那么你用随便什么 Class 都可以, 仅仅是作为一个内存中的数据容器. 然后用 root_widget.addLayout() 的方式将父 Widget 的 Layout 添加进来.

sub_widget_and_sub_layout_1_1.py
 1# -*- coding: utf-8 -*-
 2
 3"""
 4这是一个用于演示对于拥有自己一套 layout 的子 widget, 在父 widget 中什么时候应该用 addWidget,
 5什么时候应该用 addLayout 的演示. 这个例子要用
 6``sub_widget_and_sub_layout_1_1.py`` 和 ``sub_widget_and_sub_layout_1_2.py``
 7一起比较来看才能看出区别.
 8
 9这个例子是在子 widget 用使用 setLayout 方法定义子 widget 的 layout, 然后在父 widget 中使用
10addWidget 方法添加子 widget 到父 widget 中.
11
12结论, 子 widget 有一个隐形的方框, 并且有 margin, 所以看起来要离最外面的边框远一点.
13"""
14
15from PySide6.QtWidgets import (
16    QWidget,
17    QLabel,
18    QVBoxLayout,
19    QMainWindow,
20)
21from PySide6 import QtWidgets
22import sys
23
24
25class LabelListWidget(QWidget):
26    def __init__(self, parent, ith: int):
27        super().__init__(parent)
28        self.ith = ith
29
30        self.main_layout = QVBoxLayout()
31
32        for j in range(1, 1 + 3):
33            label_wgt = QLabel(f"Label {self.ith}-{j}", parent=parent)
34            self.main_layout.addWidget(label_wgt)
35
36        self.setLayout(self.main_layout)
37
38
39class MainWidget(QWidget):
40    def __init__(self, parent):
41        super().__init__(parent)
42        self.label_list_1 = LabelListWidget(self, ith=1)
43        self.main_lay = QVBoxLayout()
44        self.main_lay.addWidget(self.label_list_1)
45        self.setLayout(self.main_lay)
46
47
48class MainWindow(QMainWindow):
49    def __init__(self):
50        super().__init__()
51        self.main_wgt = MainWidget(self)
52        self.main_wgt.setStyleSheet("Border: 1px solid black;")
53        self.setCentralWidget(self.main_wgt)
54        self.setWindowTitle("Sub Widget and Sub Layout Example 1-1")
55        self.show()
56
57
58if __name__ == "__main__":
59    app = QtWidgets.QApplication(sys.argv)
60    main = MainWindow()
61    sys.exit(app.exec())
sub_widget_and_sub_layout_1_2.py
 1# -*- coding: utf-8 -*-
 2
 3"""
 4这是一个用于演示对于拥有自己一套 layout 的子 widget, 在父 widget 中什么时候应该用 addWidget,
 5什么时候应该用 addLayout 的演示. 这个例子要用
 6``sub_widget_and_sub_layout_1_1.py`` 和 ``sub_widget_and_sub_layout_1_2.py``
 7一起比较来看才能看出区别.
 8
 9这个例子是在子 widget 不使用 setLayout 方法, 而只是定义子 widget 中的 main_layout 的定义,
10然后再然后在父 widget 中使用 addLayout 方法添加子 widget 的 layout 到父 widget 的 layout 中.
11
12结论, 子 widget 有一个隐形的方框, 由于这里没有用子 widget, 而是直接将子 widget 中的 子 widget
13的元素加到总的 layout 中取, 所以看起来离外面的边框近一点.
14"""
15
16from PySide6.QtWidgets import (
17    QWidget,
18    QLabel,
19    QVBoxLayout,
20    QMainWindow,
21)
22from PySide6 import QtWidgets
23import sys
24
25
26class LabelListWidget(QWidget):
27    def __init__(self, parent, ith: int):
28        super().__init__(parent)
29        self.ith = ith
30
31        self.main_layout = QVBoxLayout()
32
33        for j in range(1, 1 + 3):
34            label_wgt = QLabel(f"Label {self.ith}-{j}", parent=parent)
35            self.main_layout.addWidget(label_wgt)
36
37
38class MainWidget(QWidget):
39    def __init__(self, parent):
40        super().__init__(parent)
41        self.label_list_1 = LabelListWidget(self, ith=1)
42        self.main_lay = QVBoxLayout()
43        self.main_lay.addLayout(self.label_list_1.main_layout)
44        self.setLayout(self.main_lay)
45
46
47class MainWindow(QMainWindow):
48    def __init__(self):
49        super().__init__()
50        self.main_wgt = MainWidget(self)
51        self.main_wgt.setStyleSheet("Border: 1px solid black;")
52        self.setCentralWidget(self.main_wgt)
53        self.setWindowTitle("Sub Widget and Sub Layout Example 1-2")
54        self.show()
55
56
57if __name__ == "__main__":
58    app = QtWidgets.QApplication(sys.argv)
59    main = MainWindow()
60    sys.exit(app.exec())