Issue
I'm having trouble making slots receiving signals after a worker class has been moveToThread
.
The goal is that the slots inside the worker will be executed on another thread.
This is my test worker class:
class DbWorker(QObject):
def __init__(self):
super().__init__()
logger.info(f"{threading.get_ident()}: DbWorker instantiated...")
@Slot(str)
def test_slot_str(self, test_text: str):
logger.info(f"{threading.get_ident()}: test_slot_str fired. Got Message '{test_text}'")
@Slot()
def test_slot_empty(self):
logger.info(f"{threading.get_ident()}: test_slot_empty fired ")
@Slot()
def process(self):
logger.info(f"{threading.get_ident()}: Process slot activated!")
When I execute this:
# Signals must inherit QObject
class Communicate(QObject):
signal_str = Signal(str)
signal_empty = Signal()
def moveToThreadTest():
logger.info(f"{threading.get_ident()}: main started...")
worker = DbWorker()
communicate = Communicate()
my_thread = QThread()
worker.moveToThread(my_thread)
communicate.signal_str.connect(worker.test_slot_str)
communicate.signal_empty.connect(worker.test_slot_empty)
my_thread.started.connect(worker.process)
my_thread.start()
communicate.signal_str.emit("test")
communicate.signal_empty.emit()
logger.info(f"{threading.get_ident()}: main thread end...")
The output I'm getting is output:
- INFO [db_worker.py:35 - moveToThreadTest() ] 13824: main started...
- INFO [db_worker.py:12 - __init__() ] 13824: DbWorker instantiated...
- INFO [db_worker.py:48 - moveToThreadTest() ] 13824: main thread end...
- INFO [db_worker.py:25 - process() ] 20776: Process slot activated!
So the slot that's connected to my_thread.started
manages to execute from another thread. But none o the other slots do the same. Also, it's worth pointing out that the last line is printed only if I run this code without debugging. If I run in debug mode, I get this:
- INFO [db_worker.py:35 - moveToThreadTest() ] 25128: main started...
- INFO [db_worker.py:12 - __init__() ] 25128: DbWorker instantiated...
- INFO [db_worker.py:48 - moveToThreadTest() ] 25128: main thread end...
I tried to see if I connect the signals to slots in a correct way so I ran the same example without moving the worker to a thread like so:
def withoutMoveToThread():
logger.info(f"{threading.get_ident()}: main started...")
communicate = Communicate()
worker = DbWorker()
communicate.signal_str.connect(worker.test_slot_str)
communicate.signal_empty.connect(worker.test_slot_empty)
communicate.signal_str.emit("Signal str")
communicate.signal_empty.emit()
logger.info(f"{threading.get_ident()}: main finished...")
And it seems to work just fine:
- INFO [db_worker.py:52 - withoutMoveToThread() ] 6052: main started...
- INFO [db_worker.py:12 - __init__() ] 6052: DbWorker instantiated...
- INFO [db_worker.py:16 - test_slot_str() ] 6052: test_slot_str fired. Got Message 'Signal str'
- INFO [db_worker.py:20 - test_slot_empty() ] 6052: test_slot_empty fired
- INFO [db_worker.py:59 - withoutMoveToThread() ] 6052: main finished...
I don't understand what I'm doing wrong and why are my slots not receiving the connected signals. Please help me understand what I'm doing wrong.
Here is the full example file:
import threading
from PySide2 import QtCore
from PySide2.QtCore import QObject, Slot, Signal, QThread
from logger import logger
class DbWorker(QObject):
def __init__(self):
super().__init__()
logger.info(f"{threading.get_ident()}: DbWorker instantiated...")
@Slot(str)
def test_slot_str(self, test_text: str):
logger.info(f"{threading.get_ident()}: test_slot_str fired. Got Message '{test_text}'")
@Slot()
def test_slot_empty(self):
logger.info(f"{threading.get_ident()}: test_slot_empty fired ")
@Slot()
def process(self):
# print("Process Activated!")
logger.info(f"{threading.get_ident()}: Process slot activated!")
# Signals must inherit QObject
class Communicate(QObject):
signal_str = Signal(str)
signal_empty = Signal()
def moveToThreadTest():
logger.info(f"{threading.get_ident()}: main started...")
worker = DbWorker()
communicate = Communicate()
my_thread = QThread()
worker.moveToThread(my_thread)
communicate.signal_str.connect(worker.test_slot_str)
communicate.signal_empty.connect(worker.test_slot_empty)
my_thread.started.connect(worker.process)
my_thread.start()
communicate.signal_str.emit("test")
communicate.signal_empty.emit()
logger.info(f"{threading.get_ident()}: main thread end...")
def withoutMoveToThread():
logger.info(f"{threading.get_ident()}: main started...")
communicate = Communicate()
worker = DbWorker()
communicate.signal_str.connect(worker.test_slot_str)
communicate.signal_empty.connect(worker.test_slot_empty)
communicate.signal_str.emit("Signal str")
communicate.signal_empty.emit()
logger.info(f"{threading.get_ident()}: main finished...")
if __name__ == '__main__':
withoutMoveToThread()
Solution
The signals need the event loop to work, in your case you must create a QCoreApplication:
def moveToThreadTest():
app = QCoreApplication.instance()
if app is None:
app = QCoreApplication()
logger.info(f"{threading.get_ident()}: main started...")
worker = DbWorker()
communicate = Communicate()
my_thread = QThread()
worker.moveToThread(my_thread)
communicate.signal_str.connect(worker.test_slot_str)
communicate.signal_empty.connect(worker.test_slot_empty)
my_thread.started.connect(worker.process)
my_thread.start()
communicate.signal_str.emit("test")
communicate.signal_empty.emit()
logger.info(f"{threading.get_ident()}: main thread end...")
app.exec_()
And change threading.get_ident()
by threading.current_thread()
you get:
INFO:140406642902848: main started...
INFO:<_MainThread(MainThread, started 140406642902848)>: DbWorker instantiated...
INFO:140406642902848: main thread end...
INFO:<_DummyThread(Dummy-1, started daemon 140406565365312)>: Process slot activated!
INFO:<_DummyThread(Dummy-1, started daemon 140406565365312)>: test_slot_str fired. Got Message 'test'
INFO:<_DummyThread(Dummy-1, started daemon 140406565365312)>: test_slot_empty fire
In your initial case (which partially works) it happens that there was time to create the thread but since the function is terminated instantly then the object is also destroyed.
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.