Qt をはじめよう! 第16回: GUI デザイナでスロットを作成しよう

前回 は GUI デザイナ上でのシグナル/スロットの接続機能について学びました。

今回は GUI デザイナのシグナル/スロット関連機能のもう一歩進んだ使い方を学びましょう。

独自シグナル/スロットの接続

前回は既存のウィジェットで定義済みのシグナル/スロットを接続しましたが、自分で追加したシグナル/スロットの接続も可能です。これを試すための準備として、プロジェクトの作成時に生成される QWidget のサブクラス Widget にシグナルとスロットを1つずつ追加しましょう。

widget.h

public slots:
void setText(const QString &text);

signals:
void textLengthChanged(int textLength);

widget.cpp

void Widget::setText(const QString &text)
{
emit textLengthChanged(text.length());
}

この記事の最後に全てのソースコードを掲載しており、上記の行番号はそこに対応しています。

文字列を設定するスロット(setText())を作成し、その中では設定された文字列の長さが変わった事を通知するシグナル(textLengthChanged())を発生させています。簡略化のため、設定された文字列を内部で保持したり、現状の文字列と新しい文字列の長さが本当に違うかどうかの判断などはここでは省略しています。

それではフォームを編集し、このシグナル/スロットを他のウィジェットと接続してみましょう。

今回はテキストを入力するための LineEdit とそこに入力された文字列の長さを表示するための Label を以下のように配置します。

次に、シグナル/スロットを接続します。まずはじめに LineEdit の textChanged シグナルを Widget クラスに追加した setText スロットに接続します。シグナルを発生させるウィジェット(LineEdit)からそれを受け取るウィジェット(フォーム自体)にマウスをドロップすると、シグナル/スロットの一覧のダイアログが表示されます。

Widget に作成したスロットはこの一覧には表示されていません。これは、デザイナがこのフォームを QWidget クラスとして扱っているためです。自分で作成したスロットを接続する場合はそのスロットの定義を手動で追加する必要があります。

このダイアログのスロット一覧の下にある「編集...」ボタンを押し、スロット一覧の編集画面を開きます。

次に、Slots の下にある「+」ボタンを押し、Slots の一覧に「setText(QString)」を追加しましょう。

第10回 で解説したとおり、接続するシグナル/スロットの記述に関しては括弧の中に引数の型のみを指定する形になります。
OK ボタンをクリックすると、元のダイアログのスロットの一覧にも追加した setText(QString) が表示されます。LineEdit の textChanged(QString) をこのスロットに接続します。

同じ要領で、Widget に作成した「textLengthChanged(int)」シグナルを Label の setNum(int) スロットに接続してみましょう。

フォームから Label へマウスをドラッグし、シグナルの「編集...」ボタンを押し、Signals に textLengthChanged(int) を追加し、そのシグナルと setNum(int) を接続します。

それでは動作を確認してみましょう。今回は Widget クラスに実装したシグナル/スロットを使用するため、デザイナのプレビューでは確認できません。プロジェクトをビルドし、アプリケーションを実行して確認します。

LineEdit に文字を入力し、配置したラベルに文字数が表示されている事を確認してください。

これで ui ファイルのデザインを適用するクラスで既に作成済みのシグナル/スロットをデザイナから使用する事ができるようになりました。

なお、この機能を使用する場合は、シグナル/スロットの定義を ui ファイルに埋め込む形になるため、
Widget に作成したシグナル/スロット名や引数の変更した場合にはこちらの定義もそれに追従して変更する必要があります。
また、ui ファイルを設定するクラスに存在しないシグナル/スロットを使用しようとしたり、タイプミスなどがあった場合にはアプリケーションの実行時に、接続に失敗した旨の警告メッセージが Qt Creator の"アプリケーション出力ペイン"に表示されますので、動作がおかしい場合にはこれらをチェックしてみてください。

シグナルに対するスロットの作成

UI のデザインをする際には、配置したウィジェットのシグナルが発生した際に(既存のスロットに接続するのではなく)新たにスロットを作成し、そこで様々な処理を行う場面が多く出てくると思います。デザイナにはこの作業を簡単に行える機能もありますので、次はこれを試してみましょう。

以下のようにフォームに PushButton を追加してください。

次に、この PushButton を右クリックし、コンテキストメニューの「Go to slot...」をクリックします。画面は Qt Creator 2.0 ですが、Qt Creator 2.1 の場合は「スロットへ移動...」と日本語化されています。

そのウィジェットが発生させるシグナルの一覧が表示されます。

ここでは clicked() シグナルを選択します。「OK」ボタンをクリックすると以下のように widget.cpp に画面が切り替わります。

Qt Creator によりスロットが追加され、widget.cpp には以下のコードが自動で生成されています。

void Widget::on_pushButton_clicked()
{

}

ここにボタンが押された際の処理を書きます。ここでは LineEdit の文字をクリアすることにします。

void Widget::on_pushButton_clicked()
{
ui->lineEdit->clear();
}

Qt Creator は追加したスロットの定義を widget.h に生成しています。こちらも確認してみましょう。

private slots:
void on_pushButton_clicked();

それでは、この追加したスロットが動作する事を確認しましょう。

アプリケーションを実行し、LineEdit に適当な文字を入力した後、PushButton をクリックし、LineEdit の内容がクリアされることを確認してみてください。

シグナル/スロットの自動接続機能

今回 Go to slot を使用してのスロット作成時には、シグナル/スロットの接続を行いませんでした。この接続に関しては 第15回 で解説したような接続のためのコードも ui_widget.h をはじめ、どこにも生成されていません。しかし、上記で確認した通り、PushButton をクリックした際には作成したスロットのコードが実行されています。それではこのシグナルとスロットの接続はどこで行われているのでしょうか。

答えは ui_widget.h の setupUi() メソッドにあります。

void setupUi(QWidget *Widget)
{
...
    ...
QObject::connect(lineEdit, SIGNAL(textChanged(QString)), Widget, SLOT(setText(QString)));
QObject::connect(Widget, SIGNAL(textLengthChanged(int)), label, SLOT(setNum(int)));

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

このメソッドの最後で実行している [qt QMetaObject connectSlotsByName] では、引数で渡されたオブジェクトに「on_<オブジェクト名>_<シグナル名>(<引数>)」という命名規則のスロットがある場合に一致するオブジェクト、スロットを検索し、自動で接続します。

今回の場合は、Widget クラスに(Qt Creator が追加した)「on_pushButton_clicked()」というスロットがあるので、「pushButton」というオブジェクト名の子オブジェクトを探し、そのオブジェクトに「clicked()」というシグナルがあるかを確認した上で pushButton の clicked() シグナルを Widget の on_pushButton_clicked() スロットに接続しています。

終わりに

今回はウィジェットに追加したシグナル/スロットを Qt Creator のデザイナから接続する方法と、デザイナから直接スロットを作成する方法について学びました。このデザイナが、単にユーザーインターフェースをデザインするだけではなく、アプリケーションを効率的に進めるための便利な機能を提供していることを理解していただけたでしょうか。

最後に、今回変更したソースコードを以下に掲載します。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();

public slots:
void setText(const QString &text);

signals:
void textLengthChanged(int textLength);

private:
Ui::Widget *ui;

private slots:
void on_pushButton_clicked();
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

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

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

void Widget::setText(const QString &text)
{
emit textLengthChanged(text.length());
}

void Widget::on_pushButton_clicked()
{
ui->lineEdit->clear();
}


Blog Topics:

Comments