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 :

Problème : créer des fenêtres différentes reposant sur le même modèle de façon automatique


Sujet :

PyQt Python

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 3
    Points : 2
    Points
    2
    Par défaut Problème : créer des fenêtres différentes reposant sur le même modèle de façon automatique
    Bonjour à tous,

    Mon problème est le suivant :
    J'ai 36 pushButtons qui doivent chacun ouvrir une fenêtre (QDialog) différente mais qui reposent sur le même modèle.
    Cela ne me pose pas de problème à faire quand il y a peu de boutons mais avec 36 boutons j'aimerais automatiser la chose.

    Dans l'idée, j'aimerais remplacer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            self.connect(self.ui.pushButton1,SIGNAL('clicked()'),self.interfaceDialog1)
            self.connect(self.ui.pushButton2,SIGNAL('clicked()'),self.interfaceDialog2)
            self.connect(self.ui.pushButton3,SIGNAL('clicked()'),self.interfaceDialog3)
            self.connect(self.ui.pushButton4,SIGNAL('clicked()'),self.interfaceDialog4)
            self.connect(self.ui.pushButton5,SIGNAL('clicked()'),self.interfaceDialog5)
            self.connect(self.ui.pushButton6,SIGNAL('clicked()'),self.interfaceDialog6)
            #etc...
    par quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for k in range(1,37):
            self.connect(self.ui.pushButton1k,SIGNAL('clicked()'),self.interfaceDialogk)
    et d'autre part, remplacer :
    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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
        def interfaceDialog1(self):
            self.dialog1 = Interface_Dialog()
            self.dialog1.setWindowTitle('Dialog 1')
            self.dialog1.ui.label.setText('Fenetre 1')
            self.dialog1.show()
     
        def interfaceDialog2(self):
            self.dialog2 = Interface_Dialog()
            self.dialog2.setWindowTitle('Dialog 2')
            self.dialog2.ui.label.setText('Fenetre 2')
            self.dialog2.show()
     
        def interfaceDialog3(self):
            self.dialog3 = Interface_Dialog()
            self.dialog3.setWindowTitle('Dialog 3')
            self.dialog3.ui.label.setText('Fenetre 3')
            self.dialog3.show()
     
        def interfaceDialog4(self):
            self.dialog4 = Interface_Dialog()
            self.dialog4.setWindowTitle('Dialog 4')
            self.dialog4.ui.label.setText('Fenetre 4')
            self.dialog4.show()
     
        def interfaceDialog5(self):
            self.dialog5 = Interface_Dialog()
            self.dialog5.setWindowTitle('Dialog 5')
            self.dialog5.ui.label.setText('Fenetre 5')
            self.dialog5.show()
     
        def interfaceDialog6(self):
            self.dialog6 = Interface_Dialog()
            self.dialog6.setWindowTitle('Dialog 6')
            self.dialog6.ui.label.setText('Fenetre 6')
            self.dialog6.show()
     
        #etc....
    par :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    for k in range(1,37):
        def interfaceDialogk(self):
            self.dialogk = Interface_Dialog()
            self.dialogk.setWindowTitle('Dialog {}'.format(k))
            self.dialogk.ui.label.setText('Fenetre {}'.format(k))
            self.dialogk.show()

    Je mets en pièces-jointes un exemple simplifié de ce que je veux faire. Le fichier à exécuter est "mainCode.py"

    Merci d'avance de votre aide !

    Alexandre
    Fichiers attachés Fichiers attachés

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    Pour autant qu'il soit agréable côté utilisateur d'avoir à choisir parmi 36 buttons quelle interface lancer, la façon la plus simple de connecter çà serait:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for button, dialog in zip(buttons, dialogs):
         self.connect(button,SIGNAL('clicked()'),dialog)
    i.e. plutôt que de fabriquer 36 variables numérotées button1, button2,.... et dialog1, dialog2,.... fabriquer des listes buttons et dialogs contenant les différents objets.

    Bien sûr, Python permet aussi de faire çà autrement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for ix in range(1, 36):
            self.connect(getattr(self.ui, 'pushButton%d' % ix),SIGNAL('clicked()'), getattr(self, 'interfaceDialog%d' % ix))
    mais il est préférable d'utiliser la magie lorsqu'on maîtrise les structures de base - et qu'on a de bonnes raisons pour ne pouvoir faire autrement - plutôt que pour rafistoler un code mal structuré.

    Pareil pour les recopies:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        def interfaceDialog5(self):
            self.dialog5 = Interface_Dialog()
            self.dialog5.setWindowTitle('Dialog 5')
            self.dialog5.ui.label.setText('Fenetre 5')
            self.dialog5.show()
    C'est quand même pas si compliqué d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            dialogs = []
            for x in range(1, 36):
                 dialog = Interface_Dialog()
                 dialog.setWindowTitle('Dialog %d' % x)
                 dialog.ui.label.setText('Fenetre %d' % x)
                 dialog.show()
                 dialogs.append(dialog)
    encore faut il avoir pris le temps d'apprendre ce qu'on peut faire avec les chaînes de caractères, les différents formats,... plutôt que de recopier encore et encore une séquence d'instructions qui se répètent presque partout.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Perso quand je veux utiliser une fonction commune à plusieurs widgets je fais ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    from functools import partial
     
    self.ui.pushButton1.clicked.connect(partial(self.interfaceDialog, "Valeur 1"))
    self.ui.pushButton2.clicked.connect(partial(self.interfaceDialog, "Valeur 2"))
     
    interfaceDialog(self, Valeur):
     ...
    Sous Kubuntu 20.04

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    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 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Je suis d'accord avec hizoka: comme on peut faire pointer plusieurs signaux sur la même méthode, "partial" permet d'envoyer un code qui permet à la méthode appelée de savoir qui a envoyé le signal.

    Il y a d'ailleurs une solution équivalente avec "QSignalMapper".
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Je suis d'accord avec hizoka: comme on peut faire pointer plusieurs signaux sur la même méthode, "partial" permet d'envoyer un code qui permet à la méthode appelée de savoir qui a envoyé le signal
    On peut associer plusieurs signaux à une même méthode, mais partial ou lambda fabriquent des fonctions spécifiques à la volée, parlez-vous de la même chose? Pas si sur...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 3
    Points : 2
    Points
    2
    Par défaut
    Salut,

    Tout d'abord merci pour vos réponses.

    Alors je te rassure wiztricks, l'utilisateur n'a pas 36 boutons devant lui. Ils sont répartis dans différentes fenêtres mais ouvrent tous une fenêtre du même genre. Donc pour simplifier le problème j'ai fait comme s'ils étaient tous dans la même fenêtre.



    Citation Envoyé par wiztricks Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for button, dialog in zip(buttons, dialogs):
         self.connect(button,SIGNAL('clicked()'),dialog)
    i.e. plutôt que de fabriquer 36 variables numérotées button1, button2,.... et dialog1, dialog2,.... fabriquer des listes buttons et dialogs contenant les différents objets.
    Comment me conseilles-tu de créer les listes buttons et dialogs ? Et dans tous les cas mes objets pushButtonX existent déjà car je les ai créés avec QtDesigner.



    Citation Envoyé par wiztricks Voir le message
    Bien sûr, Python permet aussi de faire çà autrement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for ix in range(1, 36):
            self.connect(getattr(self.ui, 'pushButton%d' % ix,SIGNAL('clicked()'), getattr(self, 'interfaceDialog%d' % ix)
    mais il est préférable d'utiliser la magie lorsqu'on maîtrise les structures de base - et qu'on a de bonnes raisons pour ne pouvoir faire autrement - plutôt que pour rafistoler un code mal structuré.
    Je viens de découvrir getattr cet après-midi, ainsi que exec et eval, et en les utilisant j'ai presque réussi à résoudre le problème que j'avais à ce niveau là (voir plus bas).



    Citation Envoyé par wiztricks Voir le message
    Pareil pour les recopies:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        def interfaceDialog5(self):
            self.dialog5 = Interface_Dialog()
            self.dialog5.setWindowTitle('Dialog 5')
            self.dialog5.ui.label.setText('Fenetre 5')
            self.dialog5.show()
    C'est quand même pas si compliqué d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            dialogs = []
            for x in range(1, 36):
                 dialog = Interface_Dialog()
                 dialog.setWindowTitle('Dialog %d' % x)
                 dialog.ui.label.setText('Fenetre %d' % x)
                 dialog.show()
                 dialogs.append(dialog)
    encore faut il avoir pris le temps d'apprendre ce qu'on peut faire avec les chaînes de caractères, les différents formats,... plutôt que de recopier encore et encore une séquence d'instructions qui se répètent presque partout.
    Enfin je ne vois pas comment utiliser ce bout de code, parce que je n'arrive pas à comprendre comment faire en sorte que la fenêtre dialogX s'ouvre quand on clique sur le pushButtonX. Peux-tu préciser ?



    Concernant partial et lambda, j'ai utilisé le même schéma que hizoka mais avec lambda dans la nouvelle version de mon code (voir encore un peu plus bas).
    1ère question : Y a-t-il un avantage à utiliser l'un ou l'autre ? tyrtamos, tu disais que partial permettait à la méthode appelée de savoir qui a envoyé le signal. Est-ce que cela peut servir dans mon cas ?
    2ème question : wiztricks tu as dit "On peut associer plusieurs signaux à une même méthode, mais partial ou lambda fabriquent des fonctions spécifiques à la volée, parlez-vous de la même chose? Pas si sur..." et je n'ai pas tout saisi. Qu'entends-tu par "des fonctions spécifiques" ?



    Comme je vous le disais, j'ai avancé de mon côté.
    D'une part, j'ai remplacé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            self.connect(self.ui.pushButton1,SIGNAL('clicked()'),self.interfaceDialog1)
            self.connect(self.ui.pushButton2,SIGNAL('clicked()'),self.interfaceDialog2)
            self.connect(self.ui.pushButton3,SIGNAL('clicked()'),self.interfaceDialog3)
            self.connect(self.ui.pushButton4,SIGNAL('clicked()'),self.interfaceDialog4)
            self.connect(self.ui.pushButton5,SIGNAL('clicked()'),self.interfaceDialog5)
            self.connect(self.ui.pushButton6,SIGNAL('clicked()'),self.interfaceDialog6)
            #etc...
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            for k in range(1,7):
                self.connect(eval('self.ui.pushButton{}'.format(k)),SIGNAL('clicked()'),lambda : self.interfaceDialog(k))
    et d'autre part, j'ai remplacé
    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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
        def interfaceDialog1(self):
            self.dialog1 = Interface_Dialog()
            self.dialog1.setWindowTitle('Dialog 1')
            self.dialog1.ui.label.setText('Fenetre 1')
            self.dialog1.show()
     
        def interfaceDialog2(self):
            self.dialog2 = Interface_Dialog()
            self.dialog2.setWindowTitle('Dialog 2')
            self.dialog2.ui.label.setText('Fenetre 2')
            self.dialog2.show()
     
        def interfaceDialog3(self):
            self.dialog3 = Interface_Dialog()
            self.dialog3.setWindowTitle('Dialog 3')
            self.dialog3.ui.label.setText('Fenetre 3')
            self.dialog3.show()
     
        def interfaceDialog4(self):
            self.dialog4 = Interface_Dialog()
            self.dialog4.setWindowTitle('Dialog 4')
            self.dialog4.ui.label.setText('Fenetre 4')
            self.dialog4.show()
     
        def interfaceDialog5(self):
            self.dialog5 = Interface_Dialog()
            self.dialog5.setWindowTitle('Dialog 5')
            self.dialog5.ui.label.setText('Fenetre 5')
            self.dialog5.show()
     
        def interfaceDialog6(self):
            self.dialog6 = Interface_Dialog()
            self.dialog6.setWindowTitle('Dialog 6')
            self.dialog6.ui.label.setText('Fenetre 6')
            self.dialog6.show()
     
        #etc....
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        def interfaceDialog(self,k):
            exec('self.dialog{} = Interface_Dialog()'.format(k))
            eval('self.dialog{}'.format(k)).setWindowTitle('Dialog {}'.format(k))
            eval('self.dialog{}'.format(k)).ui.label.setText('Fenetre {}'.format(k))
            eval('self.dialog{}'.format(k)).show()
    Mais j'ai un problème : quelque soit le bouton sur lequel je clique, la fenêtre qui s'ouvre correspond à celle qui devrait s'ouvrir quand j'appuie sur le pushButton6 seulement.
    Je remets la nouvelle version de mon programme simplifié en pièce-jointe si vous voulez y jeter un oeil (il faut exécuter mainCode.py).


    Alexandre
    Fichiers attachés Fichiers attachés

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Citation Envoyé par Alex4416 Voir le message
    2ème question : wiztricks tu as dit "On peut associer plusieurs signaux à une même méthode, mais partial ou lambda fabriquent des fonctions spécifiques à la volée, parlez-vous de la même chose? Pas si sur..." et je n'ai pas tout saisi. Qu'entends-tu par "des fonctions spécifiques" ?
    C'est pourtant ce que vus avez écrit avec "lambda" qui crée une nouvelle fonction "spécifique" à chaque "k".
    Mais il faut écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            for k in range(1,7):
                self.connect(eval('self.ui.pushButton{}'.format(k)),SIGNAL('clicked()'),lambda k=k: self.interfaceDialog(k))
    i.e. passer la valeur de k comme paramètre par défaut à cette nouvelle fonction pour que çà fonctionne.

    Citation Envoyé par Alex4416 Voir le message
    Comment me conseilles-tu de créer les listes buttons et dialogs ? Et dans tous les cas mes objets pushButtonX existent déjà car je les ai créés avec QtDesigner.
    Le Designer vous permet de "nommer" les widgets importants que vous pouvez retrouver ensuite via findChildren... Mais nommer c'est fabriquer une chaîne de caractère explicite "Button-n" et utiliser/fabriquer une fonction qui permette de récupérer le widget. getattr, eval,.... font çà très bien - comme fonction de correspondance entre un entier k et un objet.

    Mais ici, c'est vous qui fabriquez votre liste d'attributs:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        def interfaceDialog(self,k):
            exec('self.dialog{} = Interface_Dialog()'.format(k))
            eval('self.dialog{}'.format(k)).setWindowTitle('Dialog {}'.format(k))
            eval('self.dialog{}'.format(k)).ui.label.setText('Fenetre {}'.format(k))
            eval('self.dialog{}'.format(k)).show()
    Donc pour autant qu'il soit nécessaire de les stocker quelque part, autant utiliser un dictionnaire spécifique (plutôt que celui de la liste des attributs de l'instance):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
            def interfaceDialog(self,k):
                 dialog = Interface_Dialog()
                 dialog.setWindowTitle('Dialog %d' % k)
                 dialog.ui.label.setText('Fenetre %d' % k)
                 dialog.show()
                 self.dialogs[k] =dialog # setattr(self, 'dialog%d' % k, dialog)
    Et si vous voulez mettre sous forme de closure le lambda précédent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            def interfaceDialog(self,k):
                def do_it():
                     dialog = Interface_Dialog()
                     dialog.setWindowTitle('Dialog %d' % k)
                     dialog.ui.label.setText('Fenetre %d' % k)
                     dialog.show()
                     self.dialogs[k] = dialog # setattr(self, 'dialog%d' % k, dialog)
                return do_it
    note: on voit peut être mieux sous cette forme qu'on fabrique une nouvelle fonction à chaque cas.
    Plus besoin de lambda, partial:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
                 self.connect(getattr(self.ui, 'pushButton%d' % k), SIGNAL('clicked()'),self.interfaceDialog(k))
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  8. #8
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 3
    Points : 2
    Points
    2
    Par défaut
    Merci beaucoup de ta réponse, c'est parfait : d'une part tu corriges mon programme et d'autre part tu m'en proposes un meilleur !

    J'ai enfin saisi ce que tu voulais dire avec la liste self.dialogs. Et avec le parallèle entre lambda et la closure, j'ai vraiment compris comment lambda marche.

    Bref merci beaucoup, problème résolu !

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

Discussions similaires

  1. Créer des fenêtres GTK à partir de surface SDL cliquable
    Par sociopath dans le forum GTK+ avec C & C++
    Réponses: 4
    Dernier message: 05/02/2009, 18h25
  2. Créer des Etiquettes différentes sur la meme page
    Par isabelle b dans le forum IHM
    Réponses: 2
    Dernier message: 31/08/2008, 16h33
  3. Créer des filtres de profile sur eclipse
    Par ouadii dans le forum Eclipse Java
    Réponses: 0
    Dernier message: 03/10/2007, 13h53
  4. Création de figures dans des fenêtres différentes
    Par pouette13 dans le forum MATLAB
    Réponses: 4
    Dernier message: 18/06/2007, 14h47
  5. Réponses: 4
    Dernier message: 14/05/2007, 08h53

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