Suche…


Einführung

Signale und Slots dienen zur Kommunikation zwischen Objekten. Der Signal- und Slots-Mechanismus ist ein zentrales Merkmal von Qt. Wenn wir bei der GUI-Programmierung ein Widget ändern, möchten wir häufig, dass ein anderes Widget benachrichtigt wird. Im Allgemeinen möchten wir, dass Objekte jeglicher Art miteinander kommunizieren können. Signale werden von Objekten ausgegeben, wenn sie ihren Status auf eine Art ändern, die für andere Objekte interessant sein kann. Slots können zum Empfang von Signalen verwendet werden, sie sind jedoch auch normale Elementfunktionen.

Bemerkungen

Offizielle Dokumentation zu diesem Thema finden Sie hier .

Ein kleines Beispiel

Signale und Slots dienen zur Kommunikation zwischen Objekten. Der Mechanismus für Signale und Schlitze ist ein zentrales Merkmal von Qt und wahrscheinlich der Teil, der sich am meisten von den Merkmalen anderer Frameworks unterscheidet.

Das minimale Beispiel erfordert eine Klasse mit einem Signal, einem Steckplatz und einer Verbindung:

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

Das main setzt einen neuen Wert. Wir können überprüfen, wie der Slot aufgerufen wird, und den Wert drucken.

#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();
}

Zum Schluss unsere Projektdatei:

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

Die neue Qt5-Verbindungssyntax

Die herkömmliche connect , die SIGNAL und SLOT Makros verwendet, funktioniert vollständig zur Laufzeit, was zwei Nachteile hat: Sie hat einige Laufzeit-Overhead (was auch zu einer binären Größe führt), und es wird keine Überprüfung der Kompilierzeit durchgeführt. Die neue Syntax behandelt beide Probleme. Bevor wir die Syntax in einem Beispiel überprüfen, sollten wir besser wissen, was genau passiert.

Nehmen wir an, wir bauen ein Haus und wollen die Kabel anschließen. Genau das macht die Connect-Funktion. Signale und Slots benötigen diese Verbindung. Wenn Sie nur eine Verbindung herstellen, müssen Sie auf die sich überlappenden Verbindungen achten. Wenn Sie ein Signal an einen Slot anschließen, versuchen Sie dem Compiler mitzuteilen, dass bei jeder Signalausgabe einfach die Slot-Funktion aufgerufen wird. Genau das passiert.

Hier ist ein Beispiel für 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();
}

Hinweis: Die alte Syntax ( SIGNAL / SLOT Makros) erfordert, dass der Qt-Metacompiler (MOC) für jede Klasse ausgeführt wird, die entweder über Slots oder Signale verfügt. Vom Codierungsstandpunkt aus bedeutet dies, dass solche Klassen über das Makro Q_OBJECT verfügen Q_OBJECT (was die Notwendigkeit anzeigt, MOC für diese Klasse auszuführen).

Andererseits erfordert die neue Syntax immer noch MOC, damit Signale funktionieren, nicht jedoch für Slots. Wenn eine Klasse nur über Slots und keine Signale verfügt, muss sie nicht über das Makro Q_OBJECT verfügen und daher den MOC nicht aufrufen. Q_OBJECT nicht nur die endgültige Q_OBJECT reduziert, sondern auch die Kompilierungszeit (kein MOC-Aufruf und kein nachfolgender Compiler-Aufruf für das generierte Objekt) *_moc.cpp Datei).

Überlastete Signale / Steckplätze anschließen

Die neue Verbindungssyntax in Qt5 ist zwar in vielerlei Hinsicht besser, weist jedoch eine große Schwachstelle auf: Überladen von Signalen und Slots. Um die Compiler lösen die Überlastungen wir verwenden müssen , lassen static_cast s an dem Mitglied Funktionszeiger oder (beginnend in Qt 5.7) qOverload und Freunde:

#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
}

Multi Window Signal Slot-Verbindung

Ein einfaches Beispiel aus mehreren Fenstern mit Signalen und Slots.

Es gibt eine MainWindow-Klasse, die die Ansicht des Hauptfensters steuert. Ein zweites Fenster, das von der Website-Klasse gesteuert wird.

Die beiden Klassen sind so miteinander verbunden, dass beim Klicken auf eine Schaltfläche im Website-Fenster etwas im MainWindow passiert (eine Textbezeichnung wird geändert).

Ich habe ein einfaches Beispiel gemacht, das sich auch auf GitHub befindet :

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();
}

Projektzusammenstellung:

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

HEADERS  += mainwindow.h \
    website.h

FORMS    += mainwindow.ui \
    website.ui

Betrachten Sie die Uis als zusammengesetzt:

  • Hauptfenster: eine Beschriftung namens "Text" und eine Schaltfläche namens "openButton"
  • Website-Fenster: Eine Schaltfläche namens "changeButton"

Die Schlüsselpunkte sind also die Verbindungen zwischen Signalen und Slots und die Verwaltung von Windows-Zeigern oder Referenzen.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow