上一個主題中我們討論學習了Qt布局的概念及Qt基類QLayout的認識。為了更好的進行布局控制,Qt實現了常見的布局有QFormLayout,QBoxLayout,QGridLayout,QStackLayout,每個布局都有着各自的特點,在此逐個理解學習
1、QFormLayout:表單布局,顧名思義就是實現表單模式的布局。表單就是提示用戶進行交互的一種模式,其主要有兩個列組成,第一個列用於顯示信息,給予用提提示,一般叫做label域,第二個是需要用戶選擇輸入的,一般叫field域。表單就是很多由這兩項/兩列內容組成的行的布局。label與field關系就是label是關聯field的。
表單布局完全可以使用表格布局實現,是一種多行兩列的列表,但表單布局提供一種比較完善的策略,其主要有以下優點
1)可以適應不同平台外觀和感覺的一致性
2)支持一行的label和field域換行顯示,有兩種策略一種是如果輸入域過長,field換行顯示,還有一種就是不管怎么樣都換行顯示,當然默認是一行顯示兩個域就可以了
3)創造label--field對很便捷的接口,因為使用一般的布局,想要關聯label和field,創建好label和feild,並且調用label的setBuddy才能完成,但formlayout使用addRow就可以直接對應了。
表單布局的樣式可以通過幾個方面體現
1)label的樣式,是左對齊還是右對齊,可以使用setLabelAlignment進行設置
2)form的樣式,其內容的顯示方式,則可以通過setFormAlignment進行設置
3)表單一行內容的顯示,是否換行,則使用setRowWrapPolicy設置,主要值是DontWrap
Rows,即Field域永遠跟着其label;WrapLongRows,給予label足夠的空間,剩余的空間給field域,如果field域的最小空間比剩余的控件大/寬,則field會換行到下一行顯示;WrapAllRows,所有的field域不管后面剩余的空間是否夠,都自動換行。
4)filed域拉升生長策略growth policy,其主要是FieldGrowthPolicy控制,首先是FieldStayAtSizeHint 0 ,field域永遠不會超過有效的sizehint尺寸;ExpandingFieldGrow 1,field域水平尺寸拉升或者最小值超出時會占用可用空間,其他field在sizehint尺寸下不會擴大grow;AllNonFixedFieldGrow 2所有的field允許他們長grow就長grow到填充可用的空間,如果是fixed固定尺寸策略的field則不會長
表單由兩列組成,所以一本都是分為label和field域,但是有些控件占用兩行,所以對每個行其可以通過角色來訪問,如LabelRole 0,一個label控件,FieldRole 1,一個field空間,SpanningRole就是占用了label和field兩列的控件。當訪問某一個行的某個特定的空間時,如果不知道具體名,則可以通過此角色訪問。
1.1 表單布局formLayout的屬性
1)fieldGrowthPolicy :FieldGrowthPolicy ,表示feild域如何擴張延伸的方式
如果沒有任何一個field域可以延伸並且表單form重置了大小,多余的空間會根據當前的表單form aligment對齊方式分部。
fieldGrowthPolicy()獲取此屬性。setFieldGrowthPolicy(FieldGrowthPolicy)進行設置
2)formAligment : Qt::Alignment ,此屬性表示扁擔布局formlayout內容的對齊方式。使用formAlignment()獲取,setFormAlignment(Qt::Alignment)進行設置
3)horizontalSpacing :int表示每行空間之間的空間間隔。通過horizontalSpacing()獲取,setHorizontalSpacing(int)設置
4)域horizontalSpacing對應的是verticalSpacing保存了垂直放置的控件之間的間隔。verticalSpacing獲取,setVerticalSpacing設置。
5)labelAlignment:Qt::Alignment 保存標簽label水平方向的對齊alignment模式。
通過labelAlignment()獲取,setLabelAlignment進行設置。
6)rowWrapPolicy : RowWrapPolicy保存表行每行換行的方式。其可見值概述已做詳細描述
1.2 QFormLayout的接口
表單布局是以行作為基本單位的,所以需要為表單布局添加控件,一個是標簽域label,一個是輸入域field,通過addRow方法進行添加。
1)添加一行表單
添加第一個域可以是QWidget或者QString,如果是后者會自動創建一個QLabel,並且將field的QWidget設置為label的buddy。field可以是QLayout或者QWidget。
如果是占用兩行的空間,則只有一個變量,可以是QWidget或者QLayout。具體形式如下
void addRow(QWidget *label,QWidget *field):添加一行到布局末尾,使用label和field填充表單布局的對應域。
void addRow(QWidget *label,QLayout *layout):
void addRow(const QString *labelText,QWidget *widget):會把widget設置為qlabel的buddy
void addRow(const QString *labelText,QLayout *layout):
void addRow(QWidget *widget):占用兩列
void addRow(QLayout *layout):占用兩列
int count(),返回有多少個元素,在后續查找、插入和刪除的時候用。
2)查找元素
void getItemPosition(int index,int *rowPtr,ItemRole *rolePtr):找到指定位置index元素item的行數row值和角色值。如果index越界了,rowptr值被設置為-1,讓否則將值存到rowPtr和rolePtr中。
void getLayoutPosition(QLayout *layout,int *rowPtr,ItemRole *rolePtr)
找到特定的子布局layout的行號row和role(colume)。如果layout不在formLayout,那么rowPtr設置為-1,否則正確設置。
void getWidgetPosition(QWidget *w,int *rowPtr,ItemRole *rolePtr)追溯控件w在布局中的行號row和角色role。如果此布局沒有w,則rowPtr返回 -1.
QLayoutItem *itemAt(int index)返回索引為index的控件
QLayoutItem *itemAt(int row,ItemRole role)返回布局中元素在row行角色是role的控件,如果沒有則返回0
QLayoutItem * QFormLayout::takeAt ( int index )獲取索引為index的元素並刪除原來的元素
3)插入元素,與addRow對應,只是添加一個參數及int row,指定在某個行之后添加。如果row越界,則自動添加到最后一行。
setItem(int row,ItemRole role,QLayoutItem *item)將item元素設置到指定的行號row的指定角色role的位置。如果此處的位置已經被占用了,則插入失敗。
void setLayout(int row,ItemRole role,QLayout *layout)將指定的layout設置到行row角色為role的位置,根據需要擴展沒有行元素的布局。如果此單元被占用,則不會插入
void setWidget(int row,ItemRole,QWidget *widget)將指定的widget設置到行row角色為role的位置,根據需要擴展沒有行元素的布局。如果此單元被占用,則不會插入
4)QWidget *labelForField(QWidget *field)返回與field關聯的label控件
QWidget *labelForLayout(QLayout *layout)返回與field關聯的label控件
5)QSize minimumSize()獲取布局的最小尺寸,對應的有maxmumSize最大尺寸
6)rowCount()返回此布局中有多少行
7)setGeometry(QRect rect)設置布局的空間大小
8)void setSpacing(int spacing)設置垂直和水平方向的空間間隔。spacing()返回這個值,前提是horizontal和vertical空間相等
具體使用見一個簡單的輸入例子:用戶登錄信息窗口
#include<QLabel>
#include<QLineEdit>
#include<QFormLayout>
#include<QPushButton>
voidinitLayout(QWidget&w)
{
QFormLayout*mainFormLayout=newQFormLayout();
mainFormLayout->setSizeConstraint(QLayout::SetFixedSize);
mainFormLayout->setVerticalSpacing(40);
mainFormLayout->setHorizontalSpacing(10);
mainFormLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
mainFormLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
mainFormLayout->setLabelAlignment(Qt::AlignRight|Qt::AlignVCenter);
mainFormLayout->setFormAlignment(Qt::AlignCenter);
QLineEdit*nameLineEdit=newQLineEdit("enteryourname");
//nameLineEdit->setInputMask("ANNNNNnnnnnnnnnnnnnn");
nameLineEdit->setDragEnabled(true);
nameLineEdit->setAlignment(Qt::AlignCenter);
nameLineEdit->setFrame(false);
nameLineEdit->show();
QLineEdit*pwdLineEdit=newQLineEdit();
pwdLineEdit->setEchoMode(QLineEdit::Password);
pwdLineEdit->setInputMask("XXXXXX");//6-20
pwdLineEdit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
pwdLineEdit->setDragEnabled(false);
pwdLineEdit->setFrame(true);
pwdLineEdit->show();
QPushButton*buttonOk=newQPushButton("OK");
QObject::connect(buttonOk,SIGNAL(pressed()),nameLineEdit,SLOT(clear()));
QPushButton*buttonCancel=newQPushButton("Cancel");
QObject::connect(buttonCancel,SIGNAL(pressed()),nameLineEdit,SLOT(clear()));
QHBoxLayout*buttonLayout=newQHBoxLayout();
buttonLayout->setSizeConstraint(QLayout::SetFixedSize);
buttonLayout->addWidget(buttonOk);
buttonLayout->addWidget(buttonCancel);
mainFormLayout->addRow("&Name:",nameLineEdit);
mainFormLayout->addRow("&Password:",pwdLineEdit);
mainFormLayout->addRow(buttonLayout);
//mainFormLayout->addRow(buttonOk);
//mainFormLayout->addRow(buttonCancel);
w.setLayout(mainFormLayout);
}
intmain(intargc,char*argv[])
{
QApplicationa(argc,argv);
FormLayouTestw;
w.resize(1280,720);
initLayout(w);
w.show();
returna.exec();
}
運行結果如下
2、 QBoxLayout:框布局,這是最簡單的一種布局,這種布局將需要管理的控件排成一列或者一行。其實講QBoxLayout占用的空間分成一行或者一列框,然后把布局所管理的控件填進取。
此布局的方向orientation有兩種,水平的即horizontal和垂直的vertical。每一個被放置到框里面的布局獲取的大小最小是其minimum大小,最大不超過maxmumSize。任何多余的空間將根據伸展因子共享處理。如果此布局不是最頂層布局,則一定需要將布局放置到父窗口或者布局里面
為了更快捷的使用,可以使用QHBoxLayout和QVBoxLayout創建指定方向的boxlayout,當然可以根據排列空間的方式不同,使用QBoxLayout創建布局,其方向主要是水平方向:LeftToRight、RightToLeft;垂直方向:BottonToTop,TopToBottom.
添加一個box控件主要方式如下:
1)addWidget()添加一個窗口不見widget到QBoxLayout,並且設置此不見得伸縮因子。伸展因子是boxes的行使用。
2)addSpacing()添加一個空的box。
3)addStretch()創建一個可伸縮的box
4)addLayout()將一個包含其他控件的布局添加到box上並設置layout的伸縮因子。同樣有對應的insert方法。
QBoxLayout有兩個邊界寬度,一個是內容邊界,setContentsMargins,設置每個窗口部件的外部邊距。這是每個QBoxLayout四邊的保留空間
setSpacing設置兩個相鄰的box之間的間距,可以使用addSpacing獲取更多的空間。
刪除一個控件,使用removeWidget,或者調用QWidget::hide()一樣可以從布局中刪除,知道show被調用。
簡單的講,這個布局是要注意以下幾點:
第一,布局控件的排列方式決定了布局的方向,其主要有兩個布局,水平和垂直
第二,布局各個空間的拉升,不是所有的控件平分布局的空間,而是根據伸縮因子和顯示比例完成控件空間的分配。
第三,各個控件之間的間隔及整個控件和布局的外部邊距大小。這三點決定了此布局的使用
2.1 常用的函數
QBoxLayout::QBoxLayout ( Directiondir, QWidget * parent = 0 )
創建一個框布局,dir有TopToBottom,BottomToLeft,LeftToRight及RightToLeft
void QBoxLayout::addLayout ( QLayout * layout, int stretch = 0 )
添加一個布局到框的末尾,設置伸縮因子stretch factor為stretch值,默認為0
void QBoxLayout::addSpacing ( int size )
添加一個不能伸縮的空間(一個QSpacerItem),其寬度設置為size到布局末尾。框布局提供了默認的邊距margin和spacing,這是額外添加的空間。
void addStretch(int stretch = 0)
添加一個可伸縮的空間(一個QSpacerItem),設0為最小值並且伸縮因子為stretch值到布局末尾
void addStrut(int size)
限制垂直尺寸到最小值size,如一個LeftToRight的框布局的高度。
void addWidget(QWidget *w,int stretch=0,Qt::Alignment alignment=0)
添加一個窗口部件到此框布局的末尾,並設定其伸縮率為stretch和alignment。
stretch設置為0,並且沒有其他空間有stretch大於0的控件,則空間的分配按照默認的值進行分配。alignment被賦值為alignment,默認值為0,表示控件將填充整個單元。
注意此分配因子的工作原理,stretch其實相當於一個占位符號,其可伸縮在0與stretch值之間,原則就是如果分配完空間有額外的空間,則根據stretch值大小比例去分,如果布局空間不夠,則會壓縮這個空間以被其他控件使用。
比如有兩個控件,在添加的時候第一個控件設置為200,第二個設置為0,那么最后結果是第一個控件比第二個控件多占用0-200的空間,如果第二個也設置為200,則兩個伸縮系數一樣的,所以兩個控件占用空間基本一致,如果第二個設置為100,則其空間占有約以2:1的比例分多余的空間。
本質上來講spacing和stretch都是QSpaceItem控件,占用空間用的,只是一個固定的,一個可伸縮的。
int count()返回此布局有多少個空間,此值在后續中inset相關的函數中進行使用。
Direction direction()返回布局的方向,添加控件與伸縮與這個方向一致。
void insertLayout ( int index, QLayout * layout, int stretch = 0 )
插入布局到指定index位置,並設定伸縮因子stretch。如果index是負數,layout添加到末尾。
void QBoxLayout::insertSpacing ( int index, int size )
在指定位置插入大小為size空間的不可伸縮的box。次函數常用於在指定位置添加空間。
void QBoxLayout::insertStretch ( int index, int stretch = 0 )
在指定位置添加可伸縮的空間,從0到伸縮因子stretch伸縮。
void QBoxLayout::insertWidget ( int index, QWidget * widget, int stretch = 0,Qt::Alignmentalignment = 0 )
在指定位置index插入控件widget
QSize minimumSize() QSize maxmumSize()獲取布局的最大最小空間尺寸
setDirection()設置方向
setGeometry(QRect r)設置布局空間大小尺寸
setSpacing(int spacing)設置相鄰控件的間距。
setStretch(int index,int stretch)
設置指定位置的伸縮因子為stretch值
bool setStretchFactor(QWidget *w,int stretch)設置窗口部件QWidget的伸縮因子為stretch,如果找到此部件返回true,否則返回false
bool setStretchFactor(QLayout *layout,int stretch)設置子布局的伸縮因子為stretch,如果找到此布局返回true,否則返回false
QSize sizeHint()返回合適的布局尺寸
int spacing()如果空間屬性可用,則直接返回,否則需要通過計算后返回。因為布局間距受窗口控件樣式決定。
int QBoxLayout::stretch ( int index )
返回index位置的伸縮因子stretch
注:stretch其實是規定了一個可以伸縮的范圍,在這個范圍內更好的合理的展現布局,如果空間過大,則此stretch會占用的多一點,如果空間太少,則會占用的少一點,addSpacing是添加一個固定大小空間的box,此相當於一個控件占用一個空間,只是不顯示,其布局在處理的時候不可壓縮這部分空間,舉例如下:
QPushButton*buttonOk=newQPushButton("OK");
QPushButton*buttonCancel=newQPushButton("Cancel");
QBoxLayout*buttonLayout=newQBoxLayout(QBoxLayout::LeftToRight);
buttonLayout->setSizeConstraint(QLayout::SetFixedSize);
buttonLayout->addStretch(10);
buttonLayout->addWidget(buttonOk);
buttonLayout->addStretch(200);
//buttonLayout->addSpacing(200);
buttonLayout->addWidget(buttonCancel);
buttonLayout->addStretch(10);
效果如下:
buttonLayout->addStretch(200); buttonLayout->addSpacing(200);
說明:QSpacerItem是為布局提供空白空間的類,在addStretch及addSpacing實際上就是處理這些。
3、QStackedLayout:堆棧布局,是將一堆widget控件放置在一起,只有一個控件是能看見的。其不是管理控件的尺寸即位置,而是尺寸的顯示,可以被一些字窗口控件(頁)在同一空間填充。多用於頁面切換等的操作。但是QStackedWidget並沒有提供一種方法給用戶區切換頁。這種方式就需要借助QComboBox或者QListWidget去存儲每個頁面的標題,從而配合實現切換頁面。
當填充布局的時候,窗口部件widget會加載到內部的一個列表中,通過indexOf()方法返回一個窗口部件widge的索引在內部的列表中。當然widget可以被添加到列表的末尾,或者插入到指定的索引位置。removeWidget()方法刪除在索引序號為index的窗口部件。通過count()方法可以獲取到加載到布局layout的元素個數。
widget()方法返回在指定位置上的部件widget,當前屏幕上顯示的的窗口部件的索引序號值可以用使用currentIndex()獲取,並且使用setCurrentIndex()設置。相似的情況,當前的顯示可見的窗口部件可以通過currentWidget獲取到,使用setCurrentWidget()更新。
但是不管什么情況,只要當前widget的布局變了或者remove刪除,則currentChanged()信號和widgetRemoved()信號會一次發送。
3.1 重要的屬性
1)count:const int表示布局layout包含的窗口控件的數量,count()訪問。
2)currentIndex:int,此屬性保存當前可見窗口部件的index值,如果沒有當前窗口部件則返回-1,使用currentIndex()訪問,setCurrentIndex(int index)進行設置。如果currentIndex改變了,則信號currentChanged(int index)信號發射。
3)stackingMode:StackingMode,此屬性決定子窗口部件可見方式的處理,默認是StackOne,即只有當前窗口是可見的,stackAll表示所有窗口都可見,但是只是提出了當前窗口。通過stackingMode()訪問,setStackingMode(StackingMode)進行設置
3.2 重要的函數:
int addWidget(QWidget *widget)將指定的widget添加到布局的末尾,並返回對應部件的位置index。如果執行此函數之前QStackedLayout是空,則將此設置為當前窗口部件。
void currentChanged(int intx)信號,當當前窗口部件改變后發射此信號。index是最新的部件索引,或則-1,如果沒有一個新的當前激活的窗口,,如為空時
QWidget *currentWidget()返回當前窗口部件,如果沒有窗口部件則返回0
int i當前indensertWidget(int index,QWidget *widget)在QStackedLayout的指定位置index插入widget。如果index越界,則追加到末尾。如果之前layout是空的,則此被設定為當前widget。這種插入的方式,index小於等於當前窗口的currentIndex,則會增加當前的index,但是保留當前活躍的用戶。
void setCurrentWidget(QWidget *widget)設置當前窗口為指定widget。
widget(int intdex)返回index邊上的widget或者0,如果沒有這個值則。
widgetRemoved(int index)窗口被刪除時信號被發出。
#include <QApplication>
#include <QLabel>
#include <QStackedLayout>
#include <QDebug>
void initStackedLayout(QWidget &w)
{
QStackedLayout *stackedLayout = new QStackedLayout();
stackedLayout->setStackingMode(QStackedLayout::StackAll);
stackedLayout->setSizeConstraint(QLayout::SetFixedSize);
QLabel *firstPage = new QLabel();
firstPage->setAlignment(Qt::AlignLeft);
firstPage->setWordWrap(true);
firstPage->setText("1111");
QLabel *first1Page = new QLabel();
first1Page->setAlignment(Qt::AlignCenter);
first1Page->setWordWrap(true);
first1Page->setFrame(tue);
first1Page->setText("2222");
QLabel *first2Page = new QLabel();
first2Page->setAlignment(Qt::AlignRight);
first2Page->setWordWrap(true);
first2Page->setText("3333");
qDebug()<<stackedLayout->addWidget(firstPage)<<endl;
qDebug()<<stackedLayout->addWidget(first1Page)<<endl;
qDebug()<<stackedLayout->addWidget(first2Page)<<endl;
stackedLayout->setCurrentIndex(1);
w.setLayout(stackedLayout);
}
int main(int argc,char **argv)
{
QApplication app(argc,argv);
QWidget w;
w.setFixedSize(1280,720);
initStackedLayout(w);
w.show();
return app.exec();
}
這是stackedMode,前者是StackedAll ,后者是StackedOne
4、QGridLayout:網格布局,顧名思義就是將所要管理的窗口控件放置到一個網格中,也就是一個二維表。所以其有行與列的概念。QGridLayout從其父窗口或者布局獲取到可用的空間,並分為行和列rows and columns,然后將每個窗口widget放置到對應的單元cell中並管理起來。
有列,又有行,其兩者的行為是一樣的。所以以列為點先討論。每個列都有一個最小的寬度,還有一個伸縮因子,用於在嗎某個范圍內占用空用的空閑空間。每個列的最小寬度是由設置setColumnMinimumWidth()和控件widget的最小尺寸的相互結果。使用setColumnStretch()設置此列的伸縮參數,其決定了一列多少空間是可以使用的並且在其之上的最小空間是可用的。
一般來講,每列所管理的控件或者布局可以通過使用addWidget()添加,但是也有可能性就是一個控件需要占用多行,這樣就需要使用行或者列的spanning參數,表示需要占用的行數或者列數。
和其他布局一樣,如果從布局拿掉一個管理的控件,調用removeWidget()參數,或者調用QWidget的hide函數也可以釋放其占用的空間。
4.1 屬性
1)horizontalSpacing:int 用於保留兩個控件之間水平方向的空間,horizontalSpacing獲取,setHorizontalSpacing(int spacing)進行設置。
2)verticalSpacing : int在垂直方向上兩個控件之間的間距,通過verticalSpacing獲取,setVerticalSpacing進行設置
上兩個屬性如果沒有設置,則繼承子其父類布局或者控件。其和layout本身的spacing關系是如果兩者相等,則使用spacing,否則spacing為-1值
4.2 主要的函數
1)addLayout(Qlayout *layout,int row,int column,Qt::Alignment =0)
layout:需要添加的布局對象;row column表示要進價進入網格的行列號,top-left位置是(0,0),alignment表示控件放置在cell但願的位置,默認為0表示填充整個單元cell。
2)addLayout(QLayout layout,int row,int column,int rowSpan,int columnSpan,Qt::Alignment = 0)同上面實現一樣的功能,這里需要注意的就是添加的控件占用多少個單元,跨越多少個單元及rowSpan與columnSpan
3)addWidget(QWidget *widget,int row,int column,Qt::Alignment =0)
widget:需要添加的窗口不見;row column表示要進價進入網格的行列號,top-left位置是(0,0),alignment表示控件放置在cell但願的位置,默認為0表示填充整個單元cell。
如果rowSpan與/或者columnspan為-1,則布局會擴展到bottom與/或者right邊界位置。
4)addWidget(QWidget *widget,int fromrow,int fromcolumn,int rowSpan,int columnSpan,Qt::Alignment = 0)同上面實現一樣的功能,這里需要注意的就是添加的控件占用多少個單元,跨越多少個單元及rowSpan與columnSpan。如果rowSpan與/或者columnspan為-1,則布局會擴展到bottom與/或者right邊界位置。
5)QRect cellRect(int row,int column)獲取網格單元在row與column決定的單元的空間尺寸。如果row和column越界,則返回無效的QRect
6)int columnCount() 獲取columns列的個數。
同樣有rowCount返回row數
7)int columnMinimumWidth(int column)獲取指定列的最小寬度,通過setColumnMinimuWidth(int column,int width)設置
同樣有rowMinimumHeight(int row)獲取某一行的最小高度setRowMinimunHeight(int row,int height)進行設置
8)int columnStretch(int column)獲取指定列column的拉升因子,可以用setColumnStretch(int column,int stretch)進行設置
同樣 int rowStretch(int row)獲取行row的伸縮值,通過setRowStretch(int row,int stretch)設置。
9)count()返回網格有多少個單元
10)void getItemPosition(int index,int *row,int *column,int *rowSpan,int *columnspan)
用於獲取index所制定的元素所在的行列號及水平垂直所占用的跨越單元個數。
11)setSpacing()設置網格垂直及水平方向控件之間的間隔尺寸。spacing()獲取,如果horizontalSpacing與verticalSpacing不同,返回-1
綜上所述布局管理所管理的都是各個控件存放的位置及尺寸,以及控件拉升的策略。對控件的操作,主要獲取其相關的信息,如spacing及strech,對於多行這種有行列不同的spacing,則根據具體情況獲取設置。
根據不同的布局特點,都添加了添加控件及子布局的接口,也提供了通過index獲取布局的相關信息。
布局的使用方法介紹就暫時告一段落,下一講將會針對具體的應用使用布局。后續也會介紹如何實現自己的布局。