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'
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_()