第3章講述了一些窗口部件,當時往界面上拖放部件時都是隨意放置的,這對於學習部件的使用沒有太大的影響,但是,對於一個完善的軟件,布局管理卻是必不可少的。 無論是想要界面中部件有一個很整齊的排列,還是想要界面能適應窗口的大小變化,都 要進行布局管理。Qt中主要提供了 QLayout 類及其子類來作為布局管理器,它們可以實現常用的布局管理功能,QLayout及其子類的關系如下圖所示。
一、布局管理系統
Qt的布局管理系統提供了簡單而強大的機制來自動排列一個窗口中的部件,確保它們有效地使用空間。Qt包含了一組布局管理類來描述怎樣在應用程序的用戶界面中對部件進行布局,比如QLayout的幾個子類,這里將它們稱作布局管理器。所有 QWidget 類的子類的實例(對象)都可以使用布局管理器來管理位於其中的子部件, QWidget::setLayOut()函數可以在一個部件上應用布局管理器。一旦一個部件上設置了布局管理器,那么它會完成以下幾種任務:
- 定位子部件;
- 感知窗口默認大小;
- 感知窗口最小大小;
- 改變大小處理;
- 當內容改變時自動更新:
- 字體大小,文本或子部件的其他內容隨之改變;
- 隱藏或顯示子部件;
- 移除一個子部件。
1.1 布局管理器
QLayout類是布局管理器的基類,是一個抽象基類,繼承自 QObject 和 QLayoutItem 類,而 QLayoutltem 類提供了 一個供 QLayout 操作的抽象項目。QLayout 和 QLayoutItem 都是在設計自己的布局管理器時才使用的,一般只需要使用 QLayout 的幾個子類就可以了,分別是 QBoxLayout(基本布局管理器)、QGridLayout(柵格布局管理器)、QFormLayout(表單布局管理器)和QStackedLayout(棧布局管理器)。
下面先來看一個例子。新建 QtGui 應用,項目名稱為 myLayout,類名 MyWidget,基類選擇 QWidget。然后打開 mywidget. ui 文件,在設計模式中向界面拖人一個字體選擇框 Font Combo Box 和文本編輯器 Text Edit 部件。然后單擊主界面並按下 Ctrl + L 快捷鍵,或者單擊設計器上邊欄中的“垂直布局”圖標來對主界面進行垂直布局管理。也可以在主界面上右擊,在彈出的菜單中選擇“布局→垂直布局”,這樣便設置了頂層布局管理器,可以看到兩個部件已經填滿了整個界面。這時運行程序,然后拉伸窗口,兩個部件會隨着窗口的大小變化而變化,如下圖所示。這就是布局管理器在起作用。
1. 基本布局管理器(QBoxLayout)
基本布局管理器 QBoxLayout 類可以使子部件在水平方向或者垂直方向排成一列,將所有的空間分成一行盒子,然后將每個部件放入一個盒子中。它有兩個子類 QHBoxLayont 水平布局管理器和 QVBoxLayout 垂直布局管理器。單擊主界面,查看它的屬性欄,最后面的部分是其使用的布局管理器的屬性,如下圖所示。
下面打破已有布局,使用代碼實現水平布局。在界面上右擊,然后在右鍵菜單中選擇“打破布局”,或者單擊設計器上邊欄中的打破布局圖標。在 mywidget. cpp 文件中 添加頭文件 #include <QHBoxLayout>,並在 MyWidget 類的構造閑數中添加如下代碼:
QHBoxLayout *layout = new QHBoxLayout; // 新建水平布局管理器
layout->addWidget(ui->fontComboBox); // 向布局管理器中添加部件
layout->addWidget(ui->textEdit);
layout->setSpacing(50); // 設置部件間的間隔
layout->setContentsMargins(0, 0, 50, 100); // 設置布局管理器到邊界的距離
// 四個參數順序是左,上,右,下
setLayout(layout); // 將這個布局設置為MyWidget類的布局
這里使用了 addWidget() 函數向布局管理器的末尾添加部件,還有一個 insertWidget() 函數可以實現向指定位置添加部件,它比前者更靈活。
2. 柵格布局管理器(QGridLayout)
柵格布局管理器QGridLaycnit類使得部件在網格中布局,它將所有的空間分隔成一些行和列,行和列的交叉處就形成了單元格,然后將部件放人一個確定的單元格中。下面編寫代碼來使用柵格布局管理器。先往界面上拖放一個 Push Button,然后在 mywidget. cpp 中添加頭文件 #include <QGridLayout>, 再使用/ * * /注釋掉上面添加的關於水平布局管理器的代碼,最后再添加如下代碼:
QGridLayout *layout = new QGridLayout;
// 添加部件,從第0行0列開始,占據1行2列
layout->addWidget(ui->fontComboBox, 0, 0, 1, 2);
// 添加部件,從第0行2列開始,占據1行1列
layout->addWidget(ui->pushButton, 0, 2, 1, 1);
// 添加部件,從第1行0列開始,占據1行3列
layout->addWidget(ui->textEdit, 1, 0, 1, 3);
setLayout(layout);
這里主要是設置了部件在柵格布局管理器中的位罝,將 fontComBox 部件設置為占據1行2列,而 pushBimon 部件占據1行1列,這主要是為了將 fontComBox 部件和 pushBuuon 部件的長度設置為2: 1。而這樣一來,textEdit部件要想占滿剩下的空間,就要使它的跨度為3列。
這里需要說明,當部件加入到一個布局管理器中,然后這個布局管理器再放到一個窗口部件上時,這個布局管理器以及它包含的所有部件都會把該窗口部件自動重新定義為自己的父對象(parent),所以在創建布局管理器和其中的部件時不用指定父部件。
3. 表單布局管理器(QFormLayout)
表單布局管理器 QFormLayout 類用來管理表格的輸入部件及其相關的標簽,將它的子部件分為兩列,左邊是一些標簽,右邊是一些輸入部件,比如行編輯器或者數字選擇框等。其實如果只是起到這樣的布局作用,那么用QGridLayout就完全可以做到 了,之所以添加QFormLayout類,是因為它有獨特的功能。下面看一個例子。
先將前面在MyWidget類的構造函數中自己添加的代碼全部注釋掉,然后進入設計模式,這里使用另外一種方法來使用布局管理器。從部件欄中找到 Form Layout,將其拖入到界面上,然后雙擊或者在它上面右擊,選擇“添加窗體布局行菜單。然后在彈出的“添加表單布局行”對話框中輸人標簽文字“姓名(&N):”,則下面自動填寫了“標簽名稱”、“字段類型”和“字段名稱”等,並且設置了伙伴關系。這里使用了 QLineEdit 行編輯器,當然也可以選擇其他部件。而填寫的標簽文字中(&N)要注意括號必須是英語半角的,表明它的加速鍵是Alt + N,設置伙伴關系表示當按下Alt + N時,光標會自動跳轉到標簽后面對應的行編輯器中。按下確定鍵便會在布局管理器中添加一個標簽和一個行編輯器。按照這種方法,再添加3行:性別(&S),使用 QComoBox;年齡 (&A),使用QSpinBox;郵箱(&M),使用 QLineEdit。可以按下加速鍵 Alt + N,光標就可以定位到“姓名”標簽后的行編輯器中。
上面的添加表單行是在設計器中完成的,也可以在代碼中使用 addRow() 函數來完成。表單布局管理器為設計填寫表單的窗口提供了方便的功能,其實還有一些實用的特性。表單管理器也可以像普通管理器一樣使用,但是,如果不是為了設計這樣的表單,一般會使用柵格布局管理器。
4. 綜合設計布局管理器)
前面講到了 3 種布局管理器,真正使用時很多時候是將它們綜合起來應用的。現在將前面的界面再進行設計:按下Ctrl鍵同時選中界面上的字體選擇框 fontComboBox 和按鈕 pushButton,然后按下Ctrl + H快捷鍵將它們放人一個水平布局管理器中 。然后再從部件欄中拖入一個 Vertical Spacer 垂直分隔符,它們是用來在部件間產生間隔的,將它放在表單布局管理器與水平布局管理器之間。然后單擊主界面,然后按下Ctrl + L快捷鍵,讓整個界面處於一個垂直布局管理器中。這時可以在右上角的對象列表中選擇分隔符Spacer,然后在屬性欄中設置它的高度為100。這時運行程序,效果如下圖所示。
這里綜合使用了表單布局管理器、水平布局管理器和垂直布局管理器,其中垂直布局管理器是頂級布局管理器,因為它是主界面的布局,其他兩個布局管理器都包含在它里面。如果要使用代碼來實現將一個子布局管理器放入一個父布局管理器之中,可以使用父布局管理器的 addLayout() 函數。
1.2 可擴展窗口
一個窗口可能有很多選項是擴充的,只有在必要的時候才顯示出來,這時就可以使用一個按鈕,用來隱藏或者顯示多余的內容,就是所謂的可擴展窗口。要實現可擴展窗 口,就要得力於布局管理器的特性,那就是當子部件隱藏時,布局管理器自動縮小,當子部件重新顯示時,布局管理器再次放大。下面看一個具體的例子。
依然在前面的程序中進行更改。首先將界面上 pushBimon 的顯示文本更改為“顯示可擴展窗口”,然后在其屬性欄中選中 checkable 選項。然后轉到它的 toggled(bool) 信號的槽,更改如下:
// 顯隱窗口按鈕
void MyWidget::on_pushButton_toggled(bool checked)
{
ui->textEdit->setVisible(checked); // 設置文本編輯器的顯示和隱藏
if(checked)
ui->pushButton->setText(tr("隱藏可擴展窗口"));
else
ui->pushButton->setText(tr("顯示可擴展窗口"));
}
使用按鈕的按下與否兩種狀態來設置文本編輯器是否顯示,並且相應更改按鈕的文本。為了讓文本編輯器在一開始是隱藏的,還要在MyWidget類的構造函數中添加—行代碼:
ui->textEdit->hide(); // 讓文本編輯器隱藏,也可以使用setVisible(false)函數
運行程序。可擴展窗口隱藏時效果如下圖1所示,可擴展窗口顯示時如下圖2所示。也可以參考Qt自帶的示例程序 Extension Dialog,它在 Dialogs 分類中。
1.3 分裂器
分裂器 QSplitter 類提供了一個分裂器部件,和 QBoxLayout 很相似,可以完成布局管理器的功能,但是包含在它里面的部件,默認是可以隨着分裂器的大小變化而進行相應大小變化的。比如一個按鈕放在布局管理器中,它的垂直方向默認是不會被拉伸的,但是放到分裂器中就可以被拉伸。還有一個不同就是,布局管理器是繼承自 QObject 類的,而分裂器卻是繼承自 QFrame 類,QFrame 類又是繼承自 QWidget 類,也就是說,分裂器擁有 QWidget 類的特性,它是可見的,而且可以像 QFrame —樣設置邊框。下面看一個例子。
新建Qt Gui應用,項目名稱為mySplitter,類名My Widget,基類選擇QWidget。 建好項目后打開 mywidget. ui 文 件,然后往上面拖人4個 Push Button, 同時選中這4個按鈕然后右擊,選擇 “布局—使用分裂器水平布局”菜單項, 將這4個按鈕放到一個分裂器中。將分裂器拉大點,並在屬性欄中設置其 frameShape 為 Box,frameShadow 為 Raised,line Width為5。此時運行程序,效果如下圖所示。
二、設置伙伴
講述表單布局管理器時提到了設置一個標簽和一個部件的伙伴關系。其實,伙伴 (buddy) 是在 QLabel 類中提出的一個概念。因為一個標簽經常用作一個交互式部件的說明,就像在講表單布局管理器時看到的那樣,一個 lineEdit 部件前面有一個標簽說明這個 lineEdit 的作用。為了方便定位,QLabel 提供了一個有用的機制,那就是提供了助記符來設置鍵盤焦點到對應的部件上,這個部件就叫這個 QLabel 的伙伴。其中助記符就是我們所說的加速鍵。使用英文標簽時,在字符串的一個字母前面添加“&” 符號,那么就可以指定這個標簽的加速鍵是Alt加上這個字母,而對於中文,需要在小括號中指定加速鍵字母。Qt設計器中也提供了伙伴設計模式,下面看一個例子。
新建Qt Gui應用,項目名稱為myBuddy, 類名MyWidget,基類選擇 QWidget。建好項目后打開 mywidget. ui 文件,然后往界面上拖放4個標簽 Label,再在標簽后面依次放上 PushButton、CheckBox、LineEdit和 SpinBox。然后將 PushButton 前面的標簽的文本改為“&Button:”;CheckBox 前面的標簽文本改為“C&heckBox:”; LineEdit 前面的標簽的文本改為“行編輯器(&L):”;SpinBox前面的標簽文本改為“數字選擇框(&N):”。單擊下設計器頂部欄中的“編輯伙伴圖標“進人伙伴設計模式,分別將各個標簽與它們后面的部件連起來,如下圖所示。然后按下F3回到正常編輯模式,可以看到所有的&符號都沒有了,只是在字母下面多了一 個橫杠,表示這個標簽的加速鍵就是Alt加這個字母。
現在可以運行程序,使用加速鍵測試效果。如果要在代碼中設置伙伴關系,只需要使用 QLabel 的 setBuddy() 函數就可以了。對應於本小節內容可以在幫助索引中查看 Qt Designer's Buddy Editing Mode 關鍵字。
三、設置Tab順序
對於一個應用程序,我們有時總希望使用Tab鍵來將焦點從一個部件移動到下一個部件。在設計模式,設計器提供了Tab鍵的設置功能。上面程序的設計模式中,單擊上邊欄的“編輯Tab順序(Edit Tab Order)”按鈕進人編輯Tab鍵順序模式,這時已經顯示出了各個部件的Tab鍵順序,只需要單擊這些數字即可以更改。需要說明,當程序啟動時,焦點會在Tab鍵順序為1的部件上。這里進行的設置等價於在MyWidget類的構造函數中使用如下代碼:
setTabOrder(ui->lineEdit, ui->spinBox); //lineEdit,在spinBox前面
setTabOrder(ui->spinBox, ui->pushButton); //spinBox,在pushButton前面
setTabOrder(ui->pushButton, ui->checkBox); //pushButton,在checkBox前面
關於在設計器中設置Tab鍵順序,讀者也可以在幫助索引中查看 Qt DeSigner's Tab Order Editing Mode 關鍵字。