Nested DirectConnection Signals are causing a segfault
-
I am using PySide v1.2.2 which wraps the Qt 4.8 framework. I have asked this question on "stack overflow":http://stackoverflow.com/questions/27690865/nested-directconnection-signals-are-causing-a-segfault but wanted to ask it here also to increase the chances of a quick resolution.
The code, exactly as it is below crashes with a segfault (copy/paste it into your system and run it). When I remove type=QtCore.Qt.DirectConnection from one or both of the signal constructor calls (thereby using QtCore.Qt.AutoConnection instead), everything runs the way it should: A widget appears, showing five progress bars filling up then emptying in an endless loop.
@from PySide import QtCore, QtGui
import timeclass Worker(QtCore.QThread):
sig_worker_update_progress = QtCore.Signal(int, int)def __init__(self, thread_id, *args, **kwargs): super(Worker, self).__init__(*args, **kwargs) self.thread_id = thread_id self.stop_requested = False def slot_interrupt(self): self.stop_requested = True def run(self): progress = 0 while(True): self.sig_worker_update_progress.emit(self.thread_id, progress % 100) progress += 1 if self.stop_requested: break else: time.sleep(0.1)
class Controller(QtCore.QObject):
sig_controller_update_progress = QtCore.Signal(int, int)def __init__(self, num_workers, *args, **kwargs): super(Controller, self).__init__(*args, **kwargs) self.workers = [] for i in range(num_workers): self.workers.append(Worker(i)) self.workers[i].sig_worker_update_progress.connect( self.slot_worker_update_progress, type=QtCore.Qt.DirectConnection) for worker in self.workers: worker.start() def slot_worker_update_progress(self, thread_id, progress): # Do # Stuff self.sig_controller_update_progress.emit(thread_id, progress)
class Monitor(QtGui.QWidget):
def init(self, num_workers, *args, **kwargs):
super(Monitor, self).init(*args, **kwargs)
main_layout = QtGui.QVBoxLayout()
self.setLayout(main_layout)
self.progress_bars = []for _ in range(num_workers): progress_bar = QtGui.QProgressBar() main_layout.addWidget(progress_bar) self.progress_bars.append(progress_bar) self.controller = Controller(num_workers) self.controller.sig_controller_update_progress.connect( self.slot_controller_update_progress, type=QtCore.Qt.DirectConnection) def slot_controller_update_progress(self, thread_id, progress): self.progress_bars[thread_id].setValue(progress)
if name == "main":
app = QtGui.QApplication([])
monitor = Monitor(5)
monitor.show()
app.exec_()@Why does using two nested DirectConnection signals cause a segfault? If Qt does not want you to do that, why isn't a more informative error given?
-
My python is a little rusty but from what I see you emit the signals from a worker thread and update the ui in the connected slot (is that correct?).
The default type of connection between threads is QueuedConnection and it is executed in the thread the slot owner lives. In your case this is the UI thread, which is fine. The signals from different threads get serializerd in a queue.
If you change it to DirectConnection it becomes more or less a function call and thus gets executed in the worker thread. There are 2 things wrong with that. First - you can only modify the UI from the main thread. Second - even if you weren't modifying the UI it becomes a race since you've got several threads accessing the same objects.
-
Hmm that makes sense. Is there a simple way of forcing delivery of all remaining queue items? I used a DirectConnection because the documentation advertised it as a way of ensuring the delivery of items before continuing with program flow. I understand that an AutoConnection is the recommended way of dealing with signals, but my use case needs a guarantee that the queue is cleared when the user presses an "Interrupt" button.
-
The queue is filled constantly by various stuff e.g. signals resulted from events of the OS, not only by your own signals.
The usual way to deal with this sort of thing is to increment an "atomic counter":http://doc.qt.io/qt-5/qatomicint.html in the worker and then decrement it in the handler. "Interrupt" would then stop the threads and "process events":http://doc.qt.io/qt-5/qcoreapplication.html#processEvents until the counter zeroes. -
Great. Thanks!