[PYTHON] À propos des expressions de signal, de connexion et de lambda de PyQt

Conclusion

Vous devez utiliser lambda pour l'argument de connect () dans PyQt.

introduction

Il s'agit d'un article sur la gestion des événements de PyQt, une bibliothèque d'encapsuleurs pour la gestion du framework d'interface graphique C ++ Qt en Python. Chaque classe Qt a plusieurs événements qui se déclenchent à des moments spécifiques. C'est ce qu'on appelle un signal. L'allumage du signal est appelé émettre. Rien ne se passe simplement en émettant, vous devez donc connecter le signal à «quelque chose». connect est une fonction et passe "un traitement" comme argument. Un exemple est présenté ci-dessous.

#closePushButton hérite de la classe QPushButton
#Dans ce qui suit, le signal appelé cliqué est self.close()Connecte la fonction
closePushButton.clicked.connect(self.close)

L'argument de la fonction connect () est une fonction appelée self.close ().

expression lambda

Maintenant, Python a une syntaxe appelée expressions lambda qui peut générer des fonctions anonymes. Les fonctions ne peuvent généralement être déclarées qu'avec une instruction def, à l'exception de cette expression lambda. Un exemple est présenté ci-dessous.

#Définition de fonction normale
def def_add(a, b):
    return a + b

#Style Lambda
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)

Les expressions Lambda ont une syntaxe particulière, mais elles peuvent implémenter le même comportement que les définitions de fonctions normales.

Différence entre l'instruction def et l'expression lambda

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

Imprimons la fonction précédente.

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

Vous pouvez voir que les deux sont des types de fonction. La différence est que le premier stocke le nom de la fonction def_add, tandis que le second est \ . C'est la raison de la fonction anonyme. Il y avait le commentaire suivant dans stackoverflow.

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

La fonction lambda ne peut pas être décapée car elle n'a pas de nom unique associé (elle ne peut donc pas être utilisée pour le multi-traitement - dans mon cas, j'ai plusieurs PicklingErrors (ce qui signifie pas une seule fois))

Il peut également être intéressant de souligner que lambda est une expression tandis que def est une instruction. Puisque lambda est une expression, elle ne peut contenir que des expressions (elle ne peut pas contenir d'instructions). C'est un problème au niveau du programmeur plutôt que "pourquoi Python garde la trace (ou stocke) les différences (def et lambda)".

Ce que j'ai trouvé après avoir enquêté, c'est que «la fonction lambda n'a pas de nom et ne peut pas contenir d'instructions». Je n'ai vu aucune autre différence de comportement.

Def et lambda comme arguments de connexion

Voici le problème principal. D'après l'histoire ci-dessus, il semble que la fonction def et l'expression lambda se comportent probablement de la même manière. Ensuite, lorsque la fonction est utilisée comme argument de connect () avec PyQt, elle doit se comporter de la même manière ... ne fonctionne pas. </ b> Non, le comportement sera le même si vous l'utilisez normalement, mais le comportement changera si la structure est un peu compliquée, et cela peut ne pas fonctionner avec la fonction def. </ b>

Quand la fonction def est-elle inutile?

Pour conclure, je ne connaissais pas la cause détaillée et le moment choisi. </ b> J'ai en quelque sorte compris qu'il semble y avoir une cause autour de la mémoire, mais je n'ai pas été en mesure de clarifier le calendrier détaillé et la cause. En tant que situation spécifique, lors de la connexion d'un ensemble de signaux personnalisé dans une classe qui hérite de QThread, la fonction def ne fonctionnait pas.

Utiliser une expression lambda pour se connecter

Dans la situation ci-dessus, prendre la fonction lambda comme argument au lieu de la fonction def a résolu le problème. Je ne connais pas la cause exacte, mais je suppose que si vous passez la fonction def comme argument, elle sera enregistrée en tant que chose de type variable </ b> dans PyQt. Par contre, dans le cas des expressions lamda, je me demande si seules les expressions à exécuter sont enregistrées </ b>. Ce qui suit est un modèle de transmission d'une expression lambda à connecter, mais si vous lisez ceci, vous comprendrez peut-être ce que je veux dire (une image que l'expression elle-même appelée self.close () est passée?).


#closePushButton pour la fonction def.clicked.connect(self.close)
closePushButton.clicked.connect(lambda:self.close())

Je ne connais pas la cause exacte jusqu'à présent, mais j'en suis venu à la conclusion que il se comporte essentiellement de la même manière et que vous devriez quand même utiliser l'expression lambda </ b>.

Pourquoi vous ne devriez pas passer la fonction def pour vous connecter

Voici une autre raison pour laquelle vous ne devriez pas utiliser la fonction def comme argument pour vous connecter. le signal peut passer une certaine valeur. À ce moment, la fonction def passée à se connecter doit être prête à recevoir l'argument . </ b> Ce qui suit est un exemple.

#Un signal appelé progressChanged passe un int à la fonction connectée
self.tile_downloader.progressChanged.connect(self.update_download_progress)

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

Des arguments tels que update_download_progress (100) sont spécifiés lors de l'appel d'une fonction normale. Cependant, si vous le passez comme argument pour vous connecter, l'argument n'apparaîtra nulle part. </ b> PyQt a besoin de le savoir, et le code est très illisible (à moins que vous ne sachiez que progressChanged passe un int, vous ne pouvez pas voir le comportement en lisant simplement cette connexion). .. S'il s'agit d'une expression lambda, ce sera comme suit.

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)

Dans le cas d'une expression lambda, si vous lisez connect, vous pouvez voir que le signal appelé progressChanged passe une valeur (s'il y a une variable que vous voulez passer en argument en plus de la valeur de signal, <https://stackoverflow.com/questions/35819538 Voir / using-lambda-expression-to-connect-slots-in-pyqt>).

À la fin

Cela fait longtemps, mais la conclusion est d'utiliser une expression lambda pour connect. </ b> C'est tout. Il y a deux raisons: (1) Je ne sais pas pourquoi, mais l'expression lambda est plus stable, et (2) lambda est plus lisible. Si vous avez des opinions ou des informations (en particulier concernant ①), veuillez commenter. Merci d'avoir lu jusqu'ici.