关于在 PyQt4 和 Python 2.7 中使用 QThread
的快速问题。我正在创建一个继承自 QObject
的进程,并将其分配给我在单独的类(也继承自 QObject
)中创建的 Qthread
。
将 QThread
对象传递给进程对象是否安全,以便我可以从进程本身内部调用 thread.msleep(mseconds)
?
我希望能够让线程等待或休眠,但我读到 time.sleep(seconds) 在与 PyQt 多线程一起使用时是不可靠的。
我确实尝试将信号从进程对象发送到主线程中的一个槽(附加到该进程对象的 thread.msleep(mseconds)
),但我发现这失败了工作;进程对象继续执行直到完成,只有在这个时间之后才执行槽。即使在调整了优先级之后,这种情况仍在继续发生。这是 Not Acceptable ,因为我希望流程循环连续运行。
还有其他建议吗?
最佳答案
我最终设法改变了我的代码以实现我在问题中所需的功能:即让线程等待或休眠指定时间的能力。
首先,我的研究似乎表明,在 Qt 中子类化 QThread
变得不明智的主要原因之一是线程不应该能够 self 管理。虽然没有关于我的问题的官方文档,但我只能推测将线程对象传递给在其上运行的进程对象也是不明智的,因为线程将再次能够直接控制自己。
我找到的解决方案是完全放弃 msleep()
。 QThread 上的 Qt 文档建议避免使用 sleep()
和 wait()
函数,因为它们不适合 Qt 的事件驱动特性。他们推荐 QTimer()
用于在超时后通过信号调用函数,代替 msleep()
。默认情况下,QTimer()
用于在每个时间间隔发送一个重复信号,但也可以使用 QTimer.singleShot()
发送一次信号。文档中还指出,从线程内调用 QSleep()
是安全的。
我只使用重复的 QTimer
多次调用单个插槽 foo()
,但在 foo()
中添加延迟, QTimer.singleShot()
可用于在设定的毫秒数后调用第二个函数 moo()
。
编辑:我已经决定包括我的线程代码,它是 QObject 和 QThread 的子类,以在每个给定时间间隔的连续循环中在线程上执行任务。据我所知,它功能齐全,但需要做一些工作。
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
# Class to be assigned to a thread.
# This should be subclassed to provide new functionality.
class GenericLoop(QtCore.QObject):
def __init__(self):
super(GenericLoop, self).__init__()
# We use this signal to tell the main thread
# when this thread is finished.
finished_Sig = QtCore.pyqtSignal()
# Default timeout is 0, i.e. do work on thread after
# other events have been dealt with
__timeout = 0
__processTimer = None
__args = None
__kwargs = None
# We use this function to set the arguments used by run(),
# if we want to change them mid-execution
@QtCore.pyqtSlot(tuple, dict)
def changeArgs(self, args, kwargs):
self.__args = args
self.__kwargs = kwargs
# We can change the timeout used to make the thread run
# at given intervals. Note that the timing is not exact,
# since this is impossible with a real time operating system
@QtCore.pyqtSlot(int)
def setTimeout(self, mseconds):
self.__timeout = int(mseconds)
# Call either a singleShot QTimer (one execution),
# or a normal QTimer (repeated), to start the loop
@QtCore.pyqtSlot(bool, tuple, dict)
def startTimer(self, singleShot, args, kwargs):
self.__processTimer = QtCore.QTimer()
# We can't pass args and kwargs directly because QTimer.timeout
# emits a signal with allowing no contained variables
# so we copy args and kwargs to local variables instead
self.changeArgs(args, kwargs)
if singleShot:
self.__processTimer.singleShot(self.__timeout, self.callRun)
else:
self.__processTimer.timeout.connect(self.callRun)
self.__processTimer.start(self.__timeout)
# Call finish from within subclass using self.finish(), or
# from another thread using signals. finish() will stop the
# QTimer causing execution of the loop. The loop can be started again
# by calling startTimer() or stopTimer() from another thread
@QtCore.pyqtSlot()
def stopTimer(self):
if self.__processTimer.isActive():
self.__processTimer.stop()
else:
print "ERROR: stopTimer() has been called but no timer is running!"
# We call this to delete the thread.
@QtCore.pyqtSlot()
def deleteThread(self):
self.finished_Sig.emit()
# This calls run(), in order to enable the passing of
# command line arguments to the loop
@QtCore.pyqtSlot()
def callRun(self):
self.run(self.__args, self.__kwargs)
# run() can be called directly from another thread if required
@QtCore.pyqtSlot(tuple, dict)
def run(self, args, kwargs):
print "ERROR: run() has not been defined! Stopping thread..."
self.stopTimer()
# Class for creating threads
class GenericThread(QtCore.QObject):
# Private variables include the thread.
__sendArguments_Sig = QtCore.pyqtSignal(tuple, dict)
__startTimer_Sig = QtCore.pyqtSignal(int, tuple, dict)
__setTimeout_Sig = QtCore.pyqtSignal(int)
__obj = None
__finished_Sig = None
__thread = QtCore.QThread()
# Object to be threaded must be specified when
# creating a GenericThread object
def __init__(self, obj):
super(GenericThread, self).__init__()
self.__obj = obj
self.moreInit()
# Set up object on thread
def moreInit(self):
self.__thread = QtCore.QThread()
self.__obj.moveToThread(self.__thread)
# Allows thread to delete itself when done
self.__obj.finished_Sig.connect(self.__thread.deleteLater)
self.__sendArguments_Sig.connect(self.__obj.changeArgs)
self.__startTimer_Sig.connect(self.__obj.startTimer)
self.__setTimeout_Sig.connect(self.__obj.setTimeout)
self.__thread.start()
# Sets the QTimer timeout and does some checking
# to make sure that types are as they should be
def setTimeout(self, mseconds):
if mseconds >= 0 and type(mseconds) is type(int()):
self.__setTimeout_Sig.emit(mseconds)
elif mseconds < 0 and type(mseconds) is type(int()):
print "Error: timeout of below 0 ms specified."
else:
print "Error: timeout period is specified with a type other than int."
# Starts a function in the thread via signals, and can pass
# it arguments if required. Function executes until QTimer is stopped
def startLoop(self, *args, **kwargs):
if (self.__thread == None):
print "ERROR: Thread has been deleted!"
else:
self.__startTimer_Sig.emit(False, args, kwargs)
# Starts a function in the thread via signals, once
def startOnce(self, *args, **kwargs):
if (self.__thread == None):
print "ERROR: Thread has been deleted!"
else:
self.__startTimer_Sig.emit(True, args, kwargs)
# Calls a very simple GUI just to show that the program is responsive
class GUIBox(QtGui.QWidget):
def __init__(self):
super(GUIBox, self).__init__()
self.initUI()
def initUI(self):
self.resize(250, 150)
self.setWindowTitle('Threading!')
self.show()
# Subclass GenericLoop to reimplement run and such.
class SubClassedLoop(GenericLoop):
def __init__(self):
super(SubClassedLoop, self).__init__()
__i = 0
@QtCore.pyqtSlot(tuple, dict)
def run(self, args, kwargs):
if self.__i>=50:
self.stopTimer()
return
print self.__i, args
self.__i += 1
app = QtGui.QApplication(sys.argv)
ex = GUIBox()
# Create 3 worker objects to do the actual calculation
worker1 = SubClassedLoop()
worker2 = SubClassedLoop()
worker3 = SubClassedLoop()
# Create 3 thread managing objects to do the thread control
thread1 = GenericThread(worker1)
thread2 = GenericThread(worker2)
thread3 = GenericThread(worker3)
# Set the threads to execute as soon as there is no work to do
thread1.setTimeout(125)
thread2.setTimeout(125)
thread3.setTimeout(125)
# Start threads
thread1.startLoop(1)
thread2.startLoop(2)
thread3.startLoop(3)
# Quit the program when the GUI window is closed
sys.exit( app.exec_() )
关于python - 将线程传递给线程对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24013910/