QTimer的timeout多函数的并行执行

最近在用Python和pyqt5编写一个界面程序,需要用到QTimer。在使用QTimer时遇到一个很有趣的问题,特此记录一下。

先将问题简化成如下所示的小程序。
先定义一个界面,界面包括一个按钮和一个文本显示框,代码如下所示(不用看):

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setMaximumSize(QtCore.QSize(200, 16777215))
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setMaximumSize(QtCore.QSize(16777215, 200))
        self.textEdit.setObjectName("textEdit")
        self.verticalLayout.addWidget(self.textEdit)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 18))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))

然后再调用这个界面类(代码也不用看):

from QTimerTest import Ui_MainWindow
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow
from PyQt5.QtCore import QTimer

class TimerTest(QMainWindow,Ui_MainWindow):
    def __init__(self,parent=None):
        super(TimerTest,self).__init__(parent)
        self.setupUi(self)
        self.timer=QTimer()
        self.Num=0
        self.pushButton.clicked.connect(self.timerStart)
    def timerStart(self):
        self.timer.timeout.connect(self.DisplayNum)
        self.textEdit.clear()
        self.Num=0
        self.timer.start(1000)
    def DisplayNum(self):
        if self.Num<10:
            self.textEdit.append(str(self.Num))
            self.Num+=1
        else:
            self.textEdit.append('Done!')
            self.timer.stop()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui=TimerTest()
    ui.show()
    sys.exit(app.exec_())

程序运行后的结果如下所示:
QTimer的timeout多函数的并行执行_第1张图片
预期想要实现的功能是:每次按下按钮后,程序会自动从0开始计数,每秒加1,并将结果显示在文本框中,直到9结束。结束时在文本框中显示“Done”。
最初的实现流程如下:
先定义QTimer计时器:

self.timer=QTimer()

按下pushButton按钮后,链接到计时器启动函数:

self.pushButton.clicked.connect(self.timerStart)

计时器启动函数内容如下:
先规定好计时器的超时链接函数self.DisplayNum。然后初始化系统:清空文本框,计数归零。接下来启动计时器,计时间隔为1000毫秒。

 def timerStart(self):
        self.timer.timeout.connect(self.DisplayNum)
        self.textEdit.clear()
        self.Num=0
        self.timer.start(1000)

DisplayNum函数内容如下所示:

def DisplayNum(self):
    if self.Num<10:
        self.textEdit.append(str(self.Num))
        self.Num+=1
    else:
        self.textEdit.append('Done!')
        self.timer.stop()

好了,程序完成,运行程序,结果如下所示:
QTimer的timeout多函数的并行执行_第2张图片
跟预期的一样,似乎没什么问题。
可是当我们再次点击pushButton这个按钮的时候,运行的结果却是这个样子滴:
QTimer的timeout多函数的并行执行_第3张图片
数字的末尾多出来一个Done!,而且计时的速度好像也变快了。。
继续点击pushButton这个按钮,会发现计时的速度越来越快,而且Done越来越多。。。
QTimer的timeout多函数的并行执行_第4张图片
这是什么原因呢?看起来很像多进程并行计时的结果。



经过各种尝试,多方排查,终于发现问题所在:
原来是self.timer.timeout.connect(self.DisplayNum)这行代码在作妖。由于我把这行代码放在pushuButton的链接函数self.timerStart()里了,这样一来,每次点击pushButton这个按钮都会执行一次代码self.timer.timeout.connect(self.DisplayNum)。而每执行一次这行代码,都会给给计时器增加一个新的链接函数,注意是增加而不是替换!原来的链接函数依然在。也就是说,如果我们点击了N次按钮,那么计时器就有N个链接函数,当计时器timeout时就会并行地执行这些函数,尽管这些其实是同一个函数。结果就是我们看到的现象了。。
当然,反过来讲,这似乎也并不是什么坏事,如果我们想要实时地并行执行某些函数的话可以考虑把他们全部链接到timeout上。

你可能感兴趣的:(Pyton语言)