QThread: Destroyed while thread is still running

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Hallo Zusammen,

vielleicht könnt ihr mir helfen?! Seit Tagen sitze ich an diesem simplen Problem und kriege es nicht gebacken...Das Problem ist simpel: Drücke ich auf den "Create Alarms" Button in der GUI, wird eine zuvor erstellte *.csv geladen, die ich dann in einer for-Schleife durchlaufe. Nach jedem Durchlauf soll die Zeile im QTextBrowser angezeigt werden.

Mache ich da keinen extra Thread, freezt die GUI ein. Das ist mir soweit bewusst. Wie kriege ich das hin?

Ich hab es schon mit QRunnable Klassen versucht, mit QtCore.QMetaObject.invokeMethod()...aber nix löst mein simples Problem. Für jeden Hinweis, wie ich das Problem gelöst bekomme und weshalb diese Fehlermeldung auftaucht, wäre ich sehr dankbar. Die run(self) Funktion der AlarmLog(QThread) Klasse wird nicht mal ausgeführt.

Das habe ich bei einer von QRunnable abgeleiteten KLasse immerhin geschafft, aber dann kam beim Versuch, in der GUI die Information anzuzeigen, der Fehler, dass dieser Thread nicht auf Objekte des PyQT Threads zugreifen könnte.

Also zusammengefasst: Ich möchte nur über meine csv Datei iterieren und jede Zeile in die GUI mittels extra Thread anzeigen lassen.

Code: Alles auswählen

class AlarmLog(QThread):
    """ raise Alarmingtime into AlarmLog in GUI as a new Thread to avoid freezing gui """

    def __init__(self, GUI):
        super(AlarmLog, self).__init__()
        self.GUI = GUI
        self.alarmSplitted = []
           
    AlarmLogChange = pyqtSignal(str)
    
    def run(self): 
        print("inside run")
    #   QtCore.QMetaObject.invokeMethod(self.GUI.alarmLog, QtCore.Qt.QueuedConnection, QtCore.Q_ARG(str, text))
    
        # *********** SPEAK ENGINE **********************
        print("inside run")
        engine = pyttsx3.init()
        engine.setProperty('rate', 120)
        voices = engine.getProperty('voices')  
        engine.setProperty('voice', voices[1].id)

        with open("ALARMTIME_PTS_DISCOVERY_SORTED.txt") as fileobject:
            for row in fileobject:
               self.alarmSplitted.append(row) 

        for alarmtime_pts in self.alarmSplitted:
            speak = alarmtime_pts[20:35].replace("_", "")
            channel = alarmtime_pts[41:47]
            if channel == "DMAXHD":
                channel = "deemax"
            elif channel == "TLCHD":
                channel = "Teelcee"
            elif channel == "HGTVHD":
                channel = "HGTVee"
            if speak == "Cornerbug in":
                speak = "Kornerbaak in"
            if speak == "Commercial promo":
                speak = "commercial proomoooo"

            # ****** Dateobject (without dT Alias not working!) ******
            self.timestamp_CEST = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.timestamp_CEST = dt.datetime(int(self.timestamp_CEST[0:4]), int(self.timestamp_CEST[5:7]), int(self.timestamp_CEST[8:10]) , int(self.timestamp_CEST[11:13]) ,int(self.timestamp_CEST[14:16]) ,int(self.timestamp_CEST[17:19]))
            self.timestamp_PTS = dt.datetime(int(alarmtime_pts[0:4]), int(alarmtime_pts[5:7]), int(alarmtime_pts[8:10]) , int(alarmtime_pts[11:13]) ,int(alarmtime_pts[14:16]) ,int(alarmtime_pts[17:19]))
            self.timestamp_SLEEP = self.timestamp_PTS - self.timestamp_CEST  
            self.timestamp_SLEEP = self.timestamp_SLEEP.total_seconds()           # Converting in Seconds for sleep()
            self.Alarmtext = f"{alarmtime_pts:>20}{'(':>2}{self.timestamp_SLEEP:>6}{' seconds )':>1}"
            self.AlarmLogChange.emit(self.Alarmtext)
          
            
    
class GUI(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi(r"C:\Users\Marc\SINGLECHANNEL_ALARMING_TOOL\VERS_02_DISCOVERY\SINGECHANNEL_ALARMING_TOOL_DISCOVERY.ui", self)
        self.ui.setWindowTitle('DISCOVERY ALARMING TOOL')
        self.ui.createAlarms.clicked.connect(self.initAlarmingtool)
        self.threadpool = QThreadPool()
        
        
    def initAlarmingtool(self):
        GUI = self
        call_class_alarmingtool(GUI)
        AlarmlogThread = AlarmLog(GUI) 

        # QTREAD *********************
        AlarmlogThread.AlarmLogChange.connect(self.ChangeLog)
        AlarmlogThread.start()
 

    def ChangeLog(self, Alarmtext):
        self.ui.alarmLog.setText(Alarmtext)
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hier ein Beispiel, das zeigt, wie es geht: viewtopic.php?f=24&t=44250&start=15#p335559

Und bitte, bitte, bitte: beachte PEP8. Mir wird beim betrachten deines Codes ganz schummerig. Die Benamung ist Kraut und Rueben und verstoesst gegen etablierte und wichtige Konventionen.
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

__deets__ hat geschrieben: Samstag 5. Dezember 2020, 17:04 Hier ein Beispiel, das zeigt, wie es geht: viewtopic.php?f=24&t=44250&start=15#p335559

Und bitte, bitte, bitte: beachte PEP8. Mir wird beim betrachten deines Codes ganz schummerig. Die Benamung ist Kraut und Rueben und verstoesst gegen etablierte und wichtige Konventionen.
Also ich verstehe deinen Code nicht, da ich kein Crack bin sondern mich mühsam versuche, durch die Materie zu kauen. Ich stehe jetzt bei 98% Fertigstellung meines Skripts und brauche auf meine Frage kein neues Skript mit 158 Zeilen, sondern einen Hinweiß, wo in meinem Programm der Fehler liegt. Wenn ich dein Beispiel kapieren würde, müsste ich die Frage nicht stellen. Den Hinweiß, dass die Benamen "Kraut und Rueben uns) ist, nehme ich gerne an und werde mir PEP8 sicher mal zur Gemüte führen.

Trotzdem: Mein Problem bleibt bestehen.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich verstehe, dass es dir nicht zuzumuten ist, durch 158 Zeilen Code zu wuehlen. Darum hast du sicher auch Verstaendnis dafuer, dass es mir mit ~70 Zeilen Code, die noch nicht mal das Problem reproduzierbar darstellen, aehnlich geht.

Viel Erfolg bei den verbleibenden 2%.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ein Nachtrag, weil ich es gerade gesehen habe: AlarmlogThread.start() ist das *ERSTE* deiner Probleme, denn damit wird dein Thread-Objekt nicht festgehalten. Und das provoziert die Fehlermeldung. Abe danach kommst du in Probleme, die nur durch Verstaendnis der meinem Code unterliegenden Mechanismen geloest werden koennen. Der im uebrigen nicht meiner Genialitaet geschuldet ist, sondern das von Qt so empfohlene Vorgehen abbildet.
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Ich hab versucht, den Code zu adaptieren. Außerdem hatte ich eine stop(self) Methode und eine __del__(self) Methode implementiert, um irgendwie diesen blöden Thread zu stoppen. Nichts funktioniert. Ich bekomme immer noch diese blöde o.g. Meldung. Es kann doch nicht so schwer sein, einen blöden Thread nach ablauf zu beenden, damit das Programm ohne Absturz läuft?!

Code: Alles auswählen

class Progress(QThread):
    """
    Runs a counter thread.
    """
      
    work_started = pyqtSignal()
    work_done = pyqtSignal()
    countChanged = pyqtSignal(int)

    def run(self):

        self.work_started.emit()

        try:
            TIME_LIMIT = 1000
            count = 0
            while count < TIME_LIMIT:
                count +=1
                self.countChanged.emit(count)
        except Exception as e:
                logging.exception("Exception occurred")

        self.work_done.emit()
        
 class GUI(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi('SINGLECHANNEL_ALARMING_TOOL_DISCOVERY.ui', self)
        self.ui.setWindowTitle('DISCOVERY ALARMING TOOL')
        self.ui.createAlarms.clicked.connect(self.initAlarmingtool)
        self.ui.progressBar.setValue(0)


                    
    def initAlarmingtool(self):
        try:
            GUI = self
        
            worker_thread = QThread()
            self.progressini = Progress()
            self.progressini.moveToThread(worker_thread)
            self.progressini.countChanged.connect(self.onCountChanged)
            self.progressini.start()  
            
            
            my_exit_function()
            call_class_alarmingtool(GUI)

            self.calc = External()
            self.calc.AlarmLogChange.connect(self.displayAlarmLog)
            self.calc.CounterChange.connect(self.countChange)
            self.calc.start()

        except Exception as e:
                logging.exception("Exception occurred")
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist keine Adaption meines Codes. Denn der leitet bewusst nicht von QThread ab, sondern nutzt thread ownership und queued connections. Nichts von dem ist hier zu sehen. Und immer noch hältst du das erzeugte Objekt nicht fest. So wird es garbage collected & der Fehler den du beobachtest tritt auf. Wenn du das änderst, kommt der nächste - Signale die aus dem falschen Thread kommen.

Nebenläufige Programmierung ist schwer. Nebenläufige GUI Programmierung ist schwerer. Da muss man halt auch mal probieren ein komplexes Beispiel Code zu verstehen 🤷‍♂️
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich muss mich korrigieren, “nichts zu sehen” ist falsch. Ich bin schon beim Progress Objekt abgebogen. Das darf NICHT von qthread erben, und tut das in meinem Beispiel auch nicht. Und der worker_thread wird stattdessen nicht gestartet. Und such nicht gemerkt. Und der self.calc Kram ist der alte Unfug.
Antworten