概述:通过前面对QGraphicsLinearLayout, QGraphicsGridLayout , QGraphicsAnchorLayout的初步分析,可以知道,QT的布局引擎分成两个:
QGraphicsLayoutEngine 负责对 QGraphicsLinearLayout, QGraphicsGridLayout 进行排版。QGraphicsAnchorLayoutPrivate 负责对 QGraphicsAnchorLayout 进行排版。这种分割是基于布局的两种类型。QGraphicsLinearLayout和QGraphicsGridLayout 是属于表格驱动的布局,而QGraphicsAnchorLayout是属于有向图驱动的布局(锚点,吸附点的概念, 这个布局上是一个由锚点组成的有向图)。
纵观QGraphicsLinearLayout和QGraphicsGridLayout ,不难发现,两者的紧密关系,QGraphicsLinerLayout(Qt::Vertical) 就相当于一列多行的 QGraphicsGridLayout,而QGraphicsLinearLayout(Qt::Horizontal )相当于一行多列的QGraphicsGridLayout.
在接受排版前先看一个工具函数引来的规则:
QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom)
if( QGraphicsLayout 有设置margin) 则返回之
else,则判断有没有 parent ,if (没有 parent) marign = 0.0,
else 判断一下parent 是不是 layout if( 是),margin = 0.0, Qt 规定的subLayout 的margin 为0
else 则调用 QGraphicsWidget::style()::pixelMetric(QStyle::PixelMetric)获取
对于layout来说,触发排版引擎的接口是:setGeometry(rect), 看下setGeometry的时序调用示意图:
排版的具体工作全部集中在 QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo,
const QRectF &contentsGeometry);
在解析排版算法前,看下QGridLayoutEngine模块相关的类图:
对上述类做一个简述:
- QLayoutParameter 是一个私有的模板工具类,可以给变量附加一个状态(默认,用户自定义,缓存)
- QStretchParameter 是从 QLayoutParameter<int> 继承而来的,参数的默认值为-1,用来存储布局原始的伸展因子
- QGridLayoutBox 集中存储布局中一个单元格大小相关的参数,主要包括minSize, maxSize, prefedSize, descentSize, ascentSize,同时提供 add 和 combine 接口用来 扩充以及合并单元格。注意这里接口的理解,通常add == expand, combine == merge。
- QGridLayoutMultiCellData合 有两个成员变量,一个是QGridLayoutBox,另外一个是默认值为-1的 stretch
- MultiCellMap 成,是一个模板参数化工具类定义如下:typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap;如可以看得出这个工具类把(indexL, indexR)映射到一个QGridLayoutMultiCellData,用来存储单元格的几何数据。通常我们会想到把表格中的坐标(row, column)映射过去,其实在这里Qt用这个存储行信息,来映射区段,(start, pan) 映射到 一个 MultiCellData,也就相当于[start, start+pan)的单元格对应一个MultiCellData,注意这里还有一个小细节,映射的key是一个左闭右开的区间。
- QGridLayoutRowDatad存储表格中的整行数据,具体包括如下:
QBitArray ignore; // ### rename q_
QVector<QGridLayoutBox> boxes;
MultiCellMap multiCellMap;
QVector<int> stretches;
QVector<qreal> spacings;
bool hasIgnoreFlag;
下全部是单元格的控制参数包括,每一个单元格对应 一个 QGridLayoutBox, 一个 stretch, 一个 spaceing, 一个 ignore 标志位。这个类是布局排版引擎中的一个重要的工具类。
- QGridLayoutRowInfo 存储表格中一行的原生数据,这些数据没有经过排版整合,具体包括如下:
int count;
QVector<QStretchParameter> stretches;
QVector<QLayoutParameter<qreal> > spacings;
QVector<Qt::Alignment> alignments;
QVector<QGridLayoutBox> boxes;
这的数据面向调用者,调用者在布局表格上进行增加和删除行。这里的信息需要经过一个解析过程会分解到一个QGridLayoutRowData里面。
LayoutEngine 把数据分成三部分,
用户数据数据,
从用户数据转换成排版需要的layoutItem属性
从layoutItem的属性转换成布局坐标和Size。
用图示如下:
在整个算法过程中,分工非常明确,
- engine主体负责对grid进行排版,计算两个信息rowData 和 columnData,详细的算法实现参考函数:QGridLayoutEngine::ensureColumnAndRowData
- rowData和colunmData负责对cell 进行自适应的空间分配,详细的算法实现参考函数:QGridLayoutRowData::calculateGeometries
- griditem 负责对widget的坐标和大小计算,详细的算法实现参考函数:QGridLayoutItem::geometryWithin
上述算法的实现中对rowData和columnData的填充较为繁琐,但可以提炼为以下几件事情:
- 获取用户输入数据, 主要是rowInfo 和columnInfo
- 根据style 初始化option, spacing
- 提取用户的spacing 和stretch 输入数据到rowData,这里需要做合并运算,因为提供给用户设置的数据来源有三,1. row和column的的数据, 2. 每一个item的spacing和stretch数据,3. style option (比如最后一行,和最后一列的spacing是需要特殊处理的)和 特殊的widget提供的数据
- 填充rowData的boxes 数据,也据是行的最小宽度,最大宽度,(首选)理想宽度等信息
- 计算row 的spacin, 对于最后的行和列,PushButton 之间需要结合用style 中定义的边距进行修正反馈
待续。