python - 将线程传递给线程对象

标签 python multithreading pyqt4 qthread

关于在 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/

相关文章:

multithreading - QMutex::lock:在线程0xfe8(主线程?)中检测到死锁

python - QTableView 未在 dataChanged 上更新

python - 我怎样才能传递给信号槽的信号在 pyqt4 中发送的参数更多?

python - 我可以在我的 PyQt4 应用程序中嵌入绘图(离线)吗?

python - 我如何使用 2 gpu 在 tensorflow 中进行计算?

C++ Boost::asio::io_service 如何在程序完成时安全销毁 io_service 资源

python - 如何从 PyPi 包中提取依赖项

multithreading - 与GCD并行进行光线追踪时,线程捕获了正在燃烧的CPU

python - 向 Bokeh 堆叠条形图添加交互

python - Scala 读取连续的 http 流