Nachfrage aus einer Funktion heraus?

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
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hallo,

ich stehe gerade vor dem Problem, wie man denn eine „Nachfrage“ aus einer Funktion bewerkstelligen kann.

Code: Alles auswählen

def upload(file_entry, expiry, server_data, ssh_client, callback=None):
    sftp = ssh_client.open_sftp()
    remote_file_name = os.path.join(server_data["remote_path"] + file_entry["file_name"])
    try:
        sftp.stat(remote_file_name)
        overwrite_yesno = input("Die Datei ist bereits auf dem Server vorhanden. Soll sie überschrieben werden? [j/n] ")
        if overwrite_yesno == "j":
            sftp.remove(remote_file_name)
        else:
            return None
    except FileNotFoundError as e:
        pass

    try:
        sftp.put(file_entry["file_entry"],
                 remote_file_name,
                 callback=callback)
    except TypeError as e:
        sftp.putfo(file_entry["file_entry"],
                   remote_file_name,
                   file_entry["file_size"],
                   callback=callback)

    database_entry = utilities.create_csv([remote_file_name, expiry])
    with sftp.file(server_data["remote_database"], mode="a") as database_file:
        database_file.write(database_entry + "\n")

    sftp.close()
    return server_data["url"] + file_entry["file_name"]
Im oberen Teil prüft er mit sftp.stat(remote_file_name), ob die Datei auf dem Server schon vorhanden ist. Auf der Konsole kann ich dann natürlich so eine einfache (und schlecht programmierte, ich weiß :D) Abfrage machen, aber ich rufe die Funktion jetzt auch innerhalb eines QThreads in der GUI auf. Und da stehe ich jetzt vor dem Problem, wie ich denn dazwischen kommunizieren kann. Mein QThread sieht so aus:

Code: Alles auswählen

class UploadThread(QtCore.QThread):
    transferred_bytes = QtCore.pyqtSignal(int)
    upload_finished = QtCore.pyqtSignal(list)

    def __init__(self, server_data, files, expiry):
        super().__init__()
        self.server_data = server_data
        self.files = files
        self.expiry = expiry
        self.overall_file_size = 0
        for file_entry in files:
            self.overall_file_size += file_entry["file_size"]
        self.already_uploaded_size = 0

    def run(self):
        ssh_client = ssh.connect_to_ssh(self.server_data)
        ssh.check_server_requirements(self.server_data, ssh_client)
        uploaded_files = []
        for file_entry in self.files:
            uploaded_file = ssh.upload(file_entry,
                                       self.expiry,
                                       self.server_data,
                                       ssh_client,
                                       self.upload_callback)
            if uploaded_file is not None:
                uploaded_files.append(uploaded_file)
                self.already_uploaded_size += file_entry["file_size"]
        ssh_client.close()
        self.upload_finished.emit(uploaded_files)

    def upload_callback(self, bytes_transferred, bytes_total):
        self.transferred_bytes.emit(self.already_uploaded_size + bytes_transferred)
Grob gesagt soll der mit sftp.stat(remote_file_name) ausprobieren, ob die Datei schon vorhanden ist. Wenn das klappt, soll er das an die GUI melden und dort soll dann eine QMessageBox erscheinen. Das Problem ist ja nur, dass ich dort jetzt keine Exception o.ä. aufrufen kann, da der dann ja aus der Funktion herausspringt. Der soll stattdessen ja warten, bis es eine Rückmeldung gibt und dann in der Funktion weitermachen.
Sollte ich dafür dann eine Callback-Funktion einbauen? Aber dann kann der ja auch nicht wirklich „warten“, bis irgend etwas passiert.

Oder muss ich die ganze upload-Funktion komplett auftrennen und das in dem QThread alles einzeln aufrufen? Sprich das Vorhandensein-Überprüfen vor dem eigentlichen Upload überprüfen?

Danke!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hellstorm hat geschrieben: Oder muss ich die ganze upload-Funktion komplett auftrennen und das in dem QThread alles einzeln aufrufen? Sprich das Vorhandensein-Überprüfen vor dem eigentlichen Upload überprüfen?
Aus dem Bauch heraus würde ich sagen: ja! Zum einen trennst Du damit ja auch inhaltlich verschiedene Dinge (Prüfen, Upload), was per se eigentlich immer gut ist, zum anderen kannst Du dann einfach ein Signal aus dem QThread emitten, welches Du in Deiner GUI mit einem Callable verknüpfst, der entsprechend eine grafische Nachfrage stellt und je nach Antwort den Upload-Prozess startet.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Ich wollte eventuell gerne später noch eine Konsolenversion schreiben. Da möchte ich natürlich ungern den Code zum größten Teil noch einmal neu schreiben, aber da wird dann wohl kein Weg drum herum führen, oder?

Also grob gesehen mache ich dann im QThread folgendes:

1. Verbindung aufbauen
2. Prüfen ob Voraussetzungen auf dem Server vorhanden sind → Wenn nicht, ein Signal emittieren.
3. Prüfen, ob die Datei schon vorhanden ist → Signal emittieren, falls doch
4. Hochladen
5. Erfolgreich/nicht erfolgreich zurückgeben.

Naja, ich mache mich mal dran und probiere das aus. Danke! Wenn ich wieder Probleme habe (werde ich wohl :D) melde ich mich wieder.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hellstorm hat geschrieben:Ich wollte eventuell gerne später noch eine Konsolenversion schreiben. Da möchte ich natürlich ungern den Code zum größten Teil noch einmal neu schreiben, aber da wird dann wohl kein Weg drum herum führen, oder?
Nehmen wir mal an, dass Du kein Qt als Abhängigkeit in Deiner Konsolenapplikation haben willst (denn auch mit Qt kann man ja Konsolenapps schreiben ;-) ), dann musst Du alles, was mit dem FTP-Transfer zu tun hat so unabhängig vom QThread usw. kapseln, dass Du aus Deinem GUI-Tool eben nur noch diese Funktionalität, die Du brauchst aufrufen kannst und musst.

Das ist eigentlich auch generell sinnvoll, selbst wenn man a priori nicht vor hat, eine andere Shell für Funktionalität zu entwickeln, wie Du es planst. Denn damit trennt man eben die Funktionalität von der Präsentation ab, was zu saubereren und übersichtlicherem und auch oftmals besser testbarem Code führt :-)

Dein QThread wäre schlussendlich nur eine Art "Vermittler", der zwischen GUI und FTP-Lib liegt und Infos oder Kommandos weiterleitet.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Hellstorm: Was meinst Du mit zum grössten Teil noch mal schreiben? Du musst halt den GUI-Code noch mal schreiben, dann halt als Text-UI. Ob Dein Thread das Signal an eine GUI oder eine TUI schickt, sollte dem Code eigentlich egal sein.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:Ob Dein Thread das Signal an eine GUI oder eine TUI schickt, sollte dem Code eigentlich egal sein.
Wie ich schon mutmaßte, will er vielleicht kein Qt als Abhängigkeit für die TUI - aber braucht es ja auch nicht, sofern die FTP-Funktionalitäten aufgeteilt werden :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Trenne immer FP, denn es tut ihm nicht ... ehm ja 1+ für Trennung von Funktionalität und Präsentation :D

Z.B. kannst Du die Funktionalität in ein Verbindungsobjekt kapseln, welches um die Checks und Übertragungsmethoden angereichert ist. Dann ist es Wurscht, ob die UI threaded oder nicht oder Konsole oder GUI oder was auch immer ist. Den UI Code musst natürlich "nochmal" schreiben, er soll ja was anders darstellen.
Und Qt in der Konsolenversion ist eher lästig imho.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hyperion hat geschrieben:
BlackJack hat geschrieben:Ob Dein Thread das Signal an eine GUI oder eine TUI schickt, sollte dem Code eigentlich egal sein.
Wie ich schon mutmaßte, will er vielleicht kein Qt als Abhängigkeit für die TUI - aber braucht es ja auch nicht, sofern die FTP-Funktionalitäten aufgeteilt werden :-)
Ja, genau, darum geht es mir. Nur wenn ich diese ganzen Überprüfungen, Nachfragen usw. alles in den QThread schreibe, dann müsste ich bei der Konsolenanwendung eben all das noch einmal schreiben (Nicht die Detailimplementierung selber, aber im Grunde alles was im QThread steht).

Naja, wahrscheinlich sind das nur 20–30 Zeilen Code, aber wenn man dann wieder irgendwelche Veränderungen hat, muss man das wieder korrigieren. Aber ich werde das mal machen und mich dann wieder melden :D

PyQt für Konsolenanwendungen finde ich nicht gut. Bei C++ ist das eventuell noch sinnvoll wenn man die ganzen Module von Qt nutzen will, aber Python bietet das ja praktisch alles schon von Haus. Und auf der Konsole brauche ich kein Threading, da kann das auch ohne laufen (habe ich schon ausprobiert :D).
Antworten