布局管理
以下是Qt手册中的《布局管理》的译文
在一个Widget中,Qt布局管理系统提供了一个简单而有效的方式来自动组织子widget,以保证他们能够很好地利用可用空间。
介绍
Qt包含一个布局管理类的集合,它们被用来描述widgets如何在应用程序的用户界面中呈现的。当可用空间发生变化时,这些布局将自动调整widgets的位置和大小,以确保它们布局的一致性和用户界面主体可用。
所有QWidget的子类都可以用布局来管理它们的子类。QWidget::setLayout()函数给widget提供一个布局。当布局通过这种方式设置到widget,它将负责以下任务:
l子widget的定位
l窗口的合理默认空间
l窗口的合理最小空间
l调整大小处理
l当内容发生变化时自动调整
n字体、大小或者内容变化
n显示或隐藏widget
n移除子widget
Qt的布局类
QGraphicsAnchorLayout |
LayoutwhereonecananchorwidgetstogetherinGraphicsView |
在制图视图中布局widget |
QGraphicsAnchor |
RepresentsananchorbetweentwoitemsinaQGraphicsAnchorLayout |
? |
QBoxLayout |
Linesupchildwidgetshorizontallyorvertically |
水平或垂直整理子widget |
QHBoxLayout |
Linesupwidgetshorizontally |
水平整理子控件 |
QVBoxLayout |
Linesupwidgetsvertically |
垂直整理子控件 |
QFormLayout |
Managesformsofinputwidgetsandtheirassociatedlabels |
label-inputwidget模式表单布局 |
QGridLayout |
Laysoutwidgetsinagrid |
网格布局 |
QLayout |
Thebaseclassofgeometrymanagers |
布局,几何管理的基类 |
QLayoutItem |
AbstractitemthataQLayoutmanipulates |
管理的抽象元素 |
QSpacerItem |
Blankspaceinalayout |
空白区域布局 |
QWidgetItem |
Layoutitemthatrepresentsawidget |
布局元素 |
QSizePolicy |
Layoutattributedescribinghorizontalandverticalresizingpolicy |
大小策略 |
QStackedLayout |
Stackofwidgetswhereonlyonewidgetisvisibleatatime |
栈模式布局,一次只显示一个 |
QButtonGroup |
Containertoorganizegroupsofbuttonwidgets |
管理按钮的容器 |
QGroupBox |
Groupboxframewithatitle |
带标题的组箱框架 |
QStackedWidget |
Stackofwidgetswhereonlyonewidgetisvisibleatatime |
栈模式的widget,一次只显示一个 |
水平、垂直、网格和表格布局
给widgets一个很好布局的最好方式是使用内置的布局管理器:QHBoxLayout,QVBoxLayout,QGridLayout,
andQFormLayout.这些类都从QLayout继承而来,它们都来源于QObject(而不是QWidget)。创建更加复杂的布局,可以让它们彼此嵌套完成。
lQHBoxLayout是水平布局,将从左往右(orrighttoleftforright-to-leftlanguages)widget布局成水平行
lQVBoxLayout是垂直布局,从顶部到底部
lQGridLayout是二位的网格布局。它可以容纳多个单元格:
lQFormLayout是两列label-field式的表单布局
代码举例
下面代码创建QHBoxLayout来管理5个QPushButtons的几何图形:
QWidget*window=newQWidget;
QPushButton*button1=newQPushButton("One");
QPushButton*button2=newQPushButton("Two");
QPushButton*button3=newQPushButton("Three");
QPushButton*button4=newQPushButton("Four");
QPushButton*button5=newQPushButton("Five");
QHBoxLayout*layout=newQHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();
QGridLayout示例如下:
QWidget*window=newQWidget;
QPushButton*button1=newQPushButton("One");
QPushButton*button2=newQPushButton("Two");
QPushButton*button3=newQPushButton("Three");
QPushButton*button4=newQPushButton("Four");
QPushButton*button5=newQPushButton("Five");
QGridLayout*layout=newQGridLayout;
layout->addWidget(button1,0,0);
layout->addWidget(button2,0,1);
layout->addWidget(button3,1,0,1,2);
layout->addWidget(button4,2,0);
layout->addWidget(button5,2,1);
window->setLayout(layout);
window->show();
QFormLayout示例如下:
QWidget*window=newQWidget;
QPushButton*button1=newQPushButton("One");
QLineEdit*lineEdit1=newQLineEdit();
QPushButton*button2=newQPushButton("Two");
QLineEdit*lineEdit2=newQLineEdit();
QPushButton*button3=newQPushButton("Three");
QLineEdit*lineEdit3=newQLineEdit();
QFormLayout*layout=newQFormLayout;
layout->addRow(button1,lineEdit1);
layout->addRow(button2,lineEdit2);
layout->addRow(button3,lineEdit3);
window->setLayout(layout);
window->show();
布局技巧
当使用布局的时候,在创建子widget时,没必要给它传递父类。布局会自动重新定义它们的父类(通过QWidget::setParent())以确保它们是装载布局的widget的子类。
注意1:布局中的控件是装载布局控件的子控件,不是布局的子控件。控件只能以其他控件作为父类,不可以以布局作为父类。在布局上,可以使用addLayout来嵌套布局;被嵌套的布局,将变成上层布局的子布局。
向布局添加widgets
添加布局到widgets时,布局过程执行如下:
1.所有widgets将根据它们的QWidget::sizePolicy()andQWidget::sizeHint()首先分配一些空间。
2.如果有widgets设置了大于0的拉伸系数,接下来它们将按照拉伸系数的比例来分配空间。
3.如果有widgets设置的拉伸系数是0,它将在没有其他widgets需要空间时获取更多空间。其中,带Expanding大小策略的widget将首先获得空间。
4.所有分配了小于最小空间(或者设置了最小的sizehint)的widget将按要求分配最小空间。(在拉伸系数成为决定因子时,widgets没必要再用最小值或者最小hint)。
5.任何分配了大于最大空间的widget将按要求分配最大空间。(拉伸系数起着决定作用)
拉伸系数
通常,widgets创建的时候没有设置拉伸系数。当widget整理到一个布局中时,它们将根据QWidget::sizePolicy()或者最小大小hint(取决于谁更大)分配一定空间。拉伸系数被用于按比例改变widget的分配空间。
如果3个widget用QHBoxLayout来布局,不带拉伸系数,它们将得到像下面的布局:
如果带上拉伸系数,情况将变成这样:
自定义widget的布局
当编写自定义widget类时,需要显示提供它的布局属性。如果widget有Qt自带的布局,它能够自己满足自己。如果没有任何子布局,或者使用手动布局,可以通过下面的机制来改变widget的行为:
l实现QWidget::sizeHint()来返回首先大小
l实现QWidget::minimumSizeHint()来返回widget可以拥有的最小空间
l调用QWidget::setSizePolicy来描述widget所需的空间
当sizehint、minimumsize或sizepolicy改变时,调用QWidget::updateGeometry()。这将促使布局重新进行计算。连续多次调用QWidget::updateGeometry()只会发生一次布局重新计算。
即便实现了QWidget::heightForWidth(),也有必要提供合理的sizeHint()。
进一步了解,参见:TradingHeightforWidth.
布局问题Theuseofrichtextinalabelwidgetcanintroducesomeproblemstothelayoutofitsparentwidget.ProblemsoccurduetothewayrichtextishandledbyQt'slayoutmanagerswhenthelabeliswordwrapped.
IncertaincasestheparentlayoutisputintoQLayout::FreeResizemode,meaningthatitwillnotadaptthelayoutofitscontentstofitinsidesmallsizedwindows,orevenpreventtheuserfrommakingthewindowtoosmalltobeusable.Thiscanbeovercomebysubclassingtheproblematicwidgets,andimplementingsuitablesizeHint()andminimumSizeHint()functions.
Insomecases,itisrelevantwhenalayoutisaddedtoawidget.WhenyousetthewidgetofaQDockWidgetoraQScrollArea(withQDockWidget::setWidget()
andQScrollArea::setWidget()),thelayoutmustalreadyhavebeensetonthewidget.Ifnot,thewidgetwillnotbevisible.
在QLabel中使用富文本会给布局的父类widget带来一些问题。问题发生的原因是因为当label被文字环绕时,富文本被Qt的布局管理器控制。
在某些情况下,父类布局被放入QLayout::FreeResize模式,这意味着它将不适应内容布局所设置的最小窗口,或者甚至阻止用户让窗口小到不可用的情况。这个可以通过将问题控件作为子类来解决,并实现合适的sizeHint()和minimumSizeHint()函数。
在一些情况下,当布局被添加到widget时需要特别注意。当设置QDockWidgetoraQScrollAreawidget时(用QDockWidget::setWidget()
andQScrollArea::setWidget()),布局必须已经被设置到widget上。否则,这些widget将不可见。
手动布局
如果想自定义一个独特的布局,可以按如上所述地自定义一个widget。实现QWidget::resizeEvent()来计算所需的大小分配并在每个子类中调用setGeometry()。
需要布局需要重新计算大小时,widget将提供一个事件接口QEvent::LayoutRequest。实现QWidget::event()来接收QEvent::LayoutRequest事件。
自定义布局管理
自定义布局的唯一方法是继承QLayout来完成自己布局管理器。BorderLayout和Flow
Layout例子将说明如何来完成。
下面将举个例子来说明。CardLayout类,受同名java布局管理的启发。它分层管理每个元素,每个元素的通过QLayout::spacing()来设置位移量。
编写自定义布局类,必须定义以下内容:
l由布局控制的存放元素的数据结构。每个元素都是一个QLayoutItem。在这个例子中,我们将使用QList。
laddItem(),描述如何添加元素到布局。
lsetGeometry(),描述如何完成布局
lsizeHint(),布局的首选大小
litemAt(),描述如何递归布局
ltakeAt(),描述如何移除布局中的元素。
在大多数情况下,还需要实现minimumSize()。
头文件
card.h
#ifndefCARD_H
#defineCARD_H
#include
#include
classCardLayout:publicQLayout
{
public:
CardLayout(QWidget*parent,intdist):QLayout(parent,0,dist){}
CardLayout(QLayout*parent,intdist):QLayout(parent,dist){}
CardLayout(intdist):QLayout(dist){}
~CardLayout();
voidaddItem(QLayoutItem*item);
QSizesizeHint()const;
QSizeminimumSize()const;
intcount()const;
QLayoutItem*itemAt(int)const;
QLayoutItem*takeAt(int);
voidsetGeometry(constQRect&rect);
private:
QListlist;
};
#endif
实现文件
card.cpp
#include"card.h"
intCardLayout::count()const
{
//QList::size()returnsthenumberofQLayoutItemsinthelist
returnlist.size();
}
intCardLayout::count()const
{
//QList::size()returnsthenumberofQLayoutItemsinthelist
returnlist.size();
}
intCardLayout::count()const
{
//QList::size()returnsthenumberofQLayoutItemsinthelist
returnlist.size();
}
CardLayout::~CardLayout()
{
QLayoutItem*item;
while((item=takeAt(0)))
deleteitem;
}
voidCardLayout::setGeometry(constQRect&r)
{
QLayout::setGeometry(r);
if(list.size()==0)
return;
intw=r.width()-(list.count()-1)*spacing();
inth=r.height()-(list.count()-1)*spacing();
inti=0;
while(i
QLayoutItem*o=list.at(i);
QRectgeom(r.x()+i*spacing(),r.y()+i*spacing(),w,h);
o->setGeometry(geom);
++i;
}
}
QSizeCardLayout::sizeHint()const
{
QSizes(0,0);
intn=list.count();
if(n>0)
s=QSize(100,70);//startwithanicedefaultsize
inti=0;
while(i
QLayoutItem*o=list.at(i);
s=s.expandedTo(o->sizeHint());
++i;
}
returns+n*QSize(spacing(),spacing());
}
QSizeCardLayout::minimumSize()const
{
QSizes(0,0);
intn=list.count();
inti=0;
while(i
QLayoutItem*o=list.at(i);
s=s.expandedTo(o->minimumSize());
++i;
}
returns+n*QSize(spacing(),spacing());
}
进一步说明
自定义布局没有控制宽和高。
忽略了QLayoutItem::isEmpty(),这意味着布局将把隐藏widget作为可见的。
对于复杂布局,通过缓存计算将大大提高速度。在那种情况下,实现QLayoutItem::invalidate()来标记数据是脏数据。
调用QLayoutItem::sizeHint()等的代价比较大。在通过函数中,需要再次使用,最好将结果保存在本地变量中。
在同样函数的同一个元素中,不应该调用两次QLayoutItem::setGeometry()。这个调用将耗费巨大,如果它用几个子widget,因为布局管理器每次都要做一个完整的布局。替代方法:先计算geometry,然后再设置(这种事情,不仅应该在布局时注意,在实现resizeEvent()时也需要按同样方法来做)。
参考
1.Qt手册《LayoutManagement》
窗体小部件和布局
窗体小部件
窗体小部件(Widgets)是Qt中创建用户界面的主要元素。窗体小部件可以显示数据和状态信息,接受用户输入,和提供组织其他窗体小部件的容器。
没有嵌入到父级窗体小部件的部件被称为窗口(window)。
布局
布局是一个种高雅而灵活的方式来自动把子类窗体小部件组织到它们的容器中。每个窗体小部件通过sizeHint和sizePolicy属性向布局提供大小需求,布局根据可用空间进行分配。
窗体小部件的样式
样式(styles)绘制窗体小部件,并封装了GUI的外观和感觉。Qt的内置窗体小部件使用QStyle类完成几乎所有的绘制工作,以确保它们看来确实是一致的、本地窗体小部件。
QSS(QtStyleSheets)允许自定义窗体小部件的外观。
窗体小部件的类
QtWidgetGallery
基础部件
QCheckBox |
Checkboxwithatextlabel |
QComboBox |
Combinedbuttonandpopuplist |
QCommandLinkButton |
Vistastylecommandlinkbutton |
QDateEdit |
WidgetforeditingdatesbasedontheQDateTimeEditwidget |
QDateTimeEdit |
Widgetforeditingdatesandtimes |
QDial |
Roundedrangecontrol(likeaspeedometerorpotentiometer) |
QDoubleSpinBox |
Spinboxwidgetthattakesdoubles |
QFocusFrame |
Focusframewhichcanbeoutsideofawidget'snormalpaintablearea |
QFontComboBox |
Comboboxthatletstheuserselectafontfamily |
QLCDNumber |
DisplaysanumberwithLCD-likedigits |
QLabel |
Textorimagedisplay |
QLineEdit |
One-linetexteditor |
QMenu |
Menuwidgetforuseinmenubars,contextmenus,andotherpopupmenus |
QProgressBar |
Horizontalorverticalprogressbar |
QPushButton |
Commandbutton |
QRadioButton |
Radiobuttonwithatextlabel |
QScrollArea |
Scrollingviewontoanotherwidget |
QScrollBar |
Verticalorhorizontalscrollbar |
QSizeGrip |
Resizehandleforresizingtop-levelwindows |
QSlider |
Verticalorhorizontalslider |
QSpinBox |
Spinboxwidget |
QTabBar |
Tabbar,e.g.foruseintabbeddialogs |
QTabWidget |
Stackoftabbedwidgets |
QTimeEdit |
WidgetforeditingtimesbasedontheQDateTimeEditwidget |
QToolBox |
Columnoftabbedwidgetitems |
QToolButton |
Quick-accessbuttontocommandsoroptions,usuallyusedinsideaQToolBar |
QWidget |
Thebaseclassofalluserinterfaceobjects |
高级部件
QCalendarWidget |
Monthlybasedcalendarwidgetallowingtheusertoselectadate |
QColumnView |
Model/viewimplementationofacolumnview |
QDataWidgetMapper |
Mappingbetweenasectionofadatamodeltowidgets |
QDesktopWidget |
Accesstoscreeninformationonmulti-headsystems |
QListView |
Listoriconviewontoamodel |
QMacCocoaViewContainer |
WidgetforMacOSXthatcanbeusedtowraparbitraryCocoaviews(i.e.,NSViewsubclasses)andinsertthemintoQthierarchies |
QMacNativeWidget |
WidgetforMacOSXthatprovidesawaytoputQtwidgetsintoCarbonorCocoahierarchiesdependingonhowQtwasconfigured |
QTableView |
Defaultmodel/viewimplementationofatableview |
QTreeView |
Defaultmodel/viewimplementationofatreeview |
QUndoView |
DisplaysthecontentsofaQUndoStack |
QWSEmbedWidget |
Enablesembeddedtop-levelwidgetsinQtforEmbeddedLinux |
QWebView |
Widgetthatisusedtoviewandeditwebdocuments |
QX11EmbedContainer |
XEmbedcontainerwidget |
QX11EmbedWidget |
XEmbedclientwidget |
Phonon::VideoWidget |
Widgetthatisusedtodisplayvideo |
组织者部件
QButtonGroup |
Containertoorganizegroupsofbuttonwidgets |
QGroupBox |
Groupboxframewithatitle |
QSplitter |
Implementsasplitterwidget |
QSplitterHandle |
Handlefunctionalityofthesplitter |
QStackedWidget |
Stackofwidgetswhereonlyonewidgetisvisibleatatime |
QTabWidget |
Stackoftabbedwidgets |
抽象部件类
QAbstractButton |
Theabstractbaseclassofbuttonwidgets,providingfunctionalitycommontobuttons |
QAbstractScrollArea |
Scrollingareawithon-demandscrollbars |
QAbstractSlider |
Integervaluewithinarange |
QAbstractSpinBox |
Spinboxandalineedittodisplayvalues |
QDialog |
Thebaseclassofdialogwindows |
QFrame |
Thebaseclassofwidgetsthatcanhaveaframe |
参考
WidgetsandLayouts4.8