他们背后原理的差别我就不细说了(我也不知道),你只要记住使用上基本差不多就行,网上搜索他们用法的时候,以哪个为关键词搜索都行吧,官网 给出了他们的差异,聚焦我们要讲的问题,在信号与槽机制和多线程机制上,他们的差别如下:
# PyQt5 引入线程类和信号
from PyQt5.QtCore import QThread, pyqtSignal
# PySide2 引入线程类和信号
from PySide2.QtCore import QThread, Signal # 注意区别就在于这里的信号是 Signal,和 PyQt5 不一样,而线程类是一样的
信号是一个载体,装着自定义类型的数据(例如下面的object),将数据传送到绑定(connect)的函数(连接槽)中(数据作为参数传入函数)
信号与槽的基本框架如下:
signal = pyqtSignal(object) # 自定义信号传送的数据类型为 object
signal.emit(object) # 发射信号 object,每发射一次,连接槽函数就执行一次
signal.connect(custom_function) # 信号绑定的连接槽函数
# 自定义的连接槽函数,传入的参数就是信号
def custom_function(object):
pass
线程类用于实现函数并行执行,假如我在动态的画函数曲线的同时想显示已经画了多长时间,这个情况在串行下就不好实现,因为要等到函数曲线画完才能开始执行下面的函数,就不能实现同步了。
而且 PyQt5/PySide2 不支持 python 的多线程类 threading,会报错 QObject: Cannot create children for a parent that is in a different thread. 。
线程类的基本框架如下:
class NewThread(QThread):
signal = pyqtSignal(object) # 自定义信号,其中 object 为信号承载数据的类型
def __init__(self, parent=None):
super().__init__()
self.x = 0 # 线程中自定义变量
# 线程内可自定义其他函数
def custom_function(self):
pass
# new_thread = NewThread()
# 通过 new_thread.start() 调用此 run() 函数
def run(self):
self.custon_function()
self.signal.emit(self.x) # 发射信号
new_thread = NewThread()
new_thread.start() # 为线程分配资源,让它执行
# 下面两个都是停止执行,但我一般用第二个
new_thread.wait()
new_thread.terminate()
定时器顾名思义就是一个计时的东西,按照指定的时间间隔执行一次指定函数。
定时器的基本框架如下:
timer = QTimer() # 生成一个定时器
timer.timeout.connect(custom_function) # 将定时器和自定义函数绑定,每隔下面指定时间执行一次自定义函数。注意它一般在生成定时器时就指定了,不要和 start 同时调用,否则会绑定多个自定义函数,导致每隔一定时间同时执行多个绑定函数(我就是因为这个 bug 才被迫学了其他两个骚操作,奇怪现象在最后一个参考上,果然踩坑的不止我一个)
timer.start(interval) # interval 是没两次执行的时间间隔,interval 的单位是毫秒,例如 interva=1000 即间隔时间为1秒
timer.stop() # 停止定时器
下面是我结合了上面3个工具写的小玩意儿:
左边是计时器,点击“开始”按钮,然后按钮变成了“结束”,左边开始计时,右边同时画出函数图像,运行过程中点击“结束”则结束运行。其中 timer 间隔为1秒,即每隔1秒刷新一次左侧界面,输出时间值。
import sys
import pyqtgraph as pg # 画图的工具,安装方法:pip install pyqtgraph
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal, QTimer
import numpy as np
class PlotSin(QThread):
signal = pyqtSignal(object) # 定义信号,self.y 是 numpy.array,所以信号数据类型为 object
def __init__(self, parent=None):
super().__init__()
self.y = None
self.phase = 0
def sin(self):
self.x = np.arange(0, 3.0, 0.01)
self.y = np.sin(2 * np.pi * self.x + self.phase)
self.phase += 0.1
QThread.msleep(200) # 等待200毫秒
def run(self):
for _ in range(300):
self.sin()
self.signal.emit(self.y) # 向连接槽发射信号 self.y
class PlotSin_MainWindow(QDialog):
def __init__(self):
super().__init__()
self.initUI()
self.clock_time = 0
self.timer = QTimer(self) # 生成定时器
self.timer.timeout.connect(self.clock) # 绑定计时函数 self.clock
def initUI(self):
self.creatContorls("时间显示:")
self.creatResult("函数绘制:")
layout = QHBoxLayout()
layout.addWidget(self.controlsGroup)
layout.addWidget(self.resultGroup)
self.setLayout(layout)
self.beginButton.clicked.connect(self.clock_begin)
self.setGeometry(300, 300, 600, 300)
self.setWindowTitle('Plot Sine')
self.show()
def creatContorls(self,title):
self.controlsGroup = QGroupBox(title)
self.beginButton = QPushButton("开始")
numberLabel = QLabel("运行时间:")
self.clockLabel = QLabel("")
controlsLayout = QGridLayout()
controlsLayout.addWidget(numberLabel, 0, 0)
controlsLayout.addWidget(self.clockLabel, 0, 1)
controlsLayout.addWidget(self.beginButton, 3, 0)
self.controlsGroup.setLayout(controlsLayout)
def creatResult(self,title):
self.resultGroup = QGroupBox(title)
self.guiplot = pg.PlotWidget()
gridLayout = QGridLayout()
gridLayout.addWidget(self.guiplot,0,2,2,3)
self.resultGroup.setLayout(gridLayout)
def clock_begin(self):
if not self.timer.isActive():
self.recorder_thread = PlotSin()
self.recorder_thread.signal.connect(self.displaySin) # 绑定信号槽函数
self.recorder_thread.start() # 线程执行
self.clock()
self.timer.start(1000)
else:
self.beginButton.setText("开始")
self.clockLabel.setText("")
self.recorder_thread.terminate() # 终止线程
self.timer.stop() # 终止定时器
self.clock_time = 0
text = str(self.clock_time) + "s"
self.clockLabel.setText(text)
def clock(self):
text = str(self.clock_time) + "s"
self.clockLabel.setText(text)
if self.clock_time == 0:
self.beginButton.setText("结束")
self.clock_time += 1
def plotSin(self, y):
self.guiplot.clear()
self.guiplot.plot(y)
def displaySin(self, y):
self.plotSin(y)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = PlotSin_MainWindow()
window.show()
sys.exit(app.exec_())
Differences Between PySide and PyQt/zh
PySide/PyQt Tutorial: Creating Your Own Signals and Slots
Pyqt5系列(八)-自定义信号
QT信号槽机制
PyQt5进阶(二)——多线程:QThread & 事件处理
Is it possible to get an array from a thread in PyQt5?
Pyside2 - Unexpected behaviour