Qtをメインに、プログラムやITに関する情報を発信

Qtの戯言

シグナル/スロット

デザイン画面で設定したシグナル/スロットが、connectしなくても呼び出される理由

更新日:

シグナル/スロット

Qtの代表的な仕組みに、シグナルとスロットがあります。
シグナル/スロットの詳細については、既にたくさんのサイトで解説されているので割愛しますが、簡単に言うと、あるオブジェクトから別のオブジェクトに対して、何らかの事象の発生を通知する仕組みといった感じでしょうか。

シグナル/スロットを設定するには、コード上でconnect()関数を用いる方法と、デザイン画面からUI操作(対象を右クリック→「スロットへ移動」)で設定する方法があります。
後者で設定した場合、コード中にconnect()関数が出現するわけでもないのに、なぜスロット関数が呼び出されるのか?というお話です。

 

QMetaObject::connectSlotsByName()

QWidgetやQMainWindowなどを継承したクラスは、コンストラクタ内の自動生成コードで以下のような呼び出しがあります。

ui->setupUi(this);

 

この関数の最後に、以下の呼び出しがあります。

void setupUi(QMainWindow *MainWindow)
{
    if (MainWindow->objectName().isEmpty())
        MainWindow->setObjectName(QString::fromUtf8("MainWindow"));

    // ~~~中略~~~

    retranslateUi(MainWindow);

    QMetaObject::connectSlotsByName(MainWindow);
} // setupUi

 

10行目のQMetaObject::connectSlotsByName()関数がポイントです。
何をやっているかと言うと、引数で渡されたオブジェクトが持っている全てのスロットに対し、以下を満たす関数名が存在するかをチェックします。

on_<子オブジェクト名>_<子オブジェクトのシグナル名>
※ < や > は、便宜上記載しています。

 

サンプル

と言っても文章では分かりにくいので、サンプルで説明します。

まずはデザイン。(クリックで拡大)

 

続いてコード。

#include <QTimer>
#include "mainwindow.h"
#include "ui_mainwindow.h"

Label::Label(QWidget *parent) :
    QLabel(parent)
{
    QTimer::singleShot(2000, [this]()
    {
        emit testSignal();
    });
}

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

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

void MainWindow::on_testLabel_testSignal()
{
    ui->testLabel->setText(QString(u8"テスト"));
}

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QLabel>
#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class Label : public QLabel
{
    Q_OBJECT

public:
    explicit Label(QWidget *parent = nullptr);
    ~Label() {}

signals:
    void testSignal();
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

public slots:
    void on_testLabel_testSignal();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

 

例によって、1ファイルに色々詰め込んですみません。

デザインでは、メインウィンドウに「testLabel」というオブジェクト名のラベルを配置し、口述するLabelクラスへと昇格させています。

ヘッダでは、QLabelを継承したLabelクラスを作成し、testSignalというシグナルを用意しています。
また、QMainWindowを継承したMainWindowクラスでは、on_testLabel_testSignal()というスロット関数を用意しています。

ソースでは、Labelのコンストラクタ内で、2秒後にtestSignalをemitするようにQTimer::singleShot()を設定し、MainWindowのon_testLabel_testSignal()スロット関数では、ラベルに「テスト」の文字を表示しています。

 

ソース内ではconnectをしていませんが、実行すると「テスト」と表示されます。

これは、MainWindowのスロット関数が、先ほど記載した関数名「on_<子オブジェクト名>_<子オブジェクトのシグナル名>」を満たすためです。
子オブジェクト名:testLabel
子オブジェクトのシグナル名:testSignal
→ on_testLabel_testSignal()

とはいえ、個人的にはコード上で明示的にconnectしている方が、分かりやすいかなと思います。

-シグナル/スロット
-, ,

Copyright© Qtの戯言 , 2020 All Rights Reserved Powered by STINGER.