我們現在來看看FindDialog 類別的實作細節finddialog.cpp這個檔案。
1: #include <QtGui>
2: #include "finddialog.h"
首先,我們引入了<QtGui>,在這個標頭裡定義了不同的QT GUI 類別。QT包含很多種模組,每一種模組放置在他所屬的函式庫(library)‧當中最重要的模組是QtCore、QtGui、QtNetwork、QtOpenGL、QtScript、QtSql、QtSvg、QtXml。其中在 <QtGui> 標頭檔包含了QtCore 和QtGui部份模組中所有的類別定義‧引入這個標頭檔可以 幫助我們引入各種類別,又保持各自獨立。
在finddialog.h,並非引入<QDialog> 和使用之前給QCheckBox、QLabel、 QLineEdit和 QPushButton, 的宣告,我們可以簡單的只引入<QtGui>。然而,從其他的標頭檔去引入一個大標頭檔並不是一個很好的寫法,特別是對大型的應用程式來說。
3: FindDialog::FindDialog(QWidget *parent)
4: : QDialog(parent)
5: {
6: label = new QLabel(tr("Find &what:"));
7: lineEdit = new QLineEdit;
8: label->setBuddy(lineEdit);
9: caseCheckBox = new QCheckBox(tr("Match &case"));
10: backwardCheckBox = new QCheckBox(tr("Search &backward"));
11: findButton = new QPushButton(tr("&Find"));
12: findButton->setDefault(true);
13: findButton->setEnabled(false);
14: closeButton = new QPushButton(tr("Close"));
第四行, 我們傳送了父參數(parent parameter)給基本的類別建構子(class constructor),然後我們可以創造一個子部件(Child Widget)。tr()函式將他所呼叫的字串標記起來並轉換成其他的語言。這個函式被宣告在QObject裡,而且每個子類別都包含了Q_OBJECT 巨集。用tr()去表達想讓使用者看見的字串是一個好習慣,即使你並沒有想要將應用程式的語言轉換成其他語言的計畫。在第18章,我們會介紹如何將QT應用程式轉換成其他語言。
在這個字串裡面,我們使用了&符號去表示快捷鍵(Shortcut keys)。舉例來說,第十一行建立了一個搜尋紐(Find button),可以讓使用者藉由在平台上輸入Alt+F快捷鍵去觸動。
&符號也可以被用來控制,在第六行我們建立了一個標籤使用快捷鍵(Alt+W),在第八行我們設置了標籤好友(Label’s buddy)為單行編輯器(line editor)。Buddy是一種用來接受快捷鍵訊息的部件(Widget),所以當使用者按下了Alt+W (標籤的快捷鍵),便會跳到所謂的標籤好友單行編輯器(label’s buddy)上。
在第12行,我們藉由呼叫setDefault(true)建了一個搜尋按鈕作為對話方塊預設按鈕,這個預設按鈕會在使用者按下Enter時被觸動。第13行,我們無法使用搜尋鈕,當這個部件(Widget)停用的時候,他通常會顯示為灰色,並且不會回應使用者的互動。
15 connect(lineEdit, SIGNAL(textChanged(const QString &)),
16 this, SLOT(enableFindButton(const QString &)));
17 connect(findButton, SIGNAL(clicked()),
18 this, SLOT(findClicked()));
19 connect(closeButton, SIGNAL(clicked()),
20 this, SLOT(close()));
在單行編輯器(line editor)出現文字改變的時候,私用訊息孔enableFindButton(const QString &) 會被呼叫。當使用者按下搜尋紐時,私用訊息孔findClicked() 也會被呼叫。當使用者按下Close後,這個對話方塊會自己關閉。close() 訊息孔(slot )繼承了QWidget,而且預設行為是將這個部件(Widget)隱藏起來。我們等下將會看看關於enableFindButton() 和findClicked() slots 的程式碼。
因為QObject 是FindDialog的祖先之一,我們可以省略QObject::這些字眼在connect()的前面。
21: QHBoxLayout *topLeftLayout = new QHBoxLayout;
22: topLeftLayout->addWidget(label);
23: topLeftLayout->addWidget(lineEdit);
24: QVBoxLayout *leftLayout = new QVBoxLayout;
25: leftLayout->addLayout(topLeftLayout);
26: leftLayout->addWidget(caseCheckBox);
27: leftLayout->addWidget(backwardCheckBox);
28: QVBoxLayout *rightLayout = new QVBoxLayout;
29: rightLayout->addWidget(findButton);
30: rightLayout->addWidget(closeButton);
31: rightLayout->addStretch();
32: QHBoxLayout *mainLayout = new QHBoxLayout;
33: mainLayout->addLayout(leftLayout);
34: mainLayout->addLayout(rightLayout);
35: setLayout(mainLayout);
接下來,我們使用排版管理去排版子部件(Child Widget)。排版可以包含所有的部件和其他的排版。巢狀的QHBoxLayouts、QVBoxLayouts和 QGridLayouts 將會被各自結合在一起,這可以是個非常複雜的對話方塊(Dialog)。
在Find對話方塊中,我們使用了 QHBoxLayouts 和 QVBoxLayouts的編排方式,如圖2.2所示。
外層的布局(layout)是最主要的,撰寫在第35行代表的是整個對話方塊的區域。
其他的三組布局(layout)屬於子布局,在按鈕的右邊的間隔區域可以用來拉長對話方塊。使用Find與Close按鈕底下的空間,可以用來確保這些按鈕有貼近頂部。
Figure 2.2. The Find dialog’s layouts
有趣的是,排版管理類別並不是一個部件(Widget)。他並非來自QLayout,反而是由QObject所提供。如圖所示,部件(widgets)用實線表示,而佈局(Layouts)用虛線表示,用以區分兩者的不同。 對一個正在執行的應用程式來說,佈局(Layout)是看不見的。
當子佈局被加入到父佈局裡(第25、33和34行)時,子佈局會自動的被表示在父佈局之下。當對話方塊已經被設置好了主要佈局(行35),主要佈局將會成為對話方塊每個子佈局溝通方式。父子結構如圖2.3所描述。
36: setWindowTitle(tr("Find"));
37: setFixedHeight(sizeHint().height());
38: }
Figure 2.3. The Find dialog’s parent–child relationships
最後,我們將設置好對話方塊的標題(title),並且讓這個視窗有固定的高度,因為目前還沒有任何部件(Widget)可以有效的利用垂直空間。QWidget::sizeHint() 函式將會回傳一個部件(widget)理想的尺寸。
這樣就完成了FindDialog的建構子(Constructor)。因為我們使用new去建造這個部件的對話方塊和佈局,這樣看起來,我們需要寫一些解構子(Destructor)來刪除我們所建造的部件與佈局。但是這不是很必要,因為當父部件被關閉時,QT會自動刪除掉子物件(child objects );而且所有的子部件與佈局都是FindDialog的子孫。
—
本文是因為蕾咪愛睏會無法專心,所以乾脆邊讀邊做個小翻譯記錄,幫助理解,原文出自
C++ GUI Programming with Qt 4, Second Edition by Jasmin Blanchette; Mark Summerfield