PyQt | Signals not handled in QThread but in main thread - pyqt

In this simple PyQt demo program, I emit signals from the main thread. In a worker thread I connect to them, but the signal handlers are run in the main thread:
from PyQt4 import QtGui, QtCore
import threading
from time import sleep
import sys
class Data():
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return "Data having %d and %d" % (self.a, self.b)
class Worker(QtCore.QThread):
def __init__(self, parent):
QtCore.QThread.__init__(self)
self.p = parent
def run(self):
self.connect(self.p, QtCore.SIGNAL("newTask"), self.task)
print "[%s] running exec_()" % threading.currentThread()
self.exec_()
def task(self, dataobj):
print "[%s] Processing" % threading.currentThread(), dataobj
sleep(3)
print "Done with", dataobj
self.emit(QtCore.SIGNAL("taskDone"), str(dataobj))
class App(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
self.w = Worker(self)
self.connect(self.w, QtCore.SIGNAL("taskDone"), self.on_task_done)
self.w.start()
def assign_tasks(self):
self.emit(QtCore.SIGNAL("newTask"), Data(3, 4))
self.emit(QtCore.SIGNAL("newTask"), Data(5, 6))
print "[%s] Tasks sent" % threading.currentThread()
#staticmethod
def on_task_done(objstr):
print "[%s] App: Worker finished with" % threading.currentThread(), objstr
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
a = App()
sleep(1)
a.assign_tasks()
sleep(20)
sys.exit(app.exec_())
But the result reveals that the callbacks are run in the main thread:
[<_DummyThread(Dummy-1, started daemon 105564)>] running exec_()
[<_MainThread(MainThread, started 105612)>] Processing Data having 3 and 4
Done with Data having 3 and 4
[<_MainThread(MainThread, started 105612)>] App: Worker finished with Data having 3 and 4
[<_MainThread(MainThread, started 105612)>] Processing Data having 5 and 6
Done with Data having 5 and 6
[<_MainThread(MainThread, started 105612)>] App: Worker finished with Data having 5 and 6
[<_MainThread(MainThread, started 105612)>] Tasks sent
What am I doing wrong? Unfortunately, the PyQt docs on this are very incomplete and contradicting.
I'm getting similar results if I use the moveToThread technique:
from PyQt4 import QtGui, QtCore
import threading
from time import sleep
import sys
class Data():
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return "Data having %d and %d" % (self.a, self.b)
class Worker(QtCore.QObject):
def __init__(self, parent):
QtCore.QObject.__init__(self)
self.connect(parent, QtCore.SIGNAL("newTask"), self.task)
def task(self, dataobj):
print "[%s] Processing" % threading.currentThread(), dataobj
sleep(3)
print "Done with", dataobj
self.emit(QtCore.SIGNAL("taskDone"), str(dataobj))
def start(self):
print "[%s] start()" % threading.currentThread()
class App(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
self.w = Worker(self)
self.t = QtCore.QThread(self)
self.w.moveToThread(self.t)
self.connect(self.w, QtCore.SIGNAL("taskDone"), self.on_task_done)
self.connect(self.t, QtCore.SIGNAL("started()"), self.w.start)
self.t.start()
def assign_tasks(self):
self.emit(QtCore.SIGNAL("newTask"), Data(3, 4))
self.emit(QtCore.SIGNAL("newTask"), Data(5, 6))
print "[%s] Tasks sent" % threading.currentThread()
#staticmethod
def on_task_done(objstr):
print "[%s] App: Worker finished with" % threading.currentThread(), objstr
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
a = App()
sleep(1)
a.assign_tasks()
sleep(20)
sys.exit(app.exec_())
Which results in:
[<_DummyThread(Dummy-1, started daemon 108992)>] start()
[<_MainThread(MainThread, started 107004)>] Processing Data having 3 and 4
Done with Data having 3 and 4
[<_MainThread(MainThread, started 107004)>] App: Worker finished with Data having 3 and 4
[<_MainThread(MainThread, started 107004)>] Processing Data having 5 and 6
Done with Data having 5 and 6
[<_MainThread(MainThread, started 107004)>] App: Worker finished with Data having 5 and 6
[<_MainThread(MainThread, started 107004)>] Tasks sent

Your Worker objects 'live' in the main thread, that means all their signals will be handled by the main thread's event loop. The fact that these objects are QThreads doesn't change that.
If you want signals to be processed by a different thread, you first need to move the worker object to that thread by using it's moveToThread method.
So in your case, only the run method is acutally executed in a different thread, the task method is still executed in the main thread. A way of changing this would be:
make your Worker a regular QObject, not a QThread
create a QThread in your App, start it and move the worker to that thread
then send the signal to the worker that causes it to start processing
And you should check out these references:
QObject thread affinity
Qt threading basics
Multithreading technologies in Qt
edit:
A few other things I noticed in your code:
you're mixing python threading and qt threading. threading.currentThread will not correctly reflect the current qt thread. use QThread.currentThread() for that.
decorate the slots you're calling as pyqtSlots, not doing so can be a cause of problems like these.
use new style signals. Old style signals are no longer supported in PyQt5, and new style signals are much easier and nicer to use.
So here is a version of your code that should work:
from PyQt4 import QtGui, QtCore
import threading
from time import sleep
import sys
class Data():
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return "Data having %d and %d" % (self.a, self.b)
class Worker(QtCore.QObject):
taskDone = QtCore.pyqtSignal(str)
def __init__(self, parent):
QtCore.QObject.__init__(self)
parent.newTask.connect(self.task)
#QtCore.pyqtSlot(object)
def task(self, dataobj):
print "[%s] Processing" % QtCore.QThread.currentThread().objectName(), dataobj
sleep(3)
print "Done with", dataobj
self.taskDone.emit(str(dataobj))
#QtCore.pyqtSlot()
def start(self):
print "[%s] start()" % QtCore.QThread.currentThread().objectName()
class App(QtCore.QObject):
newTask = QtCore.pyqtSignal(object)
def __init__(self):
QtCore.QObject.__init__(self)
self.w = Worker(self)
self.t = QtCore.QThread(self, objectName='workerThread')
self.w.moveToThread(self.t)
self.w.taskDone.connect(self.on_task_done)
self.t.started.connect(self.w.start)
self.t.start()
def assign_tasks(self):
self.newTask.emit(Data(3, 4))
self.newTask.emit(Data(5, 6))
print "[%s] Tasks sent" % QtCore.QThread.currentThread().objectName()
#staticmethod
def on_task_done(objstr):
print "[%s] App: Worker finished with" % QtCore.QThread.currentThread().objectName(), objstr
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
QtCore.QThread.currentThread().setObjectName('main')
a = App()
sleep(1)
a.assign_tasks()
from utils import sigint
sys.exit(app.exec_())
I've set the thread's objectNames to make the output better readable:
[workerThread] start()
[main] Tasks sent
[workerThread] Processing Data having 3 and 4
Done with Data having 3 and 4
[workerThread] Processing Data having 5 and 6
[main] App: Worker finished with Data having 3 and 4
Done with Data having 5 and 6
[main] App: Worker finished with Data having 5 and 6

Related

Python PyQt5: based on condition, run a CPU intensive QThread [duplicate]

I am trying to figure out why this code crashes if I try to run the threads for a second time once they are completed.
The first time I click "Start 5 Threads" It runs just fine and finishes. But if I click it again. The entire program crashes and I get the QThread: Destroyed while thread is still running Error
This code was found on the web. I am trying to learn from it.
import time
import sys
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget
def trap_exc_during_debug(*args):
# when app raises uncaught exception, print info
print(args)
# install exception hook: without this, uncaught exception would cause application to exit
sys.excepthook = trap_exc_during_debug
class Worker(QObject):
"""
Must derive from QObject in order to emit signals, connect slots to other signals, and operate in a QThread.
"""
sig_step = pyqtSignal(int, str) # worker id, step description: emitted every step through work() loop
sig_done = pyqtSignal(int) # worker id: emitted at end of work()
sig_msg = pyqtSignal(str) # message to be shown to user
def __init__(self, id: int):
super().__init__()
self.__id = id
self.__abort = False
#pyqtSlot()
def work(self):
"""
Pretend this worker method does work that takes a long time. During this time, the thread's
event loop is blocked, except if the application's processEvents() is called: this gives every
thread (incl. main) a chance to process events, which in this sample means processing signals
received from GUI (such as abort).
"""
thread_name = QThread.currentThread().objectName()
thread_id = int(QThread.currentThreadId()) # cast to int() is necessary
self.sig_msg.emit('Running worker #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id))
for step in range(100):
time.sleep(0.1)
self.sig_step.emit(self.__id, 'step ' + str(step))
# check if we need to abort the loop; need to process events to receive signals;
app.processEvents() # this could cause change to self.__abort
if self.__abort:
# note that "step" value will not necessarily be same for every thread
self.sig_msg.emit('Worker #{} aborting work at step {}'.format(self.__id, step))
break
self.sig_done.emit(self.__id)
def abort(self):
self.sig_msg.emit('Worker #{} notified to abort'.format(self.__id))
self.__abort = True
class MyWidget(QWidget):
NUM_THREADS = 5
# sig_start = pyqtSignal() # needed only due to PyCharm debugger bug (!)
sig_abort_workers = pyqtSignal()
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 800)
self.button_start_threads = QPushButton()
self.button_start_threads.clicked.connect(self.start_threads)
self.button_start_threads.setText("Start {} threads".format(self.NUM_THREADS))
form_layout.addWidget(self.button_start_threads)
self.button_stop_threads = QPushButton()
self.button_stop_threads.clicked.connect(self.abort_workers)
self.button_stop_threads.setText("Stop threads")
self.button_stop_threads.setDisabled(True)
form_layout.addWidget(self.button_stop_threads)
self.log = QTextEdit()
form_layout.addWidget(self.log)
self.progress = QTextEdit()
form_layout.addWidget(self.progress)
QThread.currentThread().setObjectName('main') # threads can be named, useful for log output
self.__workers_done = None
self.__threads = None
def start_threads(self):
self.log.append('starting {} threads'.format(self.NUM_THREADS))
self.button_start_threads.setDisabled(True)
self.button_stop_threads.setEnabled(True)
self.__workers_done = 0
self.__threads = []
for idx in range(self.NUM_THREADS):
worker = Worker(idx)
thread = QThread()
thread.setObjectName('thread_' + str(idx))
self.__threads.append((thread, worker)) # need to store worker too otherwise will be gc'd
worker.moveToThread(thread)
# get progress messages from worker:
worker.sig_step.connect(self.on_worker_step)
worker.sig_done.connect(self.on_worker_done)
worker.sig_msg.connect(self.log.append)
# control worker:
self.sig_abort_workers.connect(worker.abort)
# get read to start worker:
# self.sig_start.connect(worker.work) # needed due to PyCharm debugger bug (!); comment out next line
thread.started.connect(worker.work)
thread.start() # this will emit 'started' and start thread's event loop
# self.sig_start.emit() # needed due to PyCharm debugger bug (!)
#pyqtSlot(int, str)
def on_worker_step(self, worker_id: int, data: str):
self.log.append('Worker #{}: {}'.format(worker_id, data))
self.progress.append('{}: {}'.format(worker_id, data))
#pyqtSlot(int)
def on_worker_done(self, worker_id):
self.log.append('worker #{} done'.format(worker_id))
self.progress.append('-- Worker {} DONE'.format(worker_id))
self.__workers_done += 1
if self.__workers_done == self.NUM_THREADS:
self.log.append('No more workers active')
self.button_start_threads.setEnabled(True)
self.button_stop_threads.setDisabled(True)
# self.__threads = None
#pyqtSlot()
def abort_workers(self):
self.sig_abort_workers.emit()
self.log.append('Asking each worker to abort')
for thread, worker in self.__threads: # note nice unpacking by Python, avoids indexing
thread.quit() # this will quit **as soon as thread event loop unblocks**
thread.wait() # <- so you need to wait for it to *actually* quit
# even though threads have exited, there may still be messages on the main thread's
# queue (messages that threads emitted before the abort):
self.log.append('All threads exited')
if __name__ == "__main__":
app = QApplication([])
form = MyWidget()
form.show()
sys.exit(app.exec_())
The problem is solved by passing him as a parent to self. You must change:
thread = QThread()
to:
thread = QThread(parent=self)

PyQt - Signaling worker in QThread [duplicate]

I am trying to figure out why this code crashes if I try to run the threads for a second time once they are completed.
The first time I click "Start 5 Threads" It runs just fine and finishes. But if I click it again. The entire program crashes and I get the QThread: Destroyed while thread is still running Error
This code was found on the web. I am trying to learn from it.
import time
import sys
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget
def trap_exc_during_debug(*args):
# when app raises uncaught exception, print info
print(args)
# install exception hook: without this, uncaught exception would cause application to exit
sys.excepthook = trap_exc_during_debug
class Worker(QObject):
"""
Must derive from QObject in order to emit signals, connect slots to other signals, and operate in a QThread.
"""
sig_step = pyqtSignal(int, str) # worker id, step description: emitted every step through work() loop
sig_done = pyqtSignal(int) # worker id: emitted at end of work()
sig_msg = pyqtSignal(str) # message to be shown to user
def __init__(self, id: int):
super().__init__()
self.__id = id
self.__abort = False
#pyqtSlot()
def work(self):
"""
Pretend this worker method does work that takes a long time. During this time, the thread's
event loop is blocked, except if the application's processEvents() is called: this gives every
thread (incl. main) a chance to process events, which in this sample means processing signals
received from GUI (such as abort).
"""
thread_name = QThread.currentThread().objectName()
thread_id = int(QThread.currentThreadId()) # cast to int() is necessary
self.sig_msg.emit('Running worker #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id))
for step in range(100):
time.sleep(0.1)
self.sig_step.emit(self.__id, 'step ' + str(step))
# check if we need to abort the loop; need to process events to receive signals;
app.processEvents() # this could cause change to self.__abort
if self.__abort:
# note that "step" value will not necessarily be same for every thread
self.sig_msg.emit('Worker #{} aborting work at step {}'.format(self.__id, step))
break
self.sig_done.emit(self.__id)
def abort(self):
self.sig_msg.emit('Worker #{} notified to abort'.format(self.__id))
self.__abort = True
class MyWidget(QWidget):
NUM_THREADS = 5
# sig_start = pyqtSignal() # needed only due to PyCharm debugger bug (!)
sig_abort_workers = pyqtSignal()
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 800)
self.button_start_threads = QPushButton()
self.button_start_threads.clicked.connect(self.start_threads)
self.button_start_threads.setText("Start {} threads".format(self.NUM_THREADS))
form_layout.addWidget(self.button_start_threads)
self.button_stop_threads = QPushButton()
self.button_stop_threads.clicked.connect(self.abort_workers)
self.button_stop_threads.setText("Stop threads")
self.button_stop_threads.setDisabled(True)
form_layout.addWidget(self.button_stop_threads)
self.log = QTextEdit()
form_layout.addWidget(self.log)
self.progress = QTextEdit()
form_layout.addWidget(self.progress)
QThread.currentThread().setObjectName('main') # threads can be named, useful for log output
self.__workers_done = None
self.__threads = None
def start_threads(self):
self.log.append('starting {} threads'.format(self.NUM_THREADS))
self.button_start_threads.setDisabled(True)
self.button_stop_threads.setEnabled(True)
self.__workers_done = 0
self.__threads = []
for idx in range(self.NUM_THREADS):
worker = Worker(idx)
thread = QThread()
thread.setObjectName('thread_' + str(idx))
self.__threads.append((thread, worker)) # need to store worker too otherwise will be gc'd
worker.moveToThread(thread)
# get progress messages from worker:
worker.sig_step.connect(self.on_worker_step)
worker.sig_done.connect(self.on_worker_done)
worker.sig_msg.connect(self.log.append)
# control worker:
self.sig_abort_workers.connect(worker.abort)
# get read to start worker:
# self.sig_start.connect(worker.work) # needed due to PyCharm debugger bug (!); comment out next line
thread.started.connect(worker.work)
thread.start() # this will emit 'started' and start thread's event loop
# self.sig_start.emit() # needed due to PyCharm debugger bug (!)
#pyqtSlot(int, str)
def on_worker_step(self, worker_id: int, data: str):
self.log.append('Worker #{}: {}'.format(worker_id, data))
self.progress.append('{}: {}'.format(worker_id, data))
#pyqtSlot(int)
def on_worker_done(self, worker_id):
self.log.append('worker #{} done'.format(worker_id))
self.progress.append('-- Worker {} DONE'.format(worker_id))
self.__workers_done += 1
if self.__workers_done == self.NUM_THREADS:
self.log.append('No more workers active')
self.button_start_threads.setEnabled(True)
self.button_stop_threads.setDisabled(True)
# self.__threads = None
#pyqtSlot()
def abort_workers(self):
self.sig_abort_workers.emit()
self.log.append('Asking each worker to abort')
for thread, worker in self.__threads: # note nice unpacking by Python, avoids indexing
thread.quit() # this will quit **as soon as thread event loop unblocks**
thread.wait() # <- so you need to wait for it to *actually* quit
# even though threads have exited, there may still be messages on the main thread's
# queue (messages that threads emitted before the abort):
self.log.append('All threads exited')
if __name__ == "__main__":
app = QApplication([])
form = MyWidget()
form.show()
sys.exit(app.exec_())
The problem is solved by passing him as a parent to self. You must change:
thread = QThread()
to:
thread = QThread(parent=self)

How to gracefully terminate a multithreaded Python application that uses queue.Queue

I have been trying to get my application to terminate gracefully for quite some time now, but so far none of the answers I have found worked.
The sample code below illustrates the structure of my application. It basically is a chain of threads that passes data to one another using Queues.
from abc import abstractmethod
from time import sleep
from threading import Thread, Event
from queue import Queue
import signal
import sys
class StoppableThread(Thread):
def __init__(self):
super().__init__()
self.stopper = Event()
self.queue = Queue()
#abstractmethod
def actual_job(self):
pass
def stop_running(self):
self.stopper.set()
def run(self):
while not self.stopper.is_set():
print(self.stopper.is_set())
self.actual_job()
self.queue.join()
class SomeObjectOne(StoppableThread):
def __init__(self, name, some_object_two):
super().__init__()
self.name = name
self.obj_two = some_object_two
def actual_job(self):
# print('{} is currently running'.format(self.name))
input_string = 'some string'
print('{} outputs {}'.format(self.name, input_string))
self.obj_two.queue.put(input_string)
sleep(2)
class SomeObjectTwo(StoppableThread):
def __init__(self, name, some_object_three):
super().__init__()
self.name = name
self.some_object_three = some_object_three
def actual_job(self):
# print('{} is currently running'.format(self.name))
some_string = self.queue.get()
inverted = some_string[::-1]
print('{} outputs {}'.format(self.name , inverted))
self.some_object_three.queue.put(inverted)
sleep(2)
class SomeObjectThree(StoppableThread):
def __init__(self, name):
super().__init__()
self.name = name
def actual_job(self):
print('{} is currently running'.format(self.name))
some_string = self.queue.get()
print('{} outputs {}'.format(self.name ,some_string[::-1]))
sleep(2)
class ServiceExit(Exception):
"""
Custom exception which is used to trigger the clean exit
of all running threads and the main program.
"""
pass
def service_shutdown(signum, frame):
print('Caught signal %d' % signum)
raise ServiceExit
signal.signal(signal.SIGTERM, service_shutdown)
signal.signal(signal.SIGINT, service_shutdown)
if __name__ == '__main__':
thread_three = SomeObjectThree('SomeObjectThree')
thread_two = SomeObjectTwo('SomeObjectTwo', thread_three)
thread_one = SomeObjectOne('SomeObjectOne', thread_two)
try:
thread_three.start()
thread_two.start()
thread_one.start()
# Keep the main thread running, otherwise signals are ignored.
while True:
sleep(0.5)
except ServiceExit:
print('Running service exit')
thread_three.stop_running()
thread_two.stop_running()
thread_one.stop_running()
thread_one.join()
thread_two.join()
thread_three.join()
sys.exit(0)
Now, if I run this code and ctrl-C to terminate, thread_one seems to join as expected, but the code gets stuck at thread_two.join().
Because thread_one is the only thread with a continuous empty queue, I expect it has something to do with the queue.
Any ideas?
In the run() method of StoppableThread you have this:
self.queue.join()
join() is a blocking method:
Blocks until all items in the queue have been gotten and processed.
The count of unfinished tasks goes up whenever an item is added to the
queue. The count goes down whenever a consumer thread calls
task_done() to indicate that the item was retrieved and all work on it
is complete. When the count of unfinished tasks drops to zero, join()
unblocks.
So in order for join() to return, it's not enough to get() an item in the other thread, you must also indicate that it's been processed with task_done():
from abc import abstractmethod
from time import sleep
from threading import Thread, Event
from queue import Queue
import signal
import sys
class StoppableThread(Thread):
def __init__(self):
super().__init__()
self.stopper = Event()
self.queue = Queue()
#abstractmethod
def actual_job(self):
pass
def stop_running(self):
self.stopper.set()
def run(self):
while not self.stopper.is_set():
print(self.stopper.is_set())
self.actual_job()
self.queue.join()
class SomeObjectOne(StoppableThread):
def __init__(self, name, some_object_two):
super().__init__()
self.name = name
self.obj_two = some_object_two
def actual_job(self):
# print('{} is currently running'.format(self.name))
input_string = 'some string'
print('{} outputs {}'.format(self.name, input_string))
self.obj_two.queue.put(input_string)
sleep(2)
class SomeObjectTwo(StoppableThread):
def __init__(self, name, some_object_three):
super().__init__()
self.name = name
self.some_object_three = some_object_three
def actual_job(self):
# print('{} is currently running'.format(self.name))
some_string = self.queue.get()
inverted = some_string[::-1]
print('{} outputs {}'.format(self.name , inverted))
self.queue.task_done()
self.some_object_three.queue.put(inverted)
sleep(2)
class SomeObjectThree(StoppableThread):
def __init__(self, name):
super().__init__()
self.name = name
def actual_job(self):
print('{} is currently running'.format(self.name))
some_string = self.queue.get()
print('{} outputs {}'.format(self.name ,some_string[::-1]))
self.queue.task_done()
sleep(2)
class ServiceExit(Exception):
"""
Custom exception which is used to trigger the clean exit
of all running threads and the main program.
"""
pass
def service_shutdown(signum, frame):
print('Caught signal %d' % signum)
raise ServiceExit
signal.signal(signal.SIGTERM, service_shutdown)
signal.signal(signal.SIGINT, service_shutdown)
if __name__ == '__main__':
thread_three = SomeObjectThree('SomeObjectThree')
thread_two = SomeObjectTwo('SomeObjectTwo', thread_three)
thread_one = SomeObjectOne('SomeObjectOne', thread_two)
try:
thread_three.start()
thread_two.start()
thread_one.start()
# Keep the main thread running, otherwise signals are ignored.
while True:
sleep(0.5)
except ServiceExit:
print('Running service exit')
thread_three.stop_running()
thread_two.stop_running()
thread_one.stop_running()
thread_one.join()
thread_two.join()
thread_three.join()

What is the best way to run a Python function after some PyQt5 QThread classes finish work?

I'm using PyQt5 and Python3, I use 3 QThread classes to run something and after they are done I need to execute a 4th QThread class. But the execution of the 4th need to take place after all of the QThread classes finish work, or only 2 or only 1. It must not run while the first 3 are working.
I looked on the internet but I couldn't find a solution. My code looks like this:
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
file_path = os.path.abspath('builder_gui.ui')
uic.loadUi(file_path, self)
self.obj1 = TasksThread1(self.comboBox.currentText(),self.comboBox_6.currentText())
self.obj2 = TasksThread2(self.comboBox_2.currentText(),self.comboBox_5.currentText())
self.obj3 = TasksThread3(self.comboBox_3.currentText(),self.comboBox_4.currentText())
self.obj4 = TasksThread4()
self.menubar.setNativeMenuBar(False)
self.progressVal = 1
self.cwd = os.getcwd()
self.obj1.newValueProgress.connect(self.increment_progress)
self.obj1.message.connect(self.status_bar)
self.obj2.newValueProgress.connect(self.increment_progress)
self.obj2.message.connect(self.status_bar)
self.obj3.newValueProgress.connect(self.increment_progress)
self.obj3.message.connect(self.status_bar)
self.obj4.newValueProgress.connect(self.increment_progress)
self.obj4.message.connect(self.status_bar)
self.obj4.doneSignal.connect(self.calculate_done_limit)
self.pushButton.pressed.connect(self.execute_build_script)
def calculate_done_limit(self):
limitCalc = 100 - int(self.progressBar.value())
self.increment_progress(limitCalc)
def run_gits_all(self):
if self.crowdTwistCheck.isChecked():
self.obj1.start()
else:
pass
if self.ThemeCheck.isChecked():
self.obj2.start()
else:
pass
if self.mainAwsCheck.isChecked():
self.obj3.start()
else:
pass
def execute_build_script(self):
self.progressBar.setValue(1)
self.progressVal = 1
self.run_gits_all()
def execute_last_part(self):
self.obj4.start()
def status_bar(self, value_in):
read1 = self.textBrowser.toPlainText()
self.textBrowser.setText(read1 + "\n" + value_in)
def increment_progress(self,valueIn):
self.progressVal += valueIn
self.progressBar.setValue(self.progressVal)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
My first 3 QThreads are like this:
class TasksThread1(QThread):
newValueProgress = QtCore.pyqtSignal(int)
message = QtCore.pyqtSignal(str)
doneSignal = QtCore.pyqtSignal()
def __init__(self, branch, git):
QThread.__init__(self)
self.branch = branch
self.git = git
def remove_folder(self):
do_something_1
def CrowdTwistRepo(self):
do_something_2
def run(self):
self.remove_folder()
self.CrowdTwistRepo()
My last QThread looks like this:
class TasksThread4(QThread):
newValueProgress = QtCore.pyqtSignal(int)
message = QtCore.pyqtSignal(str)
doneSignal = QtCore.pyqtSignal()
def __init__(self):
QThread.__init__(self)
def gulp_sass_function(self):
do_something_1
def gulp_uglify_function(self):
do_something_2
def zipping_function(self):
do_something_3
def run(self):
self.gulp_sass_function()
self.gulp_uglify_function()
self.zipping_function()
If I run the code, all of the QThreads start and I want my 4th QThread to start only after the first 3 have done working. I used QThreads to improve the GUI experience, the GUI froze alot.
thanks,
When your first 3 threads are done, send a signal. Then connect this signal to a function that will start the last thread.

Starting a new thread in Gui application python

I've got a python Gui application which has a thread that does some updating.
This is how its implemented.
GObject.threads_init()
Class main:
#Extra stuff here
update_thread = Thread(target= update_func, args=(Blah blah,))
update_thread.setDaemon(True)
update_thread.start()
Gtk.main()
This is how the update_func looke like
def update_func():
try:
#do updating
time.sleep(#6hrs)
except:
#catch error
time.sleep(#5 min)
finally:
update_func()
The thread runs as long as the program is running and the program I have runs for days
The problem is that sometimes the thread dies and updates do not occur and I have to restart the application.
Is there a way to start a new thread if the current one dies, especially in a Gui application?
Below is a thread example snipped from one of my gtk apps. It might be helpful.
#!/usr/bin/env python3
# Copyright (C) 2013 LiuLang <gsushzhsosgsu#gmail.com>
# Use of this source code is governed by GPLv3 license that can be found
# in http://www.gnu.org/licenses/gpl-3.0.html
from gi.repository import GObject
from gi.repository import Gtk
import threading
#UPDATE_INTERVAL = 6 * 60 * 60 * 1000 # 6 hours
UPDATE_INTERVAL = 2 * 1000 # 2 secs, for test only
def async_call(func, func_done, *args):
'''
Call func in another thread, without blocking gtk main loop.
`func` does time-consuming job in background, like access website.
If `func_done` is not None, it will be called after func() ends.
func_done is called in gtk main thread, and this function is often used
to update GUI widgets in app.
`args` are parameters for func()
'''
def do_call(*args):
result = None
error = None
try:
result = func(*args)
except Exception as e:
error = e
if func_done is not None:
GObject.idle_add(lambda: func_done(result, error))
thread = threading.Thread(target=do_call, args=args)
thread.start()
class App(Gtk.Window):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.set_default_size(480, 320)
self.set_border_width(5)
self.set_title('Gtk Threads')
self.connect('delete-event', self.on_app_exit)
self.update_timer = GObject.timeout_add(UPDATE_INTERVAL,
self.check_update)
def run(self):
self.show_all()
Gtk.main()
def on_app_exit(self, *args):
Gtk.main_quit()
def check_update(self):
def _do_check_update():
print('do check update: will check for updating info')
print(threading.current_thread(), '\n')
print('check update')
print(threading.current_thread())
async_call(_do_check_update, None)
return True
if __name__ == '__main__':
app = App()
app.run()

Resources