Python schließt nicht alle Threads - ASYNCIO - PyQT

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
schabi
User
Beiträge: 6
Registriert: Donnerstag 8. Februar 2018, 12:45

Hallo,
ich experimentiere gerade mit Python und versuche eine Textbox (QTGui) durch einen anderen Thread befüllen zu lassen. Das Problem jedoch ist wenn ich die 2 Threads anhalte, Python nicht beendet wird. Ich habe das Gefühl das irgendein Thread weiterläuft. Ebenfalls ist mir bewusst das man mein Beispiel wahrscheinlich über Signal und Slots besser verarbeiten kann. Jedoch wollte ich irgendwie anfangen und so eine richtiges Tutorial über das selbsterstellen von Signals und Slots hab ich noch nicht gefunden. Für Beispiele und Tutorials die mir dies erklären oder eine Erläuterung was ich in meinem konkreten Beispiel falsch gemacht habe wäre ich dankbar. :)

Code: Alles auswählen

import sys
import random
#asyncio test
import asyncio
import threading
#hier hole ich mir die Schnittstelle
from qtpy import QtWidgets
from qtpy import QtGui
#hier wird die GUI selbst geladen
from ui.textbox import Ui_MainWindow
import time

#hiermit wird die Applikation erzeugt ist grundvorraussetzung
app = QtWidgets.QApplication(sys.argv)

#Grundlegende Initalisierung der GUI
class textbox_thread_test(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_MainWindow()
        self.lauf = True
        self.ui.setupUi(self)
        self.show()
    #Beginn eigener Funktionen
    def text_anhängen(self, text):
        self.ui.textEdit.append(text)
    async def _schreib_test_b(self,text):
        while self.lauf:
            print("test aus der qtapp beginnt")
            self.text_anhängen("test vor dem schlafen gehen")

            await asyncio.sleep(2)
            self.text_anhängen("text nacht dem schlafen")

            print("fertig")
            if self.lauf == False:
                break
        print("schreibtest ende")
    async def process_events(self):
        while self.lauf:
            app.processEvents()

            await asyncio.sleep(0.02)
            if self.lauf == False:
                break
        print("process_events_ende")
    def async_task_start(self):
        ioloop = asyncio.get_event_loop()
        #""" gatherer methode
        task1 = asyncio.gather(*[self._schreib_test_b("Testmessage aus asynchronen Thread")])
        task2 = asyncio.gather(*[self.process_events()])
        print("vor dem start des Tasks")
        all_tasks = asyncio.gather(task1, task2)
        #"""
        #ensure_feature test
        #task1= asyncio.ensure_future(self._schreib_test_b("Testmessage"))
        #task_2 =asyncio.ensure_future(self.process_events())

        """
        tasks = [ioloop.create_task(self._schreib_test_b("Testmessage"))]
        tasks_2 = [ioloop.create_task(self.process_events())]
        all_tasks = asyncio.wait(tasks, tasks_2)"""
        ioloop.run_until_complete(all_tasks)
        #ioloop.run_forever()
        print("nach dem Start des Task")
        ioloop.close()
        print("loop wurde beendet")
    def closeEvent(self, a0: QtGui.QCloseEvent):
        self.lauf = False
        print("wurde geschlossen")
        a0.accept()
        self.close()

#Erzeugen des Fensters
test_fenster = textbox_thread_test()

#start des Codes

test_fenster.ui.textEdit.append("test")
test_fenster.text_anhängen("test2 \n")
test_fenster.async_task_start()



#Programm sauber beenden
print("vor dem sys.exit")
sys.exit(app.exec_())
sys.exit()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte schau dir mal PEP8 an - du haelst dich nicht an die Python Namenskonventionen, und auch wenn das inhaltlich mit deinem Problem nix zu tun hat, verwirrt es uns, die wir deinen Code lesen und verstehen sollen.

Dann zu deinem Problem: du hast keinen Thread. Du hast einen Task, der asynchron abgearbeitet werden soll. Prinzipiell erstmal sehr loeblich (ich versuche auch gerade mehr und mehr asyncio zu verwenden).

Aber was dann kracht ist, das den event-Loop selbst laufen laesst. Qt (und jedes GUI-framework) moechte seinen eigenen Event-Loop laufen lassen. Das beisst sich damit, dass du in deiner Methode da einfach rumstehst im asyncio-Loop.

Dazu musst du etwas tiefer in die Trickkiste greifen, aber spaghettimonsterlob hat das wer gemacht fuer dich: https://pypi.python.org/pypi/Quamash

Das solltest du dir anschauen.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ein GUI hat eine eigenen Main-Loop. Den mit einem anderen (wie dem von asyncio) zu kombinieren ist ein ganz schlechte Idee - das geht in der Regel schief.

asyncio ist hier IMHO so wie so die komplett falsch Wahl, weil das nicht dafür gemacht ist, was du vor hast. asyncio ist dafür gedacht, wenn man auf I/O wie Netzwerkverkehr warten muss, aber alles in einem nicht-blockierenden Main-Loop haben will.
Und: asyncio != threading! Das sind zwei verschiedene Ansätzes, die du scheinbar durcheinander wirfst.

Qt geht in Kombination mit Threads, dafür hat Qt aber eigene Klassen für Threads. Vorteil: die harmonieren besser mit dem Main-Loop von Qt. Dazu können dir sicherlich Leute, die mehr Ahnung von Qt haben als ich und hier im Forum aktiv sind, mehr sagen.

Im Prinzip programmierst du IMHO ziemlich an Qt und dessen Möglichkeiten vorbei in deinem Beispiel, was logischer Weise nicht gut ist.

Zu deiner Beruhigung: asyncio braucht man als "Hobbyprogrammierer" eher selten bis gar nicht, jedenfalls nicht, wenn man nicht einen Server, Socketserver oder so was programmieren will oder massiv-parallel Daten von Webseiten / Web-APIs abfragen will.
Ich habe für mich auch noch keinen brauchbaren "Use case" gefunden.

Gruß, noisefloor
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

noisefloor hat geschrieben: asyncio ist hier IMHO so wie so die komplett falsch Wahl, weil das nicht dafür gemacht ist, was du vor hast. asyncio ist dafür gedacht, wenn man auf I/O wie Netzwerkverkehr warten muss, aber alles in einem nicht-blockierenden Main-Loop haben will.
Und: asyncio != threading! Das sind zwei verschiedene Ansätzes, die du scheinbar durcheinander wirfst.
Das sehe ich nicht so. Wir muessen in unserer GUI-Anwendung eine Vielzahl von Aufgaben erledigen, die nur im Main-Thread getaetigt werden koennen, aber eben genau um keine Probleme mit einer blockierenden GUI zu bekommen in kurze Happen aufgeteilt werden muessen, die dann muehselig per Timer-Calls aneinander gestoppelt werden. Genau dazu ist asyncio gedacht. Ob nun ein Timer feuert, oder ein Filedeskriptor Daten anliegen hat: vom OS aus betrachtet ist das eine Sosse. Die Arbeit also als coroutine zu definieren, und dann mit vielen freiwilligen rueck-delegierungen an den Mainloop zu garnieren ist genau die richtige Vorgehensweise (erst recht, wenn dabei dann wiederum auf IO gewartet wird).

Das sich das beim TE noch zurechtruckeln muss steht auf einem anderen Blatt, muss es bei mir auch. Ist halt nicht an einem Nachmittag gelernt. Aber trotzdem wertvoll.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@__deets__: Ok, Korrektur: wenn der von dir genannten Integration von asyncio in Qt macht die Nutzung mehr Sinn - das Projekt kannte ich bis dato auch nicht. Wenn man sich das selber basteln müsste wäre es halt sehr schwierig (zumindest für Hobbyprogrammierer) und dann macht der Ansatz halt wenig Sinn. Wobei im Beispiel des TE IMHO Signals und Slots mehr Sinn machen.
Ist halt nicht an einem Nachmittag gelernt.
Das stimmt wohl. Asyncio ist noch relativ neu und entsprechend "dünn" ist die Anzahl der "real world" Beispiele im Netz. Was IMHO ganz gut für's Grundverständnis ist, sind die asyncio-Seiten bei PMOTW.

Gruß, noisefloor
schabi
User
Beiträge: 6
Registriert: Donnerstag 8. Februar 2018, 12:45

Hallo Ihr beiden :)
ich bedanke mir für eure Antworten und euer Feedback. Mein Ziel ist es eine einfache Server-Chat-Anwendung zu schreiben (um mich mit Python einzuarbeiten) und da hatte ich mich versucht einzulesen und bin öfters über das neue Modul Asyncio gestolpert das dies für diese Richtung genau das richtige ist. Ich muss auch zugeben das mein Quelltext ziemlich durcheinnander aussieht aber es war auch einfach eine mülldatei in der ich alles möglich einfach nur testen wollte. Mittlerweile habe ich eine komplett neue angefangen und die sauberer strukturiert und ebenfalls versucht die Namenskonventionen einzuhalten ^^.
Antworten