Why does PyQt crashes without information? (exit code 0xC0000409)
Asked Answered
A

2

9

I'm trying to develop a software with PyQt, but I often get stuck on software crashes without debug information (only the exit code 0xC0000409). I'm using QThread, and I wrote a system like this:

class serialThreadC(QThread):
    updateOutBox = QtCore.pyqtSignal(str)
    updateStatus = QtCore.pyqtSignal(int)

    def __init__(self):
        super(serialThreadC, self).__init__()
        self.ser = False
        self.state = 0
        self.serialEnabled = False

    def run(self):
        while True:
            if self.state == -3 or self.state == -2:
                if self.SerialEnabled:
                    self.updatePB(20)
            elif self.state == 0:
                if self.serialEnabled:
                    self.updatePB(20)

    def ConnDisconn(self):
        self.serialEnabled = not self.serialEnabled

    def updatePB(self, stat):
        self.state = stat
        self.updateStatus.emit(self.state)

serialThread = serialThreadC()
serialThread.start()

## sw is a QDialog already loaded
serialThread.updateOutBox.connect(sw.updateOutBox)
serialThread.updateStatus.connect(sw.updateStatus)

sw.PB_ConnDisconn.clicked.connect(serialThread.ConnDisconn)

I have crashes when I read/write serialEnabled in run() or in ConnDisconn(). I know that PyQt is not thread-safe and that a wrong handling of variables gives crashes of my type, but I can't understand what is wrong with my code. My idea (maybe wrong) is that all serialThread methods are executed on the same thread, also if they are connected to a gui (main thread). Is that wrong? In the same way, I emit events from serialThread and I connected them to the GUI, but that never gave me problems.

Can you see the mistake I made? Is there a way to debug the code if there is a crash without other infos? (I use PyCharm 2017.1.3).

Aureliaaurelian answered 12/10, 2017 at 13:1 Comment(3)
Have you tried to run from the terminal?Estradiol
That's true! In the terminal i have the cause of the crash :| And i waste like 8hours to debug a code without informations... In that case it seems python can't understand the overload i did of two similar functions updatePB(self, stat) and updatePB(self), crying because i gave 2 parameters instead of 1 when i call it.Aureliaaurelian
Thanks @eyllanesc! I was trying to run code with PyCharm's run/debug configurations and got only error code.Lorettalorette
P
10

PyQt is thread-safe to the same extent that Qt is thread-safe. The Qt docs will tell you which parts of their API are guaranteed to be so, and under what circumstances.

Cross-thread signals are thread-safe, so calling the updatePB method in your example is okay. Your ConnDisconn method is not thread-safe, but that has got nothing to do with PyQt or Qt - it's just a consequence of how you wrote it. The serialEnabled attribute could be read/written by two threads simultaneously, so the behaviour is strictly undefined. A thread-safe way of writing this would be to use a mutex, like so:

class serialThreadC(QThread):
    updateOutBox = QtCore.pyqtSignal(str)
    updateStatus = QtCore.pyqtSignal(int)

    def __init__(self):
        super(serialThreadC, self).__init__()
        self.ser = False
        self.state = 0
        self._mutex = QMutex()
        self.serialEnabled = False   

    def ConnDisconn(self):
        self._mutex.lock()
        self.serialEnabled = not self.serialEnabled
        self._mutex.unlock()

    def run(self):
        while True:
            if self.state == -3 or self.state == -2:
                self._mutex.lock()
                if self.serialEnabled:
                    self.updatePB(20)
                self._mutex.unlock()
            elif self.state == 0:
                self._mutex.lock()
                if self.serialEnabled:
                    self.updatePB(20)
                self._mutex.unlock()

(NB: if you're using any kind of IDE or debugger, and you are getting unexpected errors or crashes, your first step in diagnosing the problem should always be to test the code in a standard console. Quite often, the IDE or debugger itself can be the cause of the problem, or may mask error messages comming either from Python or from underlying libraries, such as Qt).

Personalty answered 12/10, 2017 at 14:7 Comment(6)
I don't get the point. Since you told that the ConnDisconn(self) is a different thread than serialThread (that makes sense, ConnDisconn is connected to a GUI signal) the same should be for sw.updateStatus (that's not in the main thread). In the sw.updateStatus I MyButton.setText(), is this operation allowed? And more, is it also forbidden to read variables concurrently using different threads?Aureliaaurelian
@brazoayeye. I did not say that ConnDisconn is in a different thread (and it isn't, it lives in the main thread, and is called by the main thread). But I did say that updatePB is okay, because it emits a cross-thread signal, which is guaranteed (by Qt) to be thread-safe. So yes, as a direct consequence of that, it is safe to call setText(). This is all standard stuff in PyQt, and there are dozens of similar questions about it on SO. It is never forbidden to read/write anything by multiple threads - that is the whole problem! (And that is what the mutex is for).Personalty
@brazoayeye. I should add, though, that for your specific example, the mutex is probably overkill, because (I assume) there is only ever one thread that modifies serialEnabled (i.e. the main thread). So your example code looks okay, even though it is not 100% strictly correct. As far as fixing your code is concerned, the most important part of my answer is probably the note at the bottom. The rest just tries to answer the more general questions you asked.Personalty
if ConnDisconn lives in the main thread because it's called by the main thread with signal-slots, why doesn't sw.updateStatus lives in the serialThread since it's called by serialThread in a mostly identical way? Can you post most relevant questions-tutorial to deepen the topic?Aureliaaurelian
@brazoayeye. Because serialThread also lives in the main thread. The QThread class is not itself a thread - it is merely a class which manages an underlying thread provided by the OS. So your serialThread and its methods all live in the main thread. The only part of your example that is executed outside the main thread is the code in the run method. This calls updatePB, which emits a signal. Qt automatically detects when a signal is sent from one thread to another, and so does it in a thread-safe way (by posting an event to the event-loop of the receiving thread).Personalty
I used anaconda terminal to run the code and voila, I spotted the bug! Thanks @PersonaltyPalestine
B
-1

I set all float variables to int values and it worked

before


var1 = 10.0

var2 = 26.0

after


var1 = 10

var2 = 26
Bearskin answered 22/12, 2021 at 16:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.