Skip to content

Commit 4262b63

Browse files
Adjust Workers to do everything in thread as expected to stop crashes (#958)
1 parent b71abf1 commit 4262b63

1 file changed

Lines changed: 42 additions & 2 deletions

File tree

src/Workers.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
import threading
1212

1313

14+
class _ProgressEvent(QEvent):
15+
_type = QEvent.Type(QEvent.registerEventType())
16+
17+
def __init__(self, n, t):
18+
super().__init__(self._type)
19+
self.n = n
20+
self.t = t
21+
22+
1423
class WorkerSignals(QObject):
1524
'''
1625
Defines the signals available from a running worker thread.
@@ -27,14 +36,20 @@ class WorkerSignals(QObject):
2736
`object` data returned from processing, anything
2837
2938
progress
30-
`int` indicating % progress
39+
`int` indicating % progress
3140
3241
'''
3342
finished = Signal()
3443
error = Signal(tuple)
3544
result = Signal(object)
3645
progress = Signal(int, int)
3746

47+
def event(self, e):
48+
if e.type() == _ProgressEvent._type:
49+
self.progress.emit(e.n, e.t)
50+
return True
51+
return super().event(e)
52+
3853

3954
class Worker(QRunnable):
4055
'''
@@ -59,8 +74,33 @@ def __init__(self, fn, *args, **kwargs):
5974
self.kwargs = kwargs
6075
self.signals = WorkerSignals()
6176

77+
# ORIGINAL METHOD
6278
# Add the callback to our kwargs
63-
self.kwargs['progress_callback'] = lambda n, t: self.signals.progress.emit(n, t)
79+
# self.kwargs['progress_callback'] = lambda n, t: self.signals.progress.emit(n, t)
80+
81+
# SECOND WAY, STILL FAILED
82+
# Add the callback to our kwargs.
83+
# Use QTimer.singleShot with self.signals as the context object so the
84+
# emission is always marshalled to the GUI thread, regardless of which
85+
# thread calls the callback. Directly emitting Signal(int, int) from a
86+
# non-GUI thread triggers a PyQt6 bug in its typed-argument marshaling
87+
# that corrupts the heap and causes hard crashes in unrelated threads.
88+
# _signals = self.signals
89+
# self.kwargs['progress_callback'] = lambda n, t: QTimer.singleShot(
90+
# 0, _signals, lambda: _signals.progress.emit(n, t)
91+
# )
92+
93+
# NEW WAY
94+
# Add the callback to our kwargs.
95+
# Use QTimer.singleShot with self.signals as the context object so the
96+
# emission is always marshalled to the GUI thread, regardless of which
97+
# thread calls the callback. Directly emitting Signal(int, int) from a
98+
# non-GUI thread triggers a PyQt6 bug in its typed-argument marshaling
99+
# that corrupts the heap and causes hard crashes in unrelated threads.
100+
_signals = self.signals
101+
self.kwargs['progress_callback'] = lambda n, t: QApplication.postEvent(
102+
_signals, _ProgressEvent(n, t)
103+
)
64104

65105
# Cancellation event
66106
self.cancel_event = threading.Event()

0 commit comments

Comments
 (0)