QtでGUIアプリーション作成入門(1)

Qtフレームワークを使って下のようなアプリケーションを作ってみる。色んな所でQt入門記事があるけど、公式Qtドキュメントのチュートリアルが結局わかりやすかった。

通常はユーザーインターフェースの作成にはQtデザイナー使用するが、Qtの動作を理解するために、あえてそれを使わないで、ソースコードからUIを作成する方法を紹介する。

対象はC++は既にわかっていてCUIソフトは作ったことあるけど、GUIはない人。

現在のQtのバージョンはQt5.1、もうすぐQt5.2の正式版がリリースされる、5.2ではiOSもサポートされるので、iPhoneアプリも作成できるようになります。

作成するのは以下の様なアプリ
http://qt-project.org/doc/qt-5.1/qtwidgets/images/addressbook-tutorial-screenshot.png

プロジェクトの作成

プロジェクトの新規作成>Qt Guiアプリケーション

  • 名前:AddressBook
  • クラス名:AddressBook
  • 基底クラス:QWidgets

以下のファイルが生成される

  • AddressBook.pro:プロジェクトの設定ファイル
  • main.cpp:メイン関数がある
  • addressbook.h:AddressBookクラスのヘッダ
  • addressbook.cpp:AddressBookクラスの実装

コンパイル&実行はQtクリエイターの実行ボタンでもいいけど、コマンドラインからは.proがあるディレクトリにcdして。
qmake&&make
qmakeがMakefileを作成して、それをmakeする。するとAddressBook.exeなり.appができる。

ちなみにC++11を有効化するには、プロジェクトファイルに以下を追加
CONFIG+=c++11

基本的UIの作成

ソースコードで作成したUIとQtデザイナーで作成したUIは両立するが表示が重なるので配置には注意が必要。

UI要素=ウィジェット

UIには様々な要素がある。ボタン、メニューバー、ステータスバー、入力ボックスなど、Qtではこれらの要素をウィジェットと呼んでおり、クラスとして実装されている。メインウィンドウはただの四角い窓だが、これがトップレベルのウィジェットとなる。メインウィンドウの子ウィジェットとして、ボタンなどの他のウィジェットを追加してゆく。
http://qt-project.org/doc/qt-5.1/qtwidgets/images/addressbook-tutorial-part1-labeled-screenshot.png

addressbook.h

#include "addressbook.h"
#include "ui_AddressBook.h"//Qtデザイナーで作成したui用
#include <QtWidgets>

class QLineEdit;
class QTextEdit;
class AddressBook : public QWidget
{
    Q_OBJECT
public:
    AddressBook(QWidget *parent = 0);
private:
    QLineEdit *nameLine;//名前を入力するUI要素
    QTextEdit *addressText;//住所を入力するUI要素
};

AddressBookクラスを QWidget クラスのサブクラスとして定義するので、をインクルードする。Q_OBJECT マクロは国際化(tr())とシグナル/スロット(connect())を利用するために必要。

このクラスは QLineEdit andQTextEdit のポインタをメンバ変数として持つ。それぞれ名前と住所を格納する。メンバ変数がポインタなのは、インスタンス化は実行時にするため。これらのクラスのためのヘッダ(紛らわしい)は addressbook.cpp でインクルードされるので、ここでは前方宣言するに留める。

他にもQLabelは文字列をウィンドウに表示するのに使うが、これのオブジェクトの宣言は含まない、これを参照する必要はないため。

addressbookクラスのコンストラクタは QWidget 型の引数 parent を受け取る。これはAddressBookウィジェットが他のウィジェットから呼び出された時に、呼び出し元の親ウィジェットを指定するために使うが、ここではAddressBookはユーザーが呼び出すトップレベルのウィジェットなので=0がデフォルトになっている。

addressbook.cpp

AddressBook::AddressBook(QWidget *parent)
    : QWidget(parent)
{
    QLabel *nameLabel = new QLabel(tr("Name:"));
    nameLine = new QLineEdit;

    QLabel *addressLabel = new QLabel(tr("Address:"));
    addressText = new QTextEdit;

Qtでのトップレベル・ウィジェットのコンストラクタの主な役割はUI要素をセットアップすること。このコンストラクタでは、QLabel オブジェクト nameLabel と addressLabel がインスタンス化される、同様に、nameLine と addressText もインスタンス化される。
tr() 関数は翻訳された文字列を返す(なければそのままの文字列)。この関数は引数として与えられた QString 文字列を他の言語に翻訳するべきものとして印を付ける。翻訳すべき文字列が現れるときには常に使うべき。

レイアウト

Qtでプログラミンする際にはレイアウトがどのように働くのか理解しておくと便利。Qtは、各ウィジェットを配置するために、3つの主要なレイアウトクラスを提供する: QHBoxLayout, QVBoxLayout and QGridLayout 。
http://qt-project.org/doc/qt-5.1/qtwidgets/images/addressbook-tutorial-part1-labeled-layout.png

ここでは QGridLayout を使い、ラベルと入力フィールドを構造的に配置する。QGridLayout は空間を格子状に分け、ウィジェットを配置する、行と列の数を指定する。
上のダイヤグラムはセルのレイアウトとウィジェットの位置を示している、次のコードでこのアレンジメントを指定する。

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->addWidget(nameLabel, 0, 0);
    mainLayout->addWidget(nameLine, 0, 1);
    mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
    mainLayout->addWidget(addressText, 1, 1);

addressLabel は Qt::AlignTop を付加的な引数として使っている。これはセル(1,0)が垂直中心に位置しないようにするため。
レイアウトオブジェクトをこのウィジェット上にインスタンス化するために、ウィジェットの setLayout() 関数を呼び出す必要がある:

    setLayout(mainLayout);
    setWindowTitle(tr("Simple Address Book"));
}

最後に、ウィンドウ名を "Simple Address Book" とする。

アプリケーションの実行

main.cppにメイン関数が記述されている。この関数内で QApplication オブジェクト, app をインスタンス化する。
QApplication は様々なアプリケーションワイドな機能を提供する、例えば、デフォルトフォントやカーソル、イベントループ。なのでQtのGUIアプリには常に1つの QApplication オブジェクトが存在する。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    AddressBook addressBook;
    addressBook.show();

    return app.exec();
}

メイン関数内で addressbookウィジェットインスタンス化し、表示するために show() 関数を呼び出している。しかし、ウィジェットはアプリケーションのイベントループが始まるまでは表示されない。アプリケーション・クラスの exec() 関数を呼び出すことでイベントループを開始している。この関数の返り値がmain関数の返り値として利用されている。
アドレスブック・ウィジェットをメイン関数のメモリ領域でインスタンス化することで、メイン関数のスコープを外れるとアドレスブックやその子ウィジェットのメモリは開放され、メモリーリークを防ぐ。

コンパイル

ターミナルを開き、プロジェクトファイル(addressbook.pro)が置いてあるディレクトリへ移動
qmake && make
そのディレクトリにaddressbook.appができる。