【Qt筆記】佈局管理器

所謂 GUI 界面,歸根結底,就是一堆組件的疊加。咱們建立一個窗口,把按鈕放上面,把圖標放上面,這樣就成了一個界面。在放置時,組件的位置尤爲重要。咱們必需要指定組件放在哪裏,以便窗口可以按照咱們須要的方式進行渲染。這就涉及到組件定位的機制。Qt 提供了兩種組件定位機制:絕對定位和佈局定位。ide

 

顧名思義,絕對定位就是一種最原始的定位方法:給出這個組件的座標和長寬值。這樣,Qt 就知道該把組件放在哪裏以及如何設置組件的大小。可是這樣作帶來的一個問題是,若是用戶改變了窗口大小,好比點擊最大化按鈕或者使用鼠標拖動窗口邊緣,採用絕對定位的組件是不會有任何響應的。這也很天然,由於你並無告訴 Qt,在窗口變化時,組件是否要更新本身以及如何更新。若是你須要讓組件自動更新——這是很常見的需求,好比在最大化時,Word 總會把稿紙區放大,把工具欄拉長——就要本身編寫相應的函數來響應這些變化。或者,還有更簡單的方法:禁止用戶改變窗口大小。但這總不是長遠之計。函數

針對這種變化的需求,Qt 提供了另外的一種機制——佈局——來解決這個問題。你只要把組件放入某一種佈局,佈局由專門的佈局管理器進行管理。當須要調整大小或者位置的時候,Qt 使用對應的佈局管理器進行調整。下面來看一個例子:工具

#include <QApplication>
#include <QWidget>
#include <QSpinBox>
#include <QSlider>
#include <QHBoxLayout>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;

    w.setWindowTitle("Enter your age");

    QSpinBox *spinBox = new QSpinBox(&w);
    QSlider *slider = new QSlider(Qt::Horizontal, &w);
    spinBox->setRange(0, 120);
    slider->setRange(0, 120);
    QObject::connect(slider, &QSlider::valueChanged, spinBox, &QSpinBox::setValue);
    void (QSpinBox:: *spinBoxSignal)(int) = &QSpinBox::valueChanged;
    QObject::connect(spinBox, spinBoxSignal, slider, &QSlider::setValue);

    spinBox->setValue(35);

    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(spinBox);
    layout->addWidget(slider);
    w.setLayout(layout);

    w.show();
    return a.exec();
}

咱們能夠先來看看運行結果:佈局

當咱們拖動窗口時,能夠看到組件自動有了變化:指針

咱們在這段代碼中引入了兩個新的組件:QSpinBoxQSliderQSpinBox就是隻能輸入數字的輸入框,而且帶有上下箭頭的步進按鈕。QSlider則是帶有滑塊的滑竿。咱們能夠從上面的截圖中清楚地辨別出這兩個組件。當咱們建立了這兩個組件的實例以後,咱們使用setRange()函數設置其範圍。既然咱們的窗口標題是「Enter your age(輸入你的年齡)」,那麼把 range(範圍)設置爲 0 到 130 應該足夠了。code

有趣的部分在下面的connect()函數。咱們已經清楚connect()函數的使用,所以咱們寫出:orm

QObject::connect(slider, &QSlider::valueChanged, spinBox, &QSpinBox::setValue);

將 slider 的valueChanged()信號同 spinBox 的setValue()函數相連。這是咱們熟悉的。可是,當咱們直接寫:對象

QObject::connect(spinBox, &QSpinBox::valueChanged, slider, &QSlider::setValue);

的時候,編譯器卻會報錯:文檔

no matching function for call to 'QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))'

這是怎麼回事呢?從出錯信息能夠看出,編譯器認爲QSpinBox::valueChanged是一個 overloaded 的函數。咱們看一下QSpinBox的文檔發現,QSpinBox的確有兩個信號:get

  • void valueChanged(int)
  • void valueChanged(const QString &)

當咱們使用&QSpinBox::valueChanged取函數指針時,編譯器不知道應該取哪個函數(記住前面咱們介紹過的,通過 moc 預處理後,signal 也是一個普通的函數。)的地址,所以報錯。解決的方法很簡單,編譯器不是不能肯定哪個函數嗎?那麼咱們就顯式指定一個函數。方法就是,咱們建立一個函數指針,這個函數指針參數指定爲 int:

void (QSpinBox:: *spinBoxSignal)(int) = &QSpinBox::valueChanged;

而後咱們將這個函數指針做爲 signal,與 QSlider 的函數鏈接:

QObject::connect(spinBox, spinBoxSignal, slider, &QSlider::setValue);

這樣便避免了編譯錯誤。

仔細觀察這兩個connect()的做用,它們實際完成了一個雙向的數據綁定。固然,對於 Qt 本身的信號函數,咱們能夠比較放心地使用。可是,若是是咱們本身的信號,應當注意避免發生無限循環!

下面的代碼,咱們建立了一個QHBoxLayout對象。顯然,這就是一個佈局管理器。而後將這兩個組件都添加到這個佈局管理器,而且把該佈局管理器設置爲窗口的佈局管理器。這些代碼看起來都是瓜熟蒂落的,應該很容易明白。而且,佈局管理器很聰明地作出了正確的行爲:保持QSpinBox寬度不變,自動拉伸QSlider的寬度。

Qt 提供了幾種佈局管理器供咱們選擇:

  • QHBoxLayout:按照水平方向從左到右佈局;
  • QVBoxLayout:按照豎直方向從上到下佈局;
  • QGridLayout:在一個網格中進行佈局,相似於 HTML 的 table;
  • QFormLayout:按照表格佈局,每一行前面是一段文本,文本後面跟隨一個組件(一般是輸入框),相似 HTML 的 form;
  • QStackedLayout:層疊的佈局,容許咱們將幾個組件按照 Z 軸方向堆疊,能夠造成嚮導那種一頁一頁的效果。
相關文章
相關標籤/搜索