Python PyQt中QTimer vs timer的使用

Python PyQt中QTimer vs timer的使用,python,pyqt,qtimer,Python,Pyqt,Qtimer,我正试图在GUI应用程序中了解线程如何在PyQt和Qtimer中工作。对于这样一个基础性的问题,我深表歉意,但我已经搜索了其他类似的问题,但没有找到有用的答案 我试图从一系列传感器收集数据,并根据结果处理控制结构的变化。此操作不需要用户交互。但是,有一个种类繁多的控制面板和一系列状态指示器,用于在运行时显示。我在其他问题和示例中注意到,使用normal time.sleep()函数的人通常被指示使用QTimer 在进一步的挖掘中,我发现了附带的代码示例,它使用time.sleep()工作得很好。

我正试图在GUI应用程序中了解线程如何在PyQt和Qtimer中工作。对于这样一个基础性的问题,我深表歉意,但我已经搜索了其他类似的问题,但没有找到有用的答案

我试图从一系列传感器收集数据,并根据结果处理控制结构的变化。此操作不需要用户交互。但是,有一个种类繁多的控制面板和一系列状态指示器,用于在运行时显示。我在其他问题和示例中注意到,使用normal time.sleep()函数的人通常被指示使用QTimer

在进一步的挖掘中,我发现了附带的代码示例,它使用time.sleep()工作得很好。作为一个对象练习,我试图用各种各样的QTimer调用来代替它,但没有一个是有效的

我还发现有参考文献指出,QTimer只能在QCoreApplication或类似应用程序内部工作。但是我们首先使用Qtimer的原因是QApplication正在运行以支持GUI;没有它,我们无法访问任何小部件。如果没有GUI,我们就不需要QTimer,并且可以使用time.sleep(),对吗

我错过了什么?为什么这段代码似乎与time.sleep()配合得很好,而许多其他示例似乎需要QTimer呢?如果使用了QTimer,那么在QApplication中该如何实现

#!/usr/bin/python3
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import RPi.GPIO as GPIO
import time
import datetime
import sys

class QCustomThread (QThread):
    startLoad    = pyqtSignal(int)
    progressLoad = pyqtSignal(int)
    statusLoad   = pyqtSignal(bool)

    def __init__ (self, parentQWidget = None):
        super(QCustomThread, self).__init__(parentQWidget)
        self.wasCanceled = False

    def run (self):
        # Simulate data load estimation
        numberOfprogress = 100
        self.startLoad.emit(numberOfprogress)
        for progress in range(numberOfprogress + 1):
            # Delay
            time.sleep(0.1)
            if not self.wasCanceled:
                self.progressLoad.emit(progress)
            else:
                break
        self.statusLoad.emit(True if progress == numberOfprogress else False)
        self.exit(0)

    def cancel (self):
        self.wasCanceled = True

class QCustomMainWindow (QMainWindow):
    def __init__ (self):
        super(QCustomMainWindow, self).__init__()
        # Create action with QPushButton
        self.startQPushButton = QPushButton('START')
        self.startQPushButton.released.connect(self.startWork)
        self.setCentralWidget(self.startQPushButton)
        # Create QProgressDialog
        self.loadingQProgressDialog = QProgressDialog(self)
        self.loadingQProgressDialog.setLabelText('Loading')
        self.loadingQProgressDialog.setCancelButtonText('Cancel')
        self.loadingQProgressDialog.setWindowModality(Qt.WindowModal)

    def startWork (self):
        myQCustomThread = QCustomThread(self)
        def startLoadCallBack (numberOfprogress):
            self.loadingQProgressDialog.setMinimum(0)
            self.loadingQProgressDialog.setMaximum(numberOfprogress)
            self.loadingQProgressDialog.show()
        def progressLoadCallBack (progress):
            self.loadingQProgressDialog.setValue(progress)
        def statusLoadCallBack (flag):
            print ('SUCCESSFUL') if flag else print ('FAILED')
        myQCustomThread.startLoad.connect(startLoadCallBack)
        myQCustomThread.progressLoad.connect(progressLoadCallBack)
        myQCustomThread.statusLoad.connect(statusLoadCallBack)
        self.loadingQProgressDialog.canceled.connect(myQCustomThread.cancel)
        myQCustomThread.start()

myQApplication = QApplication(sys.argv)
myQCustomMainWindow = QCustomMainWindow()
myQCustomMainWindow.show()
sys.exit(myQApplication.exec_())

您需要考虑两个重要方面:

  • time.sleep
    被阻塞,QTimer没有
  • Q*应用程序是事件驱动的,这意味着它在其事件队列中等待传入事件,如果不是非常短的时间(通常是执行插槽/函数所需的时间),则应该永远不会被阻止
在Qt主线程中使用诸如
sleep
之类的阻塞函数的问题在于,它不允许Qt应用程序处理任何事件,无论它是QApplication、QGuiApplication还是QCoreApplication

如果在主Qt线程中调用了阻塞函数(无论应用程序是否由GUI驱动),所有内容都将被阻塞并排队,直到该函数返回,包括任何信号发射和后续处理

请注意,当涉及不同的线程时,这两种方式都不起作用:如果Qt检测到发送方和接收方处于不同的线程中,信号将排队进入接收方事件队列,发送方线程立即返回
emit()

最后。如果您有一个单独的线程实际上需要等待一段时间(通常需要在for/loop周期内进行处理,以允许通过
pass
)避免无用的CPU调用),请使用
time.sleep
或QThread的sleep函数(
sleep()
秒,
msleep()
毫秒,
usleep()
用于微秒)。

如果您不需要单独的线程(这意味着所涉及的处理不会占用CPU),您可以只使用QTimer(或者,最终使用静态的
QTimer.singleShot()
)。

请记住,本机python线程与QT不兼容。一旦你使用了QT、定时器和互联网连接,所有包括另一项工作的事情都必须通过QT本身的模块来完成。原因是Qt重新定义C++中与Cpython线程不兼容的线程。我理解在主线程中不使用阻塞函数。如果我理解正确的话,您是在支持在辅助线程中使用计时器/睡眠功能(如果需要的话),它在这里工作。其他人似乎认为这些线程中也需要QTimer。然而,我也尝试在主线程中使用Qtimer,它不会等待。如果将QTimer.singleshot()或QTimer实例放置在主线程中的任何一点上,则添加带有后续start()的QTimer.singleshot()或QTimer实例都不起作用。研究这个问题后,我开始讨论QCoreApplication,它与GUI不兼容。每当需要在线程中执行定时事件而不阻止其处理时,线程中都需要QTimer,如前所述,它不是阻塞:它在创建它的线程中启动,只为该线程发出
超时
信号(调用连接的插槽)。我不理解你的评论的后半部分:QTimer可以在任何地方工作,包括主Qt线程,如果它在测试中不工作,可能是你没有正确连接它,或者你试图创建一个没有父级或引用的QTimer实例,这导致计时器被垃圾收集。