サーチ…


前書き

信号とスロットは、オブジェクト間の通信に使用されます。シグナルとスロットのメカニズムはQtの中心的な機能です。 GUIプログラミングでは、1つのウィジェットを変更すると、別のウィジェットに通知されることがよくあります。より一般的には、あらゆる種類のオブジェクトがお互いに通信できるようにしたいと考えています。信号は、他のオブジェクトにとって興味深い方法で状態を変更したときにオブジェクトによって放出されます。スロットは信号を受信するために使用できますが、通常のメンバー機能でもあります。

備考

このトピックの公式文書はここにあります

小さな例

信号とスロットは、オブジェクト間の通信に使用されます。シグナルとスロットのメカニズムはQtの中心的な機能であり、おそらく他のフレームワークが提供する機能と最も異なる部分です。

最小限の例では、1つの信号、1つのスロット、1つの接続を持つクラスが必要です。

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

mainは新しい値を設定します。スロットがどのように呼び出されているかを確認し、値を出力することができます。

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

最後に、私たちのプロジェクトファイル:

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

新しいQt5接続構文

SIGNALSLOTマクロを使用connect従来のconnect構文は、実行時に完全に機能します。ランタイムオーバーヘッド(バイナリサイズオーバーヘッドにもなります)があり、コンパイル時の正しさチェックはありません。新しい構文は両方の問題に対処します。例で構文をチェックする前に、特に何が起こるかを知っておくとよいでしょう。

家を建ててケーブルを接続したいとしましょう。これはまさにconnect関数がするものです。信号とスロットは、この接続を必要とするものです。要点は、1つの接続を行う場合は、さらに重複する接続に注意する必要があります。信号をスロットに接続するときはいつでも、信号が放射されるたびにスロット機能を呼び出すようコンパイラーに指示しようとしています。これはまさに何が起こるかです。

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

ヒント:古いシンタックス( SIGNAL / SLOTマクロ)では、スロットやシグナルを持つクラスに対してQtメタコンパイラ(MOC)を実行する必要があります。コーディングの観点からは、そのようなクラスにはQ_OBJECTマクロ(このクラスでMOCを実行する必要性を示す)が必要であることを意味します。

一方、新しいシンタックスでは、シグナル用にはMOCが必要ですが、スロット用ではありません 。クラスにスロットとシグナルしか持たない場合は、 Q_OBJECTマクロを持つ必要がないため、MOCを呼び出すことはできません。最終バイナリサイズを減らすだけでなく、コンパイル時間を短縮します(MOC呼び出しなし、 *_moc.cppファイル)。

過負荷信号/スロットの接続

Qt5の新しい接続構文は、多くの点で優れていますが、過負荷信号とスロットを接続するという大きな弱点があります。コンパイラにオーバーロードを解決させるために、メンバ関数ポインタにstatic_castを使用する必要があります。(Qt 5.7から開始します) qOverloadとfriends:

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

マルチウィンドウ信号スロット接続

信号とスロットを使った簡単なマルチウィンドウの例。

メインウィンドウビューを制御するMainWindowクラスがあります。ウェブサイトクラスによって制御される第2のウィンドウ。

2つのクラスが接続されているため、ウェブサイトウィンドウのボタンをクリックすると、MainWindowで何かが発生します(テキストラベルが変更されます)。

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

プロジェクト構成:

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

HEADERS  += mainwindow.h \
    website.h

FORMS    += mainwindow.ui \
    website.ui

Uisを構成すると考えてください:

  • メインウィンドウ: "text"というラベルと "openButton"というボタン
  • ウェブサイトウィンドウ:「changeButton」ボタン

したがって、キーポイントは、信号とスロットの間の接続と、ウィンドウポインタまたは参照の管理です。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow