2月 17, 2009

【翻譯】Address Book 3 - Navigating between Entries

@
tutorials/addressbook/part3/addressbook.cpp
tutorials/addressbook/part3/addressbook.h
tutorials/addressbook/part3/main.cpp
tutorials/addressbook/part3/part3.pro

  Address Book 程式現在完成一半了。我們需要加入一些函式以在聯絡人之間做導覽(navigation)。不過首先,我們必須要決定:我們要使用哪種資料結構來存取這些聯絡人。

  在章節 2,我們使用一個以聯絡人的姓名為鍵、以聯絡人住址為值的一對 key-value QMap。對於我們的情況,這是有效的。然而,為了要導覽並顯示每一個項目,是需要做點改良的。

  我們藉由將 QMap 複製成一個類似於循環連結串列(circularly-linked list)的資料結構,其中所有的元素(element)都是被連接著的,包含了第一個元素與最後一個元素。下圖說明了這個資料結構。




Defining the AddressBook Class

  為了要加入導覽函式到 Address Book 程式中,我們還需要加入兩個 slot 到我們的 AddressBook 類別中:next()previous()。它們被加入到我們的 addressbook.h 檔案:

     void next();
     void previous();

  我們也需要另外兩個 QPushButton 物件,所以我們在私有變數中宣告了 nextButtonpreviousButton

     QPushButton *nextButton;
     QPushButton *previousButton;


Implementing the AddressBook Class

  在 addressbook.cpp 中的 AddressBook 建構子,我們實體化 nextButtonpreviousButton 並在預設情況下使他們失效。這是因為只有在在 Address Book 中的聯絡人多於一個時才能夠進行導覽。

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

  然後,我們將這些按鈕連接到它們各自的 slot。

     connect(nextButton, SIGNAL(clicked()), this, SLOT(next()));
     connect(previousButton, SIGNAL(clicked()), this, SLOT(previous()));

  下圖是我們預期的圖形使用者介面。注意,它正在接近我們預期的最終成果。



  對於 next()previous() 函式,我們遵循基本的慣例,將 nextButton 放在右邊、將 previousButton 放在左邊。為了達到這個直觀的佈局,我們使用了 QHBoxLayout 來一步一步擺放元件:

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

  接著,QHBoxLayout 物件:buttonLayout2 被加入到 mainLayout 中。

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

  下圖顯示了元件在 mainLayout 中的座標。



  在我們的 addContact() 函式中,我們必須讓這些按鈕失效,所以使用者不會在加入一個聯絡人時試圖進行導覽。

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

  在我們的 submitContact() 函式,我們再讓導覽按鈕--nextButtonpreviousButton--根據聯絡人的數量生效。如同先前提到的,導覽只有在 Address Book 中的聯絡人多於一個時才是有效的。接下來的幾行程式展示了該如何做到這件事:

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

  我們也在 cancel() 函式中包含了這幾行程式。

  回想到我們打算利用我們的 QMap 物件來模擬一個循環連結串列:contacts。所以,在 next() 函式中,我們為 contacts 取得一個迭代器(iterator),然後:

  • 若是迭代器不在 contacts 的末端,我們將它加一
  • 若是迭代器在 contacts 的末端,我們移動它到 contacts 的開頭。這給了我們 QMap 像是一個循環連結串列在運作的錯覺。

 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());
 }

一旦我們已經迭代到了 contacts 中正確的物件,我們在 nameLineaddressText 顯示它的內容。

同樣的,對於 previous() 函式,我們為 contacts 取得一個迭代器(iterator),然後:

  • 若是迭代器在 contacts 的末端,我們清除顯示並返回。
  • 若是迭代器在 contacts 的開頭,我們將它移動到末端。
  • 然後,我們將迭代器減一。

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

     if (i == contacts.end()){
         nameLine->clear();
         addressText->clear();
         return;
     }

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

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

  再一次,我們顯示了當前 contacts 中的物件內容。


來源:Address Book 3 - Navigating between Entries
版本:4.4.3

0 回覆:

張貼留言