一直被QLayout::setSizeConstraint()搞得很头大。其实,很大部分原因只能怪小弟我英文水平不过关,因为qt文档里已经说明得够清楚的了。如下:
enum QLayout::SizeConstraint
The possible values are:
不过,这里也有几个问题是需要注意一下下的:
1)被QLayout::setSizeConstraint()所设置的约束,是对qlayout的父窗口而言的。譬如,当某qlayout设置了QLayout::SetMinimumSize约束属性,则它的父窗口(mainW)的minimumSize则会设置为qlayout的totalMinimumSize()值;相当于, mainW->setMinimumSize(qlayout->totalMinimumSize())。
2)QLayout::totalMinimumSize()的计算里有点猫腻。
下面看看代码的,
1)对于第一点,直接看QLayout::activate()函数就可以一目了然的。
bool QLayout::activate()
{
Q_D(QLayout);
if (!d->enabled || !parent())
return false;
if (!d->topLevel)
return static_cast<QLayout*>(parent())->activate();
if (d->activated)
return false;
QWidget *mw = static_cast<QWidget*>(parent());
if (mw == 0) {
qWarning("QLayout::activate: %s \"%s\" does not have a main widget",
QObject::metaObject()->className(), QObject::objectName().toLocal8Bit().data());
return false;
}
activateRecursiveHelper(this);
QWidgetPrivate *md = mw->d_func();
uint explMin = md->extra ? md->extra->explicitMinSize : 0;
uint explMax = md->extra ? md->extra->explicitMaxSize : 0;
// 此处根据不同的约束条件,来给qlayout的父窗口设置相应的Size约束条件
// *****************************************************************
switch (d->constraint) {
case SetFixedSize:
// will trigger resize
mw->setFixedSize(totalSizeHint());
break;
case SetMinimumSize:
mw->setMinimumSize(totalMinimumSize());
break;
case SetMaximumSize:
mw->setMaximumSize(totalMaximumSize());
break;
case SetMinAndMaxSize:
mw->setMinimumSize(totalMinimumSize());
mw->setMaximumSize(totalMaximumSize());
break;
case SetDefaultConstraint: {
bool widthSet = explMin & Qt::Horizontal;
bool heightSet = explMin & Qt::Vertical;
if (mw->isWindow()) {
QSize ms = totalMinimumSize();
if (widthSet)
ms.setWidth(mw->minimumSize().width());
if (heightSet)
ms.setHeight(mw->minimumSize().height());
if ((!heightSet || !widthSet) && hasHeightForWidth()) {
int h = minimumHeightForWidth(ms.width());
if (h > ms.height()) {
if (!heightSet)
ms.setHeight(0);
if (!widthSet)
ms.setWidth(0);
}
}
mw->setMinimumSize(ms);
} else if (!widthSet || !heightSet) {
QSize ms = mw->minimumSize();
if (!widthSet)
ms.setWidth(0);
if (!heightSet)
ms.setHeight(0);
mw->setMinimumSize(ms);
}
break;
}
case SetNoConstraint:
break;
}
d->doResize(mw->size());
if (md->extra) {
md->extra->explicitMinSize = explMin;
md->extra->explicitMaxSize = explMax;
}
// ideally only if sizeHint() or sizePolicy() has changed
mw->updateGeometry();
return true;
}
2)对于第二个问题,以SetMinimumSize为例子,首先得从QLayout::totalMinimumSize()函数开始的:
QSize QLayout::totalMinimumSize() const
{
Q_D(const QLayout);
int side=0, top=0;
if (d->topLevel) {
QWidget *pw = parentWidget();
pw->ensurePolished();
QWidgetPrivate *wd = pw->d_func();
side += wd->leftmargin + wd->rightmargin;
top += wd->topmargin + wd->bottommargin;
}
// 这里需要看这一行就行了:说明我们要再看minimumSize()这个函数。
QSize s = minimumSize();
#ifndef QT_NO_MENUBAR
top += menuBarHeightForWidth(d->menubar, s.width() + side);
#endif
return s + QSize(side, top);
}
好吧,顺藤摸瓜,依次经历了(注意我调试代码时,是用QHBoxLayout作为例子的)QBoxLayout::minimumSize() -> QBoxLayoutPrivate::setupGeom() -> QWidgetItemV2::minimumSize()
最后,发现了QWidgetItemV2::updateCacheIfNecessary()这个函数
void QWidgetItemV2::updateCacheIfNecessary() const
{
if (q_cachedMinimumSize.width() != Dirty)
return;
const QSize sizeHint(wid->sizeHint());
const QSize minimumSizeHint(wid->minimumSizeHint());
const QSize minimumSize(wid->minimumSize());
const QSize maximumSize(wid->maximumSize());
const QSizePolicy sizePolicy(wid->sizePolicy());
const QSize expandedSizeHint(sizeHint.expandedTo(minimumSizeHint));
// 于是那点猫开始浮出水面了~没错,就是qSmartMinSize()函数!
// layout item的大小计算都是经由qSmart*Size()函数来计算的。
const QSize smartMinSize(qSmartMinSize(sizeHint, minimumSizeHint, minimumSize, maximumSize, sizePolicy));
const QSize smartMaxSize(qSmartMaxSize(expandedSizeHint, minimumSize, maximumSize, sizePolicy, align));
const bool useLayoutItemRect = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect);
q_cachedMinimumSize = useLayoutItemRect
? toLayoutItemSize(wid->d_func(), smartMinSize)
: smartMinSize;
q_cachedSizeHint = expandedSizeHint;
q_cachedSizeHint = q_cachedSizeHint.boundedTo(maximumSize)
.expandedTo(minimumSize);
q_cachedSizeHint = useLayoutItemRect
? toLayoutItemSize(wid->d_func(), q_cachedSizeHint)
: q_cachedSizeHint;
if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
q_cachedSizeHint.setWidth(0);
if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
q_cachedSizeHint.setHeight(0);
q_cachedMaximumSize = useLayoutItemRect
? toLayoutItemSize(wid->d_func(), smartMaxSize)
: smartMaxSize;
}
可见,layout item的大小计算都是经由qSmart*Size()函数来计算的。于是,看一下下代码,
Q_GUI_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint,
const QSize &minSize, const QSize &maxSize,
const QSizePolicy &sizePolicy)
{
QSize s(0, 0);
// 根据不同大小策略,不同的考虑,但是,minSizeHint才是影响大小的人
if (sizePolicy.horizontalPolicy() != QSizePolicy::Ignored) {
if (sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)
s.setWidth(minSizeHint.width());
else
s.setWidth(qMax(sizeHint.width(), minSizeHint.width()));
}
if (sizePolicy.verticalPolicy() != QSizePolicy::Ignored) {
if (sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag) {
s.setHeight(minSizeHint.height());
} else {
s.setHeight(qMax(sizeHint.height(), minSizeHint.height()));
}
}
// 这里,只有当minSize大于0时,控件上所设置最小大小才会有影响力。呃,所谓的猫腻就是指这个。是我故弄玄虚了~
s = s.boundedTo(maxSize);
if (minSize.width() > 0)
s.setWidth(minSize.width());
if (minSize.height() > 0)
s.setHeight(minSize.height());
return s.expandedTo(QSize(0,0));
}
相关的其它类型的大小计算,也一并贴上来吧。它们都是alayoutengine.cpp(layout计算引擎)代码。
Q_GUI_EXPORT QSize qSmartMaxSize(const QSize &sizeHint,
const QSize &minSize, const QSize &maxSize,
const QSizePolicy &sizePolicy, Qt::Alignment align)
{
if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
QSize s = maxSize;
QSize hint = sizeHint.expandedTo(minSize);
if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
if (!(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag))
s.setWidth(hint.width());
if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
if (!(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag))
s.setHeight(hint.height());
if (align & Qt::AlignHorizontal_Mask)
s.setWidth(QLAYOUTSIZE_MAX);
if (align & Qt::AlignVertical_Mask)
s.setHeight(QLAYOUTSIZE_MAX);
return s;
}
// QSize qSmartMaxSize()会受到排列方向的影响
Q_GUI_EXPORT int qSmartSpacing(const QLayout *layout, QStyle::PixelMetric pm)
{
QObject *parent = layout->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();
}
}
// int qSmartSpacing()则反而需要看父窗口的窗口属性。
enum QLayout::SizeConstraint
The possible values are:
Constant ValueDescription:
QLayout::SetDefaultConstraint 0
The main widget's minimum size is set to minimumSize(), unless the widget already has a minimum size.
QLayout::SetFixedSize 3
The main widget's size is set to sizeHint(); it cannot be resized at all.
QLayout::SetMinimumSize 2
The main widget's minimum size is set to minimumSize(); it cannot be smaller.
QLayout::SetMaximumSize 4
The main widget's maximum size is set to maximumSize(); it cannot be larger.
QLayout::SetMinAndMaxSize 5
The main widget's minimum size is set to minimumSize() and its maximum size is set to maximumSize().
QLayout::SetNoConstraint 1
The widget is not constrained.
不过,这里也有几个问题是需要注意一下下的:
1)被QLayout::setSizeConstraint()所设置的约束,是对qlayout的父窗口而言的。譬如,当某qlayout设置了QLayout::SetMinimumSize约束属性,则它的父窗口(mainW)的minimumSize则会设置为qlayout的totalMinimumSize()值;相当于, mainW->setMinimumSize(qlayout->totalMinimumSize())。
2)QLayout::totalMinimumSize()的计算里有点猫腻。
下面看看代码的,
1)对于第一点,直接看QLayout::activate()函数就可以一目了然的。
bool QLayout::activate()
{
Q_D(QLayout);
if (!d->enabled || !parent())
return false;
if (!d->topLevel)
return static_cast<QLayout*>(parent())->activate();
if (d->activated)
return false;
QWidget *mw = static_cast<QWidget*>(parent());
if (mw == 0) {
qWarning("QLayout::activate: %s \"%s\" does not have a main widget",
QObject::metaObject()->className(), QObject::objectName().toLocal8Bit().data());
return false;
}
activateRecursiveHelper(this);
QWidgetPrivate *md = mw->d_func();
uint explMin = md->extra ? md->extra->explicitMinSize : 0;
uint explMax = md->extra ? md->extra->explicitMaxSize : 0;
// 此处根据不同的约束条件,来给qlayout的父窗口设置相应的Size约束条件
// *****************************************************************
switch (d->constraint) {
case SetFixedSize:
// will trigger resize
mw->setFixedSize(totalSizeHint());
break;
case SetMinimumSize:
mw->setMinimumSize(totalMinimumSize());
break;
case SetMaximumSize:
mw->setMaximumSize(totalMaximumSize());
break;
case SetMinAndMaxSize:
mw->setMinimumSize(totalMinimumSize());
mw->setMaximumSize(totalMaximumSize());
break;
case SetDefaultConstraint: {
bool widthSet = explMin & Qt::Horizontal;
bool heightSet = explMin & Qt::Vertical;
if (mw->isWindow()) {
QSize ms = totalMinimumSize();
if (widthSet)
ms.setWidth(mw->minimumSize().width());
if (heightSet)
ms.setHeight(mw->minimumSize().height());
if ((!heightSet || !widthSet) && hasHeightForWidth()) {
int h = minimumHeightForWidth(ms.width());
if (h > ms.height()) {
if (!heightSet)
ms.setHeight(0);
if (!widthSet)
ms.setWidth(0);
}
}
mw->setMinimumSize(ms);
} else if (!widthSet || !heightSet) {
QSize ms = mw->minimumSize();
if (!widthSet)
ms.setWidth(0);
if (!heightSet)
ms.setHeight(0);
mw->setMinimumSize(ms);
}
break;
}
case SetNoConstraint:
break;
}
d->doResize(mw->size());
if (md->extra) {
md->extra->explicitMinSize = explMin;
md->extra->explicitMaxSize = explMax;
}
// ideally only if sizeHint() or sizePolicy() has changed
mw->updateGeometry();
return true;
}
2)对于第二个问题,以SetMinimumSize为例子,首先得从QLayout::totalMinimumSize()函数开始的:
QSize QLayout::totalMinimumSize() const
{
Q_D(const QLayout);
int side=0, top=0;
if (d->topLevel) {
QWidget *pw = parentWidget();
pw->ensurePolished();
QWidgetPrivate *wd = pw->d_func();
side += wd->leftmargin + wd->rightmargin;
top += wd->topmargin + wd->bottommargin;
}
// 这里需要看这一行就行了:说明我们要再看minimumSize()这个函数。
QSize s = minimumSize();
#ifndef QT_NO_MENUBAR
top += menuBarHeightForWidth(d->menubar, s.width() + side);
#endif
return s + QSize(side, top);
}
好吧,顺藤摸瓜,依次经历了(注意我调试代码时,是用QHBoxLayout作为例子的)QBoxLayout::minimumSize() -> QBoxLayoutPrivate::setupGeom() -> QWidgetItemV2::minimumSize()
最后,发现了QWidgetItemV2::updateCacheIfNecessary()这个函数
void QWidgetItemV2::updateCacheIfNecessary() const
{
if (q_cachedMinimumSize.width() != Dirty)
return;
const QSize sizeHint(wid->sizeHint());
const QSize minimumSizeHint(wid->minimumSizeHint());
const QSize minimumSize(wid->minimumSize());
const QSize maximumSize(wid->maximumSize());
const QSizePolicy sizePolicy(wid->sizePolicy());
const QSize expandedSizeHint(sizeHint.expandedTo(minimumSizeHint));
// 于是那点猫开始浮出水面了~没错,就是qSmartMinSize()函数!
// layout item的大小计算都是经由qSmart*Size()函数来计算的。
const QSize smartMinSize(qSmartMinSize(sizeHint, minimumSizeHint, minimumSize, maximumSize, sizePolicy));
const QSize smartMaxSize(qSmartMaxSize(expandedSizeHint, minimumSize, maximumSize, sizePolicy, align));
const bool useLayoutItemRect = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect);
q_cachedMinimumSize = useLayoutItemRect
? toLayoutItemSize(wid->d_func(), smartMinSize)
: smartMinSize;
q_cachedSizeHint = expandedSizeHint;
q_cachedSizeHint = q_cachedSizeHint.boundedTo(maximumSize)
.expandedTo(minimumSize);
q_cachedSizeHint = useLayoutItemRect
? toLayoutItemSize(wid->d_func(), q_cachedSizeHint)
: q_cachedSizeHint;
if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
q_cachedSizeHint.setWidth(0);
if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
q_cachedSizeHint.setHeight(0);
q_cachedMaximumSize = useLayoutItemRect
? toLayoutItemSize(wid->d_func(), smartMaxSize)
: smartMaxSize;
}
可见,layout item的大小计算都是经由qSmart*Size()函数来计算的。于是,看一下下代码,
Q_GUI_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint,
const QSize &minSize, const QSize &maxSize,
const QSizePolicy &sizePolicy)
{
QSize s(0, 0);
// 根据不同大小策略,不同的考虑,但是,minSizeHint才是影响大小的人
if (sizePolicy.horizontalPolicy() != QSizePolicy::Ignored) {
if (sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)
s.setWidth(minSizeHint.width());
else
s.setWidth(qMax(sizeHint.width(), minSizeHint.width()));
}
if (sizePolicy.verticalPolicy() != QSizePolicy::Ignored) {
if (sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag) {
s.setHeight(minSizeHint.height());
} else {
s.setHeight(qMax(sizeHint.height(), minSizeHint.height()));
}
}
// 这里,只有当minSize大于0时,控件上所设置最小大小才会有影响力。呃,所谓的猫腻就是指这个。是我故弄玄虚了~
s = s.boundedTo(maxSize);
if (minSize.width() > 0)
s.setWidth(minSize.width());
if (minSize.height() > 0)
s.setHeight(minSize.height());
return s.expandedTo(QSize(0,0));
}
相关的其它类型的大小计算,也一并贴上来吧。它们都是alayoutengine.cpp(layout计算引擎)代码。
Q_GUI_EXPORT QSize qSmartMaxSize(const QSize &sizeHint,
const QSize &minSize, const QSize &maxSize,
const QSizePolicy &sizePolicy, Qt::Alignment align)
{
if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
QSize s = maxSize;
QSize hint = sizeHint.expandedTo(minSize);
if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
if (!(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag))
s.setWidth(hint.width());
if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
if (!(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag))
s.setHeight(hint.height());
if (align & Qt::AlignHorizontal_Mask)
s.setWidth(QLAYOUTSIZE_MAX);
if (align & Qt::AlignVertical_Mask)
s.setHeight(QLAYOUTSIZE_MAX);
return s;
}
// QSize qSmartMaxSize()会受到排列方向的影响
Q_GUI_EXPORT int qSmartSpacing(const QLayout *layout, QStyle::PixelMetric pm)
{
QObject *parent = layout->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();
}
}
// int qSmartSpacing()则反而需要看父窗口的窗口属性。