QtでGUIアプリーション作成入門(2)
作成したGUIを使って連絡先を追加できるようにする。
新しい連絡先を追加するためのボタンを加える。さらに、その連絡先をアプリ内で保存するコンテナを加える。
アドレスブック・クラスの定義
Qtではボタンをクリックするなどのユーザーの操作によりシグナルが出される、それを受け取り処理をする関数をスロットと呼ぶ。
アプリのクラス内にボタンなどのGUIクラスと、スロットになる関数を、メンバとして追加する。そして、そのGUIクラスのシグナルと、スロット関数、を結合する。スロットになるメンバ関数は public slot: セクションで宣言する必要がある。
まずは3つのボタンと、それぞれに対応する3つのスロット関数を追加する。
adressbook.h
//スロット public slots: void addContact(); void submitContact(); void cancel(); private: QLineEdit *nameLine; QTextEdit *addressText; //ボタン QPushButton *addButton; QPushButton *submitButton; QPushButton *cancelButton; //内部データ格納用 QMap<QString, QString> contacts; QString oldName; QString oldAddress; };
3つの QPushButton オブジェクト (addButton, submitButton, cancelButton) をメンバ変数として加える。
アドレスブックの連絡先を格納するコンテナが必要。QMap オブジェクト使う、連絡先は、鍵-値ペアとして保存される:連絡先の名前を鍵, アドレスを値とする。
つの QString オブジェクト (oldName and oldAddress) を宣言する。これらのオブジェクトは最後に表示した入力フォールドの内容を保持する。
アドレスブック・クラスの実装
アドレスブック・クラスのコンストラクタ内で、addButton, submitButton, and cancelButton をインスタンス化して、nameLine と addressText を読み込み専用にセットする。
adressbook.h
class AddressBook : public QWidget { ... addButton = new QPushButton(tr("&Add")); addButton->show(); submitButton = new QPushButton(tr("&Submit")); submitButton->hide(); cancelButton = new QPushButton(tr("&Cancel")); cancelButton->hide(); ... nameLine->setReadOnly(true); addressText->setReadOnly(true); ... }
addButton は show() を呼び出すことで表示されるが、 submitButton と cancelButton は hide() を呼び出すことで隠される。この2つのボタンはユーザーがAddボタンをクリックした時のみ表示されるようにする、これは下で述べるaddContact() 関数内で操作する。
tr()はアプリを複数の言語に対応させるために必要、翻訳するべき文字列を指定するのに使う。ユーザーから見える文字列には常にtr()を付けておくとよい。
次に、各ボタンが押された時の動作を記述する関数(スロット関数)を用意する。スロットして呼び出されるメンバ関数はaddressbookクラス宣言の中で private slots: のスコープ内で宣言する必要がある。
addressbook.h
private slots: void addContact(); void submitContact(); void cancel();
addressbook.cpp クラス定義に3つのスロット関数の定義を記述
void AddressBook::addContact(){} void AddressBook::submitContact(){} void AddressBook::cancel(){}
AddressBook::AddressBook()
次に、コンストラクタの中で、各プッシュボタン・クラスの clicked() シグナルと、スロットとして呼び出される関数を結びつける。
AddressBook::AddressBook(QWidget *parent) { ... //マクロを使った記法 connect(addButton, SIGNAL(clicked()), this, SLOT(addContact())); connect(submitButton, SIGNAL(clicked()), this, SLOT(submitContact())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancel())); //関数へのポインタを使った記法 connect(addButton, &QPushButton::clicked, this, &AddressBook::addContact); connect(submitButton, &QPushButton::clicked, this, &AddressBook::submitContact); connect(cancelButton, &QPushButton::clicked, this, &AddressBook::cancel); ... }
次に、プッシュボタンをAddressBookウィジェットの上にレイアウトする。まずはQVBoxLayout を用いてボタンを縦に整列する。
...
QVBoxLayout *buttonLayout1 = new QVBoxLayout;
buttonLayout1->addWidget(addButton, Qt::AlignTop);
buttonLayout1->addWidget(submitButton);
buttonLayout1->addWidget(cancelButton);
buttonLayout1->addStretch();
...
addStretch() は複数のプッシュボタンが、空間内に同じ間隔で配置するのではなく、ボタン同士の間隔は小さくウィジェットの上端に近く配置する。下の図を参照。
addLayout().を使って buttonLayout1 をメインレイアウトの(1,2)要素として追加する。これで buttonLayout1をmainLayoutの子レイアウトとして入れ子状に配置することができる。
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); mainLayout->addLayout(buttonLayout1, 1, 2);
addContact()
Addボタンを押したら、現在入力フィールドに表示されている連絡先を oldName と oldAddressに保持しておいてから、入力フィールドをクリアした後、読込専用をオフにして入力可能な状態にする。さらに、フォーカスが nameLine にセットされ submitButton と cancelButtonが表示される。
void AddressBook::addContact() { oldName = nameLine->text(); oldAddress = addressText->toPlainText(); nameLine->clear(); addressText->clear(); nameLine->setReadOnly(false); nameLine->setFocus(Qt::OtherFocusReason); addressText->setReadOnly(false); addButton->setEnabled(false); submitButton->show(); cancelButton->show(); }
submitContact()
submitContact() 関数は3つのパートに分けられる。
1)連絡先の詳細を nameLine と addressText から抽出して、 QString オブジェクトに格納する。フィールドが空なのにSubmitボタンを押さないように確かめる、もし、押したら QMessageBox を表示してnameとaddressを入力するようにメッセージを表示する。
※ QMessageBox::information() の第2引数はメッセージボックスのタイトルだが、Macでは表示されない。
void AddressBook::submitContact() { QString name = nameLine->text(); QString address = addressText->toPlainText(); if (name.isEmpty() || address.isEmpty()) { QMessageBox::information(this, tr("Empty Field"), tr("Please enter a name and address.")); return; }
連絡先が既に登録されていないかチェックする。されていないなら、連絡先を追加し、それを知らせるメッセージを表示する。
if (!contacts.contains(name)) { contacts.insert(name, address); QMessageBox::information(this, tr("Add Successful"), tr("\"%1\" has been added to your address book.").arg(name)); } else { QMessageBox::information(this, tr("Add Unsuccessful"), tr("Sorry, \"%1\" is already in your address book.").arg(name)); return; }
もし、連絡先が既に存在したら、重複して追加しないようにメッセージを表示する。連絡先オブジェクトは鍵-値ペアに基いているので、鍵が一意であることを確保する必要がある。
最後にボタンの表示を通常の状態に復帰させる。
if (contacts.isEmpty()) { nameLine->clear(); addressText->clear(); } nameLine->setReadOnly(true); addressText->setReadOnly(true); addButton->setEnabled(true); submitButton->hide(); cancelButton->hide(); }
cancel()
cancel() 関数は最後に表示した連絡先詳細を復帰して addButtom を使用可能にし、ssubmitButtonとcancelButtonを隠す。
void AddressBook::cancel() { nameLine->setText(oldName); nameLine->setReadOnly(true); addressText->setText(oldAddress); addressText->setReadOnly(true); addButton->setEnabled(true); submitButton->hide(); cancelButton->hide(); }