NOTICE

 任何跟文章無關的閒聊,請愛用 留言板(Guestbook)

 想要快速瀏覽主題,請點選單 目錄 標籤。

 停止更新ing,請見諒。 <(_ _)>


2月 25, 2009

【目錄】Qt

@
Qt 相關一覽


介紹

What is Qt
What is QtCreator - coming soon...


筆記

Installing Qt with MinGW
Small Test of Qt
Creating a Event Label
Using Qt Designer


翻譯

Qt Tutorial
 ├ 1. Hello World!
 ├ 2. Calling it Quits
 ├ 3. Family Values
 ├ 4. Let There Be Widgets
 ├ 5. Building Blocks
 ├ 6. Building Blocks Galore!
 ├ 7. One Thing Leads to Another
 ├ 8. Preparing for Battle
 ├ 9. With Cannon You Can
 ├ 10. Smooth as Silk
 ├ 11. Giving It a Shot
 ├ 12. Hanging in the Air the Way Bricks Don't
 ├ 13. Game Over
 └ 14. Facing the Wall

Widgets Tutorial

Address Book Tutorial
 ├ 1. Designing the User Interface
 ├ 2. Adding Addresses
 ├ 3. Navigating between Entries
 ├ 4. Editing and Removing Addresses
 ├ 5. Adding a Find Function
 ├ 6. Loading and Saving
 └ 7. Additional Features

【翻譯】Address Book Tutorial

@
  這份指導教學利用 Qt 的跨平台(cross-platform)框架(framework),為 GUI 程式作了一個介紹。



  在過程中,我們將要學習一些 Qt 提供的基礎技術,像是:

  • 元件(widgets)與佈局管理者(layout manager)
  • 容器類別(class)
  • signal 與 slot
  • 輸出與輸入裝置

  假如 Qt 對你而言完全是陌生的,且未讀過 How to Learn Qt 的話,請先讀過一次。

  這份指導教學的原始碼都放在 Qt 的 examples/tutorials/addressbook 資料夾中。


指導章節:
1. Designing the User Interface
2. Adding Addresses
3. Navigating between Entries
4. Editing and Removing Addresses
5. Adding a Find Function
6. Loading and Saving
7. Additional Features


  雖然這支小程式看起來並沒有這麼像一個十分時髦的 GUI 應用程式,但是它使用了相當多使用在複雜應用程式中的基礎技術。在你開始使用它之後,我們建議你試試這個 Application 範例,它提供了一個擁有選單、工具列、一個狀態列等等的小型 GUI 應用程式。


來源:Address Book Tutorial
版本:4.4.3

【翻譯】Address Book 7 - Additional Features

@
tutorials/addressbook/part7/addressbook.cpp
tutorials/addressbook/part7/addressbook.h
tutorials/addressbook/part7/finddialog.cpp
tutorials/addressbook/part7/finddialog.h
tutorials/addressbook/part7/main.cpp
tutorials/addressbook/part7/part7.pro

  這一章涵蓋了一些使得 Address Book 程式更加方便於每天使用的附加功能。



  雖然我們的 Address Book 程式已經相當有用了,但是假如我們可以與其他程式交流聯絡人將會更好。vCard 格式是一個可以被用在這種用途上的普遍檔案格式。在這一章,我們擴展我們的 Address Book 客戶端(client),以允許聯絡人被轉換成 vCard .vcf 檔。


Defining the AddressBook Class

  我們在 addressbook.h 檔案中的 AddressBook 類別加入一個 QPushButton 物件:exportButton,以及一個對應的公開 slot:exportAsVCard()

     void exportAsVCard();
     ...
     QPushButton *exportButton;


Implementing the AddressBook Class

  在 AddressBook 建構子中,我們將 exportButton 的 clicked() signal 連接到 exportAsVCard()。我們也將這個按鈕加入到我們的 buttonLayout1 中,其負責管理右邊按鈕控制板的佈局。

  在我們的 exportAsVCard() 函式,我們藉由取得聯絡人姓名到 name 中開始。我們宣告了 firstNamelastNamenameList。接下來,我們尋找姓名第一個為空格的索引值。假如有一個空格,我們將聯絡人的姓名分割成 firstNamelastName。然後,我們以一個底線("_")來取代空白。或者,假如沒有空格,我們則假定聯絡人僅有名字(first name)。

 void AddressBook::exportAsVCard()
 {
     QString name = nameLine->text();
     QString address = addressText->toPlainText();
     QString firstName;
     QString lastName;
     QStringList nameList;

     int index = name.indexOf(" ");

     if (index != -1) {
         nameList = name.split(QRegExp("\\s+"), QString::SkipEmptyParts);
         firstName = nameList.first();
         lastName = nameList.last();
     } else {
         firstName = name;
         lastName = "";
     }

     QString fileName = QFileDialog::getSaveFileName(this,
         tr("Export Contact"), "",
         tr("vCard Files (*.vcf);;All Files (*)"));

     if (fileName.isEmpty())
         return;

     QFile file(fileName);

  就像 saveToFile() 函式,我們開啟一個檔案對話視窗,以讓使用者選擇一個檔案位置。我們使用選擇的檔案名稱,建立一個 QFile 的實體用以寫入。

  我們試圖以 WriteOnly 模式去開啟檔案。假如這個程序失敗了,我們顯示一個 QMessageBox 以告知使用者這個問題並返回。否則,我們將檔案作為引數傳入ㄧ個 QTextStream 物件:out。如同 QDataStream,QTextStream 類別提供了讀寫純文字檔案的功能。因此,產生出來的 .vcf 可以在文字編輯器裡開啟編輯。

     if (!file.open(QIODevice::WriteOnly)) {
         QMessageBox::information(this, tr("Unable to open file"),
             file.errorString());
         return;
     }

     QTextStream out(&file);

  然後我們將 BEGIN:VCARD 標籤,以及 VERSION:2.1 標簽寫入到 vCard 檔案裡。聯絡人的姓名與 N: 一同寫入。 對於填入 vCard "File as" 屬性的 FN: 標籤,我們必須去確認聯絡人是否有姓氏(last name)。假如有,我們使用 nameList 中的詳細資料填入。否則,我們只寫入 firstName

     out << "BEGIN:VCARD" << "\n";
     out << "VERSION:2.1" << "\n";
     out << "N:" << lastName << ";" << firstName << "\n";

     if (!nameList.isEmpty())
        out << "FN:" << nameList.join(" ") << "\n";
     else
        out << "FN:" << firstName << "\n";

  我們繼續寫入聯絡人住址。住址中的分號(semicolon)利用 "\" 跳脫(escape)、換行被取代成分號、且逗號(comma)被取代成空格。最後,我們寫入 ADR;HOME:; 標籤以及住址,然後是 END:VCARD 標籤。

     address.replace(";", "\\;", Qt::CaseInsensitive);
     address.replace("\n", ";", Qt::CaseInsensitive);
     address.replace(",", " ", Qt::CaseInsensitive);

     out << "ADR;HOME:;" << address << "\n";
     out << "END:VCARD" << "\n";

     QMessageBox::information(this, tr("Export Successful"),
         tr("\"%1\" has been exported as a vCard.").arg(name));
 }

  結束時,一個 QMessageBox 被顯示以告知使用者:vCard 已經成功被輸出。

vCard 是一個 Internet Mail Consortium 的商標


來源:Address Book 7 - Additional Features
版本:4.4.3

2月 24, 2009

【翻譯】Address Book 6 - Loading and Saving

@
tutorials/addressbook/part6/addressbook.cpp
tutorials/addressbook/part6/addressbook.h
tutorials/addressbook/part6/finddialog.cpp
tutorials/addressbook/part6/finddialog.h
tutorials/addressbook/part6/main.cpp
tutorials/addressbook/part6/part6.pro

  這一章涵蓋了 Qt 的檔案處理功能,我們可以使用它來撰寫 Address Book 程式載入與儲存的例行程序。



  雖然導覽與搜尋聯絡人都是有用的功能,但是我們的 Address Book 在我們能儲存現存的聯絡人,並能在之後再度載入它們之前,都還不完全算是真的可以使用。Qt 為輸入(input)與輸出(output)提供了一些類別,不過我們已經選擇使用其中兩種簡易使用的組合:QFileQDataStream

  一個 QFile 物件代表一個硬碟上可被讀寫的檔案。QFile 是代表著許多不同種類裝置(device)的 QIODevice 類別的一個子類別。

  一個 QDataStream 物件被使用來序列化(serialize)二元(binary)資料,所以它可以被儲存在一個 QIODevice 並在之後再度被讀取。從一個 QIODevice 中進行讀寫,與開啟--將各自的裝置作為參數--與讀取或寫入到串流(stream)是相似的。


Defining the AddressBook Class

  我們宣告了兩個公開 slot:saveToFile()loadFromFile(),以及兩個 QPushButton 物件:loadButtonsaveButton

     void saveToFile();
     void loadFromFile();
     ...
     QPushButton *loadButton;
     QPushButton *saveButton;


Implementing the AddressBook Class

  在我們的建構子,我們實體化 loadButtonsaveButton。理論上,將按鈕的標籤設為「從一個檔案載入聯絡人(Load contacts from a file)」與「儲存聯絡人到一個檔案(Save contacts to a file)」將會更加友善於使用者的。然而,由於我們其他按鈕的大小,我們將標籤設為 Load...Save...。幸運地,Qt 提供一個簡單的方式來利用 setToolTip() 設定工具提示(tooltip),我們為我們的按鈕用接下來的方式來使用它:

     loadButton->setToolTip(tr("Load contacts from a file"));
     ...
     saveButton->setToolTip(tr("Save contacts to a file"));

  雖然並不會顯示在這裡,不過如同其餘我們實現的功能,我們將按鈕加入到右邊的佈局控制板--button1Layout--並連接按鈕的 clicked() signal 到它們各自的 slot。

  對於儲存功能,我們首先使用 QFileDialog::getSaveFileName() 取得 fileName。這是一個由 QFileDialog 提供的方便函式。QFileDialog 彈出一個典型的檔案對話視窗,並允許使用者輸入一個檔案名稱或是選擇任何存在的 .abk 檔。.abk 檔是我們的 Address Book 在我們儲存聯絡人時所建立的擴展(extension)。

 void AddressBook::saveToFile()
 {
     QString fileName = QFileDialog::getSaveFileName(this,
         tr("Save Address Book"), "",
         tr("Address Book (*.abk);;All Files (*)"));

  彈出的檔案對話視窗顯示於下方截圖:



  假如 fileName 不為空,我們利用 fileName 建立一個 QFile 物件:file。QFile 與 QDataStream 的運作,如同 QFile 是一個 QIODevice。

  接下來,我們試圖在 WriteOnly 模式下開啟檔案。假如開啟不成功,我們顯示一個 QMessageBox 以告知使用者。

     if (fileName.isEmpty())
         return;
     else {
         QFile file(fileName);
         if (!file.open(QIODevice::WriteOnly)) {
             QMessageBox::information(this, tr("Unable to open file"),
                 file.errorString());
             return;
         }

  否則,我們實體化一個 QDataStream 物件:out。QDataStream 需要與用來讀寫的串流版本相同。在序列化資料到檔案前,我們藉由設定使用的版本為採用的 Qt 4.3 版本,以確保這種情況。

         QDataStream out(&file);
         out.setVersion(QDataStream::Qt_4_3);
         out << contacts;
     }
 }

  對於讀取功能,我們也使用 QFileDialog::getOpenFileName() 來 取得 fileName。這個與 QFileDialog::getSaveFileName() 極為相像的函式,也彈出一個典型的對話視窗,並允許使用者輸入一個檔案名稱,或是選擇任何存在的 .abk 檔以讀取到 Address Book 中。

 void AddressBook::loadFromFile()
 {
     QString fileName = QFileDialog::getOpenFileName(this,
         tr("Open Address Book"), "",
         tr("Address Book (*.abk);;All Files (*)"));

  舉例來說,在 Windows 上,這個函式彈出一個原生檔案對話視窗,如同接下來顯示的截圖:



  再一次,假如 fileName 不為空,我們使用一個 QFile 物件--file--並試圖以。 ReadOnly 模式下開啟檔案。與我們在 saveToFile() 中實現相同的方式,假如開啟不成功,我們顯示一個 QMessageBox 以告知使用者。

     if (fileName.isEmpty())
         return;
     else {

         QFile file(fileName);

         if (!file.open(QIODevice::ReadOnly)) {
             QMessageBox::information(this, tr("Unable to open file"),
                 file.errorString());
             return;
         }

         QDataStream in(&file);
         in.setVersion(QDataStream::Qt_4_3);
         contacts.empty();   // empty existing contacts
         in >> contacts;

  否則,我們實體化一個 QDataStream 物件:in,將它的版本如前述設定,並讀取序列化的資料到 contacts 資料結構中。 注意,我們在讀入資料到 contacts 以簡化檔案讀取程序之前,先清空 contacts。一個更進階的方法為讀取聯絡人到暫時的 QMap 物件中,並且只有在聯絡人並非已存在於 contacts 時複製它。

         if (contacts.isEmpty()) {
             QMessageBox::information(this, tr("No contacts in file"),
                 tr("The file you are attempting to open contains no contacts."));
         } else {
             QMap<QString, QString>::iterator i = contacts.begin();
             nameLine->setText(i.key());
             addressText->setText(i.value());
         }
     }

     updateInterface(NavigationMode);
 }

  為了顯示已經從檔案讀取到的聯絡人,首先我們必須驗證(validate)獲取的資料,以確保我們讀取的檔案真的包含 Address Book 聯絡人。假如是,我們顯示第一個聯絡人;否則,我們顯示一個 QMessageBox 以告知使用者這個問題。之後,我們會更新介面來以此生效或失效按鈕。


來源:Address Book 6 - Loading and Saving
版本:4.4.3

2月 23, 2009

【翻譯】Address Book 5 - Adding a Find Function

@
tutorials/addressbook/part5/addressbook.cpp
tutorials/addressbook/part5/addressbook.h
tutorials/addressbook/part5/finddialog.cpp
tutorials/addressbook/part5/finddialog.h
tutorials/addressbook/part5/main.cpp
tutorials/addressbook/part5/part5.pro

  在這一章,我們著眼於 Address Book 程式中找出聯絡人與住址的方法。



  當我們持續加入聯絡人到我們的 Address Book 程式中,利用 NextPrevious 按紐來進行導覽將變得令人厭煩。在這個情況下,一個 Find 函式將會更加有效率的找到聯絡人。上方的截圖顯示了 Find 按紐,以及它在按鈕面板(panel)上的位置。

  當使用者點擊 Find 按鈕時,顯示一個對話視窗(dialog)是相當有用的,它可以提示使用者輸入聯絡人的姓名。Qt 提供了 QDialog--我們在這個章節子類別化它--來實現一個 FindDialog 類別。


Defining the FindDialog Class



  為了要子類別化 QDialog,我們首先在 finddialog.h 檔案中引入 QDialog 的標頭。同樣的,由於我們將要在我們的對話視窗類別中使用 QLineEditQPushButton,所以我們使用前向宣告來宣告它們。

  正如我們的 AddressBook 類別,FindDialog 類別包含了 Q_OBJECT 巨集,且它的建構子被宣告成允許一個 QWidget 父元件,即使對話視窗將會做為一個單獨的視窗開啟。

 #include <QDialog>

 class QLineEdit;
 class QPushButton;

 class FindDialog : public QDialog
 {
     Q_OBJECT

 public:
     FindDialog(QWidget *parent = 0);
     QString getFindText();

 public slots:
     void findClicked();

 private:
     QPushButton *findButton;
     QLineEdit *lineEdit;
     QString findText;
 };

  我們定義了一個公開函式--getFindText()--供 FindDialog 實體化的類別使用,這允許它們獲得由使用者輸入的文字。一個公開的 slot--findClicked()--被定義成在使用者點擊 Find 按鈕時處理搜尋字串。

  最後,我們定義私有變數:findButtonlineEditfindText,分別對應 Find 按鈕、使用者輸入搜尋字串的單行文字框(line edit)、與一個用以儲存稍後使用的搜尋字串的內部(internal)字串。


Implementing the FindDialog Class

  在 FindDialog 的建構子中,我們設置了私有變數:findButtonlineEditfindText。我們使用一個 QHBoxLayout 以將元件放在適當位置。

 FindDialog::FindDialog(QWidget *parent)
     : QDialog(parent)
 {
     QLabel *findLabel = new QLabel(tr("Enter the name of a contact:"));
     lineEdit = new QLineEdit;

     findButton = new QPushButton(tr("&Find"));
     findText = "";

     QHBoxLayout *layout = new QHBoxLayout;
     layout->addWidget(findLabel);
     layout->addWidget(lineEdit);
     layout->addWidget(findButton);

     setLayout(layout);
     setWindowTitle(tr("Find a Contact"));
     connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked()));
     connect(findButton, SIGNAL(clicked()), this, SLOT(accept()));
 }

  我們設定了佈局與視窗標題,也將 signal 連接到它們各自的 slot。注意,findButton 的 clicked() signal 被連接到了 findClicked()accept()。QDialog 提供的 accept() slot 隱藏對話視窗,並將結果代碼(result code)設為 Accepted。我們使用這個函式,來幫助 AddressBookfindContact() 得知:FindDialog 物件被關閉了。這將會在討論 findContact() 函式時進一步的解釋。



  在 findClicked() 中,我們確保使用者不會在沒有輸入一個聯絡人姓名時點擊 Find 按鈕。然後,我們將 findText 設為從 lineEdit 中取得的搜尋字串。之後,我們清除 lineEdit 的聯絡人,並隱藏對話視窗。

 void FindDialog::findClicked()
 {
     QString text = lineEdit->text();

     if (text.isEmpty()) {
         QMessageBox::information(this, tr("Empty Field"),
             tr("Please enter a name."));
         return;
     } else {
         findText = text;
         lineEdit->clear();
         hide();
     }
 }

  findText 變數有一個與之相關的公開獲取函數(getter function):getFindText()。之前我們僅在建構子與 findClicked() 函式中直接設置 findText,我們並沒有隨著 getFindText() 建立一個設置函式(setter function)。因為 getFindText() 是公開的,類別實體化與使用 FindDialog 總是能存取到使用者輸入並接受(accept)的搜尋字串。

 QString FindDialog::getFindText()
 {
     return findText;
 }


Defining the AddressBook Class

  為了確保我們能夠在我們的 AddressBook 類別中使用 FindDialog,我們在 addressbook.h 檔案中引入 finddialog.h

 #include "finddialog.h"

  到目前為止,所有我們的 Address Book 功能都有一個 QPushButton 和一個對應的 slot。同樣的,對於 Find 功能,我們有 findButtonfindContact()

  findButton 被宣告成一個私有變數,且 findContact() 函式被宣告成一個公開的 slot。

     void findContact();
     ...
     QPushButton *findButton;

  最後,我們宣告我們將會用以參照一個 FindDialog 實體的私有變數:dialog。

     FindDialog *dialog;

  一旦我們實體化一個對話視窗,我們將會不只一次地使用它;使用一個私有變數允許我們在類別中不只一個地方參照它。


Implementing the AddressBook Class

  在 AddressBook 類別的建構子中,我們實體化我們的私有物件,findButtonfindDialog

     findButton = new QPushButton(tr("&Find"));
     findButton->setEnabled(false);
     ...
     dialog = new FindDialog;

  接下來,我們將 findButton 的 clicked() signal 連接到 findContact()

     connect(findButton, SIGNAL(clicked()), this, SLOT(findContact()));

  現在所有剩下的就是我們 findContact() 函式的程式碼:

 void AddressBook::findContact()
 {
     dialog->show();

     if (dialog->exec() == QDialog::Accepted) {
         QString contactName = dialog->getFindText();

         if (contacts.contains(contactName)) {
             nameLine->setText(contactName);
             addressText->setText(contacts.value(contactName));
         } else {
             QMessageBox::information(this, tr("Contact Not Found"),
                 tr("Sorry, \"%1\" is not in your address book.").arg(contactName));
             return;
         }
     }

     updateInterface(NavigationMode);
 }

  我們藉由顯示 FindDialog 實體 dialog 開始。這是當使用者輸入一個聯絡人姓名供查閱的時候。一旦使用者點擊對話視窗的 findButton,對話視窗會被隱藏,且將結果代碼設為 QDialog::Accepted。這使得我們的 if 敘訴為真(true)。

  然後我們開始使用 FindDialoggetFindText() 函式取得搜尋字串--這個例子中的 contactName。假如聯絡人存在於我們的 Address Book,我們立刻顯示它。否則,我們顯示如下顯示的 QMessageBox 以告知它們的搜尋失敗。




來源:Address Book 5 - Adding a Find Function
版本:4.4.3

2月 18, 2009

【翻譯】Address Book 4 - Editing and Removing Addresses

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

  在這一章,我們著眼於修改儲存在 Address Book 程式中聯絡人內容的方法。



  我們現在擁有一個不只能以有系統的方式存取資料,還能夠允許導覽的 Address Book。而包含編輯和移除函式--使得聯絡人的詳細資料可以在需要時被改變--將會是方便的。然而,這需要在列舉(enum)的形式下做一點改良。在先前的章節中,我們有兩種模式(mode):AddingModeNavigationMode--但是它們並不是作為列舉而被定義的。相反的,我們手動讓對應的按鈕生效或失效,結果就是多行重複的程式碼。

  在這一章,我們定義了包含三種值的 Mode 列舉:

  • NavigationMode,
  • AddingMode, 與
  • EditingMode.


Defining the AddressBook Class

  addressbook.h 被更新成包含了 Mode 列舉:

     enum Mode { NavigationMode, AddingMode, EditingMode };

  我們也加入了兩個新的 slot--editContact()removeContact()--到我們當前的公開 slot 列表。

     void editContact();
     void removeContact();

  為了在模式間做轉換,我們引入了 updateInterface() 函式來管理所有 QPushButton 物件的生效與失效。為了先前提到的編輯和刪除函式,我們也加入了兩個新按鈕:editButtonremoveButton

     void updateInterface(Mode mode);
     ...
     QPushButton *editButton;
     QPushButton *removeButton;
     ...
     Mode currentMode;

  最後,我們宣告了 currentMode 以追蹤當前的列舉模式。


Implementing the AddressBook Class

  我們現在必須要實現 Address Book 程式的模式改變功能。editButtonremoveButton 被實體化,並在預設情況下使其失效,因為 Address Book 啟動時並無聯絡人在記憶體中。

     editButton = new QPushButton(tr("&Edit"));
     editButton->setEnabled(false);
     removeButton = new QPushButton(tr("&Remove"));
     removeButton->setEnabled(false);

  然後,這些按鈕被連接到它們各自的 slot:editContact()removeContact()。且我們將這些按鈕加入到 buttonLayout1e 中。

     connect(editButton, SIGNAL(clicked()), this, SLOT(editContact()));
     connect(removeButton, SIGNAL(clicked()), this, SLOT(removeContact()));
     ...
     buttonLayout1->addWidget(editButton);
     buttonLayout1->addWidget(removeButton);

  在模式轉換成 EditingMode 之前,editContact() 函式將聯絡人的舊資料儲存在 oldNameoldAddress 中。在這個模式,submitButtoncancelButton 都是有效的。因此,使用者可以改變聯絡人的詳細資料並點擊任一按鈕。

 void AddressBook::editContact()
 {
     oldName = nameLine->text();
     oldAddress = addressText->toPlainText();

     updateInterface(EditingMode);
 }

  submitContact() 函式被一個 if-else 敘訴(statement)分割成兩個部分。我們檢查 currentMode 以確認它是否在 AddingMode 下。假如是,我們開始我們的加入動作。

 void AddressBook::submitContact()
 {
     ...
     if (currentMode == AddingMode) {

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

  否則,我們檢查 currentMode 以確認它是否在 EditingMode 下。假如是,我們比較 oldNamename。假如名字被改變了,我們從 contacts 移除舊的聯絡人,並插入更新過的新聯絡人。

     } else if (currentMode == EditingMode) {

         if (oldName != name) {
             if (!contacts.contains(name)) {
                 QMessageBox::information(this, tr("Edit Successful"),
                     tr("\"%1\" has been edited in your address book.").arg(oldName));
                 contacts.remove(oldName);
                 contacts.insert(name, address);
             } else {
                 QMessageBox::information(this, tr("Edit Unsuccessful"),
                     tr("Sorry, \"%1\" is already in your address book.").arg(name));
                 return;
             }
         } else if (oldAddress != address) {
             QMessageBox::information(this, tr("Edit Successful"),
                 tr("\"%1\" has been edited in your address book.").arg(name));
             contacts[name] = address;
         }
     }

     updateInterface(NavigationMode);
 }

  只有在住址被改變(換言之,oldAddressaddress 不相同)時,我們才更新聯絡人的住址。最後,我們將 currentMode 設為 NavigationMode。這是作為重新啟動所有失效按鈕的一個重要步驟。

  為了從 Address Book 中移除一個聯絡人,我們實現了 removeContact() 函式。這個函式確認聯絡人是否在 contacts 中。

 void AddressBook::removeContact()
 {
     QString name = nameLine->text();
     QString address = addressText->toPlainText();

     if (contacts.contains(name)) {

         int button = QMessageBox::question(this,
             tr("Confirm Remove"),
             tr("Are you sure you want to remove \"%1\"?").arg(name),
             QMessageBox::Yes | QMessageBox::No);

         if (button == QMessageBox::Yes) {

             previous();
             contacts.remove(name);

             QMessageBox::information(this, tr("Remove Successful"),
                 tr("\"%1\" has been removed from your address book.").arg(name));
         }
     }

     updateInterface(NavigationMode);
 }

  假如存在,我們顯示一個 QMessageBox,以讓使用者確認(confirm)刪除動作。一旦使用者確認了,我們呼叫 previous() 以確保使用者介面顯示了另一個聯絡人,且利用 QMap 的 remove() 函式來移除聯絡人。基於禮貌,我們顯示了一個 QMessageBox 以告知使用者。這個函式中所使用的訊息視窗顯示如下:




Updating the User Interface

  我們先前提到了 updateInterface() 函式作為使按鈕根據當前模式生效與失效的工具。函式根據傳入的 mode 引數更新當前模式,在確認它的值之前賦值(assign)給 currentMode

  這時,每個按鈕都根據當前模式生效或失效。其於 AddingModeEditingMode 的程式碼顯示如下:

 void AddressBook::updateInterface(Mode mode)
 {
     currentMode = mode;

     switch (currentMode) {

     case AddingMode:
     case EditingMode:

         nameLine->setReadOnly(false);
         nameLine->setFocus(Qt::OtherFocusReason);
         addressText->setReadOnly(false);

         addButton->setEnabled(false);
         editButton->setEnabled(false);
         removeButton->setEnabled(false);

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

         submitButton->show();
         cancelButton->show();
         break;

  然而,對於 NavigationMode,我們在 QPushButton::setEnabled() 的參數中包含條件。這是為了確保在 Address Book 中至少有一個聯絡人時,能使 editButtonremoveButton 按紐生效;只有在 Address Book 中有多於一個聯絡人時,能使 nextButtonpreviousButton 按紐生效。

     case NavigationMode:

         if (contacts.isEmpty()) {
             nameLine->clear();
             addressText->clear();
         }

         nameLine->setReadOnly(true);
         addressText->setReadOnly(true);
         addButton->setEnabled(true);

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

         submitButton->hide();
         cancelButton->hide();
         break;
     }
 }

  藉由執行設置模式的任務以及在同一函式中更新使用者介面,我們避免了使用者介面的程式內部狀態發生「不同步(out of sync)」的可能性。


來源:Address Book 4 - Editing and Removing Addresses
版本:4.4.3

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

2月 15, 2009

【翻譯】Address Book 2 - Adding Addresses

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

  建立我們 Address Book 的下一步是,允許一點點使用者的互動。



  我們將要提供一個可以讓使用者點擊以新增一個聯絡人的按鈕(push button),以及允許加入新聯絡人的按鈕。另外,還需要某種資料結構(data structure)以有組織的方式儲存這些聯絡人。


Defining the AddressBook Class

  現在,我們已經設置好了標籤和輸入區塊,我們加入按鈕以完成新增一個聯絡人的處理。這代表我們的 addressbook.h 檔案現在擁有三個 QPushButton 物件宣告,與三個對應的公開(public) slot。

 public slots:
     void addContact();
     void submitContact();
     void cancel();

  一個 slot 是一個回應一個特定 signal 的函式。我們將會在實現 AddressBook 類別時更詳細的討論這個概念。然而,你可以參照 Signals and Slots 文件,以獲得一份 Qt signal 與 slot 概念的簡介。

  三個 QPushButton 物件:addButtonsubmitButtoncancelButton,現在與前一章的 nameLineaddressText 一同被包含在我們的私有變數宣告中。

 private:
     QPushButton *addButton;
     QPushButton *submitButton;
     QPushButton *cancelButton;
     QLineEdit *nameLine;
     QTextEdit *addressText;

  我們需要一個容器以儲存我們的 Address Book 聯絡人,讓我們能夠遍歷(traverse)並顯示它們。一個 QMap 物件:contacts 用以掌握一對 key-value 來達成這個目的:聯絡人的姓名為鍵(key),而聯絡人住址為值(value)。

     QMap<QString, QString> contacts;
     QString oldName;
     QString oldAddress;
 };

  我們也宣告了兩個私有的 QString 物件:oldNameoldAddress。在使用者點擊 "Add" 之前,需要這些物件保留最後顯示的聯絡人的姓名與地址。所以,當使用者點擊 "Cancel",我們可以回復顯示最後的聯絡人詳細資料。


Implementing the AddressBook Class

  在 AddressBook 的建構子中,我們將 nameLineaddressText 設為唯讀(read-only),所以我們只能顯示它,而不能編輯已存在的聯絡人詳細資料。

     ...
     nameLine->setReadOnly(true);
     ...
     addressText->setReadOnly(true);

  現在,我們實體化我們的按鈕:addButtonsubmitButton、與 cancelButton

     addButton = new QPushButton(tr("&Add"));
     addButton->show();
     submitButton = new QPushButton(tr("&Submit"));
     submitButton->hide();
     cancelButton = new QPushButton(tr("&Cancel"));
     cancelButton->hide();

  當 submitButtoncancelButton 藉由呼叫 hide() 被隱藏時,addButton 藉由呼叫 show() 函式而被顯示。這兩個按鈕只有在使用者按下 "Add" 時才會被顯示,且這是由下方敘述的 addContact() 函式處理的。

     connect(addButton, SIGNAL(clicked()), this, SLOT(addContact()));
     connect(submitButton, SIGNAL(clicked()), this, SLOT(submitContact()));
     connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancel()));

  我們連結(connect)按紐的 clicked() signal 到它們各自的 slot。下圖說明了這件事。



  接下來,我們使用 QVBoxLayout 垂直對齊我們的按鈕,恰好排列到我們 Address Book 元件的右邊。

     QVBoxLayout *buttonLayout1 = new QVBoxLayout;
     buttonLayout1->addWidget(addButton, Qt::AlignTop);
     buttonLayout1->addWidget(submitButton);
     buttonLayout1->addWidget(cancelButton);
     buttonLayout1->addStretch();

  addStretch() 函式用以確保按鈕不會均分空間,而是緊密的排列在元件上方。下圖顯示了使用了 addStretch() 與不使用它的不同。



  我們現在使用 addLayout()buttonLayout1 加入到 mainLayout 中。這使得我們嵌套(nested)佈局,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() 函式中,我們儲存最後的聯絡人詳細資料在 oldNameoldAddress 中。現在我們清除這些輸入區塊,並關閉唯讀模式。我們將焦點(focus)設在 nameLine 上,並顯示了 submitButtoncancelButton

 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() 函式可以被分成三個部份:

  1. 我們設法從 nameLineaddressText 得到聯絡人的詳細資料,並儲存他們在 QString 物件中。我們也要確保使用者在輸入區塊空白時無法點擊 "Submit";此外,一個 QMessageBox 會被顯示,以提醒使用者輸入姓名與住址。

     void AddressBook::submitContact()
     {
         QString name = nameLine->text();
         QString address = addressText->toPlainText();
    
         if (name == "" || address == "") {
             QMessageBox::information(this, tr("Empty Field"),
                 tr("Please enter a name and address."));
             return;
         }
    

  2. 然後,我們開始確認聯絡人是否已經存在。假如不存在,我們加入聯絡人到 contacts 中,並顯示一個 QMessageBox 告知使用者:聯絡人已被加入。

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

      假如聯絡人已存在,我們同樣顯示一個 QMessageBox 以告知使用者,預防使用者加入了重複的聯絡人。我們的 contacts 物件以一對姓名與住址的 key-value 為基礎。因此,我們要確保是唯一的。

  3. 一旦我們處理完上述兩種情況,我們用接下來的程式碼來將按鈕恢復成它們的標準狀態:

         if (contacts.isEmpty()) {
             nameLine->clear();
             addressText->clear();
         }
    
         nameLine->setReadOnly(true);
         addressText->setReadOnly(true);
         addButton->setEnabled(true);
         submitButton->hide();
         cancelButton->hide();
     }
    

  下方的螢幕截圖顯示了我們用以顯示給使用者的訊息的 QMessageBox 物件。



  cancel() 函式恢復了最後顯示的聯絡人詳細資料、啟用 addButton,並隱藏了 submitButtoncancelButton

 void AddressBook::cancel()
 {
     nameLine->setText(oldName);
     nameLine->setReadOnly(true);

     addressText->setText(oldAddress);
     addressText->setReadOnly(true);

     addButton->setEnabled(true);
     submitButton->hide();
     cancelButton->hide();
 }

  加入一個聯絡人的普遍構想,給了使用者在任何時間點擊 "Submit" 或是 "Cancel" 的彈性。下方的流程圖進一步的說明了這個概念:




來源:Address Book 2 - Adding Addresses
版本:4.4.3

2月 14, 2009

【翻譯】Address Book 1 - Designing the User Interface

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

  這份指導的第一部分,涵蓋了我們使用在 Address Book 程式上的基礎圖形使用者介面(GUI,graphical user interface)設計。

  建立一個 GUI 程式的第一步,是設計使用者介面。在這一章,我們的目標是建立實現一個基礎的 Address Book 所需的標籤(label)與輸入區塊(input field)。下圖是我們期望的成果截圖。



  我們需要兩個 QLabel 物件(object):nameLabeladdressLabel,還有兩個輸入區塊:一個 QLineEdit - nameLine 和一個 QTextEdit - addressText,讓使用者能夠輸入連絡姓名以及住址。使用到的元件與它們的位置顯示在下圖。



  這裡使用了三個檔案來實現這個 Address Book:

  • addressbook.h - AddressBook 類別的定義檔,
  • addressbook.cpp - AddressBook 類別的實現檔,以及
  • main.cpp - 包含了一個 main() 函式,加上 AddressBook 實體(instance)的檔案


Qt Programming - Subclassing

  在寫 Qt 程式時,我們經常子類別化(subclass) Qt 物件以增加功能。這是建立自訂元件或是標準元件集合背後的其中一個基本要素。子類別化以擴展或改變一個元件的行為,有以下幾點好處:

  • 我們可以寫出虛擬(virtual)或是純虛擬(pure virtual)函式的實現以完全獲得我們的需求,在必要時則回頭採用基礎類別(base class)的實現。
  • 這允許我們封裝(encapsulate)部分的使用者介面在類別中,所以應用程式的其餘部分並不需要知道使用者介面中的獨立元件。
  • 子類別(subclass)可以在相同的應用程式或函式庫(library)中被用來建立多樣的自訂元件,且子類別的程式碼能夠在其他專案中被重複使用(reused)。

  由於 Qt 並沒有提供一個特有的 Address Book 元件,我們子類別化並加入功能到一個標準的 Qt 元件中。我們在這份指導教學中建立的 AddressBook 類別,在需要一個基礎 Address Book 元件的情況下可以被重複使用。


Defining the AddressBook Class

  addressbook.h 檔案被用來定義 AddressBook 類別。

  我們從將 AddressBook 定義為一個 QWidget 子類別與宣告(declare)建構子(constructor)開始。我們也使用了 Q_OBJECT 巨集(macro)以表示類別使用了國際化(internationalization)與 Qt 的 signal 與 slot 功能,即使我們在這一階段還不會使用到所有的功能。

 class AddressBook : public QWidget
 {
     Q_OBJECT

 public:
     AddressBook(QWidget *parent = 0);

 private:
     QLineEdit *nameLine;
     QTextEdit *addressText;
 };

  類別包含了 nameLineaddressText 的定義,也就是我們先前提到的 QLineEdit 與 QTextEdit 的私有(private)實體。在接下來的章節,我們將會發現將資料儲存在 nameLineaddressText 需要許多 Address Book 的函式。

  我們不需要包含我們將會用到的 QLabel 的定義,因為一旦它們被建立,我們就不需要再參照(reference)它們了。Qt 追蹤物件所有權(ownership)的方式會在下一節解釋。

  Q_OBJECT 巨集自身實現了一些 Qt 中較為進階的功能。目前,將 Q_OBJECT 巨集想成一個允許我們使用 tr()connect() 函式的捷徑(shortcut)是有用的。

  我們現在已經完成了 addressbook.h 檔案,讓我們繼續實現對應的 addressbook.cpp 檔案。


Defining the AddressBook Class

  AddressBook 的建構子接受一個 QWidget 參數(parameter)--父元件(parent)。照慣例,我們傳遞這個參數到基礎類別的建構子中。一個父元件可以擁有一個或多個子元件--這個所有權的概念,對於在 Qt 中組合元件是有用的。舉例來說,假如你刪除一個父元件,所有它的子元件也將會被刪除。

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

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

  在這個建構子中,我們宣告並實體化兩個區域(local) QLabel 物件:nameLabeladdressLabel,並實體化了 nameLineaddressText。tr() 函式返回一個字串的翻譯版本,假如它是有效的;否則,它會返回字串本身。將這個函式想成一個 <在這裡插入翻譯> 記號以標記用以翻譯的 QString 物件。你將會注意到,在接下來的章節以及 Qt Examples 中,無論我們在何時使用一個可翻譯的字串,我們都會包含它。

  寫 Qt 程式時,瞭解佈局(layout)的運作是有用的。Qt 提供了三種主要的佈局類別:QHBoxLayoutQVBoxLayoutQGridLayout 來管理元件的位置。



  我們使用一個 QGridLayout 以結構化的方式擺放我們的標籤與輸入區塊到適當的位置。QGridLayout 將可用空間分割成一個網格(grid)並放置元件在我們指定行與列數的格子(cell)中。上方的示意圖顯示了佈局網格與我們的元件位置,我們使用接下來的程式碼來指定這個排列:

     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 作為一個附加引數(argument)來做定位的。這用以確定它不會在格子 (1,0) 中垂直置中。參考 Layout Classes 文件,以獲得 Qt 佈局的基礎簡介。

  為了要將佈局物件設置到元件上,我們必須呼叫元件的 setLayout() 函式:

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

  最後,我們將元件的標題設為 "Simple Address Book"。


Running the Application

  一個獨立的檔案 main.cpp 用於 main() 函式。在這個函式中,我們實體化一個 QApplication 物件:app。QApplication 負責像是預設字體(font)和游標(cursor)等多種應用廣泛的資源,與執行事件迴圈(event loop)。因此,總是有一個 QApplication 物件在每個使用 Qt 的 GUI 應用程式中。

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

     AddressBook *addressBook = new AddressBook;
     addressBook->show();

     return app.exec();
 }

  我們使用 new 關鍵字,在堆積(heap)中建構了一個新的 AddressBook 元件,並呼叫它的 show() 函式來顯示它。然而,直到應用程式的事件迴圈開始前,元件還不會被顯示。我們藉由呼叫應用程式的 exec() 函式來開始事件迴圈;這個函式返回的執行結果同樣也作為 main() 函式的傳回值。


來源:Address Book 1 - Designing the User Interface
版本:4.4.3

2月 13, 2009

【目錄】USACO Trainng

@
USACO Trainng - 試題解題一覽


相關介紹

What's This
How To Start


題目中譯

NOCOW - USACO Training (簡)
USACO譯題のSGZOI鏡像站 (簡)


Section 1.1

Your Ride Is Here (ride)
Greedy Gift Givers (gift1)
Friday the Thirteenth (friday)
Broken Necklace (beads) - coming soon...
Your Ride Is Here (ride) - coming soon...


Section 1.2

Milking Cows (milk2) - coming soon...
Transformations (transform) - coming soon...
Name That Number (namenum) - coming soon...
Palindromic Squares (palsquare) - coming soon...
Dual Palindromes (dualpal) - coming soon...


Section 1.3

Mixing Milk (milk) - coming soon...
Barn Repair (barn1) - coming soon...
Calf Flac (calfflac) - coming soon...
Prime Cryptarithm (crypt1) - coming soon...


Section 1.4

Packing Rectangles (packrec) - coming soon...
The Clocks (clocks) - coming soon...
Arithmetic Progressions (ariprog) - coming soon...
Mother's Milk (milk3) - coming soon...


Section 1.5

Number Triangles (numtri) - coming soon...
Prime Palindromes (pprime) - coming soon...
SuperPrime Rib (sprime) - coming soon...
Checker Challenge (checker) - coming soon...


Section 2.1

The Castle (castle) - coming soon...
Ordered Fractions (frac1) - coming soon...
Sorting A Three-Valued Sequence (sort3) - coming soon...
Healthy Holsteins (holstein) - coming soon...
Hamming Codes (hamming) - coming soon...


Section 2.2

Preface Numbering (preface) - coming soon...
Subset Sums (subset) - coming soon...
Runaround Numbers (runround) - coming soon...
Party Lamps (lamps) - coming soon...


Section 2.3

The Longest Prefix (prefix) - coming soon...
Cow Pedigrees (nocows) - coming soon...
Zero Sum (zerosum) - coming soon...
Money Systems (money) - coming soon...
Controlling Companies (concom) - coming soon...


Section 2.4

The Tamworth Two (ttwo) - coming soon...
Overfencing (maze1) - coming soon...
Cow Tours (cowtour) - coming soon...
Bessie Come Home (comehome) - coming soon...
Fractions to Decimals (fracdec) - coming soon...


Section 3.1

Agri-Net (agrinet) - coming soon...
Score Inflation (inflate) - coming soon...
Humble Numbers (humble) - coming soon...
Shaping Regions (rect1) - coming soon...
Contact (contact) - coming soon...
Stamps (stamps) - coming soon...


Section 3.2

Factorials (fact4) - coming soon...
Stringsobits (kimbits) - coming soon...
Spinning Wheels (spin) - coming soon...
Feed Ratios (ratios) - coming soon...
Magic Squares (msquare) - coming soon...
Sweet Butter (butter) - coming soon...


Section 3.3

Riding The Fences (fence) - coming soon...
Shopping Offers (shopping) - coming soon...
Camelot (camelot) - coming soon...
Home on the Range (range) - coming soon...
A Game (game1) - coming soon...


Section 3.4

Closed Fences (fence4) - coming soon...
American Heritage (heritage) - coming soon...
Electric Fence (fence9) - coming soon...
Raucous Rockers (rockers) - coming soon...


Section 4.1

Beef McNuggets (nuggets) - coming soon...
Fence Rails (fence8) - coming soon...
Fence Loops (fence6) - coming soon...
Cryptcowgraphy (cryptcow) - coming soon...


Section 4.2

Drainage Ditches (ditch) - coming soon...
The Perfect Stall (stall4) - coming soon...
Job Processing (job) - coming soon...
Cowcycles (cowcycle) - coming soon...


Section 4.3

Buy Low, Buy Lower (buylow) - coming soon...
The Primes (prime3) - coming soon...
Street Race (race3) - coming soon...
Letter Game (lgame) - coming soon...


Section 4.4

Shuttle Puzzle (shuttle) - coming soon...
Pollutant Control (milk6) - coming soon...
Frame Up (frameup) - coming soon...


Section 5.1

Fencing the Cows (fc) - coming soon...
Starry Night (starry) - coming soon...
Musical Themes (theme) - coming soon...


Section 5.2

Snail Trail (snail) - coming soon...
Electric Fences (fence3) - coming soon...
Wisconsin Squares (wissqu) - coming soon...


Section 5.3

Milk Measuring (milk4) - coming soon...
Window Area (window) - coming soon...
Network of Schools (schlnet) - coming soon...
Big Barn (bigbrn) - coming soon...


Section 5.4

All Latin Squares (latin) - coming soon...
Canada Tour (tour) - coming soon...
Character Recognition (charrec) - coming soon...
Betsy's Tour (betsy) - coming soon...
TeleCowmunication (telecow) - coming soon...


Section 5.5

Picture (picture) - coming soon...
Hidden Passwords (hidden) - coming soon...
Two Five (twofive) - coming soon...


Section 6.1

Postal Vans (vans) - coming soon...
A Rectangular Barn (rectbarn) - coming soon...
Cow XOR (cowxor) - coming soon...

【解題】Friday the Thirteenth

@
USACO Section 1.1 - Friday the Thirteenth


The Problem

Is Friday the 13th really an unusual event?

That is, does the 13th of the month land on a Friday less often than on any other day of the week? To answer this question, write a program that will compute the frequency that the 13th of each month lands on Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, and Saturday over a given period of N years. The time period to test will be from January 1, 1900 to December 31, 1900+N-1 for a given number of years, N. N is non-negative and will not exceed 400.

There are few facts you need to know before you can solve this problem:
  • January 1, 1900 was on a Monday.
  • Thirty days has September, April, June, and November, all the rest have 31 except for February which has 28 except in leap years when it has 29.
  • Every year evenly divisible by 4 is a leap year (1992 = 4*498 so 1992 will be a leap year, but the year 1990 is not a leap year)
  • The rule above does not hold for century years. Century years divisible by 400 are leap years, all other are not. Thus, the century years 1700, 1800, 1900 and 2100 are not leap years, but 2000 is a leap year.
Do not use any built-in date functions in your computer language.

Don't just precompute the answers, either, please.


Program Name: friday


Input Format

One line with the integer N.


Output Format

Seven space separated integers on one line. These integers represent the number of times the 13th falls on Saturday, Sunday, Monday, Tuesday, ..., Friday.


Sample Input (file friday.in)

20


Sample Output (file friday.out)

36 33 34 33 35 35 34


解題思考

  首先,既然我們已經知道,1900 年 1 月 1 日是星期一,那麼我們只需要求出:某日是從 1900 年 1 月 1 日開始的第幾天,將這個數字除以 7 所得到的餘數,就能夠知道那一天到底是星期幾(餘數 0 ~ 6 分別對應到星期天至星期一)。

  所以,我利用迴圈,跑過從 1900 年到 1900 + n - 1 年中每一個月的 13 號(至於怎麼對應閏年,就照題目說的做吧),分別利用上面的方式得出「這個 13 號」是星期幾,並紀錄下來。

  最後,依照星期六、星期日、星期一、星期二、星期三、星期四、星期五的順序輸出紀錄的結果,就可以了。


參考解答(C++)

#include <fstream>

using namespace std;

inline bool isLeapYear(int year);

int main(void)
{
    ifstream in("friday.in", ios::in);
    ofstream out("friday.out", ios::out);

    int n;
    in >> n;
    in.close();

    int totalDay = 13, time[12];
    memset(time, 0, sizeof(time));
    for (int i = 0; i < n; i++)
    {
        for (int j = 1; j <= 12; j++)
        {
            time[totalDay % 7]++;

            // 計算從 1900 年 1 月 1 日到 (1900 + i) 年 j 月 13 日共有幾天
            int day;
            switch (j)
            {
                case 2:
                    day = (isLeapYear(1900 + i)) ? 29 : 28;
                    break;

                case 4:
                case 6:
                case 9:
                case 11:
                    day = 30;
                    break;

                default:
                    day = 31;
            }

            totalDay += day;
        }
    }

    out << time[6];
    for (int i = 0; i < 6; i++)
    {
        out << " " << time[i];
    }

    out << endl;
    out.close();

    return 0;
}

inline bool isLeapYear(int year)
{
    // 返回傳入的年份是否為閏年
    return (year % 100 != 0 && year % 4 == 0) || (year % 400 == 0);
}

【解題】Greedy Gift Givers

@
USACO Section 1.1 - Greedy Gift Givers


The Problem

A group of NP (2 ≤ NP ≤ 10) uniquely named friends has decided to exchange gifts of money. Each of these friends might or might not give some money to any or all of the other friends. Likewise, each friend might or might not receive money from any or all of the other friends. Your goal in this problem is to deduce how much more money each person gives than they receive.

The rules for gift-giving are potentially different than you might expect. Each person sets aside a certain amount of money to give and divides this money evenly among all those to whom he or she is giving a gift. No fractional money is available, so dividing 3 among 2 friends would be 1 each for the friends with 1 left over -- that 1 left over stays in the giver's "account".

In any group of friends, some people are more giving than others (or at least may have more acquaintances) and some people have more money than others.

Given a group of friends, no one of whom has a name longer than 14 characters, the money each person in the group spends on gifts, and a (sub)list of friends to whom each person gives gifts, determine how much more (or less) each person in the group gives than they receive.


Important Note

The grader machine is a Linux machine that uses standard Unix conventions: end of line is a single character often known as '\n'. This differs from Windows, which ends lines with two charcters, '\n' and '\r'. Do not let your program get trapped by this!


Program Name: gift1


Input Format

Line 1: The single integer, NP

Lines 2..NP+1: Each line contains the name of a group member

Lines NP+2..end: NP groups of lines organized like this:
The first line in the group tells the person's name who will be giving gifts.
The second line in the group contains two numbers: The initial amount of money (in the range 0..2000) to be divided up into gifts by the giver and then the number of people to whom the giver will give gifts, NGi (0 ≤ NGi ≤ NP-1).
If NGi is nonzero, each of the next NGi lines lists the the name of a recipient of a gift.


Output Format

The output is NP lines, each with the name of a person followed by a single blank followed by the net gain or loss (final_money_value - initial_money_value) for that person. The names should be printed in the same order they appear on line 2 of the input. All gifts are integers. Each person gives the same integer amount of money to each friend to whom any money is given, and gives as much as possible that meets this constraint. Any money not given is kept by the giver.


Sample Input (file gift1.in)

5
dave
laura
owen
vick
amr
dave
200 3
laura
owen
vick
owen
500 1
dave
amr
150 2
vick
owen
laura
0 2
amr
vick
vick
0 0


Sample Output (file gift1.out)

dave 302
laura 66
owen -359
vick 141
amr -150


解題思考

  首先,我宣告了一個包含了「姓名」(name)與「收支金額」(money)的結構體。在讀入所有人的名字,存入結構體中的 name 成員之後,同時將其 money 成員歸零(在起初,並未有任何收入或支出)。

  接著讀入所有的「送禮紀錄」之後,則將送禮者的 money 成員減去送禮花費的金額。但是要注意,由於送禮的金額是「盡可能多給」,且能夠「平分給所有人」的金額。所以實際花費的金額,應該是每個人的「持有金額」減去「此金額除以收禮者人數的餘數」。

  然後將每個收禮者的 money 成員加上收到的禮物金額,也就是「送禮金額」除以「收禮人數」。

  讀完所有送禮紀錄後,直接將每個人的 name 與 money 成員成對輸出,就是題目要求的結果了。


參考解答(C++)

#include <fstream>
#include <cstring>

using namespace std;

struct person
{
    char name[13];
    int money;
};

int main(void)
{
    ifstream in("gift1.in", ios::in);
    ofstream out("gift1.out", ios::out);

    int NP;
    in >> NP;

    person *people = new person[NP];
    for (int i = 0; i < NP; i++)
    {
        in >> people[i].name;

        people[i].money = 0;
    }

    for (int i = 0; i < NP; i++)
    {
        char name[13];
        int money, number;
        in >> name >> money >> number;

        // 忽略不送任何禮物的情況
        if (money && number)
        {
            for (int j = 0; j < NP; j++)
            {
                if (!strcmp(people[j].name, name))
                {
                    // 將自身金額扣除支出金額
                    people[j].money -= money - money % number;
                    break;
                }
            }
        }

        for (int j = 0; j < number; j++)
        {
            char recipient[13];
            in >> recipient;

            // 將禮物收受者金額加上禮物金額
            for (int k = 0; k < NP; k++)
            {
                if (!strcmp(people[k].name, recipient))
                {
                    people[k].money += money / number;
                    break;
                }
            }
        }
    }

    in.close();

    for (int i = 0; i < NP; i++)
    {
        out << people[i].name << " " << people[i].money << endl;
    }

    out.close();

    return 0;
}

2月 11, 2009

【解題】Your Ride Is Here

@
USACO Section 1.1 - Your Ride Is Here


The Problem

It is a well-known fact that behind every good comet is a UFO. These UFOs often come to collect loyal supporters from here on Earth. Unfortunately, they only have room to pick up one group of followers on each trip. They do, however, let the groups know ahead of time which will be picked up for each comet by a clever scheme: they pick a name for the comet which, along with the name of the group, can be used to determine if it is a particular group's turn to go (who do you think names the comets?). The details of the matching scheme are given below; your job is to write a program which takes the names of a group and a comet and then determines whether the group should go with the UFO behind that comet.

Both the name of the group and the name of the comet are converted into a number in the following manner: the final number is just the product of all the letters in the name, where "A" is 1 and "Z" is 26. For instance, the group "USACO" would be 21 * 19 * 1 * 3 * 15 = 17955. If the group's number mod 47 is the same as the comet's number mod 47, then you need to tell the group to get ready! (Remember that "a mod b" is the remainder left over after dividing a by b; 34 mod 10 is 4.)

Write a program which reads in the name of the comet and the name of the group and figures out whether according to the above scheme the names are a match, printing "GO" if they match and "STAY" if not. The names of the groups and the comets will be a string of capital letters with no spaces or punctuation, up to 6 characters long.

Examples:

Input Output
COMETQ
HVNGAT
GO
ABSTAR
USACO
STAY


Program Name: ride


Input Format

Line 1: An upper case character string of length 1..6 that is the name of the comet.
Line 2: An upper case character string of length 1..6 that is the name of the group.

NOTE: The input file has a newline at the end of each line but does not have a "return". Sometimes, programmers code for the Windows paradigm of "return" followed by "newline"; don't do that! Use simple input routines like "readln" (for Pascal) and, for C/C++, "fscanf" and "fid>>string".


Output Format

A single line containing either the word "GO" or the word "STAY".


Sample Input (file ride.in)

COMETQ
HVNGAT


Sample Output (file ride.out)

GO


解題思考

  這題基本上不難,大概是給我們「試水溫」、熟悉一下 USACO 的上傳步驟吧。

  根據題意,分別將上下兩行的每個英文字母 A - Z 轉換成數字 1 - 26 之後相乘。

  接著求出這兩個數字除以 47 的餘數,若是相等則輸出 "GO",不相等則輸出 "STAY"。完成。


參考解答(C++)

#include <fstream>

using namespace std;

int main(void)
{
    ifstream in("ride.in", ios::in);
    ofstream out("ride.out", ios::out);

    char cometName[7], groupName[7];
    in >> cometName >> groupName;
    in.close();

    // 將彗星的名字轉換成數字
    int cometNumber = 1;
    for (int i = 0; cometName[i] != '\0'; i++)
    {
        cometNumber *= cometName[i] - 'A' + 1;
    }

    // 將團體的名字轉換成數字
    int groupNumber = 1;
    for (int i = 0; groupName[i] != '\0'; i++)
    {
        groupNumber *= groupName[i] - 'A' + 1;
    }

    out << ((cometNumber % 47 == groupNumber % 47) ? "GO" : "STAY") << endl;
    out.close();

    return 0;
}

2月 07, 2009

【筆記】Installing Qt with MinGW

@
  由於安裝 Qt 感覺起來還滿麻煩的,所以在這裡,我使用 Windows XP + MinGW 的環境,紀錄一下安裝 Qt 的步驟與過程。




  首先,請先到 Qt for Open Source C++ development on Windows 的下載頁中,選擇一個載點下載當前最新版的 "qt-win-opensource-4.4.3-mingw.exe"(請自己選擇當前最新版)。



  下載完成,開始安裝之後,安裝程式會詢問你要不要將 .ui 副檔名跟 Qt 的 GUI 編輯器(Qt Designer)作檔案關聯。

  基本上不用更改直接繼續就可以了。



  接著選擇安裝 Qt 的目錄。



  在這裡,假如你已經安裝過 MinGW,請直接選擇 MinGW 所在的目錄。

  (要瞭解如何安裝 MinGW,請見這篇文章。)



  此時,若你的 MinGW 是新版(我的是 5.1.4 版),Qt 的安裝程式會對 w32api.h 版本不合提出警告。

  但是,因為此時我的 w32api.h 的版本為 3.12,照理說是比 Qt 需求的 3.2 版還要新的。既然我的版本比 Qt 需求的版本還新,安裝程式還對此提出警告,實在是有些怪異。

  無論如何,在此我們先選擇「是」,忽略這個警告繼續進行安裝。




  而若是電腦裡並沒有預先安裝 MinGW,則可以勾選上面的 "Download ans install minimal MinGW installition",再選擇欲安裝 MinGW 的目錄。



  接著它會要你選擇下載 MinGW 的鏡像點(mirror)。

  其實也沒什麼好選的,因為站點只有一個 Norway。

  至於下方的 "Download MinGW source code" 則是可以順便將 MinGW 的原始碼下載下來。沒必要的話就別勾選吧。



  之後就會開始下載 MinGW 的動作(由於鏡像點相當遠,下載可能會等很久)。





  等一段時間,就能安裝完成了。


  不過,這樣還不能夠藉由 C++ 來使用 Qt。我們還需要將 Qt 函式庫進行編譯才能夠使用。

  首先,請到系統內容中,將 MinGW 目錄下的 bin 資料夾與 Qt 目錄下的 bin 資料夾加入到 PATH 環境變數中。例如 C:\MinGW\binC:\Qt\4.4.3\bin。(不知道如何設定環境變數,可以參考這篇文章。)

  然後,執行開始功能表中 Qt 資料夾(預設名稱為 "Qt by Nokia v4.4.3 (OpenSource)")裡的 "Qt 4.4.3 (Build Debug Libraries)",以建立 Debug Library。

  剛開始,應該會看到以下這段文字:

 This is the Qt for Windows Open Source Edition.

 You are licensed to use this software under the terms of
 the GNU General Public License (GPL) version 2 or 3.

 Type '2' to view the GNU General Public License version 2 (GPLv2).
 Type '3' to view the GNU General Public License version 3 (GPLv3).
 Type 'y' to accept this license offer.
 Type 'n' to decline this license offer.

 Do you accept the terms of the license?

  這裡是在詢問你同不同意 GNU GPL 的條款。若是要詳細瞭解條款內容,就輸入 23 (GPL的版本),再按下 Enter

  若是同意接受 GNU GPL 條款,就輸入 y 並按下 Enter 繼續安裝的程序吧。


  但是,假如你在之前選擇 MinGW 的地方,也碰上了 w32api 的警告,在這個過程中將會兩項錯誤(若是選擇由鏡像點下載並安裝 MinGW 則不會出現此問題)。

  所幸的是,這兩個問題都已經在網路上找到解決的方法。你可以到這裡閱讀相關的討論。


  第一個錯誤是 MinGW 目錄下 "\include\sspi.h" 的 "error: `UNICODE_STRING' does not name a type"。根據查到的結果,"UNICODE_STRING" 是被定義在 <subauth.h> 標頭檔中的。所以要修正這個問題,請開啟 sspi.h 找到以下程式碼:

#ifndef _SSPI_H
#define _SSPI_H
#if __GNUC__ >=3
#pragma GCC system_header
#endif

#ifdef __cplusplus
extern "C" {
#endif

  並在這段程式碼下面加上這一行:

#include <subauth.h>

  然後存檔關閉,這樣就可以了。


  而碰到的第二個錯誤,則是 Qt 目錄下的 "\src\corelib\arch\qatomic_windows.h" 與 MinGW 目錄下的 "\include\winbase.h" 中有部分函式發生了原型衝突(prototype conflict),也就是相同的函式被重複定義了兩次。

  解決的方法是,開啟 qatomic_windows.h 後,找到以下程式碼:
extern "C" {
    __declspec(dllimport) long __stdcall InterlockedCompareExchange(long *, long, long);
    __declspec(dllimport) long __stdcall InterlockedIncrement(long *);
    __declspec(dllimport) long __stdcall InterlockedDecrement(long *);
    __declspec(dllimport) long __stdcall InterlockedExchange(long *, long);
    __declspec(dllimport) long __stdcall InterlockedExchangeAdd(long *, long);
}

  並將它修改成:

#ifndef __INTERLOCKED_DECLARED
#define __INTERLOCKED_DECLARED
extern "C" {
    __declspec(dllimport) long __stdcall InterlockedCompareExchange(long *, long, long);
    __declspec(dllimport) long __stdcall InterlockedIncrement(long *);
    __declspec(dllimport) long __stdcall InterlockedDecrement(long *);
    __declspec(dllimport) long __stdcall InterlockedExchange(long *, long);
    __declspec(dllimport) long __stdcall InterlockedExchangeAdd(long *, long);
}
#endif

  以巨集的方式避開重複定義,這樣就沒有問題了。


  話說回來,這個建立 Debug Library 的過程其實相當久(約兩個小時)。

  確認沒有問題之後,建議就別盯著發呆,開個即時通聊天也好


  完成建置工作之後,請點選「開始」中的「執行」,並輸入 "cmd" 以開啟命令列提示工具。

  接著,我們要將所在目錄移到 Qt 的根目錄中。請輸入 "cd" 加上 Qt 的所在目錄,例如:

cd C:\Qt\4.4.3

  然後,再輸入以下命令:

make

  這一個步驟也相當耗時。想睡的,甚至可以回去補個眠了(無誤)。


  等到睡醒了,發現 make 的步驟也順利完成了。

  最後,我們需要將一些不必要的檔案清除,請輸入以下命令:

make clean

  安裝到這裡,就大功告成囉!

2月 06, 2009

【介紹】GCC / MinGW

@
  說到 C/C++ 的開發環境,就不能不提到 GCC。GCC 是 GNU Compiler Collection (GNU 編譯器總集)的縮寫,為 GNU 計畫中一套多種程式語言編譯器的集合。在諸多 Unix-likeMac OS X 中都成為其內建的程式開發環境。

  其實,GCC 最初的名稱為 GNU C Compiler(GNU C 語言編譯器)。在當時,GCC 還只是一個專門處理 C 語言的編譯器。而在後來 GCC 擴展之後,慢慢的也可以處理 C++、FortranAdaJavaObjective-C 等語言。發展至今,就是現在我們所看到編譯器總集了。


  雖然在 Unix-like 與 Mac OS X 系統中都已經內建了 GCC 的環境,但是若要在 Windows 系統下擁有相同的環境,你可能就需要安裝 MinGW 了。

  MinGW 即 Minimalist GNU for Windows,為包含了 GCC、GDB(GNU Debugger)binutils 等工具的 GNU 工具組(toolchain)移植到 Windows 平臺上的版本。其包含了許多提供 Windows API 的標頭檔以及函式庫,使程式開發者能直接使用它來開發原生的(native) Windows 程式。


  想要在 Windows 平臺上安裝 MinGW,我們需要先連結到 MinGW 的下載頁



  點選 MinGW 5.1.4(請選擇你當前的最新版本)。



  點選 MinGW-5.1.4.exe,就能夠下載了。



  下載完成以後,就能開始安裝的動作。

  首先,安裝程式會詢問你,是否要在檔案下載好後要直接安裝檔案。

  這裡我們先解釋選擇 "Download and install",也就是下載完成之後直接安裝檔案。



  接著會詢問要安裝哪一種封包。你可以選擇舊版(Previous)、當前版(Current)、或是先行版(Candidate)。

  這裡我們建議選擇穩定的 "Current" 版本。



  然後我們需要選擇要安裝的部份。

  其中,runtime (即 runtime library)是 MinGW 運行的基礎;而 w32api 則為 Windows API 的標頭檔與函式庫。

  另外,make 則是透過 makefile 來組織程式原始檔,以進行編譯原始碼工作的工具。

  而剩下的 core compiler、g++ compiler、g77 compiler、Ada Compiler、Java Compiler 與 Objective C Compiler,則分別為 C、C++、Fortran、Ada、Java 與 Objective C 的編譯器。

  為了要建置完整的 C/C++ 環境,這裡我們需要勾選 runtimew32apicore compilerg++ compilermake

  當然,假如需要的話,你也可以自行勾選下載其它編譯器。



  接著選擇 MinGW 的安裝目錄。









  接著等待它下載、安裝之後就完成了。




  而若是我們選擇 "Download Only",也就是只下載不安裝呢?



  這裡同樣會詢問你要安裝哪一種封包。

  當然囉,我們還是選擇穩定的 "Current" 版本。



  接著選擇要安裝的部份。

  同樣為了建置完整的 C/C++ 環境,這裡我們需要勾選 MinGW base toolsg++ compilerMinGW Make







  等待下載完成之後,你應該能夠在 MinGW 安裝檔的相同目錄下,看到由安裝程式下載下來的壓縮檔。

  你只需要將它們解壓縮,並放置到你想要的地方就可以了。


  最後,我們還要做一點環境設定。

  首先,請在「我的電腦」按下右鍵,選擇「內容」。



  接著選擇「進階」標籤頁,按下「環境變數」。



  若是你 MinGW 的環境是要給所有使用者使用的,你需要把 MinGW 目錄下 bin 資料夾的完整路徑加到系統變數 PATH 的尾端。例如:C:\MinGW\bin

  若是 PATH 變數中已有變數值,則請先在尾端加入分隔符號 ";",再加入 bin 資料夾的目錄。



  而若是你的 MinGW 環境只想給當前的使用者使用,你則要改成把 MinGW 目錄下 bin 資料夾的完整路徑加到使用者變數 PATH 的尾端。

  同樣的,若是 PATH 變數中已有變數值,則請先在尾端加入分隔符號 ";",再加入 bin 資料夾的目錄。


  接著,讓我們測試一下 MinGW/GCC 是否已經配置妥當了。

  若是在 Unix-like 的系統,請開啟你的 shell;若是在 Windows 系統下,請按「開始功能表」並點選「執行」,輸入 "cmd" 以開啟命令提示工具。

  接下來,你只需要輸入:

gcc -v

  或是:

g++ -v

  假如有出現 GCC 的版本資訊,就是成功囉!


相關連結:
.GCC - http://gcc.gnu.org/
.MinGW - http://www.mingw.org/