Forum Programmation.c++ [Résolu] Qt envoyer un signal dans le slot d'un widget parent.

Posté par  . Licence CC By‑SA.
Étiquettes :
3
30
sept.
2024

Salut !

J'ai pas refais de C++ serieusement depuis le siècle dernier, je m'y remet trèès doucement, à l'aide de Qt. Je pratique assez assiduement PySide6, donc je connais bien déjà certains concepts de la bête. Cependant me voilà bloqué dans la version C++ sur un truc sûrement trivial pour les connaisseurs, mais je ne sais pas trop quoi chercher, donc je me tourne vers vous.

La question, en gros, c'est comment connecter un signal d'un widget personnalisé à une méthode de la classe qui instancie le dit widget.

Je tente une petit bout de code minimaliste plutôt que me perdre dans des explications. Je vais essayer de le faire le plus précis et juste possible, résultats non-garantis.

D'abord la classe du widget personnalisé :

#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>

class CoolWidget : public QWidget {

    Q_OBJECT

public:
    explicit CoolWidget(QWidget *parent = nullptr) {

        QHBoxLayout *cool_layout = new QHBoxLayout(this);

        QPushButton *bouton1 = new QPushButton("Bouton 1");
        QPushButton *bouton2 = new QPushButton("Bouton 2");

        cool_layout->addWidget(bouton1);
        cool_layout->addWidget(bouton2);

        connect(bouton1, &QPushButton::clicked, /* je sais pas quoi mettre ici */);
                                                /* pour que ça appelle la méthode
                                                   bouton1clicked() dans MainWindow */
    }
}

et la classe MainWindow comme ça :

#include <QMainWindow>
#include <QWidget>
#include <QVBoxLayout>
#include "cool_widget.cpp"

class MainWindow : public QMainWindow {

    Q_OBJECT

public:
    MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        QWidget *central_widget = QWidget;

        QVBoxLayout *main_layout = QVBoxLayout;

        /*
         *  Ici je place des widgets dans ma main_layout.
         */

        // Puis enfin le widget CoolWidget.
        CoolWidget *cool_widget = CoolWidget;
        main_layout->addWidget(cool_widget);

        central_widget->setLayout(main_layout);
        setCentralWidget(central_widget);
    }

    // Méthode à appeler lors de l'appui de bouton1 dans CoolWidget
    void bouton1clicked() {
        // Je fais des trucs quand le bouton est cliqué
    }

}

(bon dans cet exemple il n'y a pas de headers pour simplifier, dans la version "vraie vie" tout est bien séparé en .h et .cpp)

Voilà, si quelqu'un peut me donner un coup de pouce, d'avance bien le merci :)

  • # 2 possibilité à la grosse louche

    Posté par  . Évalué à 4 (+3/-0).

    Dans tous les cas, il faudrait définir un signal qui sera émit par une instance de CoolWidget quand bouton1 sera cliqué.

    La version lourde : ajouter un slot à CoolWidget pour recevoir le signal clicked du bouton, puis dans ce slot, émettre le nouveau signal de CoolWidget.

    La version moins lourde : connecter le signal clicked du bouton au nouveau signal de CoolWidget (si, c'est possible !). L'émission de clicked par le bouton provoquera l'émission du signal de CoolWidget.

    La solution lourde pourrait s'imposer s'il n'est pas possible ou pas souhaitable de faire transiter le même paramètre que celui du signal clicked (totalement de tête sans vérifier : bool checked. Si les paramètres ne sont pas identiques, la connexion échouera). Dans ce cas, le slot de CoolWidget aura ce paramètre, en fera quelque chose, ou pas, et fera l'émission du nouveau signal, sans paramètre, ou avec un paramètre différent selon le job.

    Quoi qu'il en soit, il est très largement préférable de connecter cool_widget à this dans le constructeur de MainWindow. CoolWidget n'a pas à connaître MainWindow ni le slot qui doit recevoir son signal.

    • [^] # Re: 2 possibilité à la grosse louche

      Posté par  . Évalué à 2 (+1/-0).

      Pardon pour ma noobitude, il doit y avoir un truc qui m'échappe, mais je ne vois pas comment l'une de ces deux méthodes va me dispenser d'avoir une reference à MainWindow dans CoolWidget, ce qu'effectivement je ne veut pas faire, et de toutes façon je ne saurais pas trop comment…

      J'en suis venu à me dire qu'il me faudrait une classe "passe-plat" qui collecte les signaux pour les renvoyer dans les bon slots, mais je pense qu'on pourrais qualifier ça de méthode super-lourde du coup ?

      Ou alors, je m'y prend mal pour la création de mon CoolWidget. J'ai du mal à croire que je suis le premier à vouloir faire un Widget perso, et il est probable que la méthode que j'utilide avec PySide ne soit pas la bonne pour le C++…

      Ich bin perdu…

      • [^] # Re: 2 possibilité à la grosse louche

        Posté par  . Évalué à 2 (+1/-0).

        Bon, aller, je fais les devoirs des gamins …

        À vue, sans tester dans QtCreator :

        #include <QWidget>
        #include <QHBoxLayout>
        #include <QPushButton>
        
        class CoolWidget : public QWidget {
        
            Q_OBJECT
        
            public:
                explicit CoolWidget(QWidget *parent = nullptr)
                {
                    QHBoxLayout *cool_layout = new QHBoxLayout(this);
        
                    QPushButton *bouton1 = new QPushButton("Bouton 1");
                    QPushButton *bouton2 = new QPushButton("Bouton 2");
        
                    cool_layout->addWidget(bouton1);
                    cool_layout->addWidget(bouton2);
        
                    connect(bouton1, & QPushButton::clicked, this, & CoolWidget::yaClickSurBtn1);
                }
        
            signal:
                void sPasseUnTruc();
        
            protected slots:
                void yaClickSurBtn1(bool checked = false)
                {
                    emit sPasseUnTruc();
                }
        }
        
        
        #include <QMainWindow>
        #include <QWidget>
        #include <QVBoxLayout>
        #include "cool_widget.cpp"
        
        class MainWindow : public QMainWindow {
        
            Q_OBJECT
        
            public:
                MainWindow(QWidget *parent)
                    : QMainWindow(parent)
                {
                    QWidget *central_widget = QWidget;
        
                    QVBoxLayout *main_layout = QVBoxLayout;
        
                    /*
                     *  Ici je place des widgets dans ma main_layout.
                     */
        
                    // Puis enfin le widget CoolWidget.
                    CoolWidget *cool_widget = CoolWidget;
                    main_layout->addWidget(cool_widget);
        
                    central_widget->setLayout(main_layout);
                    setCentralWidget(central_widget);
        
                    connect( cool_widget, & CoolWidget::sPasseUnTruc, this, & MainWindow::sPasseUnTrucCool );
                }
        
        
            protected slots:
                void sPasseUnTrucCool()
                {
                }
        
        }
        

        CoolWidget émet le signal sPasseUnTruc() quand on clique sur bouton1. Mais c'est pas ses oignons de savoir où ça va.
        MainWindow connecte le signal sPasseUnTruc() de son CoolWidget vers son slot sPasseUnTrucCool() pour réagir.

        • [^] # Re: 2 possibilité à la grosse louche

          Posté par  . Évalué à 2 (+1/-0).

          Merci ca y est, j'ai (enfin) compris le principe. En nettoyant quelques typos pour que ça compile, ça fonctionne.

          Sauf dans mon projet complet. J'ai droit à un ‘sPasseUnTruc’ was not declared in this scope.

          Mais bon, je vais bien trouver pourquoi… Probablement.

          • [^] # Re: 2 possibilité à la grosse louche

            Posté par  . Évalué à 1 (+0/-0).

            1) effacer totalement le répertoire de build. Des fois, c'est "magique" …

            2) pas forcément nécessaire, mais plus sûr et plus propre : séparer les classes dans leurs fichiers .h/.cpp respectifs

            Mieux ?

            • [^] # Re: 2 possibilité à la grosse louche

              Posté par  . Évalué à 2 (+1/-0). Dernière modification le 03 octobre 2024 à 12:06.

              C'est déjà tout en .h et .cpp, vu que la macro Q_OBJECT ne peut être que dans une en-tête, sinon elle est pas prise en compte. Je m'en suis rendu compte en compilant l'exemple que tu m'a donné, ça m'a pris un moment de grattage de tête aussi. Mais j'avais déjà fait ça bien dès le début.

              Non c'était juste une erreur de débutant, j'avais oublié de mettre Classe:: devant la définition dans le .cpp, tout bêtement.

              Merci encore en tout cas. J'avance bien maintenant.

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.