近期笔者开发了几个基于PyQt5的小项目,也算是对PyQt5及其开发流程有了一定的了解,故想以备忘的目的记录下来,供各位读者和自己参考。
本文将以PyQt5的开发流程为线索,介绍基本PyQt5图形界面应用的基本功能和原理,并拓展相关知识,覆盖PyQt5入门所需要的各方面。其余进阶内容,读者可以借助完整的文档支持和丰富的参考资料自行探索。(有需要再查阅文档即可)
PyQt5,是跨平台图形用户界面框架Qt的Python支持包。其提供了丰富的图形控件库,与优秀的跨平台性能(包括Windows, macOS, Linux, iOS, Android)。Qt官方提供的Python支持为PySide库,但语法与PyQt基本没有差异,本文就以PyQt5为例展开。
另外,Qt提供了跨语言的通用UI开发设计器Qt Designer,可以以图形化的方式构建UI界面,并可提供预览。设计完成的文件通过PyQt5自带的工具PyUIC
转换为PyQt5代码,可被Python主逻辑直接引用。转换后的代码样例如下:
关于如何安装环境,以及转换设计文件,本文不再赘述,请读者另行搜索教程。
前端的第一部分是设计UI粗稿,需要使用Qt Designer等窗体设计器。
所有的Qt对象都是基于Object基类拓展(事实上,所有的Python类都是以object类作为缺省基类)。
打开Qt Designer,新建项目,可以看到几种模板,分别代表了三类Qt基类:
QMainWindow
:带有MenuBar
, StatusBar
, and centralwidget
的窗体。
window.setXXX
方式指定,否则相当于没有初始化,不会在窗体上显示Widget
:一般控件类,为不带任何初始控件的空白窗体。
QDialog
:顾名思义,对话框类,自定义对话框内容
FileDialog
等),可以直接调用,一般不需要自定义对话框Qt以树形结构组织对象。每一个初始对象(包括窗体)创建时,都需要一个Parent参数(可为空),用于指定其父对象。一般,父子关系用于表示容器控件的包含关系。例如,在QMainWindow
类窗体中,所有的窗体控件都包含在CenterWidget
控件中,也就以其为父对象。
一个MenuBar
的属性结构类似下图例:
在Qt Designer中,所有控件被分类与控件菜单中,拖动到窗体合适位置就会创建,也可以修改大小与属性。控件的主要属性如下:
objectName
:作为QObject的属性,为任何一个Qt对象的基本属性,缺省以控件类型与编号命名。在设计窗体时,建议同时将控件按照类型+功能的格式命名,避免产生名称混乱。enabled
: 控件是否可用,disable的控件为灰色,无法响应用户操作(但可以由代码控制)size
: 控件的尺寸,即长x宽Text
: (部分控件)控件上展示的文字StyleSheet
: 控件应用的样式表(在QSS中会使用)其余部分属性为控件特有,需要使用时请参阅手册。
布局是前端设计的另一个重要部分,使用布局,我们可以有规则地安排控件,并方便地修改及调用。布局种类如下:
在较简单的项目中,可以不使用Layout,直接按照绝对坐标安放控件。
为了使前后端分离,方便后续迭代前端布局样式、改进后端代码,降低耦合度,我们使用窗口主逻辑引用前端布局类的方式实现调用前端的功能。
一个简单的窗体程序框架如下(以QMainWindow
基类为例)
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from mainWindowUI import Ui_MainWindow
class MainWindow(QMainWindow):
def __init__(self, parent=None) -> None:
super(MainWindow, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Other Initialize Code
def someFunction(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window_instance = MainWindow()
main_window_instance.show()
sys.exit(app.exec_())
super().__init__
PyUIC
转换工具将UI设计文件转换为Python文件,引用到框架中,将self.ui
设置为该前端的一个实例,并调用setupUi
方法,初始化窗体控件QApplication
,并加入系统参数,声明一个窗体Instance,并调用show()
方法显示窗体,最后使用sys.exit
函数,在窗体关闭后退出信号与槽是Qt的核心机制。信号与槽的功能类似于VB.net的事件,后端逻辑通过事件捕获前端控件的各种状态,比如单击、鼠标进入、选项改变等。在Qt中,这种机制被信号与槽取代。
信号:由前端的各种事件触发,自定义信号由代码触发
槽:是信号的处理程序(函数),一个槽连接上信号后,每当信号被触发(emit),就会调用槽,以完成响应任务。
信号与槽通过connect
方法进行连接
一般信号类似于事件,由前端控件的状态变化触发。可以在前端设计器中连接控件之间的简单信号,大多数需要后端处理的信号,要在主逻辑类初始化时手动进行连接。
self.ui.pushButton.clicked.connect(MainWindow.someFunc)
注意:连接信号的事件不需要加括号(是信号本身),槽也不需要括号(代表函数本身)
其他信号类型参阅Qt Documentation
Qt允许自定义信号,用代码可以触发该信号,并能指定信号附加参数。
Qt5以上版本的信号需要在类中以类变量的形式提前声明:
from PyQt5.QtCore import pyqtSignal
SOME_SIGNAL = pyqtSignal()
SOME_SIGNAL_WITH_PARAM_INT = pyqtSignal(int)
使用emit
方法触发自定义信号:
SOME_SIGNAL.emit()
SOME_SIGNAL_WITH_PARAM_INT.emit(some_int)
信号的连接方法与一般信号相同,这里不再赘述。
完整的PyQt项目一般至少由三部分构成: