We have an application that executes different queries. It starts up to four threads, and runs the extractions on them.
That part looks like this:
if len(self.threads) == 4:
self.__maxThreadsMsg(base)
return False
else:
self.threads.append(Extractor(self.ui, base))
self.threads[-1].start()
self.__extractionMsg(base)
return True
Our Extractor
class inherits QThread
:
class Extractor(QThread):
def init(self, ui, base):
QThread.__init__(self)
self.ui = ui
self.base = base
def run(self):
self.run_base(base)
and self.ui
is set to Ui_MainWindow()
:
class Cont(QMainWindow):
def __init__(self, parent=None):
QWidget.__init__(self,parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
There is a specific base that sends data to the user (back to the main window) before proceeding (in this case, a pop-up with two buttons):
#This code is in the main file inside a method, not in the Extractor class
msg_box = QMessagebox()
msg_box.setText('Quantity in base: '.format(n))
msg_box.setInformativeText('Would you like to continue?')
msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
signal = msg_box.exec_()
How can I pause the thread at a certain point, display the window (which I believe would be returning to the main thread) and return to the worker thread, passing the button clicked event?
I read a bit about signals but it seems confusing as it is my first time dealing with threads.
Edit: After reading this question: Similar question, I altered the code to this:
On a method inside of the Cont class
thread = QThread(self)
worker = Worker()
worker.moveToThread(thread)
worker.bv.connect(self.bv_test)
thread.started.connect(worker.process()) # This, unlike in the linked question..
#doesn't work if I remove the parentheses of the process function.
#If I remove it, nothing happens and I get QThread: "Destroyed while thread is still running"
thread.start()
@pyqtSlot(int)
def bv_test(self, n):
k = QMessageBox()
k.setText('Quantity: {}'.format(n))
k.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
ret = k.exec_()
return ret
and this is the Worker
class:
class Worker(QObject):
#Signals
bv = pyqtSignal(int)
def process(self):
self.bv.emit(99)
Now I just need to figure out how to send the ret
value back to the worker thread so it starts the second process. I also keep getting this error:
TypeError: connect() slot argument should be a callable or a signal, not 'NoneType'
Thread. sleep() method can be used to pause the execution of current thread for specified time in milliseconds.
Sleep method causes the current thread to immediately block for the number of milliseconds or the time interval you pass to the method, and yields the remainder of its time slice to another thread. Once that interval elapses, the sleeping thread resumes execution. One thread cannot call Thread. Sleep on another thread.
Using event.wait() When we want a thread to wait for an event, we can use the wait() method on the event object whose internal flag is set to false, which will block the thread until the set() method sets the internal flag of that event object to true. The thread is not blocked if the internal flag is true on entry.
Below is a simple demo based on the code in your question which does what you want. There is not much to say about it, really, other than that you need to communicate between the worker and the main thread via signals (in both directions). The finished
signal is used to quit the thread, which will stop the warning message QThread: "Destroyed while thread is still running"
being shown.
The reason why you are seeing the error:
TypeError: connect() slot argument should be a callable or a signal, not `NoneType'
is because you are trying to connect a signal with the return value of a function (which is None
), rather than the function object itself. You must always pass a python callable object to the connect
method - anything else will raise a TypeError
.
Please run the script below and confirm that it works as expected. Hopefully it should be easy to see how to adapt it to work with your real code.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Cont(QWidget):
confirmed = pyqtSignal()
def __init__(self):
super(Cont, self).__init__()
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.bv.connect(self.bv_test)
self.worker.finished.connect(self.thread.quit)
self.confirmed.connect(self.worker.process_two)
self.thread.started.connect(self.worker.process_one)
self.thread.start()
def bv_test(self, n):
k = QMessageBox(self)
k.setAttribute(Qt.WA_DeleteOnClose)
k.setText('Quantity: {}'.format(n))
k.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
if k.exec_() == QMessageBox.Yes:
self.confirmed.emit()
else:
self.thread.quit()
class Worker(QObject):
bv = pyqtSignal(int)
finished = pyqtSignal()
def process_two(self):
print('process: two: started')
QThread.sleep(1)
print('process: two: finished')
self.finished.emit()
def process_one(self):
print('process: one: started')
QThread.sleep(1)
self.bv.emit(99)
print('process: one: finished')
app = QApplication([''])
win = Cont()
win.setGeometry(100, 100, 100, 100)
win.show()
app.exec_()
If you want the thread to wait for the action, connect to a signal from the thread using
PyQt4.QtCore.Qt.BlockingQueuedConnection
as flag.
Now I do not understand why you need threading if you let them wait, which brings in a lot of complexity. For me the better solution would be to cut the task you want to perform in the threads in smaller pieces. Each time a piece is ready, you can ask if the user wants the next too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With