微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在PyQt中使用QTimer vs Timer

如何解决在PyQt中使用QTimer vs Timer

我正在设法在GUI应用程序中用Qtimer在PyQt中使用线程进行工作。对于这样一个基础性的问题表示歉意,但是我搜索了其他类似的问题,但未能找到翔实的答案。

我正在寻求从一系列传感器中收集数据,并根据结果处理对控制结构的更改。不需要用户交互。但是,有一个控制面板和一系列状态指示器,旨在在其运行时显示。我在其他问题和示例中指出,那些使用常规time.sleep()函数的对象通常被定向为使用QTimer。

在进一步的挖掘中,我发现使用time.sleep()可以正常工作的附加代码示例。作为一个对象练习,我试图用各种QTimer调用来代替它,但都无效。

我还发现了一些引用,指出QTimer只能在QCoreApplication或类似的内部运行。但是首先使用Qtimer的原因是QApplication正在运行以支持GUI。没有它,我们将无法访问任何小部件。而且如果没有GUI,我们将不需要QTimer并可以使用time.sleep(),对吗?

在这里想念什么?当很多其他示例似乎需要QTimer时,为什么这段代码似乎可以与time.sleep()一起正常工作?如果使用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 blocking ,QTimer是 not
  • Q * Application是事件驱动的,这意味着它等待事件队列中的传入事件,并且从不应被阻止,如果不是 really 少量时间(通常是执行插槽/功能所需的时间)。

在Qt主线程中使用诸如sleep之类的阻塞函数的问题是,它不允许Qt应用程序处理 any 事件,无论它是QApplication还是QGuiApplication或QCoreApplication。

如果在主Qt线程中调用了阻止功能(无论应用程序是否由GUI驱动),所有都将被阻止并排队,直到该功能返回为止,包括 any 信号发射和后续处理。

请注意,当涉及不同的线程时,不是有两种工作方式:如果Qt检测到发送方和接收方在不同的线程中,则将信号排队到接收方事件队列中,并且发送者线程会立即返回emit()

最后。如果您有一个实际上实际上需要等待一段时间的线程(通常需要在for / loop周期内才能进行处理并避免使用pass进行不必要的CPU调用),请使用{{1} }或QThread的睡眠功能({time.sleep代表秒,sleep()代表毫秒,msleep()代表微秒)。
如果不需要单独的线程(这意味着所涉及的处理不会占用CPU),则可以使用QTimer(或最终使用静态usleep())。

,

永远记住,本机python线程与QT不兼容。一旦使用了QT,计时器和Internet连接,包括其他工作在内的所有内容都必须通过QT本身的模块来完成。原因是QT在C ++中重新定义了与CPython线程不兼容的线程。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。