淺析 Qt 佈局系統

Qt 佈局系統介紹

佈局系統

做爲一名 iOS 開發人員, 見證着 iOS 佈局系統的不斷完善, 從絕對佈局, Autoresizing 到 Autolayout. 使得開發人員的工做效率愈來愈高, 項目界面的可讀性和易維護性愈來愈強. 現在 IDE 中的可視化界面工具已經很是強大, 許多網友"戲稱" iOS 開發者爲"UI 拖拽師", 可見, iOS 開發中界面佈局系統的高效. 因此, 優秀的佈局系統的使命在於讓開發者花更少的時間來完成更易維護的界面.瀏覽器

一樣的, 在 Qt 中, 系統提供了強大的排版機制來爲窗口中的視圖進行佈局排版, 通過了對 Qt 佈局一個初步的探索, 不得不對 Qt 佈局系統的簡潔高效而又功能強大表示讚歎.函數

佈局系統的功能

在 Qt 中, 佈局系統能夠完成工具

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

佈局系統的結構

Qt 提供了 QLayout 類及其子類來爲界面進行排版佈局. 結構以下圖:佈局

佈局系統結構圖

QLayout 是佈局系統中的抽象基類, 繼承自 QObject 和 QLayoutItem, 其中四個子類分別爲學習

  • QBoxLayout(箱式佈局)
  • QFormLayout(表單佈局)
  • QGridLayout(網格佈局)
  • QStackedLayout(棧佈局)

在真實使用場景中, 每每須要經過多種佈局的相結合來完成界面的設計, 接下來將分別介紹四中佈局.字體

QBoxLayout 箱式佈局

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

什麼叫排排站

你覺得我要講一下這個東西如何實現? NO, 我恰恰要以垂直箱式佈局爲例, 用一個最簡單的例子來介紹箱式佈局的使用, 首先建立一個基於 QWidget 的界面, 添加咱們須要使用的頭文件:翻譯

#include <QVBoxLayout>
#include <QPushButton>
複製代碼

並在構造函數中添加以下代碼設計

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

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

    // 設置界面的佈局爲垂直箱式佈局
    setLayout(layout);
複製代碼

運行看一下效果, 什麼? 這就能夠運行了? 座標呢? 尺寸呢? 是的, 沒看錯...點擊運行:3d

最簡單的箱式佈局

兩個按鈕已經一上一下, 乖乖的在垂直方向本身站好了位置, 就是這麼強大, 就是這麼省心.

QFormLayout 表單佈局

強大的 AlphaBox 是很外向的, 能夠很輕鬆的將你的資料分享給其餘用戶, 當咱們分享的時候, 會有這樣一個界面:

在 AlphaBox 中共享資料

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

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

那麼, 讓咱們揭開 AlphaBox 的神祕面紗, 看看這樣一個界面是怎麼實現的.

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

添加表單佈局

雙擊以後便可爲表單增長一行.

爲表單增長一行

相信你們看到這張圖時, 就已經能理解到表單佈局是如何使用的, 提供了標籤做爲用戶輸入內容的指引, 提供字段類型做爲用戶輸入的控件, 做爲 iOS 開發者, 深知這樣一個界面的搭建所須要的繁雜的工做量. 當我第一次打開這個界面時, 被這樣建立界面的方式所驚呆了.

  1. 按照圖中, 建立表單的第一行, 共享給哪一個用戶的輸入框, 能夠爲輸入框填寫佔位文字.
  2. 雙擊 Form Layout 建立字段類型爲 QComboBox (多選框)的一行. 填寫容許的權限內容.
  3. 設置整個 Widget 佈局爲垂直箱式佈局
  4. 在 Form Layout 下拖拽過去一個 Horizontal Layout(水平箱式佈局)
  5. 在箱式佈局中添加 Horizontal Spacer (水平佔位) 後拖拽兩個 Push Button 完成界面佈局

共享界面的佈局

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

一樣的, 若是是使用純代碼表單佈局的話可使用addRow()的方法來添加一行.

QGridLayout 網格佈局

強大的 AlphaBox 是這樣的

事實上, 強大的 AlphaBox 是這樣的, 咱們能夠共享給多個用戶, 並且, 下方會有一個列表, 展現共享的用戶以及權限列表. 這時, 表單佈局就沒辦法知足咱們, 只好另求新歡 QGridLayout - 網格佈局.

網格佈局顧名思義, 能夠將界面分割成行列來進行佈局管理, 在每一個單元格中來擺放控件. 因此 AlphaBox 分享的界面使用了一個 兩行三列 的網格佈局來實現的.

QGridLayout - 網格佈局

固然, 更更復雜的界面, 用 Qt 佈局的效率也是很是高的, 我作了一個外鏈分享的佈局 Demo, 能夠將內部資料生成一個下載連接共享給任何人去下載.

外鏈分享界面

這個界面中, 我在Tab以內使用了網格佈局, 佈局如圖:

外鏈分享界面佈局

從圖中能夠看出, 網格佈局像是在操做一個 Excel 同樣簡單, 佈局單元格, 合併單元格, 等等.

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

一樣的, 在代碼中, 可使用以下等的 Api 來爲網格視圖添加一個從幾行幾列開始佔據幾行幾列的控件:

void addWidget(QWidget *, int row, int column, int rowSpan, int columnSpan) 複製代碼

QStackedLayout 棧佈局

如在 AlphaBox 中, 咱們能夠經過雲端文件瀏覽器直接查看和操做雲端文件, 在加載的過程當中, 會有一個轉菊花的界面.

在轉菊花的 AlphaBox

加載失敗時的錯誤提示:

菊花轉失敗了的 AlphaBox

以及加載成功時:

一般狀況下咱們能看見的 AlphaBox

一般應用的界面會根據不一樣的狀態有不一樣的內容, 這時就可使用 QStackedLayout 棧佈局, 棧佈局提供了一個頁面的棧, 每一個頁面有徹底獨立的界面佈局. 能夠很是清晰的對不一樣狀態下的界面進行佈局管理.

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

Stacked Widget

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

佈局相關屬性

控件大小

對於控件大小, 最重要的兩個屬性是 sizeHintminimumSizeHint , 這是 QWidget 的屬性, 是隻讀屬性. 其中, sizeHint 屬性爲控件的建議大小, 對於不一樣的控件, 有不一樣的建議大小, 同理 minimumSizeHint 爲建議的最小大小. 知道了這兩個屬性才能夠理解佈局中控件的大小是如何控制的. 若是手動設置了最小尺寸的話(minimumSize), minimumSizeHint 是會被忽略的.

大小策略

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

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

大小策略

  • QSizePolicy::Fixed 只能使用 sizeHint 的大小, 任何操做都不會改變控件大小
  • QSizePolicy::Minimum sizeHint 爲最小大小, 控件能夠被拉伸
  • QSizePolicy::Maximum sizeHint 爲最大大小, 控件能夠被壓縮
  • QSizePolicy::Preferred sizeHint 爲建議大小, 控件既能夠被壓縮也能夠被拉伸
  • QSizePolicy::MinimumExpanding sizeHint 爲最小大小, 不能被壓縮, 被拉伸的優先級更高
  • QSizePolicy::Expanding sizeHint 爲建議大小, 能夠被壓縮, 被拉伸的優先級更高
  • QSizePolicy::Ignored sizeHint 的值將會被忽略

在網上或者書中, 關於這些策略的說明會有不少, 但是若是不是真的本身嘗試一下, 很難很好的理解在複雜佈局的狀況下, 大小策略是如何控制佈局的, 尤爲是 MinimumExpanding, Expanding, Ignored 這三種.

對於優先級的概念你們確定不會陌生, 這裏我認爲優先級來解釋這些策略是更清晰易懂的.

關於拉伸 Expanding , MinimumExpanding 優先級相同, 同時要比 PreferredIgnored 拉伸優先級更高, PreferredIgnored 相同.

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

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

多說一句, 若是兩個控件都爲 Fixed 沒法拉伸時, 控件間的間隙會被拉伸.

關於壓縮 若是達到了minimumSizeHint是不會被繼續壓縮了, 可是Ignored是會忽略 sizeHintminimumSizeHint 的屬性的, 因此是會繼續被壓縮的.

伸縮性

在 QLayout 中提供了一個和控件大小策略相關的屬性, layoutStretch 佈局伸縮性, 這個值是一個比例, 在可視化工具中能夠更直觀的看到這個值的設置, 若是在佈局中有三個控件, 則是三個控件的佔比, 用逗號分隔, 如: 1, 1, 1 .

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

還有一點很是重要的是設置了伸縮性的比值(若是都爲0, 則表示不設置) 剛剛提到的大小策略的優先級將會被忽略, 仍是剛剛的例子, 若是兩個控件在一個水平箱式佈局中管理, 其中一個水平大小策略爲 Preferred 另外一個爲 Expanding, 設置水平箱式佈局的 layoutStretch2, 1 則拉伸時, 並不會像剛剛所說, 只有 Expanding 的控件會被拉伸, 而是都會被拉伸, 按照一個 2 : 1 的拉伸比例拉伸.

窗體大小約束策略

最後想介紹一下 QLayout 的 layoutSizeConstraint 屬性, 用來約束窗體大小, 隻影響窗體, 因此該屬性只對最頂級的 QLayout 起做用.

layoutSizeConstraint

關於這幾個屬性一樣的, 簡單的介紹網上和書上會有不少, 若是不嘗試一下, 淺顯的字面意思沒法理解這幾個屬性的做用. 根據個人嘗試, 總結以下:

  • QLayout::SetDefaultConstraint 窗體最小值被設置爲 minimumSize 值沒法再縮小, 若是 QLayout 內控件有更大的minimumSize, 則會取更大的minimumSize.
  • QLayout::SetNoConstraint 窗體沒有約束策略
  • QLayout::SetFixedSize,窗體大小被設定爲 sizeHint 的大小,沒法改變
  • QLayout::SetMinimumSize 窗體最小爲 minimumSize 沒法再縮小, 若是 QLayout 內控件有更小的minimumSize, 則會取更小的minimumSize., 總結就是的話, 和 Default 不一樣的地方就是儘量的小.
  • QLayout::SetMaxmumSize 同理, 窗體最大值爲 maxmumSize , 沒法再放大
  • QLayout::SetMinAndMaxSize 窗體最小爲 minimumSize 沒法再縮小, 窗體最大值爲 maxmumSize , 沒法再放大

其餘屬性

關於表單佈局和網格佈局還有其餘的屬性約束單元格的一些策略, 如 layoutFieldGrowthPolicy 控件的變化方式策略等等有興趣能夠查看官方文檔, 更多的屬性間隙, 間隔, 對其方式等等都比較好理解了, 在此也不贅述了.

官方文檔

Qt 官方文檔點這裏

參考

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

相關文章
相關標籤/搜索