2017-10-12 4 views
1

J'essaie de développer un logiciel avec PyQt, mais je suis souvent bloqué sur les plantages logiciels sans informations de débogage (seulement le code de sortie 0xC0000409). J'utilise QThread, et j'ai écrit un système comme celui-ci:Pourquoi PyQt se bloque sans information? (code de sortie 0xC0000409)

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) 

Je plante lorsque je lecture/écriture serialEnabled dans run() ou ConnDisconn(). Je sais que PyQt n'est pas thread-safe et qu'une mauvaise manipulation des variables donne des accidents de mon type, mais je ne peux pas comprendre ce qui ne va pas avec mon code. Mon idée (peut-être fausse) est que toutes les méthodes serialThread sont exécutées sur le même thread, même si elles sont connectées à un gui (thread principal). Est-ce faux? De la même manière, j'émets des événements de serialThread et je les ai connectés à l'interface graphique, mais cela ne m'a jamais posé de problèmes.

Pouvez-vous voir l'erreur que j'ai faite? Y at-il un moyen de déboguer le code s'il y a un crash sans autres infos? (J'utilise PyCharm 2017.1.3).

+1

Avez-vous essayé de démarrer à partir du terminal? – eyllanesc

+0

C'est vrai! Dans le terminal, j'ai la cause de l'accident: | Et je gaspille comme 8hours pour déboguer un code sans informations ... Dans ce cas, il semble que python ne peut pas comprendre la surcharge que j'ai fait de deux fonctions similaires updatePB (self, stat) et updatePB (self), pleurer parce que j'ai donné 2 paramètres au lieu de 1 quand je l'appelle. – brazoayeye

Répondre

1

PyQt est thread-safe dans la même mesure que Qt est thread-safe. Les docs Qt vous diront quelles parties de leur API sont garanties, et dans quelles circonstances.

Les signaux d'interconnexion sont thread-safe, donc appeler la méthode updatePB dans votre exemple est correct. Votre méthode ConnDisconn n'est pas thread-safe, mais cela n'a rien à voir avec PyQt ou Qt - c'est juste une conséquence de la manière dont vous l'avez écrite. L'attribut serialEnabled peut être lu/écrit simultanément par deux threads, donc le comportement est strictement indéfini. Une façon thread-safe d'écrire serait d'utiliser un mutex, comme ceci:

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: si vous utilisez tout type d'IDE ou débogueur, et vous obtenez des erreurs inattendues ou accidents, votre La première étape dans le diagnostic du problème devrait toujours être de tester le code dans une console standard Très souvent, l'IDE ou le débogueur lui-même peut être la cause du problème ou masquer les messages d'erreur provenant de Python ou des bibliothèques sous-jacentes, telles que Qt).

+0

Je ne comprends pas. Puisque vous avez dit que le 'ConnDisconn (self)' est un thread différent de 'serialThread' (ce qui est logique,' ConnDisconn' est connecté à un signal GUI), il devrait en être de même pour 'sw.updateStatus' (ce n'est pas le cas fil). Dans 'sw.updateStatus' I' MyButton.setText() ', cette opération est-elle autorisée? Et plus, est-il également interdit de lire des variables simultanément en utilisant des threads différents? – brazoayeye

+0

@brazoayeye. Je n'ai pas dit que 'ConnDisconn' est dans un thread différent (et il ne l'est pas, il vit dans le thread principal, et est appelé par le thread principal). Mais j'ai dit que 'updatePB' est correct, car il émet un signal de cross-thread, qui est garanti (par Qt) pour être thread-safe. Donc oui, comme une conséquence directe de cela, il est sûr d'appeler 'setText()'. Tout cela est standard dans PyQt, et il y a des dizaines de questions similaires sur SO. Il n'est jamais * interdit * de lire/écrire * n'importe quoi * par plusieurs threads - c'est tout le problème! (Et c'est ce que le mutex est pour). – ekhumoro

+0

@brazoayeye. Je devrais ajouter, cependant, que pour votre exemple spécifique, le mutex est probablement overkill, parce que (je suppose) il n'y a qu'un seul thread qui modifie 'serialEnabled' (c'est-à-dire le thread principal). Votre code d'exemple semble correct, même s'il n'est pas 100% strictement correct. En ce qui concerne la fixation de votre code, la partie la plus importante de ma réponse est probablement la note en bas. Le reste essaie juste de répondre aux questions plus générales que vous avez posées. – ekhumoro