Python PyQt中QTimer vs timer的使用
我正试图在GUI应用程序中了解线程如何在PyQt和Qtimer中工作。对于这样一个基础性的问题,我深表歉意,但我已经搜索了其他类似的问题,但没有找到有用的答案 我试图从一系列传感器收集数据,并根据结果处理控制结构的变化。此操作不需要用户交互。但是,有一个种类繁多的控制面板和一系列状态指示器,用于在运行时显示。我在其他问题和示例中注意到,使用normal time.sleep()函数的人通常被指示使用QTimer 在进一步的挖掘中,我发现了附带的代码示例,它使用time.sleep()工作得很好。作为一个对象练习,我试图用各种各样的QTimer调用来代替它,但没有一个是有效的 我还发现有参考文献指出,QTimer只能在QCoreApplication或类似应用程序内部工作。但是我们首先使用Qtimer的原因是QApplication正在运行以支持GUI;没有它,我们无法访问任何小部件。如果没有GUI,我们就不需要QTimer,并且可以使用time.sleep(),对吗 我错过了什么?为什么这段代码似乎与time.sleep()配合得很好,而许多其他示例似乎需要QTimer呢?如果使用了QTimer,那么在QApplication中该如何实现Python PyQt中QTimer vs timer的使用,python,pyqt,qtimer,Python,Pyqt,Qtimer,我正试图在GUI应用程序中了解线程如何在PyQt和Qtimer中工作。对于这样一个基础性的问题,我深表歉意,但我已经搜索了其他类似的问题,但没有找到有用的答案 我试图从一系列传感器收集数据,并根据结果处理控制结构的变化。此操作不需要用户交互。但是,有一个种类繁多的控制面板和一系列状态指示器,用于在运行时显示。我在其他问题和示例中注意到,使用normal time.sleep()函数的人通常被指示使用QTimer 在进一步的挖掘中,我发现了附带的代码示例,它使用time.sleep()工作得很好。
#!/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_())
您需要考虑两个重要方面:
被阻塞,QTimer没有time.sleep
- Q*应用程序是事件驱动的,这意味着它在其事件队列中等待传入事件,如果不是非常短的时间(通常是执行插槽/函数所需的时间),则应该永远不会被阻止
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实例,这导致计时器被垃圾收集。