Python 与 Qt 搭配为开发 GUI 提供了极大的便利。本教程提供了 Qt5(PyQt5 与 PySide2,它们的差异见 PySide2 与 Pyqt5 的区别) 的使用手册[1]。更多学习资料见 Qt for Python。
为了兼容 PyQt5 与 PySide2 代码,在 Github 放置 xinetzone / xinet 包用于 Qt5 的开发。
该库提供 pip 支持:
pip install xinet
后续内容将以此模块进行 Qt 的开发。
1 Qt 模块简介[2]
在 Python 中将 Qt 划分为三大类模块:基础模块(QtCore
, QtGui
, QtWidgets
)、功能模块、工具模块。下面依次介绍这些模块。
1.1 通用基础模块
-
QtCore
:这个是 Qt5 中的其他模块使用到的核心非图形类。这个模块就像是建造房屋的地基和钢筋,为健壮的图形界面程序提供核心且强大的功能支持,提供了诸如线程(QThread、QThreadPool)、动画(QAbstractAnimation)、事件响应(Signal、Slot、QEvent)、输入/输出对象(QSettings、QFileinfo)等功能。一个使用 Qt5 编写的图形界面程序可以不使用 QtCore 模块,但是一个 Qt5 编写的强大图形界面程序,肯定会使用到 QtCore 模块。 -
QtGui
:这个是 Qt5 中图形用户界面组件的基类。其提供了用于图形窗口集成、图形事件处理、2D图形图像、字体和文本的子类。有诸如图形拖放(QDrag、QDropEvent)、图像显示(QBitmap、QImage)、字体定义(QFont)、颜色定义(QColor)、绘笔(QPen)等等的子类,类似于建造房屋时的墙面修砌。 -
QtWidgets
:这个是 Qt5 中扩展了 QtGui 模块的控件模块,所谓控件模块,就是其提供了大量的控件及其布局类。这就相当于房子里面客厅、厨房、阳台、卧室,以及其中的各种家具、摆件、电器、设备等等。我们在图形界面编程过程中会使用到大量的 QtWidget 中的子类,图形界面程序中的窗口、按钮、输入框、文本框、选择框、布局等等都是使用它的子类。如同建造房子,我们没有地基(QtCore)、不用水泥砌墙(QtGui),也能用木板和树叶(QtWidget)搭建出一个简易的小屋出来。
注意:我们知道,原生的 Qt 使用的是 C++ 语言进行图形界面的开发,从 Qt4.7 开始,Qt 中引入了一种新的图形界面开发语言——QML 作为原生 QT 开发语言 C++ 的替代品,同时其构建了一套名为 QtQuick 的类库,用于使用自定义图形界面来构建高度动态的图形界面应用程序。
1.2 功能模块
多媒体功能:
-
QtMultimedia
:一个多媒体模块,提供对音频、视频、无线电广播和相机功能的支持。 -
QtMultimediaWidgets
:一个用于实现多媒体功能的控件模块,提供了设计视频和摄像头额外的控件支持,其扩展了QtMultimedia
和QtWidget
模块的功能。
还有:
-
QtNetwork
:一个用于实现网络编程的模块,使得在 Qt 图形界面中进行网络编程变得更加方便;这些类通过使网络编程更加容易和可移植,来简化 TCP / IP和 UDP 客户端和服务器的编码。 -
QtSQL
:一个用于在 Qt 中使用 SQL 对数据库进行操作的模块。 -
QtTest
:一个用于对 Qt 图形界面程序进行单元测试的模块。 -
QtNfc
:这个模块提供了对近场通信(NFC)硬件进行访问的支持。 -
QtWebEngine
:这个模块借助开源的 Chromium 浏览器项目,在图形界面应用程序中嵌入 Web 浏览功能。 -
QtWebSocket
:这个模块为图形界面程序提供了 WebSocket 通信的功能。 -
QtSvg
:这个模块提供了用于显示 SVG 文件内容的相关功能。
1.3 工具模块
Qt 的工具模块主要是为了方便开发人员对图形界面程序进行设计和开发,其提供了 Qt 设计师(QtDesigner)这个图形化的程序设计工具,能够便捷地利用鼠标拖拽来快速绘制程序的界面和基础的事件响应。
更多模块介绍见:PyQt5 系统化学习: 架构简介。
2 创建简单的窗口
-
QtWidgets.QWidget
类是所有用户界面对象的基类。窗口部件是用户界面的一个基本单元:它从窗口系统接收鼠标、键盘和其它事件,并且在屏幕上绘制自己。每一个窗口部件都是矩形的,并且它们按Z轴顺序排列。一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分。 -
QtWidgets.QMainWindow
类提供一个有菜单条、锚接窗口(例如工具条)和一个状态条的主应用程序窗口。主窗口通常用在提供一个大的中央窗口部件(例如文本编辑或者绘制画布)以及周围 菜单、工具条和一个状态条。QMainWindow常常被继承,因为这使得封装中央部件、菜单和工具条以及窗口状态条变得更容易,当用户点击菜单项或者工具条按钮时,槽会被调用。 -
QtWidgets.QDialog
类是对话框窗口的基类。对话框窗口是主要用于短期任务以及和用户进行简要通讯的顶级窗口。QDialog可以是模态对话框也可以是非模态对话框。QDialog支持扩展性并且可以提供返回值。它们可以有默认按钮。QDialog也可以有一个QSizeGrip在它的右下角,使用setSizeGripEnabled()。
对模块有了大体的了解之后,先来创建一个空白的界面窗口。
from xinet import QtWidgets
import sys
# 实例化一个 QApplication 应用程序,用于初始化 GUI
app = QtWidgets.QApplication([])
gui = QtWidgets.QMainWindow() # 实例化一个主窗口
gui.show() # 显示主窗口
app.exec_() # 退出 GUI
# 也可利用内置模块sys的exit()方法侦听GUI的退出信号,以便关闭Python程序。
# sys.exit(app.exec_()) # 当GUI产生退出信号时Python程序结束
效果:
对于用 Qt 写的任何一个 GUI 应用,不管这个应用有没有窗口或多少个窗口,有且只有一个 QtWidgets.QApplication
应用程序。QtWidgets.QApplication
管理 GUI 程序的控制流和主要设置。它包含由窗口系统和其他来源处理过和发送过的主事件循环。它也处理应用程序的初始化和收尾工作,并提供对话管理。它还可以对系统和应用的大部分设置项进行设置。
为了让 GUI 启动,需要使用 app.exec_()
启动事件循环(启动应用,直至用户关闭它)。主事件循环从窗口系统接收事件,并将其分派给应用程序小部件。如果没有该方法,那么在运行的时候还没有进入程序的主循环就直接结束了,所以运行的时候窗口会闪退。
QtWidgets.MainWindow()
控件是 Qt 中的一个主窗口框架,其提供了构建应用程序图形用户界面的框架。
为了方便后续功能的开发,在 xinet
中定义函数 run
用于启动 GUI 程序。即上述代码可简写为:
from xinet import QtWidgets
from xinet.run_qt import run
run(QtWidgets.QMainWindow)
为该空白窗口添加菜单栏:
from xinet import QtWidgets
from xinet.run_qt import run
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 添加菜单栏
file_menu = self.menuBar() # 实例化一个菜单栏
# file_menu.setFixedWidth(200) # 设置菜单栏的宽度
for name in ["文件", "编辑", "关于"]:
file_menu.addMenu(name) # 添加菜单按钮
if __name__ == '__main__':
run(MainWindow)
效果:
添加工具栏:
from xinet import QtWidgets
from xinet.run_qt import run
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 添加工具栏
view_toolbar = self.addToolBar("View")
view_toolbar.addAction("打开")
view_toolbar.addAction("保存")
view_toolbar.addAction("撤回")
if __name__ == '__main__':
run(MainWindow)
效果:
添加状态栏:
from xinet import QtWidgets
from xinet.run_qt import run
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 添加状态栏
status = self.statusBar()
status.showMessage("这是一个状态栏消息")
if __name__ == '__main__':
run(MainWindow)
主窗口中的中央控件用于显示主窗口的基础控件,每一个主窗口都会有一个中央控件,就算其是一个占位符,也必须存在。中央控件通常来说是一个标准的 Qt 窗口小控件,比如 QWidget、QPushbutton等等。
更多精彩参考我的另一篇博文:Qt 手册。
-
关于 PyQt5 与 PySide2 该学习哪一个的问题,您可自行判断。PyQt5 的资源相对较多,而 PySide2 的 Qt 官方原生支持,它有很大的潜力,且更加 Pythonic。 ↩
-
房与图形界面编程之道 对 GUI 的剖析很精辟。 ↩