1つのシグナルと複数スロットの結びつけ

QTではシグナルスロットを使ってウィジェットのイベント処理をしています。

ただ、電卓やカラーピッカーなどウィジェットの数が多いアプリケーションではいちいちシグナルとスロットを結びつけるのが面倒になってきます。

そこでウィジェットに共通のシグナルがある場合はそれを複数のスロットに接続することで無駄なコードを減らすことができます。

複数のスロットに結び付ける方法には2種類あります。

sender関数を使って呼び出し元を調べる方法

1つめはスロットのある関数でスロット関数が呼ばれたときにsender関数を使うと、どのオブジェクトからシグナルが送られてきたか調べる方法です。

例えば次のようなウィジェットを例に考えてみます。

5つのチェックボックスが縦向きに並んでいるウィジェット

縦に5つのチェックボックスが並んでいるウィジェットです。

このウィジェットは次のようなヘッダファイルで定義してあります。

class CheckWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CheckWidget(QWidget * parent = 0);
    
private slots:
    void findCheck();

private:
    QList<QCheckBox*> checks;
};

このウィジェットのコンストラクターでチェックボックスのclickedシグナルとfindCheckスロットを結びつけます。

CheckWidget::CheckWidget(QWidget * parent)
    : QWidget(parent)
{
    QVBoxLayout * layout = new QVBoxLayout;
    
    int checkCount = 5;
    for(int i = 0; i < checkCount; ++i){
        QCheckBox * check = new QCheckBox(QString::number(i + 1));
        check->setObjectName(QString::number(i));
        connect(check, SIGNAL(clicked()), this, SLOT(findCheck()));
            ///シグナルとスロットの結びつけ
        layout->addWidget(check);
    }

    setLayout(layout);
}

ただ結びつけるだけだとどのチェックボックスからクリックされたかが分からなくなります。

そこでスロット関数で次のようにsender関数が使えます。

void CheckWidget::findCheck()
{
    QCheckBox * check = dynamic_cast<QCheckBox*>(sender());
    if(check != 0)
         qDebug() << "Check " << check->objectName() << " is clicked.";
}

sender関数はシグナル呼び出し元のオブジェクトを返してくるので、それをQCheckBoxなどの任意のウィジェットにキャストすればOKです。

QSignalMapperを使う方法

次がQSignalMapperで複数のスロットにマッピングする方法です。

先ほどのCheckWidgetのコンストラクターはこれを使うと次のように書き換えできます。

CheckWidget::CheckWidget(QWidget * parent)
    : QWidget(parent)
{
    QVBoxLayout * layout = new QVBoxLayout;

    QSignalMapper *signalMapper = new QSignalMapper(this);
    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(findCheckIndex(int)));

    int checkCount = 5;
    for(int i = 0; i < checkCount; ++i){
        QCheckBox * check = new QCheckBox(QString::number(i + 1));
        check->setObjectName(QString::number(i));
        signalMapper->setMapping(check, i);
            ///i番目のチェックボックスをマッピング
        connect(check, SIGNAL(clicked()), signalMapper, SLOT(map()));
        layout->addWidget(check);
    }

    setLayout(layout);
}

mappedシグナルがsetMappingで設定したウィジェットがクリックされたときに送信されます。

その時setMappingでウィジェットに結び付けたIDがスロットの引数に渡されるようにしてあります。

例えば1番目のチェックボックスはsetMappingで1というIDが割り当てられるのでfindCheckIndexには1が渡されて実行されます。

findCheckIndexスロットでは次のようにしてシグナルの送信元を調べることが可能です。

void CheckWidget::findCheckIndex(int i)
{
    qDebug() << "findCheckIndex : Check " << i << " is clicked.";
}

senderを使う場合と比べてキャストを使わずに済むので、無駄なキャストをしたくないというときには役に立つかもしれません。

この例ではint型をマッピングしていますが、それ以外には QString, QWidget*, QObject* 型をマッピングできます。

以上、1つのシグナルを複数のスロットに結び付ける方法でした。では、また!

関連項目
プライバシーポリシー