第4章 プログラム作成への準備

4.1 ダイアログを作ってみよう

今までコードはすべてmain関数の中に書いてきましたが、このままコードを拡張していったらmain関数が1000 行くらいになってしまうかもしれません。

ということで、今回からはクラスを使って関数を分割していくことにします。 また、ソースファイルも01.cppとmain.hファイルに分割します。

まず、今回作成するプログラムの実行結果は、図4.1,4,2のようになります。


PIC 図 4.1: 初期状態 (on Windows)



image1 図 4.2: ボタンを押した後 (on Windows)


起動した直後は図4.1の状態です. そして、テキストに何か文章を書きsetボタンを押すと、ラベルにテキストの内容がコピーされます(図4.2)。

このプログラムは次のようなコードとなります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//main.h

#ifndef MAIN_H_
#define MAIN_H_

#include <QDialog>

class QLabel;
class QPushButton;
class QLineEdit;

class MainDialog : public QDialog
{
	Q_OBJECT
public:
	MainDialog(QWidget* parent = 0);
private slots:
	void setLabelText(); 
private:
	QLabel* label;
	QPushButton* setButton;
	QLineEdit* lineEdit;
};

#endif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//01.cpp

#include <QtGui>
#include "main.h"

MainDialog::MainDialog(QWidget* parent)
	: QDialog(parent)
{
	label = new QLabel(tr("empty") );
	setButton = new QPushButton(tr("Set") );
	lineEdit = new QLineEdit;
	
	connect(setButton,SIGNAL(clicked() ),this,SLOT(setLabelText() ) );

	QHBoxLayout* layout = new QHBoxLayout;
	layout->addWidget(lineEdit);
	layout->addWidget(label);
	layout->addWidget(setButton);
	setLayout(layout);
}

void MainDialog::setLabelText()
{
	QString text = lineEdit->text();
	label->setText(text);
}

int main(int argc, char** argv)
{
	QApplication app(argc,argv);
	MainDialog* dialog = new MainDialog;
	dialog->show();
	return app.exec();
}

今回のコードはかなり長いですね。 では、main.hファイルのコードから見ていきましょう。

まず3,4行目のコードはC言語やC++を使ってやや規模の大きなプログラムを作ったことのある人なら知っていると思います。 この方法はインクルードガードと呼ばれています。 知らない方はインターネットや書籍で勉強してみてください。 VisualC++などの#pragmaonceと全く同じです。

6行目ではQDialogヘッダをインクルードしています。 今回はダイアログを作りたいので、このヘッダファイルを使います。

8,9,10行目ではQLabel,QPushButton,QLineEditのプロトタイプ宣言です。 この宣言ではQLabelなどのクラスの詳細はここでは定義していないけれど、他のヘッダファイルな どにはこのクラスが存在しているよ、ということをコンパイラ側に伝えています。

普通に#include<QLabel>などでヘッダファイルをインクルードしてもよいのですが、8~10 行目のように宣言することでいちいちヘッダファイルへクラスを探しにいく手間が省け、コンパイル時間を短縮することができます。

12行目でMainWindowクラスを作成しています。このクラスがメインのダイアログとなります。 ダイアログを作成する場合はQDialogを継承する事により、 基本的なダイアログの機能を引き継ぐことができます。 よって拡張したい機能だけを自分で付け足せばよいわけです。(図4.3)


image2 図 4.3: 継承のイメージ図


14行目のQ_OBJECTは17行目でslotsを使うためのマクロです。 最後にセミコロンが付かない事に注意してください。

17行目のprivateslots:はその先のsetLabelText関数がSlotとして呼ばれる事があるという意味です。 このようにする事でconnect関数を使ってsetLabelText関数をどこかのSignalと結びつけることができます。 他にもSignalとして呼ばれる可能性のある関数にはsignals:としておきます。 signals:はprivatesignals:やpublicsignals:にはならない事に注意してください。

次に01.cppファイルの説明に入ります。

まず3行目では<QtGui>をインクルードしています。 これはQLabelやQPushButtonなどといったQtのGUI に関わる部分のヘッダファイルをすべてインクルードしているのと同じです。 クラスの規模が大きくなると、どのヘッダファイルをインクルード したらよいのか分からなくなってしまいます。

そういった事をいちいち考えなくてもよくなるのでQtGuiはたいへん便利です。

7行目ではQDialogのコンストラクタに親へのアドレスを渡しています。 parentを0にした場合は、その部品が新しいウィンドウとして表示されます。 つまり親となるわけです。

13行目ではconnect関数を使ってボタンが押されたらsetLabelText関数が呼び出されるようにしています。 QDialogを継承するとconnect関数のQObject::というプリフィクスが不要とな ります。

22行目のsetLabelText関数ではlineEditの内容をlabelにセットしています。

以上で説明は終了です。他に部品を追加したりしていろいろ試してみてください。

4.2 モーダルダイアログを作ってみよう

複数のウィンドウを表示したい場合があると思います。 そういった場合に用いられるのが、モーダルダイアログとモードレスダイアログです。

モーダルダイアログとして新しいダイアログが呼び出された場合、呼び出した側のダイア ログはモーダルダイアログが表示されている間は操作をすることができません。 モードレスダイアログではそういった制限は特にありません。

今回はモーダルダイアログを作っていきたいと思います。 完成したプログラムは図4.4~4.6となります。


image3 図 4.4: 初期状態 (on Windows)



image4 図 4.5: ボタンを押した後に表示されたモーダルダイアログ (on Windows)



image5 図 4.6: モーダルダイアログの ok ボタンを押した後 (on Windows)


まず始めに図4.4のウィンドウが表示されます。 ShowSecondDialogボタンを押すと図4.5のモーダルダイアログが表示されます。 そして、モーダルダイアログの中にあるテキストに何か文字を書き込みOKボタンを押すと、始めにあったダイアログのラベルにテキス トの文字がセットされます。(図4.6)

このプログラムは次のようなソースコードとなります。 今回はファイルを5分割してあり、コードも長くなっています。 しかしながら決して難しくは無いと思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//main.cpp

#include <QtGui>
#include "MainDialog.h"
#include "SecondDialog.h"

int main(int argc, char** argv)
{
	QApplication app(argc, argv);
	MainDialog* dialog = new MainDialog;
	dialog->show();
	return app.exec();
}

まず、main.cppですが、これはもう見てわかると思うので説明は省略します。

MainDialog.hではメインのダイアログクラスの定義を行っています。

MainDialog.cppのコンストラクタの中にあるconnectは、ShowSecondDialogボタンが押された時に showSecondDialog関数が呼び出されるように設定しています。

今回新しく出てくるところは、showSecondDialog関数内のコードです。

まず、22行目では2つ目のダイアログであるsecondDialog変数を作成しています。 引数にthisを渡していますが、これはsecondDialog.cppのコンストラクタの定義を見てわかると思います。 MainDialogをsecondDialogの親にするということです。

次に出てくるsecondDialog.exec()ですが、これはsecondDialogをモーダルダイアログと して呼び出すということです。 モーダルダイアログとして呼び出しているので、secondDialogが表示されている間はMainDialog側を操作することができませ ん。

また、secondDialog.exec()関数はQDialog::AcceptedまたはQDialog::Rejectedを返し ます。 このQDialog::Acceptedは1、QDialog::Rejectedは0と定義されています。 つまり、Acceptedが返された場合は24,25行目が実行され、Rejectedが返された場合は24,25 行目は実行されません。

SecondDialog.hは2つ目のダイアログクラスを定義しています。

SecondDialog.cppの18,19行目を見てください。 ここでokButtonを押した場合、accept関数を呼び出し、cancelButtonを押した場合、reject関数を呼 び出すよう設定しています。 accept関数もreject関数もウィンドウを隠すという動作を行います。 ただ、MainDialog.cppの23行目で呼び出したexec関数の返却値をQDialog::Acceptedにするか QDialog::Rejectedにするかが違います。

4.3 モードレスダイアログを作ってみよう

今度はモードレスダイアログを作ってみましょう。モードレスダイアログはモーダル ダイアログのように他のウィンドウが操作できないなどの制約は特にありません。

モードレスダイアログのコードは次のようになります。ボタンの配置などは4.2節のコードは同じです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//main.cpp

#include <QtGui>
#include "MainDialog.h"
#include "SecondDialog.h"

int main(int argc, char** argv)
{
	QApplication app(argc, argv);
	MainDialog* dialog = new MainDialog;
	dialog->show();
	return app.exec();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//MainDialog.h

#ifndef MAINDIALOG_H_
#define MAINDIALOG_H_

#include <QDialog>

class QPushButton;
class QLabel;
class SecondDialog;

class MainDialog : public QDialog
{
	Q_OBJECT
public:
	MainDialog(QWidget* parent = 0);
public slots:
	void showSecondDialog();
	void setTextLabel();
private:
	QPushButton* showDialogButton;
	QLabel* textLabel;
	SecondDialog* secondDialog;
};

#endif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//MainDialog.cpp

#include <QtGui>
#include "MainDialog.h"
#include "SecondDialog.h"

MainDialog::MainDialog(QWidget* parent) : QDialog(parent), secondDialog(NULL)
{
	showDialogButton = new QPushButton("Show Second Dialog");
	textLabel = new QLabel("empty");
	connect(showDialogButton, SIGNAL(clicked()), this, SLOT(showSecondDialog()) );

	QHBoxLayout* layout = new QHBoxLayout;
	layout->addWidget(textLabel);
	layout->addWidget(showDialogButton);
	setLayout(layout);
}

void MainDialog::showSecondDialog()
{
	if(!secondDialog){
		secondDialog = new SecondDialog;
		connect(secondDialog, SIGNAL(okButtonClicked() ), this, SLOT(setTextLabel()) );
	}
	if(secondDialog->isHidden() ) {
		secondDialog->show();
	}else{
		secondDialog->activateWindow();
	}
}

void MainDialog::setTextLabel()
{
	QString str = secondDialog->getLineEditText();
	textLabel->setText(str);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//SecondDialog.h

#ifndef SECONDDIALOG_H_
#define SECONDDIALOG_H_

#include <QDialog>

class QPushButton;
class QLineEdit;
class QString;

class SecondDialog : public QDialog
{
	Q_OBJECT
public:
	SecondDialog(QWidget* parent = 0);
	QString getLineEditText();
signals:
	void okButtonClicked();
private:
	QPushButton* okButton;
	QPushButton* cancelButton;
	QLineEdit* editor;
};

#endif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//SecondDialog.cpp

#include <QtGui>
#include "SecondDialog.h"

SecondDialog::SecondDialog(QWidget* parent) : QDialog(parent)
{
	okButton = new QPushButton(tr("&OK") );
	cancelButton = new QPushButton(tr("&Cancel") );
	editor = new QLineEdit;

	QHBoxLayout* layout = new QHBoxLayout;
	layout->addWidget(editor);
	layout->addWidget(okButton);
	layout->addWidget(cancelButton);
	setLayout(layout);

	connect(okButton,SIGNAL(clicked()),this,SIGNAL(okButtonClicked()) );
	connect(okButton,SIGNAL(clicked()), this, SLOT(close()) );
	connect(cancelButton,SIGNAL(clicked()), this, SLOT(close()) );
}

QString SecondDialog::getLineEditText()
{
	return editor->text();
}

まず、MainDialog.cppのshowSecondDialog関数を見てください。 この関数はShowSecondDialogボタンを押すことによって呼び出されます。

22行目のif文の中身はsecondDialogがNULLであるときに呼び出されます。 つまりはじめてsecondDialogを呼び出すときに実行されます。

24行目ではsecondDialogのokButtonClicked関数が呼び出されたときにsetTextLabel 関数が呼び出されます。 okButtonClicked関数はSecondDialog.hに定義されています。 この関数の説明はもう少し先で説明します。

setTextLabel関数では、secondDialogにあるテキストの中身をMainDialogのラベルにセットしています。 getLineEditText関数もSecondDialog.hおよびSecondDialog.cppに定義されています。

SecondDialog.hの18,19行目及びSecondDialog.cppの18,19行目を見てください。 SecondDialog.hの18行目のsignals:は19行目のokButtonClicked関数をシグナル関数と するためのマクロです。 このokButtonClicked関数の中身はQtが勝手に作成します。よって定義のみ書けばよいです。

そしてSecondDialog.cppの18行目でokButtonが押されたときにokButtonClicked関 数が呼び出されるようにセットしています。 今まではconnect関数の第4引数はSLOTとしていましたが、18行目のようにSIGNALを呼び出すこともできます。

そして19,20行目ではokButton及びcancelButtonが押されたときにダイアログが閉じるようclose関数を connectしています。

最後のgetLineEditText関数ではsecondDialog内のテキストの中身を取得するための関数です。