PyQt/PySide2 中的 信号与槽 (pyqtSignal/Signal) 、多线程 (QThread) 和 定时器 (Timer)

写在前面

PyQt5 和 PySide2 的区别

他们背后原理的差别我就不细说了(我也不知道),你只要记住使用上基本差不多就行,网上搜索他们用法的时候,以哪个为关键词搜索都行吧,官网 给出了他们的差异,聚焦我们要讲的问题,在信号与槽机制和多线程机制上,他们的差别如下:


from PyQt5.QtCore import QThread, pyqtSignal


from PySide2.QtCore import QThread, Signal 
  • 1
  • 2
  • 3
  • 4
  • 5

信号与槽

信号是一个载体,装着自定义类型的数据(例如下面的object),将数据传送到绑定(connect)的函数(连接槽)中(数据作为参数传入函数)

信号与槽的基本框架如下:

signal = pyqtSignal(object) 

signal.emit(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) 

    def __init__(self, parent=None):
        super().__init__()
        self.x = 0 

    
    def custom_function(self):
		pass
    
    
    
    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) 

timer.start(interval) 

timer.stop() 

示例

下面是我结合了上面3个工具写的小玩意儿:

在这里插入图片描述

左边是计时器,点击“开始”按钮,然后按钮变成了“结束”,左边开始计时,右边同时画出函数图像,运行过程中点击“结束”则结束运行。其中 timer 间隔为1秒,即每隔1秒刷新一次左侧界面,输出时间值。

完整示例代码

import sys
import pyqtgraph as pg 
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) 

    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) 

    def run(self):
        for _ in range(300):
            self.sin()
            self.signal.emit(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) 

    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_())