在上一次學習 QT(8)變動布局Dynamic Layout中,我們在此總結一下:對于修改布局,可以通過removeWidget后在根據新的位置重新加載。為了創建新的合適的布局,我們需要重新resize布局的大小。我們需要注意到在修訂時,要考慮組建之間的空間,即spacing()。對如刪和增都需要考慮QSize(spacing(),spacing())。
在本次,我們延續QT(7)的學習,再次對layout的繼承進行學習。參考 http://doc.qt.nokia.com/latest/layouts-flowlayout.html /。在此之前,我們對QT編譯中碰到的一些問題進行記錄:
問題1:編譯中出現make : g++沒有找到
對于ubuntu可以使用apt-get install g++,但是在采用yum的系統,例如MeeGo,沒有g++的包,yum那里采用了另外的名字yum install gcc-c++。
問題2:編譯中出現undefined reference to `vtable for xxxx(某個類名)'
出去這種情況,需要檢查*.pro文件,看看是否將所需的*.h和*.cpp加入,或者加入一些空文件。
記錄1:制定moc生成文件存放的目錄
moc命令將含Q_OBJECT的頭文件轉換成標準.h文件,在我們定義Q_OBJECT后,很可能會生成moc_xxxxx.cpp的文件。方式:MOC_DIR = build。
言歸正卷,我們這次建立一個自定的layout,上面的widget,根據我們addWidget的先后順序,從左向右排序,如果超過范圍,就從下一排開始,也是從左向右,很像現代文字的書寫方式。如圖所示:
搭建程序框架
qtmain.cpp為主程序,mywindow.h和mywindow.cpp為窗口類,flowlayout.h和flowlayout.cpp是我們用于構造我們布局QLayout的子類。mywindow.cpp如下:
MyWindow :: MyWindow()
{
FlowLayout * layout = new FlowLayout();
layout->addWidget (new QPushButton(tr("Short")));
layout->addWidget (new QPushButton(tr("Longer")));
layout->addWidget (new QPushButton(tr("Different Text")));
layout->addWidget (new QPushButton(tr("More Text")));
layout->addWidget (new QPushButton(tr("This is a long text button!")));
setLayout(layout);
setWindowTitle("FlowLayout Test!");
}
構造自定義的布局QLayout子類:存放QLayoutItem
我們在QT(7)中學習過,這里我們使用一個QList<QLayoutItem *> itemList來存放我們的item,并且進行了addItem,count,itemAt(int index),takeAt(int index)這幾個virtual方法,同時在釋放方法~FlowLayout()中清空itemList,并釋放空間。這里,將并在詳細說明。可以參見參考中給出的 源代碼 。
完成構建函數
在MyWindow類中,我們并不需要有特別的構造函數。在Layout中,計算margin,也就是各widget之間的空隙是一個很麻煩的事情。在例子中,我們提供可定制margin(缺省值為11,由于缺省的邊框為1,所以11大抵重視覺角度看就是10px),這是Layout之間的留邊位置,同時我們也設定了組件之間的間隔大小(m_hSpace,m_vSpace),如下:
FlowLayout :: FlowLayout( QWidget * parent, int margin,int hSpacing,int vSpacing )
: QLayout(parent),m_hSpace(hSpacing),m_vSpace(vSpacing)
{
setContentsMargins(margin,margin,margin,margin);
}
這里我們看到一個有趣的寫法,實際上其等同與在方法中運行了:
QLayout(parent);
m_hSpace = hSpacing;
m_vSpace = vSpacing;
給出Layout的尺寸大小
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
這里我們要求button并會自動補充空白位置,所有給出0。對于Layout的尺寸大小,重要的是minimumSize()和sizeHint()兩個。如下面。QSize可以通過要求增加某個尺寸大小的文字,它看自動進行調整計算,并需要我們精確計算。最佳大小,我們設置等同于最小尺寸。
QSize FlowLayout::minimumSize() const
{
QSize size;
QLayoutItem * item;
foreach(item,itemList)
size = size. expandedTo (item->minimumSize());
size += QSize(2*margin(),2*margin());
return size;
}QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
我們補充繼承兩個方法,用于獲取組件之間間隔大小:
int FlowLayout::horizontalSpacing() const
{
if(m_hSpace >= 0)
return m_hSpace;
else
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing /* Default horizontal spacing for a QLayout.*/ );
}
int FlowLayout::verticalSpacing() const
{
if(m_vSpace >= 0 )
return m_vSpace;
else
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const //這是我們定義的private方法,用于從parent中獲得widget之間的間隔
{
QObject * parent = this->parent();
if(!parent){
return -1;
}else if(parent->isWidgetType()){
QWidget * pw = static_cast<QWidget *>(parent);
return pw->style()->pixelMetric(pm,0,pw);
}else{
return static_cast<QLayout*>(parent)->spacing();
}
return 0;
}
進行布局
布局采用setGemetry,這個我們在QT(7)中也介紹過:
void FlowLayout::setGeometry(const QRect & rect)
{
QLayout::setGeometry(rect);
doLayout(rect,false);
}
下面我們根據需求,對doLayout進行說明:
![]()
int FlowLayout::doLayout(const QRect & rect, bool testOnly) const
{
int left,top,right,bottom;
getContentsMargins(&left,&top,&right,&bottom);
QRect effectiveRect = rect. adjusted (left,top,-right,-bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
我們第一步,先計算有效的擺放widget的尺寸effectiveRect。
QLayoutItem * item;
foreach(item,itemList){
//It then sets the proper amount of spacing for each widget in the layout, based on the current style.
QWidget * wid = item->widget();
int spaceX = horizontalSpacing();
if(spaceX == -1)
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton,QSizePolicy::PushButton,Qt::Horizontal);
int spaceY = verticalSpacing();
if(spaceY == -1)
spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton,QSizePolicy::PushButton,Qt::Vertical);
在這里,我們獲取一些基本的數據,包括每一個item,其大小為item->sizeHint(),在水平方向各組件之間的間隔spaceX以及豎直方向的間隔spaceY。我們將在effectiveRect內順序排列widget。下面我們來進行計算,設置各個item的setGeometry,需要獲取每個item的起始左上角坐標。
int nextX = x + item->sizeHint().width() + spaceX; //下一個組件的左上角位置的x坐標
if(nextX - spaceX > effectiveRect.right() && lineHeight > 0){ //如果超出位置, 換行 ,重新計算(x,y)坐標
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x+item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if(!testOnly) //設置item的位置
item-> setGeometry (QRect(QPoint(x,y),item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight,item->sizeHint().height());
}
return y + lineHeight - rect.y() + bottom; //返回需要限制所有組件,layout至少要多高
}
對于setGeometry,我們并不需要返回值,但是我們發現,如果組件多,有多行擺放,有時無法全部顯示,這在初始顯示和我們改變window大小的時候可能會出現,而doLayout就返回了layout顯示所有組件時至少需要的height。因此我在width改變是需要重新計算height,需要設置hasHeightForWidth()為true,并heightForWidth返回相應的值。
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
int height = doLayout(QRect(0,0,width,0),true);
return height;
}
相關鏈接: 我的MeeGo/Moblin相關文章
一個小故事:民國元年的小學國文教科書中有一篇《少年》:一少年在兵營為鼓手,某日,將校會宴,大將勸飲,少年辭曰:吾不嗜酒。大將曰:汝終日擊鼓甚勞,可少飲 酒以舒之。少年固辭不飲,大將不悅。副將在旁,欲試之,厲聲曰:汝必飲一杯,是軍令也,違令將斬汝!少年改容曰:軍令不勝恐懼,然飲酒非兵士職。昔者,吾父以酒疾不起,吾入營時,吾母戒曰:汝終身勿飲酒。雖有大將之命,。不能破慈母之戒。聲淚俱下。坐中將校莫不感動。由是少年益受大將信任,有名于時。
這個故事告訴我們,什么叫做原則,原則不是領導說一句話,就可以搖擺和動搖。現在春節了,在酒桌上該不喝酒就不喝酒,勸酒是一個陋習。當然還有其他很多事情,做一個有良知的人。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
