Waiting for a PyQt/PySide.QtCore.QThread to finish before doing something

I have a data acquisition thread which samples and processes data which it then emits as a signal to a receiver.

Now, when that thread is stopped, how can I ensure it has finished the current loop and emitted its signal before proceeding (and e.g. emitting a summary signal)?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import sys
import time
from PySide6.QtCore import Signal, Slot
from PySide6 import QtCore
from PySide6 import QtWidgets
##==============================================================================
class EmitterClassThreaded(QtCore.QThread):
## Define a signal that emits a dictionary
data_signal = Signal(dict)
##--------------------------------------------------------------------------
def __init__(self):
super().__init__()
self.counter = 0
self.t_start = time.time()
self.running = True
## Connect the signal to a method within the same class
self.data_signal.connect(self.handle_data)
##--------------------------------------------------------------------------
def run(self):
while self.running:
self.counter += 1
now = time.time() - self.t_start
data = {'counter': self.counter, 'timestamp': f"{now:.1f}"}
time.sleep(1) # <------ doing something here which takes time
self.data_signal.emit(data)
##--------------------------------------------------------------------------
def stop(self):
self.running = False
##--------------------------------------------------------------------------
@Slot(dict)
def handle_data(self, data):
print(f"EmitterClassThreaded received data: {data}")
##==============================================================================
class ReceiverClass():
def __init__(self):
super().__init__()
##--------------------------------------------------------------------------
@Slot(dict)
def handle_data(self, data):
print(f"ReceiverClass received data: {data}")
##==============================================================================
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Example ThreadedEmitter-Receiver")
self.setGeometry(100, 100, 400, 200)
self.label = QtWidgets.QLabel("Waiting for signal...", self)
self.label.move(150, 80)
self.stop_button = QtWidgets.QPushButton("Stop Emitter", self)
self.stop_button.move(150, 120)
self.stop_button.clicked.connect(self.stop_emitter)
self.emitter = EmitterClassThreaded()
self.emitter.data_signal.connect(self.handle_data)
self.receiver = ReceiverClass()
## Connect the signal from EmitterClass to the method in ReceiverClass
self.emitter.data_signal.connect(self.receiver.handle_data)
## Start the emitter thread
self.emitter.start()
self.emitter.running = True
##--------------------------------------------------------------------------
@Slot(dict)
def handle_data(self, data):
self.label.setText(f"Counter: {data['counter']}nTimestamp: {data['timestamp']}")
##--------------------------------------------------------------------------
def stop_emitter(self):
print("ReceiverClass: Stopping the emitter thread...")
self.emitter.stop()
## TODO: Wait for the thread to finish (incl. emitting the last signal) before proceeding
print("Creating own data to emit.")
self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})
##******************************************************************************
##******************************************************************************
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
</code>
<code>import sys import time from PySide6.QtCore import Signal, Slot from PySide6 import QtCore from PySide6 import QtWidgets ##============================================================================== class EmitterClassThreaded(QtCore.QThread): ## Define a signal that emits a dictionary data_signal = Signal(dict) ##-------------------------------------------------------------------------- def __init__(self): super().__init__() self.counter = 0 self.t_start = time.time() self.running = True ## Connect the signal to a method within the same class self.data_signal.connect(self.handle_data) ##-------------------------------------------------------------------------- def run(self): while self.running: self.counter += 1 now = time.time() - self.t_start data = {'counter': self.counter, 'timestamp': f"{now:.1f}"} time.sleep(1) # <------ doing something here which takes time self.data_signal.emit(data) ##-------------------------------------------------------------------------- def stop(self): self.running = False ##-------------------------------------------------------------------------- @Slot(dict) def handle_data(self, data): print(f"EmitterClassThreaded received data: {data}") ##============================================================================== class ReceiverClass(): def __init__(self): super().__init__() ##-------------------------------------------------------------------------- @Slot(dict) def handle_data(self, data): print(f"ReceiverClass received data: {data}") ##============================================================================== class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Example ThreadedEmitter-Receiver") self.setGeometry(100, 100, 400, 200) self.label = QtWidgets.QLabel("Waiting for signal...", self) self.label.move(150, 80) self.stop_button = QtWidgets.QPushButton("Stop Emitter", self) self.stop_button.move(150, 120) self.stop_button.clicked.connect(self.stop_emitter) self.emitter = EmitterClassThreaded() self.emitter.data_signal.connect(self.handle_data) self.receiver = ReceiverClass() ## Connect the signal from EmitterClass to the method in ReceiverClass self.emitter.data_signal.connect(self.receiver.handle_data) ## Start the emitter thread self.emitter.start() self.emitter.running = True ##-------------------------------------------------------------------------- @Slot(dict) def handle_data(self, data): self.label.setText(f"Counter: {data['counter']}nTimestamp: {data['timestamp']}") ##-------------------------------------------------------------------------- def stop_emitter(self): print("ReceiverClass: Stopping the emitter thread...") self.emitter.stop() ## TODO: Wait for the thread to finish (incl. emitting the last signal) before proceeding print("Creating own data to emit.") self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0}) ##****************************************************************************** ##****************************************************************************** if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) </code>
import sys
import time

from PySide6.QtCore import Signal, Slot
from PySide6 import QtCore
from PySide6 import QtWidgets


##==============================================================================
class EmitterClassThreaded(QtCore.QThread):
    ## Define a signal that emits a dictionary
    data_signal = Signal(dict)
    
    ##--------------------------------------------------------------------------
    def __init__(self):
        super().__init__()
        self.counter = 0
        self.t_start = time.time()
        self.running = True

        ## Connect the signal to a method within the same class
        self.data_signal.connect(self.handle_data)

    ##--------------------------------------------------------------------------
    def run(self):
        while self.running:
            self.counter += 1
            now = time.time() - self.t_start
            data = {'counter': self.counter, 'timestamp': f"{now:.1f}"}
            time.sleep(1)  # <------ doing something here which takes time
            self.data_signal.emit(data)

    ##--------------------------------------------------------------------------
    def stop(self):
        self.running = False

    ##--------------------------------------------------------------------------
    @Slot(dict)
    def handle_data(self, data):
        print(f"EmitterClassThreaded received data: {data}")


##==============================================================================
class ReceiverClass():
    def __init__(self):
        super().__init__()

    ##--------------------------------------------------------------------------
    @Slot(dict)
    def handle_data(self, data):
        print(f"ReceiverClass received data: {data}")


##==============================================================================
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Example ThreadedEmitter-Receiver")
        self.setGeometry(100, 100, 400, 200)

        self.label = QtWidgets.QLabel("Waiting for signal...", self)
        self.label.move(150, 80)

        self.stop_button = QtWidgets.QPushButton("Stop Emitter", self)
        self.stop_button.move(150, 120)
        self.stop_button.clicked.connect(self.stop_emitter)

        self.emitter = EmitterClassThreaded()
        self.emitter.data_signal.connect(self.handle_data)

        self.receiver = ReceiverClass()

        ## Connect the signal from EmitterClass to the method in ReceiverClass
        self.emitter.data_signal.connect(self.receiver.handle_data)

        ## Start the emitter thread
        self.emitter.start()
        self.emitter.running = True

    ##--------------------------------------------------------------------------
    @Slot(dict)
    def handle_data(self, data):
        self.label.setText(f"Counter: {data['counter']}nTimestamp: {data['timestamp']}")

    ##--------------------------------------------------------------------------
    def stop_emitter(self):
        print("ReceiverClass: Stopping the emitter thread...")
        self.emitter.stop()
        ## TODO: Wait for the thread to finish (incl. emitting the last signal) before proceeding
        print("Creating own data to emit.")
        self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})

##******************************************************************************
##******************************************************************************
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

In my current example, the last signal from the thread always overwrites that summary signal.
Thanks in advance!

5

The problem is caused by the fact that you’re emitting the signal from another object, and, more specifically, from another thread.

In general, it’s normally preferred to emit signals directly from “within” their object, and emitting them externally is generally discouraged (but not forbidden nor completely wrong in principle).

Note, though, that it’s also important to be aware of the thread from which the signal is emitted.

For instance, trying to do the following will not solve the problem:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class EmitterClassThreaded(QtCore.QThread):
...
def stop(self):
self.running = False
self.data_signal.emit({'counter': -999, 'timestamp': 0})
</code>
<code>class EmitterClassThreaded(QtCore.QThread): ... def stop(self): self.running = False self.data_signal.emit({'counter': -999, 'timestamp': 0}) </code>
class EmitterClassThreaded(QtCore.QThread):
    ...
    def stop(self):
        self.running = False
        self.data_signal.emit({'counter': -999, 'timestamp': 0})

That code won’t change anything, because stop() is being directly called from the main thread, and the fact that stop() is a member of the QThread instance is irrelevant.
Remember that QThreads are objects that manage execution OS threads, they are not “the thread”: directly calling someMethod() on a QThread instance will not cause that method to be executed in the related thread.

As you correctly assumed, when you emit the signal from the main thread, the other thread is still running (doing whatever you simulated with time.sleep()), therefore a further signal from the thread will be emitted afterwards.


Depending on the situations, many alternatives exist.

Use the finished signal

The simpler solution is to make use of the finished signal that QThread provides. That signal is always emitted once the thread is finished, even when not using QThread’s own event loop by overriding run().

A possible approach, then, could be this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class EmitterClassThreaded(QtCore.QThread):
data_signal = Signal(dict)
def __init__(self):
...
self.finished.connect(self.emitFinal)
def emitFinal(self):
self.data_signal.emit({'counter': -999, 'timestamp': 0})
...
</code>
<code>class EmitterClassThreaded(QtCore.QThread): data_signal = Signal(dict) def __init__(self): ... self.finished.connect(self.emitFinal) def emitFinal(self): self.data_signal.emit({'counter': -999, 'timestamp': 0}) ... </code>
class EmitterClassThreaded(QtCore.QThread):
    data_signal = Signal(dict)
    def __init__(self):
        ...
        self.finished.connect(self.emitFinal)

    def emitFinal(self):
        self.data_signal.emit({'counter': -999, 'timestamp': 0})

    ...

Emit the signal only if the thread must continue

You could change the logic of the while loop by checking the running flag before trying to emit the signal:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> def run(self):
while True:
self.counter += 1
now = time.time() - self.t_start
data = {'counter': self.counter, 'timestamp': "{:.1f}".format(now)}
time.sleep(1) # <------ doing something here which takes time
if self.running:
self.data_signal.emit(data)
else:
self.data_signal.emit({'counter': -999, 'timestamp': 0})
break
</code>
<code> def run(self): while True: self.counter += 1 now = time.time() - self.t_start data = {'counter': self.counter, 'timestamp': "{:.1f}".format(now)} time.sleep(1) # <------ doing something here which takes time if self.running: self.data_signal.emit(data) else: self.data_signal.emit({'counter': -999, 'timestamp': 0}) break </code>
    def run(self):
        while True:
            self.counter += 1
            now = time.time() - self.t_start
            data = {'counter': self.counter, 'timestamp': "{:.1f}".format(now)}
            time.sleep(1)  # <------ doing something here which takes time
            if self.running:
                self.data_signal.emit(data)
            else:
                self.data_signal.emit({'counter': -999, 'timestamp': 0})
                break

In case you still need the last computed data, you may emit that in any case, and eventually exit the loop after emitting the “final” signal:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> ...
self.data_signal.emit(data)
if not self.running:
self.data_signal.emit({'counter': -999, 'timestamp': 0})
break
</code>
<code> ... self.data_signal.emit(data) if not self.running: self.data_signal.emit({'counter': -999, 'timestamp': 0}) break </code>
            ...
            self.data_signal.emit(data)                
            if not self.running:
                self.data_signal.emit({'counter': -999, 'timestamp': 0})
                break

Alternatively, since self.counter is a simple reference to an integer (and, therefore, thread safe), you may even change the above just by checking the self.counter value to -999 and check whether self.counter < 0:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class EmitterClassThreaded(QtCore.QThread):
data_signal = Signal(dict)
def __init__(self):
super().__init__()
self.counter = 0
self.t_start = time.time()
# no self.running
def run(self):
while True:
self.counter += 1
now = time.time() - self.t_start
data = {'counter': self.counter, 'timestamp': "{:.1f}".format(now)}
time.sleep(1) # <------ doing something here which takes time
self.data_signal.emit(data)
if self.counter < 0:
self.data_signal.emit({'counter': -999, 'timestamp': 0})
break
def stop(self):
self.counter = -999
...
</code>
<code>class EmitterClassThreaded(QtCore.QThread): data_signal = Signal(dict) def __init__(self): super().__init__() self.counter = 0 self.t_start = time.time() # no self.running def run(self): while True: self.counter += 1 now = time.time() - self.t_start data = {'counter': self.counter, 'timestamp': "{:.1f}".format(now)} time.sleep(1) # <------ doing something here which takes time self.data_signal.emit(data) if self.counter < 0: self.data_signal.emit({'counter': -999, 'timestamp': 0}) break def stop(self): self.counter = -999 ... </code>
class EmitterClassThreaded(QtCore.QThread):
    data_signal = Signal(dict)
    def __init__(self):
        super().__init__()
        self.counter = 0
        self.t_start = time.time()
        # no self.running

    def run(self):
        while True:
            self.counter += 1
            now = time.time() - self.t_start
            data = {'counter': self.counter, 'timestamp': "{:.1f}".format(now)}
            time.sleep(1)  # <------ doing something here which takes time
            self.data_signal.emit(data)                
            if self.counter < 0:
                self.data_signal.emit({'counter': -999, 'timestamp': 0})
                break

    def stop(self):
        self.counter = -999

    ...

Use wait() after trying to “stop” the thread

In some cases, it may be necessary to completely block everything until the thread is done, which can be achieved by using QThread.wait().

Note that the documentation says that it “Blocks the thread until […]”. In this case “the thread” is the calling thread; consider the following change:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> def stop_emitter(self):
print("ReceiverClass: Stopping the emitter thread...")
self.emitter.stop()
self.emitter.wait()
print("Creating own data to emit.")
self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})
</code>
<code> def stop_emitter(self): print("ReceiverClass: Stopping the emitter thread...") self.emitter.stop() self.emitter.wait() print("Creating own data to emit.") self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0}) </code>
    def stop_emitter(self):
        print("ReceiverClass: Stopping the emitter thread...")
        self.emitter.stop()

        self.emitter.wait()

        print("Creating own data to emit.")
        self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})

This is perfectly valid, in theory, because it works similarly to Python’s thread.join(). Unfortunately, it’s also discouraged in a case like this, because its blocking nature means that calling it in the main thread will block the main event loop, resulting in UI freeze until the thread has finished.

A possible alternative would be to use wait() with a small interval and ensure that the main app processEvents() is called:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> def stop_emitter(self):
print("ReceiverClass: Stopping the emitter thread...")
self.emitter.stop()
while not self.emitter.wait(10):
QApplication.processEvents()
QApplication.processEvents()
print("Creating own data to emit.")
self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})
</code>
<code> def stop_emitter(self): print("ReceiverClass: Stopping the emitter thread...") self.emitter.stop() while not self.emitter.wait(10): QApplication.processEvents() QApplication.processEvents() print("Creating own data to emit.") self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0}) </code>
    def stop_emitter(self):
        print("ReceiverClass: Stopping the emitter thread...")
        self.emitter.stop()

        while not self.emitter.wait(10):
            QApplication.processEvents()

        QApplication.processEvents()

        print("Creating own data to emit.")
        self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})

Note that the further call to processEvents outside of the loop is necessary, because there will still be pending events, most importantly, the last signal from the exiting thread (which has been queued).

Use terminate() (no, don’t)

Unlike Python, QThread provides the terminate() function, so you could add self.emitter.terminate() right after self.emitter.stop().

In reality, killing threads is considered a bad pattern, and highly discouraged in any language.

You may try to use it, at your own risk, but only IF you have deep understanding of how threading works in all involved systems and possible permutations (including hardware and software aspects), and full awareness of the objects used in the actual execution within the thread.

That’s a huge “if”: if you’re here because you’re asking yourself if you could use terminate(), then it most certainly means that you should not, because that’s one choice you can only take if your experiences tell you that you are fully aware that it is the case of using it (and if you are, you probably wouldn’t be reading this while looking for suggestions).

So: no, do not use terminate().

Define and understand what is the actual task done in the thread

It’s important to note that threads have important limitations, especially when dealing with Python (see Global Interpreter Lock).

Simply put, if whatever is done within the run() override (or any function connected to the started signal of the thread) is purely CPU bound, there is fundamentally no benefit in using threading: it just introduces further complications, and does not provide actual concurrency.

The only cases in which threading makes sense is when using IO (eg: file read/write, network access, etc.) or calls that do not require waiting for their results (but still need to be executed in separate threads).

If what you do within run() is a long and heavy computation, then you only have two options:

  • if it is or can be “broken” into smaller parts (eg. a long for loop), then ensure that sleep calls are frequently added at small intervals;
  • use multiprocessing;

Note that, in the first case, this can still be achieved without using threading (just replace sleep with QApplication.processEvents()).

To clarify, consider the following example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class EmitterClassThreaded(QtCore.QThread):
...
def run(self):
while self.running:
self.counter += 1
now = time.time() - self.t_start
data = {'counter': self.counter, 'timestamp': f"{now:.1f}"}
for i in range(1000):
# some relatively long and complex computation
time.sleep(.01) # temporarily release control to other threads
self.data_signal.emit(data)
</code>
<code>class EmitterClassThreaded(QtCore.QThread): ... def run(self): while self.running: self.counter += 1 now = time.time() - self.t_start data = {'counter': self.counter, 'timestamp': f"{now:.1f}"} for i in range(1000): # some relatively long and complex computation time.sleep(.01) # temporarily release control to other threads self.data_signal.emit(data) </code>
class EmitterClassThreaded(QtCore.QThread):
    ...
    def run(self):
        while self.running:
            self.counter += 1
            now = time.time() - self.t_start
            data = {'counter': self.counter, 'timestamp': f"{now:.1f}"}

            for i in range(1000):
                # some relatively long and complex computation
                time.sleep(.01) # temporarily release control to other threads
            self.data_signal.emit(data)

In this case, while perfectly reasonable, you’re not actually using advantages threading could provide, it’s just a different code structure that “coincidentally uses” threading, but without real benefit.

The same could be achieved with a simple class (without threading), that calls QApplication.processEvents() instead of time.sleep(.01). If properly written, it could even be more efficient, because it wouldn’t need to always wait that interval if the main event loop doesn’t have queued events that require processing.

Unrelated considerations about decorators

The PySide Slot, Property and Signal decorators (along with the pyqt* prefix based decorators in PyQt) only make sense for QObject based classes.

Those decorators should only be used in QObject subclasses (including Python object subclasses used in mix-ins with QObject based ones), otherwise they are completely useless and should be avoided to begin with:

  • the Slot (or pyqtSlot) decorator is rarely necessary, as it almost always provides very little benefits, and is only required in very specific cases (dealing with complex threading based scenarios, or when implementing QtDesigner plugins);
  • the Property (or pyqtProperty) decorator behaves like a Python property: if the class is never used as/within a QObject one, then just use @property;
  • non QObject instances cannot have Qt signals, and cannot therefore be emitted;

See this related post for further details: Why do I need to decorate connected slots with pyqtSlot?.

0

In the meantime, I’ve come across a potential solution using a finished signal. Please comment if there is anything wrong with it!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import sys
import time
from PySide6.QtCore import Signal, Slot, QEventLoop
from PySide6 import QtCore
from PySide6 import QtWidgets
##==============================================================================
class EmitterClassThreaded(QtCore.QThread):
## Define a signal that emits a dictionary
data_signal = Signal(dict)
## Define a signal for when the thread has finished
finished_signal = Signal()
##--------------------------------------------------------------------------
def __init__(self):
super().__init__()
self.counter = 0
self.t_start = time.time()
self.running = True
## Connect the signal to a method within the same class
self.data_signal.connect(self.handle_data)
##--------------------------------------------------------------------------
def run(self):
while self.running:
self.counter += 1
now = time.time() - self.t_start
data = {'counter': self.counter, 'timestamp': f"{now:.1f}"}
time.sleep(1) # <------ doing something here which takes time
self.data_signal.emit(data)
## Emit finished signal
self.finished_signal.emit()
##--------------------------------------------------------------------------
def stop(self):
self.running = False
self.wait()
##--------------------------------------------------------------------------
@Slot(dict)
def handle_data(self, data):
print(f"EmitterClassThreaded received data: {data}")
##==============================================================================
class ReceiverClass():
def __init__(self):
super().__init__()
##--------------------------------------------------------------------------
def handle_data(self, data):
print(f"ReceiverClass received data: {data}")
##==============================================================================
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Example ThreadedEmitter-Receiver")
self.setGeometry(100, 100, 400, 200)
self.label = QtWidgets.QLabel("Waiting for signal...", self)
self.label.move(150, 80)
self.stop_button = QtWidgets.QPushButton("Stop Emitter", self)
self.stop_button.move(150, 120)
self.stop_button.clicked.connect(self.stop_emitter)
self.emitter = EmitterClassThreaded()
self.emitter.data_signal.connect(self.handle_data)
self.emitter.finished_signal.connect(self.handle_finished)
self.receiver = ReceiverClass()
## Connect the signal from EmitterClass to the method in ReceiverClass
self.emitter.data_signal.connect(self.receiver.handle_data)
## Start the emitter thread
self.emitter.start()
self.emitter.running = True
##--------------------------------------------------------------------------
@Slot(dict)
def handle_data(self, data):
self.label.setText(f"Counter: {data['counter']}nTimestamp: {data['timestamp']}")
##--------------------------------------------------------------------------
def stop_emitter(self):
print("ReceiverClass: Stopping the emitter thread...")
loop = QEventLoop()
self.emitter.finished_signal.connect(loop.quit)
self.emitter.stop()
loop.exec()
##--------------------------------------------------------------------------
@Slot()
def handle_finished(self):
## NOTE: This can put into stop_emitter() method, too, after loop.exec()
print("Thread finished, creating own data to emit...")
self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})
##******************************************************************************
##******************************************************************************
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
</code>
<code>import sys import time from PySide6.QtCore import Signal, Slot, QEventLoop from PySide6 import QtCore from PySide6 import QtWidgets ##============================================================================== class EmitterClassThreaded(QtCore.QThread): ## Define a signal that emits a dictionary data_signal = Signal(dict) ## Define a signal for when the thread has finished finished_signal = Signal() ##-------------------------------------------------------------------------- def __init__(self): super().__init__() self.counter = 0 self.t_start = time.time() self.running = True ## Connect the signal to a method within the same class self.data_signal.connect(self.handle_data) ##-------------------------------------------------------------------------- def run(self): while self.running: self.counter += 1 now = time.time() - self.t_start data = {'counter': self.counter, 'timestamp': f"{now:.1f}"} time.sleep(1) # <------ doing something here which takes time self.data_signal.emit(data) ## Emit finished signal self.finished_signal.emit() ##-------------------------------------------------------------------------- def stop(self): self.running = False self.wait() ##-------------------------------------------------------------------------- @Slot(dict) def handle_data(self, data): print(f"EmitterClassThreaded received data: {data}") ##============================================================================== class ReceiverClass(): def __init__(self): super().__init__() ##-------------------------------------------------------------------------- def handle_data(self, data): print(f"ReceiverClass received data: {data}") ##============================================================================== class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Example ThreadedEmitter-Receiver") self.setGeometry(100, 100, 400, 200) self.label = QtWidgets.QLabel("Waiting for signal...", self) self.label.move(150, 80) self.stop_button = QtWidgets.QPushButton("Stop Emitter", self) self.stop_button.move(150, 120) self.stop_button.clicked.connect(self.stop_emitter) self.emitter = EmitterClassThreaded() self.emitter.data_signal.connect(self.handle_data) self.emitter.finished_signal.connect(self.handle_finished) self.receiver = ReceiverClass() ## Connect the signal from EmitterClass to the method in ReceiverClass self.emitter.data_signal.connect(self.receiver.handle_data) ## Start the emitter thread self.emitter.start() self.emitter.running = True ##-------------------------------------------------------------------------- @Slot(dict) def handle_data(self, data): self.label.setText(f"Counter: {data['counter']}nTimestamp: {data['timestamp']}") ##-------------------------------------------------------------------------- def stop_emitter(self): print("ReceiverClass: Stopping the emitter thread...") loop = QEventLoop() self.emitter.finished_signal.connect(loop.quit) self.emitter.stop() loop.exec() ##-------------------------------------------------------------------------- @Slot() def handle_finished(self): ## NOTE: This can put into stop_emitter() method, too, after loop.exec() print("Thread finished, creating own data to emit...") self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0}) ##****************************************************************************** ##****************************************************************************** if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) </code>
import sys
import time

from PySide6.QtCore import Signal, Slot, QEventLoop
from PySide6 import QtCore
from PySide6 import QtWidgets


##==============================================================================
class EmitterClassThreaded(QtCore.QThread):
    ## Define a signal that emits a dictionary
    data_signal = Signal(dict)
    ## Define a signal for when the thread has finished
    finished_signal = Signal()

    ##--------------------------------------------------------------------------
    def __init__(self):
        super().__init__()
        self.counter = 0
        self.t_start = time.time()
        self.running = True

        ## Connect the signal to a method within the same class
        self.data_signal.connect(self.handle_data)

    ##--------------------------------------------------------------------------
    def run(self):
        while self.running:
            self.counter += 1
            now = time.time() - self.t_start
            data = {'counter': self.counter, 'timestamp': f"{now:.1f}"}
            time.sleep(1)  # <------ doing something here which takes time
            self.data_signal.emit(data)

        ## Emit finished signal
        self.finished_signal.emit()

    ##--------------------------------------------------------------------------
    def stop(self):
        self.running = False
        self.wait()

    ##--------------------------------------------------------------------------
    @Slot(dict)
    def handle_data(self, data):
        print(f"EmitterClassThreaded received data: {data}")


##==============================================================================
class ReceiverClass():
    def __init__(self):
        super().__init__()

    ##--------------------------------------------------------------------------
    def handle_data(self, data):
        print(f"ReceiverClass received data: {data}")


##==============================================================================
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Example ThreadedEmitter-Receiver")
        self.setGeometry(100, 100, 400, 200)

        self.label = QtWidgets.QLabel("Waiting for signal...", self)
        self.label.move(150, 80)

        self.stop_button = QtWidgets.QPushButton("Stop Emitter", self)
        self.stop_button.move(150, 120)
        self.stop_button.clicked.connect(self.stop_emitter)

        self.emitter = EmitterClassThreaded()
        self.emitter.data_signal.connect(self.handle_data)
        self.emitter.finished_signal.connect(self.handle_finished)

        self.receiver = ReceiverClass()

        ## Connect the signal from EmitterClass to the method in ReceiverClass
        self.emitter.data_signal.connect(self.receiver.handle_data)

        ## Start the emitter thread
        self.emitter.start()
        self.emitter.running = True

    ##--------------------------------------------------------------------------
    @Slot(dict)
    def handle_data(self, data):
        self.label.setText(f"Counter: {data['counter']}nTimestamp: {data['timestamp']}")

    ##--------------------------------------------------------------------------
    def stop_emitter(self):
        print("ReceiverClass: Stopping the emitter thread...")
        loop = QEventLoop()
        self.emitter.finished_signal.connect(loop.quit)
        self.emitter.stop()
        loop.exec()

    ##--------------------------------------------------------------------------
    @Slot()
    def handle_finished(self):
        ## NOTE: This can put into stop_emitter() method, too, after loop.exec()
        print("Thread finished, creating own data to emit...")
        self.emitter.data_signal.emit({'counter': -999, 'timestamp': 0})


##******************************************************************************
##******************************************************************************
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật