IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

pyqtSlot et surcharge


Sujet :

PyQt Python

  1. #1
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 726
    Points : 31 046
    Points
    31 046
    Billets dans le blog
    1
    Par défaut pyqtSlot et surcharge
    Bonjour à tous

    D'après ce que j'ai lu ici et là, le pyqtSlot permet(trait) de surcharger un slot. Comme la surcharge (plusieurs fonctions de même nom mais avec signatures différentes) n'existe pas en Python, le décorateur pyqtSlot offre cette possibilité. C'est ce qui est écrit ici: https://www.riverbankcomputing.com/s...slot-decorator avec plusieurs méthodes "foo" en exemple.

    Sauf que de ce que j'ai essayé, je n'y arrive pas !

    Premier essai: un bouton. Son signal "clicked" pouvant être avec ou sans info
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/usr/bin/env python3
    # coding: utf-8
     
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
     
    class myWidget(QWidget):
    	def __init__(self):
    		super().__init__()
    		btn=QPushButton("bouton", parent=self)
    		btn.setObjectName("xxx")
    		mainLayout=QVBoxLayout(self)
    		mainLayout.addWidget(btn)
    		QMetaObject.connectSlotsByName(self)
     
            @pyqtSlot(bool, name="on_xxx_clicked")
    	def on_xxx_clicked(self, b):
    		print("on_quit_clicked[bool]", b, type(b))
     
            @pyqtSlot(name="on_xxx_clicked")
    	def on_xxx_clicked(self):
    		print("on_quit_clicked")
     
    if __name__ == '__main__':
    	import sys
    	app=QApplication(sys.argv)
    	widget=myWidget()
    	widget.show()
    	sys.exit(app.exec())
    Seul le second "on_xxx_clicked" (celui sans booléen) est appelé. Et si j'inverse les deux méthodes, alors c'est c'est celle avec booléen. Bref c'est la dernière méthode qui est appelée.

    Ensuite je vois un truc qui parle de QSpinBox qui envoie deux signaux identiques, un avec un int et l'autre avec un str

    Donc rebelote...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/usr/bin/env python3
    # coding: utf-8
     
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
     
    class myWidget(QWidget):
    	def __init__(self):
    		super().__init__()
    		spn=QSpinBox(parent=self)
    		spn.setObjectName("xxx")
    		mainLayout=QVBoxLayout(self)
    		mainLayout.addWidget(spn)
    		QMetaObject.connectSlotsByName(self)
     
            @pyqtSlot(int, name="on_xxx_valueChanged")
    	def on_xxx_valueChanged(self, b):
    		print("on_xxx_valueChanged[int]", b, type(b))
     
            @pyqtSlot(str, name="on_xxx_valueChanged")
    	def on_xxx_valueChanged(self, b):
    		print("on_xxx_valueChanged[str]", b, type(b))
     
    if __name__ == '__main__':
    	import sys
    	app=QApplication(sys.argv)
    	widget=myWidget()
    	widget.show()
    	sys.exit(app.exec())
    ... et toujours même souci: seul le dernier slot est appelé.

    Donc ma question: y a-t-il un truc que j'ai raté quelque part ?

    Merci à tous.

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 480
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 480
    Points : 9 280
    Points
    9 280
    Billets dans le blog
    6
    Par défaut
    Bonjour Sve@r! J'espère que tu vas bien!

    Quand j'ai vu ton message, je me suis dit: enfin une question intéressante...

    Je ne réussi pas non plus à utiliser pyqtSlot comme décorateur sur plusieurs fonctions de même nom.

    Par contre, utiliser plusieurs décorateurs pour la même fonction, ça marche:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from PyQt5.QtCore import QObject, pyqtSlot
     
    class Foo(QObject):
     
        @pyqtSlot()
        @pyqtSlot(int, result=int)
        @pyqtSlot(str, result=str)
        def foo(self, value=None):
            print(value)
            return value
     
    foo = Foo()
     
    foo.foo()
     
    foo.foo(2)
     
    foo.foo("toto")
    Ce qui affiche:

    [edit] peut-être que mon test est un peu trop "permissif": il faudrait un exemple plus discriminant, c'est à dire qui donne une erreur pour un appel non prévu.

  3. #3
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 726
    Points : 31 046
    Points
    31 046
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Bonjour Sve@r! J'espère que tu vas bien!
    Ouais, super; suis en pleine forme. De ton côté ça fait longtemps que je ne t'ai pas vu...

    Citation Envoyé par tyrtamos Voir le message
    Je ne réussi pas non plus à utiliser pyqtSlot comme décorateur sur plusieurs fonctions de même nom.
    Le plus dur c'est que dans le livre "Créer des applications graphiques en Python avec PyQt5" c'est écrit ainsi
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    @pyqtSlot(int, name="on_spinBoxChoix_valueChanged")
    def on_spinBoxChoix_valueChanged_int(self, i):
    	print(...)
    @pyqtSlot(str, name="on_spinboxChoix_valueChanged")
    def on_spinBoxChoix_valueChanged_str(self, txt):
    	print(...)

    Alors en fait c'est pas vraiment écrit comme surcharge (les deux méthodes n'ont pas le même nom) mais l'auteur indique qu'il a fait ça pour les distinguer dans le texte mais que c'est un détail et que ça marcherait aussi avec le même nom, que c'est l'option "name=" qui fait le lien...

    Citation Envoyé par tyrtamos Voir le message
    Par contre, utiliser plusieurs décorateurs pour la même fonction, ça marche:
    Oui ça je l'ai fait aussi. Ce que je voulais voir c'est si on pouvait coder deux actions différentes sous un même nom. Et (plus précisément) je voulais voir au départ l'utilité réelle du pyqtSlot. J'avais posé la question il y a quelques temps et il m'avait été répondu (je crois d'ailleurs que tu avais fait partie des intervenants) que c'était pour optimiser la mémoire. Je suis revenu là dessus car cette réponse me satisfait moyennement (je suis comme l'autre saint Truc, je ne crois que ce que je peux voir).

    Citation Envoyé par tyrtamos Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from PyQt5.QtCore import QObject, pyqtSlot
     
    class Foo(QObject):
     
        @pyqtSlot()
        @pyqtSlot(int, result=int)
        @pyqtSlot(str, result=str)
        def foo(self, value=None):
            print(value)
            return value
     
    foo = Foo()
     
    foo.foo()
     
    foo.foo(2)
     
    foo.foo("toto")
    En fait dans ton exemple, le décorateur c'est comme le "h" de "hawai", ça ne sert absolument à rien.

    Ceci dit, je crois peut-être avoir compris.

    En reprenant ton exemple adapté à mon spinbox mais avec un seul slot écrit ainsi
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #@pyqtSlot(int, name="on_xxx_valueChanged")
    #@pyqtSlot(str, name="on_xxx_valueChanged")
    def on_xxx_valueChanged(self, b):
    	print("on_xxx_valueChanged", b, type(b))
    Dans ce cas (décorateurs commentés), chaque changement de spin appelle deux fois le slot (une fois avec un int, une fois avec un str).

    Si maintenant je désactive le commentaire "int", alors seul le signal "int" est récupéré (le signal "str" est shunté). Et inversement si je désactive le commentaire "str". Et si je désactive les deux commentaires je me retrouve comme au début à avoir deux appels pour un changement.

    Je pense donc que le pyqtSlot n'est pas fait pour simuler la surcharge mais pour filtrer les signaux surchargés !!!

    Citation Envoyé par tyrtamos Voir le message
    peut-être que mon test est un peu trop "permissif": il faudrait un exemple plus discriminant, c'est à dire qui donne une erreur pour un appel non prévu.
    Suffit de tester le type de value

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 480
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 480
    Points : 9 280
    Points
    9 280
    Billets dans le blog
    6
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Suffit de tester le type de value
    C'est ce que j'ai fait, bien sûr. Mais je croyais avoir lu quelque part que lorsqu'on appelle la fonction décorée avec des arguments dont le type n'était pas prévu par pyqtSlot, cela générait une erreur. Mais ce n'est pas le cas. Les arguments de la fonction continuent à ne pas être typés comme dans le Python standard:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Foo(QObject):
     
        @pyqtSlot(int)
        def foo(self, x):
            print(x, type(x))
     
    foo = Foo()
     
    foo.foo(2)
     
    foo.foo("toto")
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    2 <class 'int'>
    toto <class 'str'>
    Et si je retire le décorateur, cela donne exactement le même résultat... A quoi ça sert pyqtSlot?

    [edit] Peut-être que pyqtSlot n'agit que lors de l'appel d'un signal, et non par appel direct de la méthode? En fait, c'est toujours avec un signal que je l'ai utilisé. Je vais essayer.

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 362
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 362
    Points : 36 894
    Points
    36 894
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Donc ma question: y a-t-il un truc que j'ai raté quelque part ?
    Dans mes souvenirs (et sans avoir retesté ce jour) on a les moutures:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            @pyqtSlot(bool, name="on_xxx_clicked")
    	def on_xxx_clicked_A(self, b):
    		print("on_quit_clicked[bool]", b, type(b))
     
            @pyqtSlot(name="on_xxx_clicked")
    	def on_xxx_clicked_B(self):
    		print("on_quit_clicked")
    ou

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            @pyqtSlot(bool)
    	def on_xxx_clicked(self, b):
    		print("on_quit_clicked[bool]", b, type(b))
     
            @pyqtSlot()
    	def on_xxx_clicked(self):
    		print("on_quit_clicked")
    - W

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 726
    Points : 31 046
    Points
    31 046
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    ou

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            @pyqtSlot(bool)
    	def on_xxx_clicked(self, b):
    		print("on_quit_clicked[bool]", b, type(b))
     
            @pyqtSlot()
    	def on_xxx_clicked(self):
    		print("on_quit_clicked")
    Oui ça c'est un exemple équivalent à celui qui est sur cette page. Mais j'ai essayé et... ça n'a pas fonctionné. Comme je l'ai dit, au début, la dernière méthode de même nom remplace toutes les autres.

    Citation Envoyé par wiztricks Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            @pyqtSlot(bool, name="on_xxx_clicked")
    	def on_xxx_clicked_A(self, b):
    		print("on_quit_clicked[bool]", b, type(b))
     
            @pyqtSlot(name="on_xxx_clicked")
    	def on_xxx_clicked_B(self):
    		print("on_quit_clicked")
    Alors là effectivement cette solution fonctionne.
    Donc si je comprends bien, le "name=" définit le "nom logique" (nom qui doit impérativement être au formalisme "on_name_signal" et qui peut être répété éventuellement) ; puis ensuite il faut quand-même des méthodes de noms différents pour que Python sache les identifier. Et grâce au nom logique et à la signature qui le précède, Qt sait associer chaque signal à la bonne fonction.

    C'est pas tout à fait ce que j'espérais dans la mesure où on peut écrire alors...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    btn.clicked.connect(self.on_xxx_clickedB)
    btn.clicked[bool].connect(self.on_xxx_clickedA)
    ... et où on retombe dans le cas général "chaque signal particulier associé à sa méthode précise" et dans lequel "name=" ne sert plus. Mais je vais m'en contenter.

    Merci de ton investissement qui m'a mieux aidé à comprendre (ou aidé à mieux comprendre)

  7. #7
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 920
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 920
    Points : 7 312
    Points
    7 312
    Par défaut
    Sve@r,

    Alors en fait c'est pas vraiment écrit comme surcharge
    Tu ne confondrais pas le terme surcharge (héritage) avec le terme "méthode générique" ?

    En python on ne peut pas faire cela, et si c'est le cas, il faut sans doute un peu de magie syntaxique en ajoutant peut-être le décorateur singledispatchmethod.

    pyqtslot quand à lui ne fait que indiquer une méthode comme slot Qt et qui sera appelée lors d'un signal.

  8. #8
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 726
    Points : 31 046
    Points
    31 046
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Tu ne confondrais pas le terme surcharge (héritage) avec le terme "méthode générique" ?
    Non, je voulais bel et bien parler de surcharge, c'est à dire différentes fonctions/méthodes de même nom mais avec signature différente.

    Citation Envoyé par fred1599 Voir le message
    En python on ne peut pas faire cela
    Yep je sais bien. Mais de ce que je vois écrit sur cette page cela correspond parfaitement à la définition (on y voit plusieurs méthodes nommées "foo" mais avec signatures différentes et toutes décorées avec un pyqtSlot ayant lui aussi différentes signatures). Je pensais donc que pyqtSlot permettait cette opération (en changeant le nom ou autre j'en sais rien).
    Et donc comme je l'ai dit, mes essais reprenant des exemples analogues à ceux de cette page n'ont jamais fonctionné. Et ensuite wiztricks a donné un exemple pas parfait mais fonctionnel et étant probablement ce qui peut se faire de plus ressemblant à la surcharge.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 7
    Dernier message: 18/12/2003, 10h23
  2. Surcharge de fonction d'un edit dynamique
    Par Tartar Ukid dans le forum C++Builder
    Réponses: 4
    Dernier message: 13/10/2003, 11h56
  3. Réponses: 5
    Dernier message: 24/04/2003, 11h47
  4. Surcharger le message d'erreur après un OnException
    Par Tirlibibi dans le forum XMLRAD
    Réponses: 2
    Dernier message: 24/04/2003, 11h42
  5. Réponses: 8
    Dernier message: 20/11/2002, 11h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo