[PYTHON] Informationen zu PyQts Signal-, Verbindungs- und Lambda-Ausdrücken

Fazit

Sie sollten Lambda für das Argument von connect () in PyQt verwenden.

Einführung

Dies ist ein Artikel über die Ereignisbehandlung von PyQt, einer Wrapper-Bibliothek zur Behandlung des C ++ - GUI-Frameworks Qt in Python. Jede Qt-Klasse hat mehrere Ereignisse, die zu bestimmten Zeiten ausgelöst werden. Dies wird als Signal bezeichnet. Die Zündung des Signals wird als emittieren bezeichnet. Nichts passiert nur durch Aussenden, also müssen Sie das Signal mit "etwas" verbinden. connect ist eine Funktion und übergibt "etwas Verarbeitung" als Argument. Ein Beispiel ist unten gezeigt.

#closePushButton erbt von der QPushButton-Klasse
#Im Folgenden ist das als geklickt bezeichnete Signal self.close()Verbindet die Funktion
closePushButton.clicked.connect(self.close)

Das Argument der Funktion connect () ist eine Funktion namens self.close ().

Lambda-Ausdruck

Jetzt hat Python eine Syntax namens Lambda-Ausdrücke, die anonyme Funktionen generieren können. Funktionen können normalerweise nur mit einer def-Anweisung deklariert werden, mit Ausnahme dieses Lambda-Ausdrucks. Ein Beispiel ist unten gezeigt.

#Normale Funktionsdefinition
def def_add(a, b):
    return a + b

#Lambda-Stil
lambda_add = lambda a, b: a + b

if __name__ == "__main__":
    def_add_value = def_add(1, 2)
    lambda_add_value = lambda_add(1,2)
    print(def_add_value, lambda_add_value) #(3, 3)

Lambda-Ausdrücke haben eine besondere Syntax, können jedoch dasselbe Verhalten wie normale Funktionsdefinitionen implementieren.

Unterschied zwischen def-Anweisung und Lambda-Ausdruck

Referenz: https://stackoverflow.com/questions/12264834/what-is-the-difference-for-python-between-lambda-and-regular-function

Lassen Sie uns die vorherige Funktion drucken.

print(def_add, lambda_add)
#<function def_add at 0x10ad214c0> <function <lambda> at 0x10ad4f700>

Sie können sehen, dass beide Funktionstypen sind. Der Unterschied besteht darin, dass der erstere den Funktionsnamen def_add speichert, während der letztere \ ist. Dies ist der Grund für die anonyme Funktion. Es gab den folgenden Kommentar im Stapelüberlauf.

lambda functions can't be pickled because they have no (unique) name associated with them. (Therefore, they can't be used with multiprocessing for example -- which has bit me with a PicklingError on more than one occasion ) – mgilson

Also, it might be worth pointing out that lambda is an expression whereas def is a statement. Since lambda is an expression, it can only contain other expressions (no statements are allowed) -- Although this is more of an issue at the programmer's level as opposed to "Why does python keep track of the difference" – mgilson

Die Lambda-Funktion kann nicht eingelegt werden, da ihr kein eindeutiger Name zugeordnet ist (daher kann sie nicht für die Mehrfachverarbeitung verwendet werden - in meinem Fall habe ich mehrere PicklingErrors (dh nicht einmal)).

Es kann auch erwähnenswert sein, dass Lambda ein Ausdruck ist, während def eine Aussage ist. Da Lambda ein Ausdruck ist, kann es nur Ausdrücke enthalten (es kann keine Anweisungen enthalten). Es ist eher ein Problem auf Programmiererebene als "warum Python die Unterschiede (def und lambda) verfolgt (oder speichert)".

Nach der Untersuchung stellte ich fest, dass "die Lambda-Funktion keinen Namen hat und keine Anweisungen enthalten kann". Ich habe keinen anderen Unterschied im Verhalten gesehen.

Def und Lambda als Argumente der Verbindung

Hier ist das Hauptproblem. Aus der obigen Geschichte geht hervor, dass sich sowohl die def-Funktion als auch der Lambda-Ausdruck im Grunde gleich verhalten. Wenn die Funktion dann als Argument von connect () mit PyQt verwendet wird, sollte sie sich genauso verhalten ... funktioniert nicht. </ b> Nein, das Verhalten ist das gleiche, wenn Sie es normal verwenden, aber das Verhalten ändert sich, wenn die Struktur etwas kompliziert ist und es möglicherweise nicht mit der def-Funktion funktioniert. </ b>

Wann ist die def-Funktion unbrauchbar?

Abschließend kannte ich die detaillierte Ursache und den Zeitpunkt nicht. </ b> Ich habe irgendwie verstanden, dass es eine Ursache in der Erinnerung zu geben scheint, aber ich konnte den detaillierten Zeitpunkt und die Ursache nicht klären. In einer bestimmten Situation funktionierte die def-Funktion nicht, wenn ein benutzerdefiniertes Signal in einer Klasse verbunden wurde, die QThread geerbt hat.

Verwenden Sie zum Verbinden einen Lambda-Ausdruck

In der obigen Situation löste die Verwendung der Lambda-Funktion als Argument anstelle der def-Funktion das Problem. Ich kenne die genaue Ursache nicht, aber ich vermute, dass wenn Sie die def-Funktion als Argument übergeben, sie als variablenähnliches Ding </ b> in PyQt gespeichert wird. Andererseits frage ich mich bei Lamda-Ausdrücken, ob nur die auszuführenden Ausdrücke gespeichert werden </ b>. Das Folgende ist ein Muster für die Übergabe eines Lambda-Ausdrucks zum Verbinden. Wenn Sie dies jedoch lesen, verstehen Sie möglicherweise, was ich meine (ein Bild, in dem der Ausdruck selbst mit dem Namen self.close () übergeben wird?).


#closePushButton für die Def-Funktion.clicked.connect(self.close)
closePushButton.clicked.connect(lambda:self.close())

Ich kenne die genaue Ursache bisher nicht, bin aber zu dem Schluss gekommen, dass es sich im Grunde gleich verhält und Sie den Lambda-Ausdruck </ b> trotzdem verwenden sollten.

Warum sollten Sie die def-Funktion nicht übergeben, um eine Verbindung herzustellen?

Hier ist ein weiterer Grund, warum Sie die def-Funktion nicht als Argument für die Verbindung verwenden sollten. Das Signal kann einen bestimmten Wert überschreiten. Zu diesem Zeitpunkt muss die an connect übergebene def-Funktion darauf vorbereitet sein, das Argument zu empfangen. </ b> Das Folgende ist ein Beispiel.

#Ein Signal namens progressChanged übergibt ein int an die verbundene Funktion
self.tile_downloader.progressChanged.connect(self.update_download_progress)

def update_download_progress(self, value:int):
    self.ui.download_progressBar.setValue(value)

Beim Aufrufen einer normalen Funktion werden Argumente wie update_download_progress (100) angegeben. Wenn Sie es jedoch als Argument für die Verbindung übergeben, wird das Argument nirgendwo angezeigt. </ b> PyQt muss das wissen, und der Code ist sehr unlesbar (es sei denn, Sie wissen, dass progressChanged ein int übergibt, können Sie das Verhalten nicht sehen, indem Sie diese Verbindung lesen). .. Wenn es sich um einen Lambda-Ausdruck handelt, lautet dieser wie folgt.

self.tile_downloader.progressChanged.connect(lambda v: self.update_download_progress(v))

def update_download_progress(self, value:int):
    self.ui.download_progressBar.setValue(value)

Wenn es sich um einen Lambda-Ausdruck handelt und Sie connect lesen, können Sie sehen, dass das Signal progressChanged einen bestimmten Wert übergibt (wenn es eine Variable gibt, die Sie als anderes Argument als den Wert des Signals übergeben möchten, <https://stackoverflow.com/questions/35819538) Siehe / using-lambda-expression-to-connect-slots-in-pyqt>).

Am Ende

Es ist lange her, aber die Schlussfolgerung ist, einen Lambda-Ausdruck für connect zu verwenden. </ b> Das war's. Es gibt zwei Gründe: (1) Ich weiß nicht warum, aber der Lambda-Ausdruck ist stabiler und (2) Lambda ist besser lesbar. Wenn Sie Meinungen oder Informationen haben (insbesondere zu ①), kommentieren Sie diese bitte. Vielen Dank, dass Sie so weit gelesen haben.