spacr.qt.thread_guard¶
Report timer starts attempted from the wrong Qt thread.
Qt normally reports only that a timer cannot start from another thread, without naming the responsible object or call site. This module wraps the timer entry points and logs a Python stack when that condition occurs. The recorded stacks can also be attached to a crash report if the process exits before the log is reviewed.
Each timer start adds one thread-affinity comparison. If the Qt entry points cannot be wrapped safely, the guard leaves them unchanged.
Functions¶
|
Stacks where a QObject was constructed away from the GUI thread. |
|
Wrap the timer entry points. Returns whether it took. |
|
Copies of the stacks recorded so far. |
Module Contents¶
- spacr.qt.thread_guard.born_off_thread() list[source]¶
Stacks where a QObject was constructed away from the GUI thread.
Nested helpers¶
- install._report(what: str, why: str) None¶
Record and log one cross-thread offence, with its stack.
The stack is trimmed of this function and its caller, so the top frame is the code that started the timer rather than the guard.
spacr/qt/thread_guard.py:59
- install._wrong_thread(obj) str¶
Why this start is illegal, or “” when it is fine.
Makes the same test Qt makes: is the object’s own thread the calling thread? Qt refuses whenever they differ, which happens both ways round – a worker touching a GUI object, and the GUI thread touching a worker-affine object; the second is easier to write by accident because the code reads as ordinary GUI-thread code.
The threads are compared with
==, notis.QThread.currentThread()returns a new Python wrapper around the same underlying QThread on each call, so an identity test would report every ordinary start, such as a plain GUI-threadtimer.start(), as illegal and bury the real reports.There is deliberately no “is the caller the GUI thread” fallback. It added nothing the affinity test does not already cover, and it was the half that misfired.
PySide6 can attach the returned QThread wrapper to the queried object. Restore the application’s adopted thread to its application after the read, so collecting a temporary timer cannot delete the GUI thread.
spacr/qt/thread_guard.py:75
- install.guarded_object_init(self, *args, **kwargs)¶
Note a QObject built off the main thread, after building it.
spacr/qt/thread_guard.py:136
- install.guarded_object_start(self, *args, **kwargs)¶
Report a QObject timer started off its own thread, then start it.
spacr/qt/thread_guard.py:127
- install.guarded_timer_start(self, *args, **kwargs)¶
Report a QTimer started off its own thread, then start it.
The start is reported and then performed. The guard is a diagnostic: refusing the start would change behaviour under the guard and hide the bug it reports.
spacr/qt/thread_guard.py:115