文章

PySide6 笔记 - 布局

本文简单介绍一点 PySide6 的界面布局

PySide6 笔记 - 布局

自己的 UI 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sys
from PySide6 import QtWidgets

class UIMyWindow(object):
    def __init__(self, window: QtWidgets.QWidget):
        self.pbn_close = QtWidgets.QPushButton("Close", window)
        self.pbn_close.setGeometry(100, 100, 150, 50)

class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = UIMyWindow(self)
        self.resize(400, 300)
        self.ui.pbn_close.clicked.connect(self.close)

def main():
    app = QtWidgets.QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec())

因为 UI 类中需要在 self 下创建各种控件,就直接在初始化函数中创建了,而不再单独用一个 setupUi 函数。这样在窗口类中直接用 self.ui = UIMyWindow(self) 就可以初始化 UI 了。之后所有的控件都可以用 self.ui 获取。

主窗口的菜单、工具栏和状态栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import sys
from PySide6 import QtWidgets, QtCore, QtGui

class UIMyWindow(object):
    def __init__(self, window: QtWidgets.QMainWindow):
        # 状态栏直接获取
        self.status_bar = window.statusBar()
        # 菜单栏直接获取
        self.menu_bar = window.menuBar()
        # 创建和设定中央控件
        self.center_widget = QtWidgets.QWidget(window)
        window.setCentralWidget(self.center_widget)
        # 给中央控件指定一个布局
        self.vly_cw = QtWidgets.QVBoxLayout(self.center_widget)

        # 创建动作
        self.act_new = QtGui.QAction("New", window)
        self.act_new.setShortcut(QtGui.QKeySequence.StandardKey.New)
        self.act_new.setStatusTip("Create a new file")
        self.act_open = QtGui.QAction("Open", window)
        self.act_open.setShortcut(QtGui.QKeySequence.StandardKey.Open)
        self.act_open.setStatusTip("Open an existing file")

        # 添加菜单
        self.menu_file = self.menu_bar.addMenu("&File")
        self.menu_file.addAction(self.act_new)
        self.menu_file.addSeparator()
        self.menu_file.addAction(self.act_open)

        # 创建工具栏,可以多个
        self.tbar_file = QtWidgets.QToolBar(window)
        self.tbar_file.addAction(self.act_new)
        self.tbar_file.addAction(self.act_open)
        self.tbar_file.setAllowedAreas(QtCore.Qt.ToolBarArea.TopToolBarArea |
                                       QtCore.Qt.ToolBarArea.BottomToolBarArea)
        window.addToolBar(self.tbar_file)

        # 创建停靠窗(在中央控件周围的可拖动、可悬浮的窗口),可以多个
        self.dw_cont = QtWidgets.QDockWidget("Table of Contents", window)
        self.dw_cont.setAllowedAreas(QtCore.Qt.DockWidgetArea.LeftDockWidgetArea |
                                     QtCore.Qt.DockWidgetArea.RightDockWidgetArea)
        window.addDockWidget(QtCore.Qt.DockWidgetArea.LeftDockWidgetArea, self.dw_cont)
        # 给停靠窗设定控件
        self.lw_heading = QtWidgets.QListWidget(self.dw_cont)
        self.lw_heading.addItem("First")
        self.lw_heading.addItem("Second")
        self.dw_cont.setWidget(self.lw_heading)

        # 给中央控件添加控件,布局会自动将该控件设为中央控件的子控件
        self.txe_1 = QtWidgets.QTextEdit()
        self.vly_cw.addWidget(self.txe_1)

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = UIMyWindow(self)
        self.resize(800, 600)

        self.ui.act_new.triggered.connect(self.act_new_triggered)
        self.ui.act_open.triggered.connect(self.act_open_triggered)

    def act_new_triggered(self):
        QtWidgets.QMessageBox.information(self, "Info", "New File")

    def act_open_triggered(self):
        QtWidgets.QMessageBox.information(self, "Info", "Open File")

def main():
    app = QtWidgets.QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec())

QMainWindow 本来就有一个 QMenuBar ,可以直接用 menuBar() 函数获取。 QStatusBar 也一样。

默认没有 QToolBar ,所以需要自己创建。

菜单栏和工具栏可以复用动作(QAction)。

对于 addToolBar 函数,

  • 如果参数是一个字符串,则创建一个新的 QToolBar 对象并命名为该字符串,然后将该工具栏插入到主窗口的上工具栏区域
  • 如果参数是一个 QToolBar 对象,则把该工具栏插入到上工具栏区域
  • 如果参数是一个工具栏区域的枚举和一个 QToolBar 对象,则把该工具栏插入到该区域。

对于 QDockWidgetsetWidget 函数:

  • 如果在停靠窗可见的情况下添加控件,也就是在窗口运行中修改了控件,需要显式地调用 show 函数显示控件。
  • 在设定控件之前必须给控件添加布局,否则控件将不可见。(这里可能是针对上一条的条件下说的吧,或者是针对多控件的情况下说的……)

关于布局

布局并非控件,但又与控件有交织,因此需要去理解一下。

首先先理解一下为什么使用 QMainWindow 的时候需要设定中央控件呢?(参考上例代码)

1
2
3
4
5
6
7
8
9
10
...
class UIMyWindow(object):
    def __init__(self, window: QtWidgets.QMainWindow):
        ...
        # 创建和设定中央控件
        self.center_widget = QtWidgets.QWidget(window)
        window.setCentralWidget(self.center_widget)
        # 给中央控件指定一个布局
        self.vly_cw = QtWidgets.QVBoxLayout(self.center_widget)
...

因为初始状态下主窗口并不会自动创建中央控件(会自动创建的只有菜单栏和状态栏),所以需要我们手动创建一个然后指定,在此之后的所有控件都建立在这个中央控件之上,也就是中央控件是它们的父控件。

其实这个中央控件可以是任何 QWidget 的子类,但通常用 QWidget 就行了。这里可以把 QWidget 理解为一个什么特性也没有的最简单的控件,而不要理解为一个抽象基类。

所以上述 self.center_widget = QtWidgets.QWidget(window) 就是创建了一个 QWidget 控件并把它的父控件设定为主窗口。此时该控件在主窗口上还是游离的,在将该控件设定为中央控件之后,它就固定在中央了。

此后,这个中央控件需要一个布局,因此就添加一个布局。

给控件设定布局有两种方式,

  1. 创建布局时直接传递控件作为参数。
  2. 创建布局时不指定控件,传空参,后续用控件的 setLayout 函数指定布局。

在设定布局时,会自动将布局内的控件都设定为使用该布局的控件的子控件,不管它们之前的父控件是谁。

如果布局中有子布局,则子布局中控件的父控件还是使用布局的控件,只不过可以认为该父控件使用了多个布局,不同的子控件在不同的布局中。

本文由作者按照 CC BY 4.0 进行授权