Interrupting QThread sleep

2019-07-09 23:36发布

I would like to know how to pause a QThread and then resume when I get a signal. I have read and know that I can do something like this:

def run(self):
    ...
    self.ready=False
    while not self.ready:
        self.sleep(1)
    ...
...
@QtCore.Slot()
def set_ready(self):
    self.ready = True

However, what I want to do is avoid the polling in the thread. I don't want to have to set the sleep to a short amount of time and keep checking. I want to go to sleep until I get a signal from my main thread to continue.

What I am doing in my thread is this:

(pseudo code)
with open file:
    read a block of data
    while data:
        sendThread = send the block via UDP
        read the next block of data
        while not ready or sendThread.isRunning:
            sleep(0.1)

In my main thread I have setup a QtNetwork.QUdpSocket to connect readyRead to a method to handle incoming datagrams and decode them. When it gets the response that I'm waiting for it sends a signal to the set_ready slot to tell the thread to send another datagram. I don't always know how long it will take for the other system to respond, though I will likely have some long timeout value of 30seconds or so.

Is there a way to interrupt the sleep of the thread? so I could do something like this:

sleep(30)
if not ready:
    Timeout occurred stop processing
else:
    Continue processing.

1条回答
神经病院院长
2楼-- · 2019-07-10 00:13

You can do this pretty easily using the worker pattern of using QThreads. There's an example in the QThread documentation. The exact code will be a little different depending on whether you're using PyQt or PySide (it looks like you're using PySide from your example).

One notable issue with PySide compared to PyQt is that they didn't wrap QtCore.Q_ARG, so in PyQt, where you could normally use QMetaObject.invokeMethod to call a slot (with arguments) on the Worker object from the main thread, you can't do that directly in PySide and have to create a dummy signal (ie. Main.send_signal) to connect to the slot on the worker so you can call it from the main thread.

import time
import sys
from PySide import QtCore, QtGui


class Worker(QtCore.QObject):

    send_signal = QtCore.Signal(str) # using PySide
    # QtCore.pyqtSignal(str) ## using PyQt

    # @QtCore.pyqtSlot(str)
    @QtCore.Slot(str)
    def receive_slot(self, data):
        # data could be a filepath
        # open file
        # ... do stuff
        # close file
        QtCore.QThread.sleep(1) # to simulate doing stuff
        self.send_signal.emit(data + ' success')


class Main(QtGui.QWidget):

    send_signal = QtCore.Signal(str)

    def __init__(self):
        super(Main, self).__init__()
        self.worker = Worker()
        self.thread = QtCore.QThread(self)
        self.worker.moveToThread(self.thread)
        self.worker.send_signal.connect(self.receive_slot)
        self.send_signal.connect(self.worker.receive_slot)
        self.thread.start()
        self.send_signal.emit('Start')

    @QtCore.Slot(str)
    def receive_slot(self, data):
        print 'Main: {}'.format(data)
        self.send_signal.emit('Message {}'.format(time.time()))


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    window = Main()
    window.show()
    app.exec_()
查看更多
登录 后发表回答