App mit QThread crasht mit Python 3.10 (ohne Exception)

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Karsten Böhme
User
Beiträge: 86
Registriert: Sonntag 23. Dezember 2012, 07:54

Hallo zusammen,

habe meine Datenbankanwendung (PyQt5 + SQLite mit Pony ORM) auf Python 3.10 "umgestellt" (also schlicht den Interpreter gewechselt) Das sollte eigentlich geräuschlos von statten gehen, allerdings chrasht der Thread mit der Datenbankabfrage ohne Fehlermeldung.
Einzige Meldung in der Console : Process finished with exit code -1073741819 (0xC0000005)
Habe schon an allen möglichen Stellen mit try-except gearbeitet. Alle Abschnitte werden klaglos durchlaufen.

Code: Alles auswählen

class ViewAllThreadClass(QThread):
    thread_finished = pyqtSignal()

    def __init__(self, parent=None):
        QThread.__init__(self, parent=parent)
        self._dal: DataAccessLayerClass = DataAccessLayerClass()
        self._tw: QTableWidget = QTableWidget()
        self._net: str = ""
        self._view_from: datetime = datetime.now().date()
        self._view_to: datetime = datetime.now().date()
        self._view_mode: ViewMode = ViewMode.ACTIVATED

    def set_properties(self, dal: DataAccessLayerClass, tw: QTableWidget, net: str,
                       view_from: datetime, view_to: datetime, view_mode: ViewMode, parent=None):
        self._dal = dal
        self._tw = tw
        self._net = net
        self._view_from = view_from
        self._view_to = view_to
        self._view_mode = view_mode

    def run(self):
        result = self._dal.get_all_in_time_delta(self._view_from, self._view_to, self._net, self._view_mode)
        self._tw.setRowCount(0)
        query_result_to_table_widget(self._tw, result)
        self.thread_finished.emit()
Nach dem Erzeugen der Instanz werden über 'set_properties' die benötigten Objekte übergeben. DataAccessLayerClass kapselt den kompletten Datenbankzugriff
Ab der letzten Zeile (self.thread_finished.emit()) ist die Ausführung des Code nicht mehr verfolgbar. Hatte noch einen Breakpoint in der an thread_finished gebundenen Routine gesetzt. Der wird noch angesprungen, danach ist dann Schluss.

Hier der aufrufende Code:

Code: Alles auswählen

def _view_all_items(self):
    """
    with thread
    """
    if self.frame_csv.isVisible(): return
    self.can_refresh = True
    dt_view_from: datetime = self.dt_view_from.dateTime().toPyDateTime()
    dt_view_to: datetime = self.dt_view_to.dateTime().toPyDateTime()
    # view progress window
    self.prg_wnd.show()
    if (dt_view_to - dt_view_from).days > 0:
        self.prg_wnd.show()
    self._thread_running = True
    # initial and run thread
    self.view_all_thread.set_properties(self.dal, self.table_widget, self.net, dt_view_from, dt_view_to, ViewMode(self.table_view_index))
    self.view_all_thread.start()
    while self._thread_running:
        qApp.processEvents()
    #
    self.prg_wnd.close()
    self.table_widget.resizeRowsToContents()            # notwendig ! wird im thread (query_result_to_table_widget) nicht ausgeführt
 
self.prg_wnd enthält eine Prgressbar mit ständig durchlaufenden Balken. Dies wird über qApp.processEvendts() ermöglicht.
Der Aufruf self.thread_finished.emit() setzt die Statusvariable self._thread_running=False . Damit wird die while-Schleife beendet, self.prg_wnd geschlossen und die Methode _view_all_items bis zum Schluss ausgeführt.

Interessant ist dabei, dass die Anwendung noch kurz mit den im QTableWidget eingetragenen Daten zu sehen ist. Auch self.table_widget.resizeRowsToContents() wurde noch korrekt ausgeführt.
Wie gesagt, unter Python 3.9 lief alles klaglos. Im Forum von 'Pony' wird der Fehler bei Qt vermutet.

Hat hier jemand eine Idee, oder einen Lösungsvorschlag ? Da keine verwertbare Fehlermeldung erscheint, bin ich bisschen ratlos.

Vielen Dank
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du machst halt den klassischen Fehler bei multithread-GUI-Programmierung: du modifizierst GUI-Objekte, also zb self._tw, aus deinem Thread heraus. Das ist in GUIs ein absolutes no-no. Und fuehrt eben auch zu solchen crashes. Das es an Python liegt, bzw dessen Version, ist erstmal unwahrscheinlich, aber sowas ist auch notorisch schwer zu debuggen.

Die Loesung besteht darin, das worker-Pattern mit Qt-Threads zu benutzen, und mittels signal/slot Verbindungen aus dem Hintergrundthread die Veraenderungen an den Main-Thread zu delegieren - siehe https://doc.qt.io/qt-5/threads-qobject.html zur Einfuehrung und weitergehenden Beispielen.
Karsten Böhme
User
Beiträge: 86
Registriert: Sonntag 23. Dezember 2012, 07:54

Vielen Dank für Deinen Tipp. Da werde ich den ganzen Spaß mal umschreiben. Gehe ich richtig in der Annahme, dass die Progressbar für die Zeit der Tablewidget-Aktualisierung und des Datenbankzugriffs dann wieder "einfriert" ?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, das ist falsch. Wenn man das richtig macht, kann man sowas natürlich machen. Genau dazu sind ja die Signale da.
Karsten Böhme
User
Beiträge: 86
Registriert: Sonntag 23. Dezember 2012, 07:54

Danke !
Antworten