淺析 Qt 布局系統(tǒng)

Qt 布局系統(tǒng)介紹

布局系統(tǒng)

作為一名 iOS 開發(fā)人員, 見證著 iOS 布局系統(tǒng)的不斷完善, 從絕對布局, Autoresizing 到 Autolayout. 使得開發(fā)人員的工作效率越來越高, 項目界面的可讀性和易維護性越來越強. 如今 IDE 中的可視化界面工具已經(jīng)非常強大, 許多網(wǎng)友"戲稱" iOS 開發(fā)者為"UI 拖拽師", 可見, iOS 開發(fā)中界面布局系統(tǒng)的高效. 所以, 優(yōu)秀的布局系統(tǒng)的使命在于讓開發(fā)者花更少的時間來完成更易維護的界面.

同樣的, 在 Qt 中, 系統(tǒng)提供了強大的排版機制來為窗口中的視圖進行布局排版, 經(jīng)過了對 Qt 布局一個初步的探索, 不得不對 Qt 布局系統(tǒng)的簡潔高效而又功能強大表示贊嘆.

布局系統(tǒng)的功能

在 Qt 中, 布局系統(tǒng)可以完成

  • 定位子控件
  • 得知窗體默認大小
  • 得知窗體最小大小
  • 窗體大小變化時進行布局排版
  • 內(nèi)容改變(字體大小文本等, 隱藏或顯示, 移除)時進行布局排版

布局系統(tǒng)的結(jié)構(gòu)

Qt 提供了 QLayout 類及其子類來為界面進行排版布局. 結(jié)構(gòu)如下圖:

布局系統(tǒng)結(jié)構(gòu)圖

QLayout 是布局系統(tǒng)中的抽象基類, 繼承自 QObject 和 QLayoutItem, 其中四個子類分別為

  • QBoxLayout(箱式布局)
  • QFormLayout(表單布局)
  • QGridLayout(網(wǎng)格布局)
  • QStackedLayout(棧布局)

在真實使用場景中, 往往需要通過多種布局的相結(jié)合來完成界面的設(shè)計, 接下來將分別介紹四中布局.

QBoxLayout 箱式布局

箱式布局提供了兩個子類分別處理水平(QHBoxLayout)和垂直(QVBoxLayout)兩個方向的排版, 可以使視圖排成一行或者一列來顯示. 簡單說, 就是可以讓控件進行排排站, 比如在我們的 AlphaBox 中, 頂部的頭像, 姓名, 和刷新按鈕排成了一排, 這就是水平箱式布局:

什么叫排排站

你以為我要講一下這個東西如何實現(xiàn)? NO, 我偏偏要以垂直箱式布局為例, 用一個最簡單的例子來介紹箱式布局的使用, 首先創(chuàng)建一個基于 QWidget 的界面, 添加我們需要使用的頭文件:

#include <QVBoxLayout>
#include <QPushButton>

并在構(gòu)造函數(shù)中添加如下代碼

    //  添加兩個按鈕
    QPushButton *okBtn  = new QPushButton;
    okBtn ->setText(tr("我在上面, 我最牛"));
    QPushButton *celBtn = new QPushButton;
    celBtn->setText(tr("我在下面, 我不服"));

    //  創(chuàng)建一個垂直箱式布局, 將兩個按鈕扔進去
    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(okBtn);
    layout->addWidget(celBtn);

    //  設(shè)置界面的布局為垂直箱式布局
    setLayout(layout);

運行看一下效果, 什么? 這就可以運行了? 坐標呢? 尺寸呢? 是的, 沒看錯...點擊運行:

最簡單的箱式布局

兩個按鈕已經(jīng)一上一下, 乖乖的在垂直方向自己站好了位置, 就是這么強大, 就是這么省心.

QFormLayout 表單布局

強大的 AlphaBox 是很外向的, 可以很輕松的將你的資料分享給其他用戶, 當我們分享的時候, 會有這樣一個界面:

在 AlphaBox 中共享資料

看到這個界面, 聰明的你可能會說, 這很簡單啊, 好幾個水平箱式布局就可以實現(xiàn), 可是, 更聰明的 Qt 提供了更高效的方式幫助你完成這樣一個界面, 那就是 QFormLayout.

在我所學習 Qt 所使用的書籍中, 將 QFormLayout 翻譯為窗體布局, 我個人認為, 將其翻譯為表單布局更為貼切, 因為 QFormLayout 的強大之處正是可以使用最快的速度完成一個用戶輸入的表單界面的搭建.

那么, 讓我們揭開 AlphaBox 的神秘面紗, 看看這樣一個界面是怎么實現(xiàn)的.

首先, 拖拽一個 Form Layout 到 Widget 中.

添加表單布局

雙擊之后即可為表單增加一行.

為表單增加一行

相信大家看到這張圖時, 就已經(jīng)能理解到表單布局是如何使用的, 提供了標簽作為用戶輸入內(nèi)容的指引, 提供字段類型作為用戶輸入的控件, 作為 iOS 開發(fā)者, 深知這樣一個界面的搭建所需要的繁雜的工作量. 當我第一次打開這個界面時, 被這樣創(chuàng)建界面的方式所驚呆了.

  1. 按照圖中, 創(chuàng)建表單的第一行, 共享給哪個用戶的輸入框, 可以為輸入框填寫占位文字.
  2. 雙擊 Form Layout 創(chuàng)建字段類型為 QComboBox (多選框)的一行. 填寫允許的權(quán)限內(nèi)容.
  3. 設(shè)置整個 Widget 布局為垂直箱式布局
  4. 在 Form Layout 下拖拽過去一個 Horizontal Layout(水平箱式布局)
  5. 在箱式布局中添加 Horizontal Spacer (水平占位) 后拖拽兩個 Push Button 完成界面布局
共享界面的布局

快不快? 快不快! 快不快!!!

同樣的, 如果是使用純代碼表單布局的話可以使用addRow()的方法來添加一行.

QGridLayout 網(wǎng)格布局

強大的 AlphaBox 是這樣的

事實上, 強大的 AlphaBox 是這樣的, 我們可以共享給多個用戶, 而且, 下方會有一個列表, 展示共享的用戶以及權(quán)限列表. 這時, 表單布局就沒辦法滿足我們, 只好另求新歡 QGridLayout - 網(wǎng)格布局.

網(wǎng)格布局顧名思義, 可以將界面分割成行列來進行布局管理, 在每個單元格中來擺放控件. 所以 AlphaBox 分享的界面使用了一個 兩行三列 的網(wǎng)格布局來實現(xiàn)的.

QGridLayout - 網(wǎng)格布局

當然, 更更復雜的界面, 用 Qt 布局的效率也是非常高的, 我做了一個外鏈分享的布局 Demo, 可以將內(nèi)部資料生成一個下載鏈接共享給任何人去下載.

外鏈分享界面

這個界面中, 我在Tab之內(nèi)使用了網(wǎng)格布局, 布局如圖:

外鏈分享界面布局

從圖中可以看出, 網(wǎng)格布局像是在操作一個 Excel 一樣簡單, 布局單元格, 合并單元格, 等等.

在這個界面中, 更靈活的使用了 QLayout 的屬性來完成了界面布局排版.

同樣的, 在代碼中, 可以使用如下等的 Api 來為網(wǎng)格視圖添加一個從幾行幾列開始占據(jù)幾行幾列的控件:

void addWidget(QWidget *, int row, int column, int rowSpan, int columnSpan)

QStackedLayout 棧布局

如在 AlphaBox 中, 我們可以通過云端文件瀏覽器直接查看和操作云端文件, 在加載的過程中, 會有一個轉(zhuǎn)菊花的界面.

在轉(zhuǎn)菊花的 AlphaBox

加載失敗時的錯誤提示:

菊花轉(zhuǎn)失敗了的 AlphaBox

以及加載成功時:

通常情況下我們能看見的 AlphaBox

通常應用的界面會根據(jù)不同的狀態(tài)有不同的內(nèi)容, 這時就可以使用 QStackedLayout 棧布局, 棧布局提供了一個頁面的棧, 每個頁面有完全獨立的界面布局. 可以非常清晰的對不同狀態(tài)下的界面進行布局管理.

在 Qt 的可視化布局工具中, 通過 Stacked Widget 來完成界面的棧布局

Stacked Widget

通過右鍵來進行頁面的插入移除和排序等操作.

布局相關(guān)屬性

控件大小

對于控件大小, 最重要的兩個屬性是 sizeHintminimumSizeHint , 這是 QWidget 的屬性, 是只讀屬性. 其中, sizeHint 屬性為控件的建議大小, 對于不同的控件, 有不同的建議大小, 同理 minimumSizeHint 為建議的最小大小. 知道了這兩個屬性才可以理解布局中控件的大小是如何控制的. 如果手動設(shè)置了最小尺寸的話(minimumSize), minimumSizeHint 是會被忽略的.

大小策略

大小策略屬性 sizePolicy 也是 QWidget 類的屬性, 這個屬性在水平和垂直兩個方向分別起作用, 控制著控件大小變化的策略.

在可視化工具中可以直觀的看到幾種大小策略, 以垂直為例如圖:

大小策略
  • QSizePolicy::Fixed 只能使用 sizeHint 的大小, 任何操作都不會改變控件大小
  • QSizePolicy::Minimum sizeHint 為最小大小, 控件可以被拉伸
  • QSizePolicy::Maximum sizeHint 為最大大小, 控件可以被壓縮
  • QSizePolicy::Preferred sizeHint 為建議大小, 控件既可以被壓縮也可以被拉伸
  • QSizePolicy::MinimumExpanding sizeHint 為最小大小, 不能被壓縮, 被拉伸的優(yōu)先級更高
  • QSizePolicy::Expanding sizeHint 為建議大小, 可以被壓縮, 被拉伸的優(yōu)先級更高
  • QSizePolicy::Ignored sizeHint 的值將會被忽略

在網(wǎng)上或者書中, 關(guān)于這些策略的說明會有很多, 可是如果不是真的自己嘗試一下, 很難很好的理解在復雜布局的情況下, 大小策略是如何控制布局的, 尤其是 MinimumExpanding, Expanding, Ignored 這三種.

對于優(yōu)先級的概念大家肯定不會陌生, 這里我認為優(yōu)先級來解釋這些策略是更清晰易懂的.

關(guān)于拉伸 Expanding , MinimumExpanding 優(yōu)先級相同, 同時要比 PreferredIgnored 拉伸優(yōu)先級更高, PreferredIgnored 相同.

換句話說, 如果兩個控件在一個水平箱式布局中管理, 其中一個水平大小策略為 Preferred 另一個為 Expanding 或者 MinimumExpanding 如果水平拉伸窗體, 則 Preferred的控件大小不會改變, Expanding 或者是 MinimumExpanding 會被拉伸.

同理, 如果兩個控件水平大小策略一個為Expanding, 一個是MinimumExpanding, 這時拉伸窗體, 則兩個控件均會拉伸.

多說一句, 如果兩個控件都為 Fixed 無法拉伸時, 控件間的間隙會被拉伸.

關(guān)于壓縮 如果達到了minimumSizeHint是不會被繼續(xù)壓縮了, 但是Ignored是會忽略 sizeHintminimumSizeHint 的屬性的, 所以是會繼續(xù)被壓縮的.

伸縮性

在 QLayout 中提供了一個和控件大小策略相關(guān)的屬性, layoutStretch 布局伸縮性, 這個值是一個比例, 在可視化工具中可以更直觀的看到這個值的設(shè)置, 如果在布局中有三個控件, 則是三個控件的占比, 用逗號分隔, 如: 1, 1, 1 .

伸縮性就好理解一些了, 但是要注意的是, 只有會被壓縮或者拉伸的控件才會受該屬性值影響(如 Fixed 是不會受該屬性影響)

還有一點非常重要的是設(shè)置了伸縮性的比值(如果都為0, 則表示不設(shè)置) 剛剛提到的大小策略的優(yōu)先級將會被忽略, 還是剛剛的例子, 如果兩個控件在一個水平箱式布局中管理, 其中一個水平大小策略為 Preferred 另一個為 Expanding, 設(shè)置水平箱式布局的 layoutStretch2, 1 則拉伸時, 并不會像剛剛所說, 只有 Expanding 的控件會被拉伸, 而是都會被拉伸, 按照一個 2 : 1 的拉伸比例拉伸.

窗體大小約束策略

最后想介紹一下 QLayout 的 layoutSizeConstraint 屬性, 用來約束窗體大小, 只影響窗體, 所以該屬性只對最頂級的 QLayout 起作用.

layoutSizeConstraint

關(guān)于這幾個屬性同樣的, 簡單的介紹網(wǎng)上和書上會有很多, 如果不嘗試一下, 淺顯的字面意思無法理解這幾個屬性的作用. 根據(jù)我的嘗試, 總結(jié)如下:

  • QLayout::SetDefaultConstraint 窗體最小值被設(shè)置為 minimumSize 值無法再縮小, 如果 QLayout 內(nèi)控件有更大的minimumSize, 則會取更大的minimumSize.
  • QLayout::SetNoConstraint 窗體沒有約束策略
  • QLayout::SetFixedSize,窗體大小被設(shè)定為 sizeHint 的大小,無法改變
  • QLayout::SetMinimumSize 窗體最小為 minimumSize 無法再縮小, 如果 QLayout 內(nèi)控件有更小的minimumSize, 則會取更小的minimumSize., 總結(jié)就是的話, 和 Default 不同的地方就是盡可能的小.
  • QLayout::SetMaxmumSize 同理, 窗體最大值為 maxmumSize , 無法再放大
  • QLayout::SetMinAndMaxSize 窗體最小為 minimumSize 無法再縮小, 窗體最大值為 maxmumSize , 無法再放大

其他屬性

關(guān)于表單布局和網(wǎng)格布局還有其他的屬性約束單元格的一些策略, 如 layoutFieldGrowthPolicy 控件的變化方式策略等等有興趣可以查看官方文檔, 更多的屬性間隙, 間隔, 對其方式等等都比較好理解了, 在此也不贅述了.

官方文檔

Qt 官方文檔點這里

參考

<< Qt Creator 快速入門>> 第三版, 霍亞飛著

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容