In this case, you're depending on the Qt side (signal/slot management) to keep your lambda alive along with its entire call stack. But python doesn't know anything about QObject pointers, and will happily GC it. The next time that signal is fired, crash is extremely likely.
This used to be a much bigger problem in PyQt and PySide 4 and earlier, but it appears to have gotten better recently in PyQt at least.
In general, the best way to work with Qt is to let it assume control, and it use its mechanisms for lifecycle-management. I converted the code so that it used a QThread-derived Python class and QTimer. Works great.
You basically have to understand the Deep Magic that makes Python work under the hood, and ditto for Qt, too.
edit: can't comment any further currently, I will try to answer everything once I'm unblocked.
We've marked your account legit so it won't happen again. Welcome to HN!
Working with QT bindings is very difficult. Debuging things is not easy. I'm not sure if using bindings is worth it. I suspect doing things in plain C++ QT might be easier to debug and maintain.
Qt is generally type safe, but it uses quite a lot of pointer types and also a rather unusual for C++ parent-child resource cleanup rule for QObjects (e.g. all widgets).
When something does crash, C++ doesn't offer any tracebacks, but if one has a core dump or can reproduce the crash under a debugger, a traceback can be easily obtained in the majority of cases.
how do you create core dump? starting program under some debugger? I tried debugging with valgrind but the output contained so much noise it took my ages to find something. I also had to recompile QT used by PyQT because QT because QT requires some flags to be able to debug. What's the best way to debug things like this?
If one can run the target program under a debugger, a core dump is not so helpful because it's a static snapshot, whereas a debugger can modify program state.
I am guessing that one has to run the python process itself under the debugger and pass the parameters such as the script path.
There will probably be a signal or exception condition at one point and one can take a look at the call stack. The call stack will probably be messy, as it will include python internals.
An alternative way to debug this is to understand the lifetimes and preconditions of C++ widgets and signals and slots and review one's python code.
Unless there's a bug in Qt itself, PyQt and Qt might have a misunderstanding about lifetimes and something gets cleaned up too soon or there's a dangling refrence to a cleaned up object
Valgrind is not really useful for debugging crashes. Running under gdb would work though.
Sidenote: It's also possible to build Qt applications in Python using AsyncIO (using quamash), by integrating the Qt event loop as an AsyncIO event loop. This can be helpful to avoid race conditions, as much more interactions can be handled without thread concurrency.
I managed to use pyqt and autobahn|python (wamp protocol, RPC and pub/sub) in the same app thanks to quamash.
I set up an example app here https://github.com/icefo/PyQt5-Wamp
I've fixed crashes by keeping stuff assigned to variables to keep it from being GCed.
Once I have been building Qt Widgets application in Python with PySide bindings. I needed to do some simple processing in several SLOTs and decided to use lambdas as an easy replacement. Kids, do not even think about attempting to do this at home, seriously.
I have run into some extremely evasive random crashes and stuff. I did not dig to the bottom of the issue, although my guess is that Python lambdas are executed in main (constructing) thread context and that causes race conditions.
My question: how do Golang's goroutines interact with native threads spawned by Qt?