Recherche…


Introduction

Les signaux et les emplacements sont utilisés pour la communication entre objets. Le mécanisme de signaux et de créneaux est une caractéristique centrale de Qt. Dans la programmation par interface graphique, lorsque nous modifions un widget, nous voulons souvent qu'un autre widget soit notifié. Plus généralement, nous souhaitons que les objets de toute nature puissent communiquer entre eux. Les signaux sont émis par les objets lorsqu'ils changent d'état d'une manière qui peut être intéressante pour d'autres objets. Les emplacements peuvent être utilisés pour recevoir des signaux, mais ils sont également des fonctions membres normales.

Remarques

La documentation officielle sur ce sujet peut être trouvée ici .

Un petit exemple

Les signaux et les emplacements sont utilisés pour la communication entre objets. Le mécanisme des signaux et des slots est une fonctionnalité centrale de Qt et probablement la partie la plus différente des fonctionnalités fournies par les autres frameworks.

L'exemple minimal nécessite une classe avec un signal, un emplacement et une connexion:

counter.h

#ifndef COUNTER_H
#define COUNTER_H

#include <QWidget>
#include <QDebug>

class Counter : public QWidget
{
    /*
     * All classes that contain signals or slots must mention Q_OBJECT
     * at the top of their declaration.
     * They must also derive (directly or indirectly) from QObject.
     */
    Q_OBJECT

public:
    Counter (QWidget *parent = 0): QWidget(parent)
    {
            m_value = 0;

            /*
             * The most important line: connect the signal to the slot.
             */
            connect(this, &Counter::valueChanged, this, &Counter::printvalue);
    }

    void setValue(int value)
    {
        if (value != m_value) {
            m_value = value;
            /*
             * The emit line emits the signal valueChanged() from
             * the object, with the new value as argument.
             */
            emit valueChanged(m_value);
        }
    }

public slots:
    void printValue(int value)
    {
        qDebug() << "new value: " << value;
    }

signals:
    void valueChanged(int newValue);

private:
    int m_value;

};

#endif

La main définit une nouvelle valeur. Nous pouvons vérifier comment l'appel est appelé, en imprimant la valeur.

#include <QtGui>
#include "counter.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Counter counter;
    counter.setValue(10);
    counter.show();

    return app.exec();
}

Enfin, notre dossier de projet:

SOURCES   = \
            main.cpp
HEADERS   = \
            counter.h

La nouvelle syntaxe de connexion Qt5

La syntaxe de connect conventionnelle qui utilise les macros SIGNAL et SLOT fonctionne entièrement au moment de l'exécution, ce qui présente deux inconvénients: elle comporte des surcharges d'exécution (entraînant également une surcharge de taille binaire) et aucune vérification de la correction à la compilation. La nouvelle syntaxe aborde les deux problèmes. Avant de vérifier la syntaxe dans un exemple, il est préférable de savoir ce qui se passe en particulier.

Disons que nous construisons une maison et que nous voulons connecter les câbles. C'est exactement ce que fait la fonction de connexion. Les signaux et les slots sont ceux qui ont besoin de cette connexion. Le fait est que si vous effectuez une connexion, vous devez faire attention aux autres connexions qui se chevauchent. Chaque fois que vous connectez un signal à un slot, vous essayez de dire au compilateur qu'à chaque fois que le signal est émis, invoquez simplement la fonction slot. C'est ce qui se passe exactement.

Voici un exemple de main.cpp :

#include <QApplication>
#include <QDebug>
#include <QTimer>

inline void onTick()
{
   qDebug() << "onTick()";
}

struct OnTimerTickListener {
   void onTimerTick()
   {
       qDebug() << "OnTimerTickListener::onTimerTick()";
   }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    OnTimerTickListener listenerObject;

    QTimer timer;
    // Connecting to a non-member function
    QObject::connect(&timer, &QTimer::timeout, onTick);
    // Connecting to an object member method
    QObject::connect(&timer, &QTimer::timeout, &listenerObject, &OnTimerTickListener::onTimerTick);
    // Connecting to a lambda
    QObject::connect(&timer, &QTimer::timeout, [](){
        qDebug() << "lambda-onTick";
    });    

    return app.exec();
}

Conseil: l'ancienne syntaxe (macros SIGNAL / SLOT ) exige que le métacompilateur Qt (MOC) soit exécuté pour toute classe comportant des slots ou des signaux. Du point de vue du codage, cela signifie que ces classes doivent avoir la macro Q_OBJECT (ce qui indique la nécessité d’exécuter MOC sur cette classe).

La nouvelle syntaxe, quant à elle, nécessite toujours MOC pour que les signaux fonctionnent, mais pas pour les slots. Si une classe ne dispose que de slots et de signaux, elle n'a pas besoin de la macro Q_OBJECT et ne peut donc pas invoquer la MOC, ce qui réduit non seulement la taille binaire finale mais réduit également le temps de compilation (aucun appel MOC *_moc.cpp fichier *_moc.cpp ).

Connexion de signaux / slots surchargés

Bien qu’elle soit meilleure à bien des égards, la nouvelle syntaxe de connexion de Qt5 présente une grande faiblesse: la connexion de signaux et de logements surchargés. Afin de permettre au compilateur de résoudre les surcharges, nous devons utiliser static_cast s pour les pointeurs des fonctions membres, ou (à partir de Qt 5.7) qOverload et amis:

#include <QObject>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    void slot(const QString &string) {}
    void slot(const int integer) {}

signals:
    void signal(const QString &string) {}
    void signal(const int integer) {}
};

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);

    // using pointers to make connect calls just a little simpler
    MyObject *a = new MyObject;
    MyObject *b = new MyObject;

    // COMPILE ERROR! the compiler does not know which overloads to pick :(
    QObject::connect(a, &MyObject::signal, b, &MyObject::slot);

    // this works, now the compiler knows which overload to pick, it is very ugly and hard to remember though...
    QObject::connect(
        a,
        static_cast<void(MyObject::*)(int)>(&MyObject::signal),
        b,
        static_cast<void(MyObject::*)(int)>(&MyObject::slot));

    // ...so starting in Qt 5.7 we can use qOverload and friends:
    // this requires C++14 enabled:
    QObject::connect(
        a,
        qOverload<int>(&MyObject::signal),
        b,
        qOverload<int>(&MyObject::slot));

    // this is slightly longer, but works in C++11:
    QObject::connect(
        a,
        QOverload<int>::of(&MyObject::signal),
        b,
        QOverload<int>::of(&MyObject::slot));

    // there are also qConstOverload/qNonConstOverload and QConstOverload/QNonConstOverload, the names should be self-explanatory
}

Connexion multi-fenêtre

Un exemple simple à plusieurs fenêtres utilisant des signaux et des slots.

Il existe une classe MainWindow qui contrôle la vue de la fenêtre principale. Une deuxième fenêtre contrôlée par la classe du site Web.

Les deux classes sont connectées de sorte que lorsque vous cliquez sur un bouton de la fenêtre du site Web, quelque chose se produit dans la fenêtre principale (une étiquette de texte est modifiée).

J'ai fait un exemple simple qui est aussi sur GitHub :

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "website.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void changeText();

private slots:
    void on_openButton_clicked();

private:
    Ui::MainWindow *ui;

    //You want to keep a pointer to a new Website window
    Website* webWindow;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeText()
{
    ui->text->setText("New Text");
    delete webWindow;
}

void MainWindow::on_openButton_clicked()
{
    webWindow = new Website();
    QObject::connect(webWindow, SIGNAL(buttonPressed()), this, SLOT(changeText()));
    webWindow->show();
}

website.h

#ifndef WEBSITE_H
#define WEBSITE_H

#include <QDialog>

namespace Ui {
class Website;
}

class Website : public QDialog
{
    Q_OBJECT

public:
    explicit Website(QWidget *parent = 0);
    ~Website();

signals:
    void buttonPressed();

private slots:
    void on_changeButton_clicked();

private:
    Ui::Website *ui;
};

#endif // WEBSITE_H

website.cpp

#include "website.h"
#include "ui_website.h"

Website::Website(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Website)
{
    ui->setupUi(this);
}

Website::~Website()
{
    delete ui;
}

void Website::on_changeButton_clicked()
{
    emit buttonPressed();
}

Composition du projet:

SOURCES += main.cpp\
        mainwindow.cpp \
    website.cpp

HEADERS  += mainwindow.h \
    website.h

FORMS    += mainwindow.ui \
    website.ui

Considérez le Uis à composer:

  • Fenêtre principale: une étiquette appelée "texte" et un bouton appelé "openButton"
  • Fenêtre du site Web: un bouton appelé "changeButton"

Les points-clés sont donc les connexions entre les signaux et les slots et la gestion des pointeurs ou des références de Windows.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow