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

元記事

登録した連絡先の間をナビゲートする能力を加える。

しかし、その前にどんな種類のデータ構造が必要なのか決める必要がある。前のセクションでは QMap の鍵-値ペア、名前を鍵/住所を値、を用いた。我々の場合ではこれでいいが、各エントリーを表示しナビゲートするにはもう少し拡張する必要がある。
QMap を円環状に結合したリストを模したデータ構造にするため拡張する。下の図で示すようなイメージ。
※QtはSTLライクなイテレータJavaライクなイテレータがある。QMap::iteratorSTLライクだが(end()は最後の要素の一つ後の空要素である)、QMapIteratorはJavaライクなイテレータ。QMapのイテレータは、QMap::iteratorでも、直接、イテレータが指し示す要素のkeyとvalueにアクセスできる。

http://qt-project.org/doc/qt-5.1/qtwidgets/images/addressbook-tutorial-part3-linkedlist.png

AddressBook クラスの定義

ナビゲーション関数を追加するために、AddressBookクラスに2つのスロット(next() と previous())を addressbook.h に加える。:

    void next();
    void previous();

また2つの QPushButton オブジェクトが必要なので nextButton と previousButton をプライベート変数に加える:

    QPushButton *nextButton;
    QPushButton *previousButton;

AddressBook クラスの実装
addressbook.cppのAddressBookコンストラクタの中で、nextButton と previousButton をインスタンス化し、デフォルトでは無効にする。これはナビゲーションは複数の連絡先が登録された時だけに有効にするため。

    nextButton = new QPushButton(tr("&Next"));
    nextButton->setEnabled(false);
    previousButton = new QPushButton(tr("&Previous"));
    previousButton->setEnabled(false);

その後、それらのpushボタンを対応するスロットに結合する。
connect(nextButton, SIGNAL(clicked()), this, SLOT(next()));
connect(previousButton, SIGNAL(clicked()), this, SLOT(previous()));
GUIのイメージは以下
http://qt-project.org/doc/qt-5.1/qtwidgets/images/addressbook-tutorial-part3-screenshot.png

基本的な慣例に従って nextButton を右 previousButton を左に配置する。この直感的なレイアウトのために QHBoxLayout を使う:

    QHBoxLayout *buttonLayout2 = new QHBoxLayout;
    buttonLayout2->addWidget(previousButton);
    buttonLayout2->addWidget(nextButton);

この QHBoxLayout オブジェクトbuttonLayout2を、メインレイアウトの(2,1)要素に加える。

    mainLayout->addLayout(buttonLayout2, 2, 1);

下の図はこのウィジェットのmainLayoutの座標を示している。

我々の addContact() 関数の中で、それらのボタンを無効にするので、連絡先を加えるときに間違えて押すこともない。

    nextButton->setEnabled(false);
    previousButton->setEnabled(false);

また、submitContact()関数の中で、nextButton と previousButton, を有効にする、連絡先の数に応じて有効にする。これをするには以下のコードを使う。

    int number = contacts.size();
    nextButton->setEnabled(number > 1);
    previousButton->setEnabled(number > 1);

このコードは cancel() 関数でも使う。
QMap オブジェクトを環状連結リストのように使うことを意図している。なので、next()関数では連絡先のイテレータを得る必要がある、なので:
もし、イテレータが終末でないなら、一つインクリメントする
もし、イテレータが終末なら、最初の連絡先に移動させる。これで環状連結リストのように振る舞う。

void AddressBook::next()
{
    QString name = nameLine->text();
    QMap<QString, QString>::iterator i = contacts.find(name);

    if (i != contacts.end())
        i++;

    if (i == contacts.end())
        i = contacts.begin();

    nameLine->setText(i.key());
    addressText->setText(i.value());
}

正しいオブジェクトに移動できたら、その内容をnameLineとaddressTextに表示する。
同様に、previous()関数でもイレテータを得る必要がある。
もし、イレテータが最初なら、終末に移動する。
そしてイテレータを1つデクリメントする。

void AddressBook::previous()
{
    QString name = nameLine->text();
    QMap<QString, QString>::iterator i = contacts.find(name);


    if (i == contacts.begin())
        i = contacts.end();

    i--;
    nameLine->setText(i.key());
    addressText->setText(i.value());
}

再び、現在のオブジェクトの内容を表示する。

Files:
tutorials/addressbook/part3/addressbook.cpp
tutorials/addressbook/part3/addressbook.h
tutorials/addressbook/part3/main.cpp
tutorials/addressbook/part3/part3.pro