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

Qt Discussion :

Modifier l'interface d'une fenêtre depuis une seconde


Sujet :

Qt

  1. #1
    Membre à l'essai
    Inscrit en
    Septembre 2010
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 17
    Points : 15
    Points
    15
    Par défaut Modifier l'interface d'une fenêtre depuis une seconde
    Bonjour tout le monde !

    Je développe en ce moment une application Qt avec Qt Creator servant d'interface graphique pour une board SBC9261, sous Linux 2.6.24.

    J'ai à l'écran deux fenêtres, A et B, et j'aimerais pouvoir modifier l'interface de A (afficher/cacher boutons, labels...) en passant par B.

    C'est A qui "crée" B de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Installation *B = new Installation(this);
    B->show();
    Dans B, je pensais pouvoir utiliser le pointeur *parent dans quelque chose du style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->parent->ui->unBouton->hide();
    Mais je n'ai pas accès à l'UI du parent.
    Quelqu'un saurait-il comment faire ?


    Sinon, mon problème de façon plus globale : depuis cette fenêtre B, qui est une fenêtre de réglages, je veux simplement donner à l'utilisateur le choix de ce qu'il veut afficher sur la fenêtre A, et que ces changements s'effectuent dynamiquement.
    Quelqu'un connaît-il une solution "officielle", propre ?

    Merci pour vos réponses !

  2. #2
    Membre émérite
    Avatar de ymoreau
    Homme Profil pro
    Ingénieur étude et développement
    Inscrit en
    Septembre 2005
    Messages
    1 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur étude et développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 154
    Points : 2 834
    Points
    2 834
    Par défaut
    Je ne vois pas vraiment de solution propre, ce qui me vient en tête c'est
    - soit créer des fonctions sur ta classe A pour chaque action possible (afficherBouton1(bool), etc) ce qui peut rapidement devenir lourd.
    - soit déclarer ta classe A amie avec la classe B, qui aura du coup accès au ui, mais aussi à tout le reste.

    Sinon une alternative peut être plus propre mais un peu plus lourde, passer par des données intermédiaires l'état de tes widgets dans A. C'est à dire soit un objet contenant toutes les valeurs (bool afficherBouton1 etc), soit tout stocker dans QSettings. Ensuite déclarer un signal dans B qui indiquerait que cette config a été modifiée, et connecter ce signal à un slot de A mettant à jour l'affichage des widgets.

  3. #3
    Membre à l'essai
    Inscrit en
    Septembre 2010
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 17
    Points : 15
    Points
    15
    Par défaut
    Citation Envoyé par YoniBlond Voir le message
    - soit créer des fonctions sur ta classe A pour chaque action possible (afficherBouton1(bool), etc) ce qui peut rapidement devenir lourd.
    J'y avait pensé, j'ai donc déclaré afficherBouton1(bool) publique dans A.
    Mais comment y accéder depuis B ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->parent->afficherBouton1(true);
    Le code ci-dessus renvoie l'erreur : 'class QObject' has no member named 'afficherBouton1'


    Sinon une alternative peut être plus propre mais un peu plus lourde, passer par des données intermédiaires l'état de tes widgets dans A. C'est à dire soit un objet contenant toutes les valeurs (bool afficherBouton1 etc), soit tout stocker dans QSettings. Ensuite déclarer un signal dans B qui indiquerait que cette config a été modifiée, et connecter ce signal à un slot de A mettant à jour l'affichage des widgets.
    En effet c'est pas mal du tout, d'autant plus que j'aurais certainement besoin d'un QSettings pour stocker la configuration de l'interface lorsque l'utilisateur ferme le programme, afin de la recharger à l'ouverture suivante. En revanche, je crains d'avoir le même problème lors du connect(), pour accéder à un slot de A depuis B !

    Merci pour ta réponse !

  4. #4
    Membre émérite
    Avatar de ymoreau
    Homme Profil pro
    Ingénieur étude et développement
    Inscrit en
    Septembre 2005
    Messages
    1 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur étude et développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 154
    Points : 2 834
    Points
    2 834
    Par défaut
    La fonction "parent" te renvoie un QObject (ou un objet d'une classe en dérivant), si tu es certains que le parent de B est de type classe A, tu peux faire un cast :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static_cast<A*>(parent())
    et tu auras accès à la classe A comme d'habitude (des détails sur le casting si besoin : http://www.cplusplus.com/doc/tutorial/typecasting/ ).

  5. #5
    Membre à l'essai
    Inscrit en
    Septembre 2010
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 17
    Points : 15
    Points
    15
    Par défaut
    Ça marche ! Merci beaucoup !

    Par contre je n'ai pas pu utiliser la fonction parent() dans B, qui compilait mais qui à l'exécution donnait une Segmentation fault.

    J'ai donc "casté" la variable parent disponible dans le constructeur de B, de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Variable globale de la classe B :
    A *parentB;
    
    B::B(QWidget *parent) : QDialog(parent), ui(new Ui::B) {
    ui->setupUi(this);
    parentB = static_cast<A*>(parent);
    }
    Ensuite je peux accéder aux fonctions/variables publiques de A ainsi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    parentB->afficherBouton1(true);
    Je vais voir maintenant si je peux ajouter des QSettings comme tu le proposais.

    Merci beaucoup pour ton aide et bonne suite !

  6. #6
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 617
    Points
    15 617
    Par défaut
    Bonjour truboy

    Quelques petites précisions :

    - il existe plusieurs types de cast permettant de convertir un pointeur en un autre. static_cast permet de faire un cast "à la dure", sans vérification de type. Si par exemple, tu donnes un autre type de QWidget que A, le cast sera réalisé mais tu auras une erreur à l'exécution (la fonction afficherBouton1 n'existant pas)

    Il est préférable d'utiliser un cast vérifiant le type pour être sur que le parent est compatible avec A. Donc il est préférable d'utiliser dynamic_cast, ou encore mieux avec Qt : qobject_cast :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    parentB = qobject_cast<A*>(parent);
    if (!parentB)
        // erreur, parent n'est pas un type compatible avec A
    else
        // c'est bon !
    - A partir du moment où tu utilises une fonction spécifique de A dans le constructeur de B, c'est que, sémantiquement, B ne peut avoir qu'un seul type de parent : A ou une classe dérivée.
    Le mieux est alors d'imposer cette contrainte directement dans le constructeur en écrivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B::B(A *parent) :
    ...
    - L'approche que tu utilises n'est pas optimale en terme de maintenance (non respect du OCP) : si tu modifies ta classe A (en renommant la fonction afficherBouton1 par exemple ou en la supprimant), tu devras également modifier les classes utilisant A.

    La solution est d'utiliser un couplage faible entre tes classes, par exemple en utilisant le système de signaux/slots, comme l'a suggéré YoniBlond : tu crées un signal dans B et un slot dans A et tu les connectes ensemble :

    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
    class A ...
    {
        ...
    public slots:
        void slotInA();
    };
     
    class B ...
    {
        ...
    signals:
        void signalFromB();
    };
     
    A::xxx(...)
    {
       B* b = new B(...);
       connect(b, SIGNAL(signalFromB()), this, SLOT(slotInA()));
    }
    Tu peux créer soit un couple de signal/slot pour chaque paramêtre que tu souhaites contrôler dans B ou tu peux créer une structure contenant l'ensemble des paramètres :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct AFormat
    {
        bool param_1;
        int param_2;
        ...
    };
     
    connect(b, SIGNAL(changed(AFormat)), this, SLOT(update(AFormat)));
    Bon courage

  7. #7
    Membre à l'essai
    Inscrit en
    Septembre 2010
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 17
    Points : 15
    Points
    15
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    La solution est d'utiliser un couplage faible entre tes classes, par exemple en utilisant le système de signaux/slots, comme l'a suggéré YoniBlond : tu crées un signal dans B et un slot dans A et tu les connectes ensemble
    En effet c'est bien plus classe (sans mauvais jeu de mots ) !

    Je vais essayer de mettre ça en place cet après-midi.

    Une question cependant : quel est l'avantage d'utiliser la fonction connect() entre un signal de B et un slot de A plutôt que d'appeler directement la fonction voulue dans A, depuis B ? Car c'est bien moi, en dur, qui vais émettre le signalFromB(), grâce à la fonction emit(), juste ?

    Merci pour ton explication en tous cas !

  8. #8
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 617
    Points
    15 617
    Par défaut
    Une question cependant : quel est l'avantage d'utiliser la fonction connect() entre un signal de B et un slot de A plutôt que d'appeler directement la fonction voulue dans A, depuis B ? Car c'est bien moi, en dur, qui vais émettre le signalFromB(), grâce à la fonction emit(), juste ?
    Tu peux le faire également. Le problème, c'est que A doit connaître B (pour créer une instance de B) et que B doit connaître A (pour appeler la fonction de A). Donc il faut jouer avec les déclarations anticipées et les includes.

    Avec les signaux/slots, B n'a pas besoin de connaitre A. Si en plus, tu ne crées pas B comme étant enfant de A, A n'a pas besoin de connaitre B : tes classes sont complètement indépendantes.

    Seule la classe parent qui crée A et B doit donc les connaitre. Et tu peux mettre ton connect dedans.

  9. #9
    Membre à l'essai
    Inscrit en
    Septembre 2010
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 17
    Points : 15
    Points
    15
    Par défaut
    En effet c'est bien plus propre !

    Merci beaucoup des conseils !

    Bonne suite !

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 16/03/2015, 21h19
  2. Afficher une fenêtre depuis une DLL
    Par swayvill dans le forum VC++ .NET
    Réponses: 3
    Dernier message: 05/04/2007, 19h14
  3. Afficher une fenêtre depuis une DLL
    Par swayvill dans le forum C++
    Réponses: 1
    Dernier message: 07/01/2007, 16h43
  4. [JAVASCRIPT] Renseigner un champ d'une fenêtre depuis une autre fenetre
    Par cobol60 dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 21/07/2006, 13h05
  5. Fermer une fenêtre depuis une autre...
    Par nicolb dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 23/03/2005, 10h22

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