實現一個簡單的地址薄,功能包括:地址的添加、瀏覽、編輯、查找、輸出文件等。 1 界面和元素 整個地址薄界面,可視為一個 AddressBook 類。而其中的 Name、Address 以及右側的兩個編輯欄,是包含在 AddressBook 中的元素,也即 “包含” 關係 (“has-a”)。因此,可 ...
實現一個簡單的地址薄,功能包括:地址的添加、瀏覽、編輯、查找、輸出文件等。
1 界面和元素
整個地址薄界面,可視為一個 AddressBook 類。而其中的 Name、Address 以及右側的兩個編輯欄,是包含在 AddressBook 中的元素,也即 “包含” 關係 (“has-a”)。因此,可以將它們聲明為AddressBook 的成員數據。
Name 和 Address 不涉及與用戶的交互,只顯示文本,因此,可用 QLabel 來實現,其描述為 ”QLabel is used for displaying text or an image. No user interaction functionality is provided“
Name 右側的單行編輯框,可用 QLineEdit 來實現,其描述為 ”The QLineEdit widget is a one-line text editor“
Address 右側的多行編輯框,可用 QTextEdit 來實現,其描述為 ”The QTextEdit class provides a widget that is used to edit and display both plain and rich text“
2 子類化 (Subclassing)
地址薄屬於自定義視窗部件 (custom widget),在 Qt 中並沒有對應的標準類。常用解決方法是:子類化 Qt 中的標準類。
另外,當一個視窗部件的功能,兼有多個標準視窗部件的功能時,也常用該方法。子類化的優點如下:
1) 只需重寫基類中的虛函數,來實現所需要的功能,體現了 "面向對象" 的 多態性
2) 將 UI 界面封裝在一個類中,隱藏了實現的細節,體現了 “面向對象” 的 封裝性
3) 實現的子類可被多個程式或庫調用,體現了設計的 可復用 原則 (reusable)
因此,可以通過子類化 QWidget 來實現地址薄類 AddressBook,使用 Qt Creator 創建工程,會自動生成如下的代碼框架 (除綠色標記的代碼外,其餘的都是自動生成的)
2.1 Q_OBJECT 巨集
當有 #8 的 Q_OBJECT 巨集時,可簡單理解為,Qt 中允許該類使用 tr() 和 connect() 函數。
#15 和 #16 處,聲明瞭兩個私有成員函數,QLineEdit 型 和 QTextEdit 型指針,分別代表地址薄中的 Name 和 Address 右側的編輯框。
那麼,在析構函數 ~AddressBook() 中,是不是需要 delete 這兩個指針呢?
1 #include <QWidget> // addressbook.h 2 3 class QLineEdit; 4 class QTextEdit; 5 6 class AddressBook : public QWidget 7 { 8 Q_OBJECT 9 10 public: 11 AddressBook(QWidget *parent = 0); 12 ~AddressBook(); 13 14 private: 15 QLineEdit *m_nameLine; 16 QTextEdit *m_addrText; 17 };
2.2 所有權 (ownership)
在 AddressBook 的構造函數中,明明 new 了 QLineEdit 和 QTextEdit 型指針,但是在析構函數中,並沒有 delete 相應的指針,難道沒有記憶體泄露麽?這要從 Qt 的記憶體管理說起。
#11 構造函數聲明中,它有一個 QWidget* 類型的參數 parent,該參數會傳給其基類的構造函數 (QWidget)。這樣,當實例化一個 AddressBook 對象時,如果為其指定了一個父類,則該父類便擁有了這個子類的 ”所有權“(ownership)。
當進行資源管理時,只需要銷毀這個父類,則它所擁有的所有子類,都會被自動刪除,這是 Qt 中的一個重要概念 -- “所有權”。
Qt 中的描述為:“The constructor of AddressBook accepts a QWidget parameter. By convention, we pass this parameter to the base class's constructor. This concept of ownership, where a parent can have one or more children, is useful for grouping widgets. For example, if you delete a parent, all of its children will be deleted as well.”
具體 AddressBook 是如何獲得 m_nameLine 和 m_addrText 所有權的,會在 “3 佈局管理中” 詳細闡述。
1 #include <QtWidgets/QLabel> 2 #include <QtWidgets/QLineEdit> 3 #include <QtWidgets/QTextEdit> 4 #include <QtWidgets/QGridLayout> 5 6 #include "addressbook.h" 7 8 AddressBook::AddressBook(QWidget *parent) 9 : QWidget(parent) 10 { 11 QLabel *nameLabel = new QLabel("Name:"); 12 m_nameLine = new QLineEdit; 13 QLabel *addressLabel = new QLabel("Address:"); 14 m_addrText = new QTextEdit; 15 16 ... ... ... 17 22 setWindowTitle("Address Book"); 23 } 24 25 AddressBook::~AddressBook() 26 { 27 }
2.3 main.cpp
#include "addressbook.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); AddressBook w; w.show(); return a.exec(); }
3 佈局管理
Qt 中有三個佈局管理類,可以處理視窗部件的位置擺放,分別是 QHBoxLayout、QVBoxLayout 和 QGridLayout
其中 QGridLayout 可以通過指定視窗部件的行數和列數,來控制各個視窗部件的佈局,如下所示:
按照上面的行數和列號,在 AddressBook 的構造函數中,添加如下代碼:
16 QGridLayout *layout = new QGridLayout; 17 18 layout->addWidget(nameLabel, 0, 0); 19 layout->addWidget(m_nameLine, 0, 1); 20 layout->addWidget(addressLabel, 1, 0, Qt::AlignTop); 21 layout->addWidget(m_addrText, 1, 1);
21 setLayout(layout);
Qt 中 setLayout() 函數的原型為:
void QWidget::setLayout(QLayout *layout);
具體描述為 “Sets the layout manager for this widget to layout. The QWidget will take ownership of layout.”
通過 #21,可以將 AddressBook 的佈局管理器設置為 layout, 同時 AddressBook 獲得了 layout 的擁有權。