How to send signal from inside of imported function on pyqt5? - python

I have one Worker Thread on my program that returns a number every second, forever, until the stop signal is emmited on the Main Window. I want it to emit a signal everytime it changes a value, so I can update the Main Window with the returned value. I tried simply emmiting a signal in the function itself, but doesn't work.
Worker:
from test import save_data
class Worker(QObject):
finished = pyqtSignal() # give worker class a finished signal
def __init__(self, parent=None):
QObject.__init__(self, parent=parent)
def do_work(self):
save_data.main()
self.finished.emit() # emit the finished signal when the loop is done
def stop(self):
save_data.run = False # set the run condition to false on stop
test.py:
import time
from Pyqt5.QtCore import pyqtSignal
run = True
value = pyqtSignal(int)
def main():
x = 1
while run:
print(x)
x += 1
time.sleep(1)
#value.emit(x)

Final code:
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot, Qt, QTimer
import time
class Main(QObject):
divisible_by_4 = pyqtSignal()
def __init__(self, parent=None):
QObject.__init__(self, parent=parent)
self.run = True
def main(self):
x = 1
while self.run:
print(x)
x += 1
if x%4 == 0:
self.divisible_by_4.emit()
time.sleep(1)
def stop(self):
self.run = False

Related

Emit signals from Worker threads to Main window and back using #pyqtSlot decorators

I'm currently learning to build GUI's using PyQt5 and here is my problem:
I have 2 Threads. The first one is QThread object which generates some data.
The other one is QObject function which runs in QThread using .moveToThread method and processes data received from my so-called data generator. On main window I emit signal to do some stuff with value returned from generator separately from main thread in order to avoid freezing GUI.
The processing looks like:
receive data;
append it to list;
if list is full enough to make some calculations -> emit the result back to GUI;
wait some time (e.g. 3 secs) and start again the reception of data.
The problem is in the last point. I freeze the thread after calculations on list items, but it does not block signal emitted from the GUI, so when time.sleep(3) is up, my list already contains 3 items, but I need to start it from 0.
Commented fragments of code are my thoughts on how to do it using #pyqtSlot decorators and QtCore.pyqtSignal() but it doesn`t work.
Let me know if any clarification is needed, maybe I missed something.
Will be glad for any help.
Here is my code:
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QGridLayout, QLabel
from PyQt5.QtCore import QObject, QThread, pyqtSlot
from PyQt5 import QtCore
import random
import time
class DataThread(QThread):
output = QtCore.pyqtSignal(list)
def __init__(self):
super().__init__()
self._run_flag = True
def run(self):
while self._run_flag:
value = round(random.random(), 4)
value_x2 = value * 2
values = [value, value_x2]
self.output.emit(values)
# print(value)
time.sleep(1)
def stop(self):
self._run_flag = False
self.quit()
class CalculationsThread(QObject):
# accepting signals from main data generator thread
calculations_result = QtCore.pyqtSignal(float)
calc_value = QtCore.pyqtSignal(float)
# emitting signal from this thread
kill = QtCore.pyqtSignal()
deny_access = QtCore.pyqtSignal(bool)
def __init__(self, parent=None):
super(CalculationsThread, self).__init__(parent)
self.list_of_heights = []
#pyqtSlot(float)
def work(self, value):
# self.deny_access.emit(True)
self.list_of_heights.append(value)
self.calculations_result.emit(len(self.list_of_heights))
if len(self.list_of_heights) == 10:
result = sum(self.list_of_heights) / len(self.list_of_heights)
self.calculations_result.emit(result)
self.list_of_heights = []
# self.deny_access.emit(False)
time.sleep(3)
class WinForm(QWidget):
variable_value = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super(WinForm, self).__init__(parent)
self.setWindowTitle('QThreads example')
self.change_flag = 0
self.calculations_flag = 0
self.list_from_data_generator_thread = []
self.thread = None
self.calculations_worker = None
self.thread1 = None
self.label = QLabel('Label')
self.startBtn = QPushButton('Start')
self.startBtn.clicked.connect(self.start_data_generator)
self.endBtn = QPushButton('Stop')
self.endBtn.clicked.connect(self.stop_data_generator)
self.output1_btn = QPushButton("Value 1")
self.output1_btn.clicked.connect(self.show_value_1)
self.output2_btn = QPushButton("Value 2")
self.output2_btn.clicked.connect(self.show_value_2)
self.calculations_label = QLabel("Calculations result: ")
self.calculations_button = QPushButton("Start calculations")
self.calculations_button.clicked.connect(self.start_calculations)
layout = QGridLayout()
layout.addWidget(self.label, 0, 0)
layout.addWidget(self.startBtn, 1, 0)
layout.addWidget(self.endBtn, 1, 1)
layout.addWidget(self.output1_btn, 2, 0)
layout.addWidget(self.output2_btn, 2, 1)
layout.addWidget(self.calculations_label, 3, 0)
layout.addWidget(self.calculations_button, 3, 1)
self.setLayout(layout)
#pyqtSlot(list)
# #pyqtSLot(bool)
def set_label_text(self, value):
# if access_flag:
self.variable_value.emit(value[1])
# if not access flag:
# pass
if self.change_flag == 0:
self.label.setText(f"1: {value[0]}")
if self.change_flag == 1:
self.label.setText(f"2: {value[1]}")
def start_data_generator(self):
self.startBtn.setEnabled(False)
self.endBtn.setEnabled(True)
self.thread = DataThread()
self.thread.output.connect(self.set_label_text)
self.thread.start()
self.endBtn.clicked.connect(self.thread.stop)
def stop_data_generator(self):
self.thread.output.disconnect(self.set_label_text)
self.startBtn.setEnabled(True)
self.endBtn.setEnabled(False)
#pyqtSlot(float)
def output_calculations(self, value):
self.calculations_label.setText(f"Calculations result: {value}")
def start_calculations(self):
self.calculations_worker = CalculationsThread()
self.thread1 = QThread()
self.calculations_worker.moveToThread(self.thread1)
self.variable_value.connect(self.calculations_worker.work)
self.calculations_worker.calculations_result.connect(self.output_calculations)
self.calculations_worker.kill.connect(self.thread1.quit)
self.thread1.start()
self.calculations_button.clicked.disconnect(self.start_calculations)
self.calculations_button.setText("Stop calculations")
self.calculations_button.clicked.connect(self.thread1.quit)
self.calculations_button.clicked.connect(self.stop_calculations)
def stop_calculations(self):
self.calculations_worker.calculations_result.disconnect(self.output_calculations)
self.calculations_button.clicked.disconnect(self.stop_calculations)
self.calculations_button.clicked.disconnect(self.thread1.quit)
self.calculations_button.setText("Start calculations")
self.calculations_button.clicked.connect(self.start_calculations)
def show_value_1(self):
self.change_flag = 0
def show_value_2(self):
self.change_flag = 1
if __name__ == '__main__':
app = QApplication(sys.argv)
form = WinForm()
form.show()
sys.exit(app.exec_())

Running multiple background threads using QThread in PyQt5

Having an issue calling multiple threads. I have no luck. Runs the first definition (procCounter) fine but does not display or run the second (procCounter2)
Below is my main and my worker:
# main.py
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QGridLayout
import sys
import worker
class Form(QWidget):
def __init__(self):
super().__init__()
self.label = QLabel("0")
self.label2 = QLabel("1")
# 1 - create Worker and Thread inside the Form
self.obj = worker.Worker() # no parent!
self.thread = QThread() # no parent!
# 2 - Connect Worker`s Signals to Form method slots to post data.
self.obj.intReady.connect(self.onIntReady)
self.obj.intReady.connect(self.onIntReady2)
# 3 - Move the Worker object to the Thread object
self.obj.moveToThread(self.thread)
# 4 - Connect Worker Signals to the Thread slots
self.obj.finished.connect(self.thread.quit)
# 5 - Connect Thread started signal to Worker operational slot method
self.thread.started.connect(self.obj.procCounter)
self.thread.started.connect(self.obj.procCounter2)
# * - Thread finished signal will close the app if you want!
#self.thread.finished.connect(app.exit)
# 6 - Start the thread
self.thread.start()
# 7 - Start the form
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
grid.addWidget(self.label,0,0)
grid.addWidget(self.label2,0,1)
self.move(300, 150)
self.setWindowTitle('thread test')
self.show()
def onIntReady(self, i):
self.label.setText("{}".format(i))
print(i)
def onIntReady2(self, i):
#self.label2.setText("{}".format(i))
print(i)
app = QApplication(sys.argv)
form = Form()
sys.exit(app.exec_())
Here is my worker:
# worker.py
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
import time
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
#pyqtSlot()
def procCounter(self): # A slot takes no params
for i in range(1, 100):
time.sleep(.5)
self.intReady.emit(i)
self.finished.emit()
#pyqtSlot(int)
def procCounter2(self): # A slot takes no params
for i in range(1000):
time.sleep(.2)
self.intReady.emit(i)
self.finished.emit()
I have even tried adding n additional pyqtSignal such as: "intReady2 = pyqtSignal(int)" in the worker and then in the main adding " self.obj.intReady2.connect(self.onIntReady2)" But still no luck.
"obj" lives in the same thread so when invoking the slots they will execute the same thread so the first function it executes will block the other. The solution is to create 2 workers that live in different threads
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
#pyqtSlot()
def procCounter(self):
pass
class Worker1(Worker):
#pyqtSlot()
def procCounter(self):
for i in range(1, 100):
time.sleep(.5)
self.intReady.emit(i)
self.finished.emit()
class Worker2(QObject):
#pyqtSlot(int)
def procCounter(self):
for i in range(1000):
time.sleep(.2)
self.intReady.emit(i)
self.finished.emit()
self.obj1 = worker.Worker1()
self.thread1 = QThread()
self.obj1.moveToThread(self.thread1)
self.obj1.intReady.connect(self.onIntReady)
self.thread1.started.connect(self.obj.procCounter)
self.obj2 = worker.Worker2()
self.thread2 = QThread()
self.obj2.moveToThread(self.thread2)
self.obj2.intReady.connect(self.onIntReady2)
self.thread2.started.connect(self.obj2.procCounter)
self.thread1.start()
self.thread2.start()
The problem is self.thread.started.connect(self.obj.procCounter), you need to replace it with self.thread.started.connect(lambda: self.obj.procCounter())
More info here

PyQt5 Gui Thread to worker Thread signal/Slot

I am trying to make a simple communication between a worker thread, in this case it is called WorkToDo via the PyQt5 Signals and Slots mechanism. I can reliably send data from the Worker to the Gui thread via this mechanism, but I cannot do the same to the gui thread. From my research I have found that this is due to the fact that I have overridden the run function with my own logic. My question, is there any way to manually handle the execution of signals in the worker thread? Is there a better way to accomplish this?
EDIT:
I am actually not overriding run as I do not see run listed in the documentation for QObject. I am mistaken as this is a default function of a QThread object. This is more perplexing to me. Unless I am just further misunderstanding this.
import sys
import time
import qdarkstyle
from PyQt5.QtWidgets import (
QWidget, QLineEdit, QMessageBox, QPushButton, QVBoxLayout, QHBoxLayout, QTabWidget,
QComboBox, QProgressBar, QApplication, QLabel, QGroupBox, QFileDialog, QTextEdit,
QStyleFactory, QFormLayout
)
from PyQt5 import (Qt, QtCore)
from PyQt5.QtCore import *
class Command:
def __init__(self, **kwargs):
self.cmd = kwargs.get('cmd', None)
self.args = kwargs.get('args')
class CommandSignals(QObject):
command = pyqtSignal(Command)
class WorkToDo(QObject):
def __init__(self):
super().__init__()
self.counter = 0
self.signals = CommandSignals()
#QtCore.pyqtSlot(Command)
def process_command(self, command):
"""
#brief process update from gui thread
#param self self
#param command command
#return none
"""
print(f'Update from GUI: {command.__dict__}')
if command.cmd == 'add_to_counter':
self.counter = self.counter + command.args.get('addition', 0)
#pyqtSlot()
def run(self):
"""
#brief actual thread function to run,
started via thread.started signal (why its decorated with a pyqtSlot)
#param self obj ref
#return none
"""
while True:
QThread.sleep(1)
# emit text change signal to main gui thread
cmd = Command(
cmd = 'update_text',
args = {
'text': f'Hello update {self.counter}'
}
)
print(f'Update from worker: {cmd.__dict__}')
self.signals.command.emit(cmd)
self.counter += 1
class Gui(QWidget):
def __init__(self):
super().__init__()
""" make gui elements """
layout = QFormLayout()
self.text_line = QLineEdit()
self.add_button = QPushButton('Add 10 To Counter')
layout.addRow(self.text_line)
layout.addRow(self.add_button)
self.add_button.clicked.connect(self.issue_button_update)
self.signals = CommandSignals()
self.MakeThreadWorker()
""" finalize gui """
self.setLayout(layout)
self.setWindowTitle('Sync Thread Command/Response test')
self.show()
def MakeThreadWorker(self):
self.worker_thread = QThread()
self.worker = WorkToDo()
""" incoming gui update command, works """
self.worker.signals.command.connect(self.process_command)
""" outgoing update to worker thread, does not work """
self.signals.command.connect(self.worker.process_command)
""" signal to start the thread function, works """
self.worker_thread.started.connect(self.worker.run)
self.worker_thread.start()
self.worker.moveToThread(self.worker_thread)
#pyqtSlot(Command)
def process_command(self, command):
"""
#brief process update from work thread
#param self self
#param command command object
#return none
"""
if command.cmd == 'update_text':
text_update = command.args.get('text', '')
self.text_line.setText(text_update)
def issue_button_update(self):
cmd = Command(
cmd = 'add_to_counter',
args = {
'addition': 10
}
)
print('Button Clicked!')
self.signals.command.emit(cmd)
if __name__ == '__main__':
APPLICATION = QApplication([])
APPLICATION.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
gui = Gui()
sys.exit(APPLICATION.exec_())
The problem is that with True while you are blocking the event loop of the secondary thread preventing the signals from being received, if you want to do a periodic task then use a QTimer.
import sys
import time
import qdarkstyle
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QThread, QTimer
from PyQt5.QtWidgets import QApplication, QFormLayout, QLineEdit, QPushButton, QWidget
class Command:
def __init__(self, **kwargs):
self.cmd = kwargs.get("cmd", None)
self.args = kwargs.get("args")
class CommandSignals(QObject):
command = pyqtSignal(Command)
class WorkToDo(QObject):
def __init__(self):
super().__init__()
self.counter = 0
self.signals = CommandSignals()
self.timer = QTimer(self, timeout=self.run, interval=1000)
#pyqtSlot()
def start(self):
self.timer.start()
#pyqtSlot()
def stop(self):
self.timer.stop()
#pyqtSlot(Command)
def process_command(self, command):
"""
#brief process update from gui thread
#param self self
#param command command
#return none
"""
print(f"Update from GUI: {command.__dict__}")
if command.cmd == "add_to_counter":
self.counter = self.counter + command.args.get("addition", 0)
#pyqtSlot()
def run(self):
print(self.thread(), self.timer.thread())
cmd = Command(cmd="update_text", args={"text": f"Hello update {self.counter}"})
print(f"Update from worker: {cmd.__dict__}")
self.signals.command.emit(cmd)
self.counter += 1
class Gui(QWidget):
def __init__(self):
super().__init__()
""" make gui elements """
layout = QFormLayout()
self.text_line = QLineEdit()
self.add_button = QPushButton("Add 10 To Counter")
layout.addRow(self.text_line)
layout.addRow(self.add_button)
self.add_button.clicked.connect(self.issue_button_update)
self.signals = CommandSignals()
self.MakeThreadWorker()
""" finalize gui """
self.setLayout(layout)
self.setWindowTitle("Sync Thread Command/Response test")
self.show()
def MakeThreadWorker(self):
self.worker_thread = QThread()
self.worker = WorkToDo()
""" incoming gui update command, works """
self.worker.signals.command.connect(self.process_command)
""" outgoing update to worker thread, does not work """
self.signals.command.connect(self.worker.process_command)
""" signal to start the thread function, works """
self.worker_thread.started.connect(self.worker.start)
self.worker_thread.start()
self.worker.moveToThread(self.worker_thread)
#pyqtSlot(Command)
def process_command(self, command):
"""
#brief process update from work thread
#param self self
#param command command object
#return none
"""
if command.cmd == "update_text":
text_update = command.args.get("text", "")
self.text_line.setText(text_update)
def issue_button_update(self):
cmd = Command(cmd="add_to_counter", args={"addition": 10})
print("Button Clicked!")
self.signals.command.emit(cmd)
if __name__ == "__main__":
APPLICATION = QApplication([])
APPLICATION.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
gui = Gui()
sys.exit(APPLICATION.exec_())

PyQt5 QThread not working, gui still freezing

I have this code (if you have pyqt5, you should be able to run it yourself):
import sys
import time
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
class Worker(QObject):
def __init__(self):
super().__init__()
self.thread = None
class Tab(QObject):
def __init__(self, _main):
super().__init__()
self._main = _main
class WorkerOne(Worker):
finished = pyqtSignal()
def __init__(self):
super().__init__()
#pyqtSlot(str)
def print_name(self, name):
for _ in range(100):
print("Hello there, {0}!".format(name))
time.sleep(1)
self.finished.emit()
self.thread.quit()
class SomeTabController(Tab):
def __init__(self, _main):
super().__init__(_main)
self.threads = {}
self._main.button_start_thread.clicked.connect(self.start_thread)
# Workers
self.worker1 = WorkerOne()
#self.worker2 = WorkerTwo()
#self.worker3 = WorkerThree()
#self.worker4 = WorkerFour()
def _threaded_call(self, worker, fn, *args, signals=None, slots=None):
thread = QThread()
thread.setObjectName('thread_' + worker.__class__.__name__)
# store because garbage collection
self.threads[worker] = thread
# give worker thread so it can be quit()
worker.thread = thread
# objects stay on threads after thread.quit()
# need to move back to main thread to recycle the same Worker.
# Error is thrown about Worker having thread (0x0) if you don't do this
worker.moveToThread(QThread.currentThread())
# move to newly created thread
worker.moveToThread(thread)
# Can now apply cross-thread signals/slots
#worker.signals.connect(self.slots)
if signals:
for signal, slot in signals.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
#self.signals.connect(worker.slots)
if slots:
for slot, signal in slots.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
thread.started.connect(lambda: fn(*args)) # fn needs to be slot
thread.start()
#pyqtSlot()
def _receive_signal(self):
print("Signal received.")
#pyqtSlot(bool)
def start_thread(self):
name = "Bob"
signals = {self.worker1.finished: self._receive_signal}
self._threaded_call(self.worker1, self.worker1.print_name, name,
signals=signals)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 400)
self.button_start_thread = QPushButton()
self.button_start_thread.setText("Start thread.")
form_layout.addWidget(self.button_start_thread)
self.controller = SomeTabController(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
_main = MainWindow()
_main.show()
sys.exit(app.exec_())
However WorkerOne still blocks my GUI thread and the window is non-responsive when WorkerOne.print_name is running.
I have been researching a lot about QThreads recently and I am not sure why this isn't working based on the research I've done.
What gives?
The problem is caused by the connection with the lambda method since this lambda is not part of the Worker so it does not run on the new thread. The solution is to use functools.partial:
from functools import partial
...
thread.started.connect(partial(fn, *args))
Complete Code:
import sys
import time
from functools import partial
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
class Worker(QObject):
def __init__(self):
super().__init__()
self.thread = None
class Tab(QObject):
def __init__(self, _main):
super().__init__()
self._main = _main
class WorkerOne(Worker):
finished = pyqtSignal()
def __init__(self):
super().__init__()
#pyqtSlot(str)
def print_name(self, name):
for _ in range(100):
print("Hello there, {0}!".format(name))
time.sleep(1)
self.finished.emit()
self.thread.quit()
class SomeTabController(Tab):
def __init__(self, _main):
super().__init__(_main)
self.threads = {}
self._main.button_start_thread.clicked.connect(self.start_thread)
# Workers
self.worker1 = WorkerOne()
#self.worker2 = WorkerTwo()
#self.worker3 = WorkerThree()
#self.worker4 = WorkerFour()
def _threaded_call(self, worker, fn, *args, signals=None, slots=None):
thread = QThread()
thread.setObjectName('thread_' + worker.__class__.__name__)
# store because garbage collection
self.threads[worker] = thread
# give worker thread so it can be quit()
worker.thread = thread
# objects stay on threads after thread.quit()
# need to move back to main thread to recycle the same Worker.
# Error is thrown about Worker having thread (0x0) if you don't do this
worker.moveToThread(QThread.currentThread())
# move to newly created thread
worker.moveToThread(thread)
# Can now apply cross-thread signals/slots
#worker.signals.connect(self.slots)
if signals:
for signal, slot in signals.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
#self.signals.connect(worker.slots)
if slots:
for slot, signal in slots.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
thread.started.connect(partial(fn, *args)) # fn needs to be slot
thread.start()
#pyqtSlot()
def _receive_signal(self):
print("Signal received.")
#pyqtSlot(bool)
def start_thread(self):
name = "Bob"
signals = {self.worker1.finished: self._receive_signal}
self._threaded_call(self.worker1, self.worker1.print_name, name,
signals=signals)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 400)
self.button_start_thread = QPushButton()
self.button_start_thread.setText("Start thread.")
form_layout.addWidget(self.button_start_thread)
self.controller = SomeTabController(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
_main = MainWindow()
_main.show()
sys.exit(app.exec_())
To avoid blocking the gui for a slideshow function showing images i run the following
simple code.
A background Thread Class to wait one second, then signal to the gui waiting is finished.
class Waiter(QThread):
result = pyqtSignal(object)
def __init__(self):
QtCore.QThread.__init__(self)
def run(self):
while self.isRunning:
self.sleep(1)
self.result.emit("waited for 1s")
Then in main window connect the slideshow start and stop button to start and stop methods of main app and connect the nextImage function to the signal the Waiter Thread emits.
self.actionstopSlideShow.triggered.connect(self.stopSlideShow)
self.actionslideShowStart.triggered.connect(self.startSlideShow)
self.waitthread = Waiter()
self.waitthread.result.connect(self.nextImage)
Then two methods of main app allow to start and stop the time ticker
def startSlideShow(self):
"""Start background thread that waits one second,
on wait result trigger next image
use thread otherwise gui freezes and stop button cannot be pressed
"""
self.waitthread.start()
def stopSlideShow(self):
self.waitthread.terminate()
self.waitthread.wait()
Up to now i have no problems subclassing from QThread in pyqt5, gui changes are all handled inside the main (gui) thread.

PySide Signals not being sent to Slot, from QThread object

I'm working with a multi-threading application, where a worker thread gets created, that emits a signal.
After creating the thread, I connect the signal with an object slot, that will perform some action.
The problem, is the object slot, is not called, can someone help to figure out what's wrong with this code ?
import time
from PySide import QtCore
from PySide.QtCore import Slot, Signal
class Worker1(QtCore.QThread):
task_done_signal = Signal(int)
def __init__(self):
super(Worker1, self).__init__()
self._run = False
def run(self):
self._loop()
def _loop(self):
count = 0
while self._run:
print("running")
count += 1
self.task_done_signal.emit(count)
def start(self):
self._run = True
super(Worker1, self).start()
def stop(self):
self._run = False
class Worker1Listener(QtCore.QObject):
def __init__(self):
super(Worker1Listener, self).__init__()
#Slot()
def print_task(self, val):
print("listener: {}".format(val))
def test_signals_and_threads():
# create the thread
worker = Worker1()
# create the listener
listener = Worker1Listener()
# connect the thread signal with the slot
worker.task_done_signal.connect(listener.print_task)
worker.start()
time.sleep(5)
worker.stop()
time.sleep(5)
if __name__ == '__main__':
test_signals_and_threads()
Your code has several errors:
You must have an event loop so that Qt handles communications between the various objects of the application, in your case you must use QCoreApplication.
The decorator Slot must have as parameter the type of data of the arguments of the function, in your case: Slot(int)
You should not use time.sleep since it is blocking and does not let the event loop do its work, a possible solution is to use QEventLoop next to a QTimer.
It is always advisable to give a short time for communications to be given, for this we use QThread.msleep.
When you connect between signals that are in different threads, the correct option is to use the Qt.QueuedConnection option.
import sys
from PySide import QtCore
class Worker1(QtCore.QThread):
task_done_signal = QtCore.Signal(int)
def __init__(self):
super(Worker1, self).__init__()
self._run = False
def run(self):
self._loop()
def _loop(self):
count = 0
while self._run:
print("running")
count += 1
self.task_done_signal.emit(count)
QtCore.QThread.msleep(1)
def start(self):
self._run = True
super(Worker1, self).start()
def stop(self):
self._run = False
class Worker1Listener(QtCore.QObject):
#QtCore.Slot(int)
def print_task(self, val):
print("listener: {}".format(val))
def test_signals_and_threads():
app = QtCore.QCoreApplication(sys.argv)
# create the thread
worker = Worker1()
# create the listener
listener = Worker1Listener()
# connect the thread signal with the slot
worker.task_done_signal.connect(listener.print_task, QtCore.Qt.QueuedConnection)
worker.start()
loop = QtCore.QEventLoop()
QtCore.QTimer.singleShot(5000, loop.quit)
loop.exec_()
worker.stop()
loop = QtCore.QEventLoop()
QtCore.QTimer.singleShot(5000, loop.quit)
loop.exec_()
#sys.exit(app.exec_())
if __name__ == '__main__':
test_signals_and_threads()

Categories