注:本文示例都是从官方文档中找到的,有条件的可以去看一下。

基本结构

首先,我们看一下,Qt应用最简单的结构是什么样。

import sys
from PySide6.QtWidgets import QApplication, QLabel

app = QApplication(sys.argv)
label = QLabel("Hello World!")
label.show()
app.exec()

一行一行解释。

前两行是导入需要使用的包。这个后面再讲。

app = QApplication(sys.argv)

这行代码创建了一个QApplication类的实例。首先什么是QApplication?回答这个问题可能会比较复杂。我们先了解一下PySide6这个包的结构。

pyside6 python pyside6 python 弹出菜单_qt


主要就是这三个模块。其中QtCore提供了Qt项目的基础机制,例如信号与槽等等。在QtCore基础上,QtGui提供了有关图形界面的基础操作,比如事件,2d或者3d的表现等等。最后才是真正我们看到的东西——QtWidgets,它包含了所有的组件。

而在上面的例子中,我们知道QApplication是从QtWidgets中导入的,因此它应该属于GUI的组件部分(QtGUI我认为是GUI的机制部分)。

但实际上QApplication并不会创造一个可视化的界面。你可以理解它为一个初始化的地基,它提供了很多初始化的能力,例如使用系统风格或者切换主题等等,详见官方文档QApplication

因此一个Qt的GUI应用都必须要实例一个QApplication,而构造函数中的参数sys.argv并不是必须的,关于sys.argv我看的这篇博客Python中的sys.argv是什么含义。简单的说就是用命令行启动的时候可以传参,实际上写成这两个也没问题。

app = QApplication()
#or
app = QApplication([])

然后是label = QLabel("Hello World!"); label.show(); 实例了一个QLabel对象,然后show。哪些组件可以show哪些不能?show又是什么方法?关于这个我们需要了解其父类。

pyside6 python pyside6 python 弹出菜单_pyside6 python_02


图中可以看到,QLabel是QWidget的子类,实际上绝大多数都是QWidget的子类,因此可以使用QWidget的show方法。

补充,关于什么是QWidget?我目前的理解是,Qt中实际上只有QWidget这一种容器,然后这个容器里又可以存放其他容器或组件,组件实际上也就是一种特殊的容器。

而show方法是创建一个界面的方法,例如我可以创建多个QLabel,然后全部show,程序运行后就会出现多个界面,每个界面里面有一个label。如下图:

pyside6 python pyside6 python 弹出菜单_ui_03


最后则是app.exec(),也可以写成app.exec_()。在Qt的应用中,实际上运行到这一步,上面创建的QLabel才开始出现

pyside6 python pyside6 python 弹出菜单_ui_04


在调试中,走到最后一步,还没有出现GUI界面。原理是,最后一行代码进入循环,直到所有的线程结束(我还不确定),程序结束。

一个更标准的代码结构

python支持面向对象,那我们必须投入面向对象的怀抱啊。
通常来说,一个应用是拥有多个界面类以及一个主函数。
先上示例:

"""PySide6 Active Qt Viewer example"""

import sys
from PySide6.QtAxContainer import QAxSelect, QAxWidget
from PySide6.QtGui import QAction
from PySide6.QtWidgets import (QApplication, QDialog,
    QMainWindow, QMessageBox, QToolBar)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        toolBar = QToolBar()
        self.addToolBar(toolBar)
        fileMenu = self.menuBar().addMenu("&File")
        loadAction = QAction("Load...", self, shortcut="Ctrl+L", triggered=self.load)
        fileMenu.addAction(loadAction)
        toolBar.addAction(loadAction)
        exitAction = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close)
        fileMenu.addAction(exitAction)

        aboutMenu = self.menuBar().addMenu("&About")
        aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
        aboutMenu.addAction(aboutQtAct)
        self.axWidget = QAxWidget()
        self.setCentralWidget(self.axWidget)

    def load(self):
        axSelect = QAxSelect(self)
        if axSelect.exec() == QDialog.Accepted:
            clsid = axSelect.clsid()
            if not self.axWidget.setControl(clsid):
                QMessageBox.warning(self, "AxViewer", f"Unable to load {clsid}.")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    availableGeometry = mainWin.screen().availableGeometry()
    mainWin.resize(availableGeometry.width() / 3, availableGeometry.height() / 2)
    mainWin.show()
    sys.exit(app.exec())

效果如下:

pyside6 python pyside6 python 弹出菜单_qt_05

还是一步步来解读代码。

  1. 首先,创建了一个MainWindow类,我们上个示例中展示了两个label,但出现了两个页面,那我想放在一个界面里怎么办?那就需要容器。QMainWindow就是一个容器,我们看一下它的继承关系,就是一个QWidget的子类。

然后这个QMainWindow有啥用呢?其实就是内置了一些布局和属性。如图:

pyside6 python pyside6 python 弹出菜单_ui_06


因而在上面的例子中,addToolBar()menuBar()就不难理解。

不过有一个小细节是我之前在思考的,为什么添加工具栏用的是addToolBar()而菜单栏是fileMenu = self.menuBar().addMenu("&File")呢?

因为工具栏可以有零或多个,而菜单栏只有一个。

  1. 再看一下这条语句:fileMenu = self.menuBar().addMenu("&File")
  2. pyside6 python pyside6 python 弹出菜单_qt_07


给菜单栏添加菜单项可以有三种方式传参,得到QMenu菜单项后,就可以给它添加下拉菜单里的QAction,如图:

pyside6 python pyside6 python 弹出菜单_pyside6 python_08

关于QAction,其实看示例也明白它的用处了,最后一个参数trigger用来绑定事件,目前还没了解这个绑定事件的操作有没有用到信号/槽。需要注意的是,QAction属于QtGui包,不属于QtWidgets。

  1. 然后是QAxWidget,例子是在官网找的,目前这一块好像官网给的资料都不多,暂且不表,目前只知道这是一个插件支持ActiveX,应该是用来加载文件的。我后续有时间必定给他解决
  2. 最后是主函数调整窗口大小位置那一块的QScreen,该类一般用于获得屏幕属性,例如屏幕的大小角度颜色深度等等,而availableVirtualGeometry()则是获得屏幕的可用尺寸,一般就是去除了任务栏的尺寸。resize()顾名思义对住窗口改变长宽。