文章目录
- 前言
- PyQt的好处
- 从一个最简单的例子入手
- PyQt5基础
- 组件体系
- 源码结构
- Qt Designer
- 基础
- 布局
- 高级界面
- Web控件
- 多线程
- 列表
- 图形绘制
- PyQt5.QtGui
- PyQtGraph
- matplotlib和PyQt结合
- 和mplfinance结合
- 工具使用
- 打包
- 链接
前言
用户界面开发,我搞过visual C++ MFC、Delphi VCL、Java Swing/JavaFX、SWT、Electron,当然web里jQuery EasyUI、ExtJS、Dojo以及现在流行的React和Vue也算。如果把Kotlin之Jetpack和SwiftUI/UIKit加上,就琳琅满目、品类齐全了。唉,程序员这一生被技术给耽误了。
PyQt的好处
目前Python上主要的GUI开发工具包括:tkinter、PyQt、PyGTK和wxPython。PyQt最正统,和其它桌面系统相比,有开发速度快,组件丰富,生命力强的优势(受众群体大、社区支持强),当然性能也不差。
从一个最简单的例子入手
if __name__ == '__main__':
app = QApplication(sys.argv)
# main = FixBtn()
main = QPushButton("测试")
main.show()
sys.exit(app.exec_())
任何一个QWidget都能独立show(),这点和其它编程语言的GUI框架不同。一个QWidget可以设置一个layout,通过layout.addWidget()就能添加组件,然后就能独立显示。QMainWindow、QFrame、QDialog只是功能更齐全的窗口而已。
递进一步就是:
if __name__ == '__main__':
app = QApplication(sys.argv)
panel = QWidget()
layout = QHBoxLayout()
btn1 = QPushButton("按钮1")
btn2 = QPushButton("按钮2")
btn3 = QPushButton("按钮3")
layout.addWidget(btn1, 1, Qt.AlignLeft)
layout.addWidget(btn2, 2, Qt.AlignCenter)
layout.addWidget(btn3, 3, Qt.AlignRight)
panel.setLayout(layout)
panel.show()
sys.exit(app.exec_())
PyQt5基础
组件体系
源码结构
- QWidget类是所有用户界面对象的基类。窗口部件(QWidget)是用户界面的一个基本单元:它从窗口系统接收鼠标,键盘和其他事件,并且在屏幕上绘制自己。每个窗口部件都是矩形的,并且它们按Z轴顺时针排列。一个窗口部件可以把他的父窗口部件或者它前面的窗口部件盖住一部分。QMainWindow、QDialog、QFrame是三个直接继承于QWidget的容器类,三者是平级的,能独立存在。
- QMainWindow 类提供一个菜单条、锚接窗口(如工具栏)和一个状态条的主应用程序窗口。主窗口通常用在提供一个大的中央窗口部件以及周围菜单、工具条和一个状态条。QMainWindow常常被继承,因为这使的封装中央部件、菜单和工具以及窗口状态条变得容易,当用户点击菜单项或工具条按钮时,槽会被调用。基于主窗口的应用程序,默认已经有了自己的布局管理器。
- QDialog类是对话框窗口的基类。对话框窗口是主要用于短时期任务以及用户进行简要通讯的顶级窗口。QDialog可以是模态对话框也可以是非模态对话框。QDialog支持扩展性并且可以提供返回值。他们可以有默认按钮。QDialog也可以有一个QSizeGrip在它的右下方,使用setSizeGripEnable()。注意:QDialog使用父窗口部件的方法和Qt中其他类不同。对话框总是顶级窗口部件,但是如果它有一个父对象,它的默认位置就是父对象的中间。他也将和父对象共享工具条条目。
- QFrame类是有框架的窗口部件的基类。它绘制部件并且调用一个虚函数drawContents()函数来填充这个框架。这个函数是被子类重新实现的。QFrame类也可以之间创建没有任何内容的简单框架,尽管通常情况下,要用到QHBox 或QVBox,因为它们可以自动布置你放到框架的窗口部件
- QScrollArea是有滚动条的窗口,继承QFrame
- 组件的Parent。没有parent的QWidget类被认为是最上层的窗体(通常是MainWindow),由于MainWindow的一些操作生成的新窗体对象,parent都应该指向MainWindow。由于parent-child关系的存在,它保证了child窗体在主窗体被回收之时也被回收。parent作为构造函数的最后一个参数被传入,但通常情况下不必显示去指定parent对象。因为当调用局管理器时,部局管理器会自动处理这种parent-child关系。但是在一些特殊的情况下,我们必须显示的指定parent-child关系。如当生成的子类不是QWidget对象但继承了QObject对象,用作dock widgets的QWidget对象。
Qt Designer
先秦的Delphi可视化工具深入人心,现在Qt Designer也不遑多让。
每一次运行QtDesigner修改了UI后,都需要通过PyUIC转换为新的.py文件,都会覆盖掉我们自己的代码,所以一般新建一个新的文件来写入自己的代码。
pyuic5 mainwindow.ui -o MainWindow.py
pyuic5可以将ui文件转为python文件。但Qt6里似乎没有提供pyuic工具,但可以如下使用:
import sys
from PyQt6 import QtWidgets, uic
app = QtWidgets.QApplication(sys.argv)
window = uic.loadUi("mainwindow.ui")
window.show()
app.exec()
当然,安装PyQt6-tools:pip install PyQt6-tools之后就有pyuic6了。和pycharm集成:
$FileName$ -o $FileNameWithoutExtension$.py
基础
布局
常用的布局:QHBoxLayout 、QVBoxLayout、QGridLayout、QFormLayout、QSplitter。
通过设置伸缩量stretch可以调节一个layout里组件的比例。主要是两个方法addStretch()和setStretch()。
app = QApplication(sys.argv)
panel = QWidget()
layout = QHBoxLayout()
btn1 = QPushButton("按钮1")
btn2 = QPushButton("按钮2")
btn3 = QPushButton("按钮3")
# layout.addWidget(btn1, 1, Qt.AlignLeft)
# layout.addWidget(btn2, 2, Qt.AlignCenter)
# layout.addWidget(btn3, 4, Qt.AlignRight)
layout.addWidget(btn1)
layout.addWidget(btn2)
layout.addWidget(btn3)
# 设置组件占据的空间比例
layout.setStretch(0, 1)
layout.setStretch(1, 2)
layout.setStretch(2, 4)
layout.setSpacing(10)
panel.setLayout(layout)
panel.resize(600, 280)
panel.show()
sys.exit(app.exec_())
这个stretch值,既可以在addWidget()时设置,也可以是在add后调用addStretch()或setStretch()设置,效果是等同的。但是,layout.addWidget(btn1, 1, Qt.AlignLeft)和layout.addWidget(btn1, 1)是有区别的。
如果在第一个控件之前addStretch(),就可以添加一个空的占位控件,这也是布局的一个技巧。
高级界面
比较复杂的控件有:QTableView、QListView、QListWidget、QTableWidget、QTreeView
Web控件
以前是QWebView,Qt5.6 后改为QWebEngineView。通过qwebchannel.js与页面中JS进行交互。
多线程
- QTimer: 提供重复的和音效的定时器
- QThread:要使用QThread开始一个线程,可以创建它的一个子类,然后覆盖其QThread.run()函数。QThread有started和finished信号,可以为这2个信号指定槽函数。
列表
QTableWidget 继承自QTableView,主要区别是QTableView 可以使用自定义的数据模型来显示内容(先要通过setModel 来绑定数据源),而QTableWidget 只能使用标准的数据模型,并且其单元格数据是通过QTableWidgetltem 对象来实现的。
图形绘制
在PyQt中常用的图像类有4个,即QPixmap、QImage、QPicture和QBitmap。
- QPixmap是专门为绘图而设计的,在绘制图片时需要使用QPixmap;
- QImage提供了一个与硬件无关的图像表示函数,可以用于图片的像素级访问;
- QPicture是一个绘图设备类,它继承自QPainter类,可以使用QPainter的begin()函数在QPicture上绘图,使用end()函数结束绘图,使用QPicture的save()函数将QPainter所使用过的绘图指令保存到文件中;
- QBitmap是一个继承自QPixmap的简单类,它提供了1bit深度的二值图像的类,QBitmap提供的单色图像可以用来制作游标(QCursor)或者笔刷(QBrush)。
绘图库:PyQt5.QtGui,PyQtGraph,Matpoltlib、Plotly
PyQt5.QtGui
painter = QPainter(self)
painter.setPen(self.pen1)
painter.drawLine(100, 10, 500, 10)
PyQtGraph
PyQtGraph 是一个基于 PyQt 和 Numpy 构建的纯 Python 图形和 GUI 库,应用于数学、科学、等工程数据可视化应用,它的主要目标是提供用于显示数据的快速交互式图形,以及提供应用程序快速开发的工具。
matplotlib和PyQt结合
让plt在画布上画图:
self.fig = plt.figure(figsize=(8, 5))
self.canvas = FigureCanvas(self.fig)
self.chartlayout.addWidget(self.canvas)
ax = self.fig.add_subplot(111)
ax = df.plot(data.......)
self.canvas.draw()
和mplfinance结合
mplfinance是一个画K线图的库。
import mplfinance as mpf
fig, axlist = mpf.plot(ohlcv_dataframe, figratio=(8, 5), returnfig=True)
canvas = FigureCanvas(fig)
chartlayout.addWidget(canvas)
canvas.draw()
工具使用
Qt Designer生成的.ui文件(实质上是XML格式的文件)通过pyuic5工具转换成.py文件。
打包
PyInstaller,加-F参数可以打成一个单一的文件。然后解开,根据需要裁剪瘦身。
python3.8是最后一个支持32位程序的版本,其runtime为最小,如果程序没有加载大数据的需求,建议采用3.8作为rumtime。
链接
- Plotly : Low-Code Python Data Apps
- PythonPlotlyCodes: 《Python 数据分析:基于 Plotly 的动态可视化绘图》 源代码