前回は独自のシグナルとスロットを作成し使用してみました。
これまでのサンプルではアプリケーションのユーザーインターフェースのデザインを C++ のコードで作成してきました。
今回はユーザーインターフェースを GUI のツールを用いてデザインする方法について解説します。
「第5回: Qt Creator を使ってみよう!」、「第6回: 簡単なブラウザを作ってみよう!」 で既に使用したとおり、Qt では UI を GUI のツールでデザインすることが可能です。「第8回: QWidget の親子関係を学ぼう」、「第9回: レイアウト管理を学ぼう」 では、ソースコードでウィジェットの生成やレイアウトの管理をしましたが、これは GUI ツールを使用して UI のデザインをする際の背景となる仕組みを理解するためでした。
今回は前回まで使用したサンプルではなく、新しくプロジェクトを作成します。
プロジェクトの作成
「ファイル(F)」 -> 「ファイル/プロジェクトの新規作成(N)…」 を選択してください。
プロジェクト中から「Qt C++ プロジェクト」を選択し、「Qt GUI アプリケーション」を選択して「選択(C)…」をクリックしてください。
名前を「example2」と変更し「次へ(N)>」をクリックしてください。
使用する Qt のバージョンを選択し、「次へ(N)>」をクリックしてください。
クラス名:Example
基底クラス:QWidget
とします。今回は GUI で UI のデザインを作成するため、「フォームを生成する(G)」のチェックはつけたままにしてください。
「完了(F)」をクリックするとアプリケーションのテンプレートが生成され、以下の画面が表示されます。
作成されたプロジェクトの確認
フォームをデザインする前に、作成されたプロジェクトの確認をしましょう。
サイドバーの「編集」ボタンをクリックしてソース画面に切り替えてください。
プロジェクトに「フォーム」というカテゴリが追加され、「example.ui」というファイルが生成されています。このファイルが Qt でウィジェットのデザインをするためのフォームとなります。ファイルの中身は右ペインに表示されているとおり XML の形式になっています。また、プロジェクトファイル「example2.pro」を開くと
FORMS += example.ui
というエントリが生成されていますので確認してみてください。
フォームのデザイン
それでは「デザイン」ボタンを押してデザイン画面に戻りましょう。
この画面が .ui ファイルを GUI で編集するツールになります。Qt と一緒に提供されている「Qt デザイナ」というツールを Qt Creator に統合したものになります。
左側にはウィジェットの一覧が表示されています。フォームにウィジェットを配置する際はここからドラッグ&ドロップします。
中央の下はアクションの一覧とシグナル/スロットの接続の一覧です。
右上はオブジェクトの一覧で、QWidget の親子関係を反映したツリーとして表示されます。
右下はプロパティの一覧で、選択されているオブジェクトのプロパティの一覧が表示され、変更ができるようになっています。
ここでフォームのオブジェクト名(objectNameプロパティの値)が「Example」となっている事に注目してください。このオブジェクト名がこのファイルから後ほど生成されるクラスのクラス名になります。
それではウィジェットを配置していきましょう。
左側のウィジェットリストから「Horizontal Slider」をドラッグし、フォーム上にドロップしてください。このとき左上のフィルタフォームに「sl(ider)」と入力することで素早く目的のウィジェットを見つけることができるでしょう。
同様に、「Push Button」をスライダの下にドラッグ&ドロップして配置してください。この時も「but(ton)」でフィルタすると探しやすいでしょう。
次にこのボタンの文字を変更します。ボタンを右クリックして「テキストを変更…」を選択し、「Close」に変更してください。
ボタンの文字の変更はプロパティエディタの text プロパティの変更や、ボタンをダブルクリックする事でも可能です。
次はレイアウトを設定します。Example ウィジェットに対してレイアウトを設定するため、Example(フォーム自体)を選択した状態で画面上部の「垂直に並べる(V)」ボタンをクリックしてください。
フォームの右クリックからも同様の操作が可能です。
以上で子ウィジェットの配置とレイアウトの設定は完了です。
最後に画面が間延びしているのでサイズを調整しましょう。
画面上部の「サイズ調整(S)」というボタンを押すと、個々のウィジェットの理想的なサイズを考慮した最適なサイズにリサイズされます。
各ウィジェットのオブジェクト名の変更
画面右上のオブジェクトインスペクタ内の各オブジェクト名をダブルクリックし、スライダのオブジェクト名を「slider」に、ボタンのオブジェクト名を「button」に変更してください。
オブジェクト名の変更はフォーム上のウィジェットの右クリックで表示されるコンテキストメニューの「オブジェクト名を変更…」や、画面右下のプロパティエディタの「objectName」の変更でも可能です。
ここで変更したオブジェクト名はフォームから生成されるクラスのメンバ変数名となり、ソースコードから各ウィジェットにアクセスする際に使用します。
フォームのプレビュー
ツール(T) -> フォーム エディタ(M) -> Preview… を選択すると現在のフォームのプレビューが見られます。
フォームを使用する
作成したフォームはコンパイル時に uic というツールによって C++ のヘッダファイルに変換されます。この時、example.ui というファイルからは ui_example.h というファイルが生成されます。この中には Ui という名前空間の中にフォームのオブジェクト名と同じ名前のクラスが定義され、子ウィジェットはそれぞれのオブジェクト名を名前とするメンバ変数として定義されます。ソースコードでフォームを使用する場合はこのクラスのインスタンスを生成し、setupUi(QWidget *parent)メソッドを呼び出します。
Qt Creator でアプリケーションを「フォームを生成する」をチェックして生成した場合には、以下のように既にこのコードが記述された状態になっています。
example.h
namespace Ui { class Example; // [1] } class Example : public QWidget { ... private: Ui::Example *ui; // [2] };
example.cpp
#include "example.h" #include "ui_example.h" // [3] Example::Example(QWidget *parent) : QWidget(parent), ui(new Ui::Example) // [4] { ui->setupUi(this); // [5] } Example::~Example() { delete ui; // [6] }
[1] Ui::Example クラスの宣言です
[2] Ui::Example クラスのポインタを private なメンバ変数として保持します
[3] ui_example.h をインクルードします
[4] Ui::Example クラスを生成しています
[5] Ui::Example クラスの setupUi メソッドを実行しています
[6] [4] で生成した ui を破棄しています
setupUi() を実行した際に、引数に渡されたウィジェットに対して親ウィジェットのプロパティの変更や子ウィジェットの生成など、フォームの内容が適用されます。
ビルドと実行
それではビルドをして実行してみてください。
コンパイル時のログを確認して uic というツールによって example.ui から ui_example.h が生成されていることを確認してみてください。
/home/tasuku/qtsdk-2010.04/qt/bin/uic ../example2/example.ui -o ui_example.h
また、ビルドディレクトリに生成されている ui_example.h を開き、クラスがどのように定義されているか、setupUi メソッドの中の処理がどうなっているかも確認してみてください。基本的な処理は「第8回: QWidget の親子関係を学ぼう」、「第9回: レイアウト管理を学ぼう」で記述した内容と同じになっています。
ボタンを押した際に閉じるようにする
最後に「Close」ボタンを押した際にウィンドウを閉じてアプリケーションが終了するようにしましょう。
Example::Example(QWidget *parent) : QWidget(parent), ui(new Ui::Example) { ui->setupUi(this); connect(ui->button, SIGNAL(clicked()), this, SLOT(close())); // [1] }
[1] 「ui」 のボタン「button」の [qt “” clicked l=qabstractbutton] シグナルを Example クラスの [qt “” close l=qwidget] スロットに接続
この接続は「第10回: シグナルとスロット」で行なったものと一緒です。ボタンのインスタンスがフォーム Ui::Example の button というメンバ変数に変わっています。
フォームを使う際のまとめ
- Qt では .ui という拡張子のファイルを使用してフォームをデザインする
- プロジェクトファイルの FORM エントリに使用するフォームを追加する
- example.ui という ui ファイルから ui_example.h というヘッダファイルが生成される
- Ui という名前空間にフォームのオブジェクト名と同じ名前のクラスが定義される
- 子ウィジェット(とレイアウト)はオブジェクト名と同じ名前のメンバ変数として定義される
- フォームを使用する際は生成されたクラスの setupUi メソッドを呼ぶ
おわりに
Qt では GUI のツールを使用することにより UI の作成が簡単にできることがお分かりいただけたでしょうか。今回のようなシンプルな UI の場合はソースコードで書いても手間は変わりませんが、UI が複雑になるにつれソースコードでの生成や変更にかかる手間は多くなるため、ツールを使用して作成するメリットは大きくなるでしょう。
次回はさらに複雑な UI のデザインやツールを用いたシグナル/スロットの接続などを学びましょう。
[…] This post was mentioned on Twitter by denis bondaryuk, Cosimo K., Qt Web Team, Yuji Watanabe, Tasuku Suzuki and others. Tasuku Suzuki said: RT @qtjapan: Qt Labs JP – Qt をはじめよう! 第13回: GUI デザイナを使おう http://bit.ly/cZFcT […]
津田伸秀さんの
Qtプログラミング入門(工学社)というのを買いました。
鈴木さんの記述と同様、新しいプロジェクトを選択すると、『Qt C++プロジェクト』という選択肢があることになっています。
ところが、私がインストールしたSDKにあるQt Creator2.4.0(Qt 4.7.4 (32bit)を使用)では、『Qt C++プロジェクト』がありません。
使いたいのはこの機能だけなので困ってしまいました。
どうしたら良いか教えて頂けませんでしょうか?
ちなみに、Qtは現時点での最新版です。
インストールは、4回やり直しましたが同じです。
ウェブでも同じような情報は見つかりませんでした。
よろしくお願いいたします。
Qt Creator 2.4.0 の場合は「Qt ウィジェットプロジェクト」の中の「Qt GUI アプリケーション」を選択してください。
uiフォームに対して、別クラスからアクセスするにはどうすればよいのでしょうか。
「MainWindowクラスとSubWindowクラスにそれぞれ2つのフォームがあった場合、SubWindowのフォーム上に
あるボタンをクリックしたらMainWindowフォームに対して処理を行う」といった場合はどのように
記述すればよいのでしょうか。
Sasaki さん、
以下の方法でいかがでしょうか。
1. SubWindow クラスに1つ独自シグナルを追加
2. ボタンの clicked() シグナルを追加したシグナルに接続
connect(ui->button, SIGNAL(clicked()), this, SIGNAL(追加したシグナル())); // のようになるはずです
3. MainWindow クラスに処理を行うスロットを追加
4. SubWindow のシグナルを MainWindow のスロットに接続
詳細は Qt をはじめよう! 第12回: シグナルとスロットを作成しよう などを参照してください。
鈴木さま
回答ありがとうございます。
お手数ですがもう少し教えてください。
以下のようにやってみましたがうまくいきませんでした。
1)subWindowクラスにSignalを設定
signals:
void subfunc();
2)MainWindowクラスにSLOTを設定
private slots:
void mainfunc();
ここでsubfunc()から直接mainfunc()を呼ぶことができないため、
中継用の関数をMainWindowに設定(static void maintuukei())
4)以下のように実施
subWindow側:
connect(ui->pushbutton,SIGNAL(clicked()),this,SIGNAL(subfunc()));
void subWindow::subfunc()
{
MainWindow::maintuukei();
}
MainWindow側:
void MainWindow::maintuukei()
{
MainWindow::mainfunc();
}
subWindowのpushbutton->subfunc()->MainWindowのmaintuukei()->mainfunc()
としてみましたがビルドすると
「ui:静的でないメンバー関数の内部においてのみ参照できます」
となりうまくコンパイルできませんでした。
どのように修正すればよいでしょうか。お手数ですがお教えください。
Sasaki さん、
Qt のシグナルは自動で実装されるため、void subWindow::subfunc() {…} のところを実装する必要はありません。(詳細は第12回の記事を参照してください)
また、中継用の関数もこの場合必要ありません。
SubWindow のインスタンスを生成しているところで、以下のような感じでシグナルとスロットの接続をしてください。
void MainWindow::aMethod() {
SubWindow *subWindow = new SubWindow(this);
connect(subWindow, SIGNAL(subfunc()), this, SLOT(mainfunc()));
subWindow->show();
}
これにより、SubWindow の subfunc シグナルが発生した場合に MainWindow の mainfunc スロットが自動的に呼び出されます。
「ui:静的でないメンバー関数の内部においてのみ参照できます」というエラーメッセージについてですが、このメッセージからだけではなにが問題なのかこちらでは判断ができないので、エラーになっている場所になにか間違いがないかご自身で確認してみてください。