说实在的,qlayout的确很好用。不过,发觉用得越多就越是疑惑。例如动不动,它里面的控件就迭在一起了。唉,忍无可忍了,只好去分析下下它的源码罗~
qt有个叫做qGeomCalc()的函数,qboxlayout里面的控件的大小与位置就是通它来计算的(注意,可不是全由它说了算的,这就是叫人疑惑的原因之一吧)。此函数主要是处理传进来的QLayoutStruct链的,QLayoutStruct主要定义如下
struct QLayoutStruct
{
... ...
// parameters(参数, 传进qGeomCalc()时就已经初始化好的了)
int stretch;
int sizeHint;
int maximumSize;
int minimumSize;
bool expansive;
bool empty;
int spacing;
// temporary storage(临时变量,用来标志是否已经算出了结果)
bool done;
// result(qGeomCalc()的计算结果)
int pos;
int size;
};
可见,qGeomCalc()其它就是计算子控件的大小与位置的。但是,主要的计算还是大小,想想,大小都出来了,放的位置不就明显得很了吗?
而qGeomCalc()计算大小主要分为三种情况来进行的,这其中也隐藏了它的控制规则,三种情况如下:
1)所分配的总空间大小 < 所有子控件的minimumSize总和
此时,会根据控件size由大到小的顺序,减小控件的size。
2)所分配的总空间大小 < 所有子控件的smartSizeHint总和
此时,会先保证满足固定大小的控件的minimumSize,然后再通过逐一地平均地减小各控件的smartSizeHint()来得到size值。
3)所分配的总空间大小 > 所有子控件的smartSizeHint的总和(即,需要扩展子控件大小)
void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
int pos, int space, int spacer)
{
int cHint = 0;
int cMin = 0;
int cMax = 0;
int sumStretch = 0;
int sumSpacing = 0;
bool wannaGrow = false; // anyone who really wants to grow?
// bool canShrink = false; // anyone who could be persuaded to shrink?
bool allEmptyNonstretch = true;
int pendingSpacing = -1;
int spacerCount = 0;
int i;
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
data->done = false;
cHint += data->smartSizeHint();
cMin += data->minimumSize;
cMax += data->maximumSize;
sumStretch += data->stretch;
if (!data->empty) {
if (pendingSpacing >= 0) {
sumSpacing += pendingSpacing;
++spacerCount;
}
pendingSpacing = data->effectiveSpacer(spacer);
}
wannaGrow = wannaGrow || data->expansive || data->stretch > 0;
allEmptyNonstretch = allEmptyNonstretch && !wannaGrow && data->empty;
}
int extraspace = 0;
// sumSpacing子项间的间隔总和
if (space < cMin + sumSpacing) {
/*
Less space than minimumSize; take from the biggest first
*/
int minSize = cMin + sumSpacing;
// 按比例缩小子项间隔
if (spacer >= 0) {
spacer = minSize > 0 ? spacer * space / minSize : 0;
sumSpacing = spacer * spacerCount;
}
// 取出子链的最小值,并排序
QList<int> list;
for (i = start; i < start + count; i++)
list << chain.at(i).minimumSize;
qSort(list);
// 剩下的可用于分配给各个子项的空间
int space_left = space - sumSpacing;
int sum = 0;
int idx = 0;
int space_used=0;
int current = 0;
// 从小到大向后查找,看哪一项开始,以它作为它后面各项的平均值时,会空间不足
while (idx < count && space_used < space_left) {
current = list.at(idx);
space_used = sum + current * (count - idx);
sum += current;
++idx;
}
--idx;
// 不足的空间大小, 以及相关的子项数目
int deficit = space_used - space_left;
int items = count - idx;
/*
// 真正移去的空间为deficitPerItem + remainder/items
* If we truncate all items to "current", we would get "deficit" too many pixels. Therefore, we have to remove
* deficit/items from each item bigger than maxval. The actual value to remove is deficitPerItem + remainder/items
* "rest" is the accumulated error from using integer arithmetic.
*/
int deficitPerItem = deficit/items;
int remainder = deficit % items;
int maxval = current - deficitPerItem;
int rest = 0;
for (i = start; i < start + count; i++) {
int maxv = maxval;
rest += remainder;
if (rest >= items) {
maxv--;
rest-=items;
}
QLayoutStruct *data = &chain[i];
data->size = qMin(data->minimumSize, maxv);
data->done = true;
}
} else if (space < cHint + sumSpacing) {
/*
Less space than smartSizeHint(), but more than minimumSize.
Currently take space equally from each, as in Qt 2.x.
Commented-out lines will give more space to stretchier
items.
*/
int n = count;
int space_left = space - sumSpacing;
int overdraft = cHint - space_left;
// first give to the fixed ones:
// 先处理固定大小的子项
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done
&& data->minimumSize >= data->smartSizeHint()) {
data->size = data->smartSizeHint();
data->done = true;
space_left -= data->smartSizeHint();
// sumStretch -= data->stretch;
n--;
}
}
bool finished = n == 0;
// 除去那个固定值,以及不符合最小值要求的值,平均地从每项中的smartSizeHint减去超出的空间
while (!finished) {
finished = true;
Fixed64 fp_over = toFixed(overdraft);
Fixed64 fp_w = 0;
for (i = start; i < start+count; i++) {
QLayoutStruct *data = &chain[i];
if (data->done)
continue;
// if (sumStretch <= 0)
fp_w += fp_over / n;
// else
// fp_w += (fp_over * data->stretch) / sumStretch;
int w = fRound(fp_w);
data->size = data->smartSizeHint() - w;
fp_w -= toFixed(w); // give the difference to the next
if (data->size < data->minimumSize) {
data->done = true;
data->size = data->minimumSize;
finished = false;
overdraft -= data->smartSizeHint() - data->minimumSize;
// sumStretch -= data->stretch;
n--;
break;
}
}
}
} else { // extra space
// 需要扩展空间的情况
int n = count;
int space_left = space - sumSpacing;
// first give to the fixed ones, and handle non-expansiveness
// 先处理固定大小以及没有扩展属性的子项
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done
&& (data->maximumSize <= data->smartSizeHint()
|| (wannaGrow && !data->expansive && data->stretch == 0)
|| (!allEmptyNonstretch && data->empty &&
!data->expansive && data->stretch == 0))) {
data->size = data->smartSizeHint();
data->done = true;
space_left -= data->size;
sumStretch -= data->stretch;
n--;
}
}
extraspace = space_left;
/*
Do a trial distribution and calculate how much it is off.
If there are more deficit pixels than surplus pixels, give
the minimum size items what they need, and repeat.
Otherwise give to the maximum size items, and repeat.
Paul Olav Tvete has a wonderful mathematical proof of the
correctness of this principle, but unfortunately this
comment is too small to contain it.
*/
int surplus, deficit;
do {
surplus = deficit = 0;
Fixed64 fp_space = toFixed(space_left);
Fixed64 fp_w = 0;
// 按照各自的扩展因子stretch成比例分配剩余空间的大小
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (data->done)
continue;
extraspace = 0;
if (sumStretch <= 0)
fp_w += fp_space / n;
else
fp_w += (fp_space * data->stretch) / sumStretch;
int w = fRound(fp_w);
data->size = w;
fp_w -= toFixed(w); // give the difference to the next
if (w < data->smartSizeHint()) {
deficit += data->smartSizeHint() - w;
} else if (w > data->maximumSize) {
surplus += w - data->maximumSize;
}
}
// 如果不足的空间比较多,则把不足项设置为smartSizeHint值
if (deficit > 0 && surplus <= deficit) {
// give to the ones that have too little
for (i = start; i < start+count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done && data->size < data->smartSizeHint()) {
data->size = data->smartSizeHint();
data->done = true;
space_left -= data->smartSizeHint();
sumStretch -= data->stretch;
n--;
}
}
}
// 如果是富余的空间比较多,则把富余项设置为其最大值
if (surplus > 0 && surplus >= deficit) {
// take from the ones that have too much
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done && data->size > data->maximumSize) {
data->size = data->maximumSize;
data->done = true;
space_left -= data->maximumSize;
sumStretch -= data->stretch;
n--;
}
}
}
} while (n > 0 && surplus != deficit);
if (n == 0)
extraspace = space_left;
}
/*
As a last resort, we distribute the unwanted space equally
among the spacers (counting the start and end of the chain). We
could, but don't, attempt a sub-pixel allocation of the extra
space.
*/
int extra = extraspace / (spacerCount + 2);
int p = pos + extra;
for (i = start; i < start+count; i++) {
QLayoutStruct *data = &chain[i];
data->pos = p;
p += data->size;
if (!data->empty)
p += data->effectiveSpacer(spacer) + extra;
}
}
qt有个叫做qGeomCalc()的函数,qboxlayout里面的控件的大小与位置就是通它来计算的(注意,可不是全由它说了算的,这就是叫人疑惑的原因之一吧)。此函数主要是处理传进来的QLayoutStruct链的,QLayoutStruct主要定义如下
struct QLayoutStruct
{
... ...
// parameters(参数, 传进qGeomCalc()时就已经初始化好的了)
int stretch;
int sizeHint;
int maximumSize;
int minimumSize;
bool expansive;
bool empty;
int spacing;
// temporary storage(临时变量,用来标志是否已经算出了结果)
bool done;
// result(qGeomCalc()的计算结果)
int pos;
int size;
};
可见,qGeomCalc()其它就是计算子控件的大小与位置的。但是,主要的计算还是大小,想想,大小都出来了,放的位置不就明显得很了吗?
而qGeomCalc()计算大小主要分为三种情况来进行的,这其中也隐藏了它的控制规则,三种情况如下:
1)所分配的总空间大小 < 所有子控件的minimumSize总和
此时,会根据控件size由大到小的顺序,减小控件的size。
2)所分配的总空间大小 < 所有子控件的smartSizeHint总和
此时,会先保证满足固定大小的控件的minimumSize,然后再通过逐一地平均地减小各控件的smartSizeHint()来得到size值。
3)所分配的总空间大小 > 所有子控件的smartSizeHint的总和(即,需要扩展子控件大小)
此时, 会先设置好固定大小的子控件,然后再平均地分配剩下的子控件的大小,再看平均分配大小后的子控件是否能够满足条件(大于smartSizeHint 且小于maximumSize),且进行调整(怎么调整,觉得好难说清楚,还是直接看源码吧)
当qboxlayout中各子控件的大小计算出来后,就是通过QWidgetPrivate::setGeometry_sys()来设置各子控件的大小了。QWidgetPrivate::setGeometry_sys()中有几行,可让我傻眼了:
if (extra) { // any size restrictions?
w = qMin(w,extra->maxw);
h = qMin(h,extra->maxh);
w = qMax(w,extra->minw);
h = qMax(h,extra->minh);
}
这样,qGeomCalc()中第一种情况计算出来的大小就完全被无视了,但是再看之后的代码,计算出来的位置却被沿用了。我的天,难怪,缩小主窗口时,子控件会叠在一起了。
void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
int pos, int space, int spacer)
{
int cHint = 0;
int cMin = 0;
int cMax = 0;
int sumStretch = 0;
int sumSpacing = 0;
bool wannaGrow = false; // anyone who really wants to grow?
// bool canShrink = false; // anyone who could be persuaded to shrink?
bool allEmptyNonstretch = true;
int pendingSpacing = -1;
int spacerCount = 0;
int i;
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
data->done = false;
cHint += data->smartSizeHint();
cMin += data->minimumSize;
cMax += data->maximumSize;
sumStretch += data->stretch;
if (!data->empty) {
if (pendingSpacing >= 0) {
sumSpacing += pendingSpacing;
++spacerCount;
}
pendingSpacing = data->effectiveSpacer(spacer);
}
wannaGrow = wannaGrow || data->expansive || data->stretch > 0;
allEmptyNonstretch = allEmptyNonstretch && !wannaGrow && data->empty;
}
int extraspace = 0;
// sumSpacing子项间的间隔总和
if (space < cMin + sumSpacing) {
/*
Less space than minimumSize; take from the biggest first
*/
int minSize = cMin + sumSpacing;
// 按比例缩小子项间隔
if (spacer >= 0) {
spacer = minSize > 0 ? spacer * space / minSize : 0;
sumSpacing = spacer * spacerCount;
}
// 取出子链的最小值,并排序
QList<int> list;
for (i = start; i < start + count; i++)
list << chain.at(i).minimumSize;
qSort(list);
// 剩下的可用于分配给各个子项的空间
int space_left = space - sumSpacing;
int sum = 0;
int idx = 0;
int space_used=0;
int current = 0;
// 从小到大向后查找,看哪一项开始,以它作为它后面各项的平均值时,会空间不足
while (idx < count && space_used < space_left) {
current = list.at(idx);
space_used = sum + current * (count - idx);
sum += current;
++idx;
}
--idx;
// 不足的空间大小, 以及相关的子项数目
int deficit = space_used - space_left;
int items = count - idx;
/*
// 真正移去的空间为deficitPerItem + remainder/items
* If we truncate all items to "current", we would get "deficit" too many pixels. Therefore, we have to remove
* deficit/items from each item bigger than maxval. The actual value to remove is deficitPerItem + remainder/items
* "rest" is the accumulated error from using integer arithmetic.
*/
int deficitPerItem = deficit/items;
int remainder = deficit % items;
int maxval = current - deficitPerItem;
int rest = 0;
for (i = start; i < start + count; i++) {
int maxv = maxval;
rest += remainder;
if (rest >= items) {
maxv--;
rest-=items;
}
QLayoutStruct *data = &chain[i];
data->size = qMin(data->minimumSize, maxv);
data->done = true;
}
} else if (space < cHint + sumSpacing) {
/*
Less space than smartSizeHint(), but more than minimumSize.
Currently take space equally from each, as in Qt 2.x.
Commented-out lines will give more space to stretchier
items.
*/
int n = count;
int space_left = space - sumSpacing;
int overdraft = cHint - space_left;
// first give to the fixed ones:
// 先处理固定大小的子项
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done
&& data->minimumSize >= data->smartSizeHint()) {
data->size = data->smartSizeHint();
data->done = true;
space_left -= data->smartSizeHint();
// sumStretch -= data->stretch;
n--;
}
}
bool finished = n == 0;
// 除去那个固定值,以及不符合最小值要求的值,平均地从每项中的smartSizeHint减去超出的空间
while (!finished) {
finished = true;
Fixed64 fp_over = toFixed(overdraft);
Fixed64 fp_w = 0;
for (i = start; i < start+count; i++) {
QLayoutStruct *data = &chain[i];
if (data->done)
continue;
// if (sumStretch <= 0)
fp_w += fp_over / n;
// else
// fp_w += (fp_over * data->stretch) / sumStretch;
int w = fRound(fp_w);
data->size = data->smartSizeHint() - w;
fp_w -= toFixed(w); // give the difference to the next
if (data->size < data->minimumSize) {
data->done = true;
data->size = data->minimumSize;
finished = false;
overdraft -= data->smartSizeHint() - data->minimumSize;
// sumStretch -= data->stretch;
n--;
break;
}
}
}
} else { // extra space
// 需要扩展空间的情况
int n = count;
int space_left = space - sumSpacing;
// first give to the fixed ones, and handle non-expansiveness
// 先处理固定大小以及没有扩展属性的子项
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done
&& (data->maximumSize <= data->smartSizeHint()
|| (wannaGrow && !data->expansive && data->stretch == 0)
|| (!allEmptyNonstretch && data->empty &&
!data->expansive && data->stretch == 0))) {
data->size = data->smartSizeHint();
data->done = true;
space_left -= data->size;
sumStretch -= data->stretch;
n--;
}
}
extraspace = space_left;
/*
Do a trial distribution and calculate how much it is off.
If there are more deficit pixels than surplus pixels, give
the minimum size items what they need, and repeat.
Otherwise give to the maximum size items, and repeat.
Paul Olav Tvete has a wonderful mathematical proof of the
correctness of this principle, but unfortunately this
comment is too small to contain it.
*/
int surplus, deficit;
do {
surplus = deficit = 0;
Fixed64 fp_space = toFixed(space_left);
Fixed64 fp_w = 0;
// 按照各自的扩展因子stretch成比例分配剩余空间的大小
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (data->done)
continue;
extraspace = 0;
if (sumStretch <= 0)
fp_w += fp_space / n;
else
fp_w += (fp_space * data->stretch) / sumStretch;
int w = fRound(fp_w);
data->size = w;
fp_w -= toFixed(w); // give the difference to the next
if (w < data->smartSizeHint()) {
deficit += data->smartSizeHint() - w;
} else if (w > data->maximumSize) {
surplus += w - data->maximumSize;
}
}
// 如果不足的空间比较多,则把不足项设置为smartSizeHint值
if (deficit > 0 && surplus <= deficit) {
// give to the ones that have too little
for (i = start; i < start+count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done && data->size < data->smartSizeHint()) {
data->size = data->smartSizeHint();
data->done = true;
space_left -= data->smartSizeHint();
sumStretch -= data->stretch;
n--;
}
}
}
// 如果是富余的空间比较多,则把富余项设置为其最大值
if (surplus > 0 && surplus >= deficit) {
// take from the ones that have too much
for (i = start; i < start + count; i++) {
QLayoutStruct *data = &chain[i];
if (!data->done && data->size > data->maximumSize) {
data->size = data->maximumSize;
data->done = true;
space_left -= data->maximumSize;
sumStretch -= data->stretch;
n--;
}
}
}
} while (n > 0 && surplus != deficit);
if (n == 0)
extraspace = space_left;
}
/*
As a last resort, we distribute the unwanted space equally
among the spacers (counting the start and end of the chain). We
could, but don't, attempt a sub-pixel allocation of the extra
space.
*/
int extra = extraspace / (spacerCount + 2);
int p = pos + extra;
for (i = start; i < start+count; i++) {
QLayoutStruct *data = &chain[i];
data->pos = p;
p += data->size;
if (!data->empty)
p += data->effectiveSpacer(spacer) + extra;
}
}