JefferyZhou

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

概述:通过前面对QGraphicsLinearLayout, QGraphicsGridLayout , QGraphicsAnchorLayout的初步分析,可以知道,QT的布局引擎分成两个:

QGraphicsLayoutEngine 负责对 QGraphicsLinearLayout, QGraphicsGridLayout 进行排版。QGraphicsAnchorLayoutPrivate 负责对 QGraphicsAnchorLayout 进行排版。这种分割是基于布局的两种类型。QGraphicsLinearLayout和QGraphicsGridLayout 是属于表格驱动的布局,而QGraphicsAnchorLayout是属于有向图驱动的布局(锚点,吸附点的概念, 这个布局上是一个由锚点组成的有向图)。

纵观QGraphicsLinearLayout和QGraphicsGridLayout ,不难发现,两者的紧密关系,QGraphicsLinerLayout(Qt::Vertical) 就相当于一列多行的 QGraphicsGridLayout,而QGraphicsLinearLayout(Qt::Horizontal )相当于一行多列的QGraphicsGridLayout.

先来看下的实作方式。回顾一下类图:Image(126)

在接受排版前先看一个工具函数引来的规则:

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的时序调用示意图:

Image(128)

排版的具体工作全部集中在 QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo,

const QRectF &contentsGeometry);

在解析排版算法前,看下QGridLayoutEngine模块相关的类图:

Image(129)

对上述类做一个简述:

  • 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。

用图示如下:

Image(130)

在整个算法过程中,分工非常明确,

  • 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 中定义的边距进行修正反馈

待续。

posted on 2012-09-24 17:09  JefferyZhou  阅读(2744)  评论(0编辑  收藏  举报