QT4.4以后的版本(包括4.4)
QT的Scene布局系统,主要构成如下图所示:
- QGraphicsWidget 通过 QGraphicsWidget::setLayout() 来绑定 QGraphicsLayout
- 当 Widget 发生 resized 事件的时候,layout自动对widget的孩子进行相应的排列。
- 因为 QGraphicsLayout 是从 QGraphicsLayoutItem继承来的,所以QGraphicsLayout能够被其他的QGraphicsLayout以及子类进行排列
当前文本中专有名词:
layout :布局
geometry : 物理形状,物理大小
size hint : 类似 geometry, 说明的是UI 的大小信息
item : 项目,layout中用来排序和布局的单元
layout所属widget : Widget A 通过接口setLayout(B)绑定 Layout B后,A就是B所属的widget
QT 建议使用现成的三个布局类来处理日常的工作任务,
如果有需求需要实现自定义的布局类,从 QGraphicsLayout继承,并且需要实现如下的几个接口:
函数:
描述:
备注:
public: virtual void setGeometry(const QRectF &rect);
当 layout的 geometry被设置的时候调用,在自定义layout类的重载实现中可以在函数中保存geometry.
protected: virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const = 0;
返回layout的 size hint(大小预估值)
public: virtual int count() const = 0;
返回layout中item的个数
public: virtual QGraphicsLayoutItem *itemAt(int index) const = 0;
返回layout中指向索引 index 位置的 item 指针
public: virtual void removeAt(int index) = 0;
从layout中移除index位置的item, 但不摧毁相应的item
布局排版方式:
当layout的geometry变化的时候,layout会立刻重新排版,对layout管理的所有item调用接口setGeometry(),这种重新排版叫做“activing”布局。layout的排版会使得自己的geometry匹配到当前QGraphicsLayoutItem::contentsRect()所管理的区域,QGraphicsLayout会缓存所有它管理的item的大小,避免频繁调用setGeometry()。
布局排版激活方式:
通常有两种方式来激活layout的排版动作,通过调用activate()或者调用invalidate()。区别如下:
用 activate()方式激活的排版会立刻生效,用invalidate()方式是延迟排版的,因为它是通过抛一个LayoutRequest 事件给当前管理的widget,invalidate()同时会清空相关的cache。注意:invalidate()是个虚函数,用户可以重定义它的行为。
layout的事件处理:
QGraphicsLayout 通过接口 virtual void widgetEvent(QEvent *e);监听layout当前管理的widget的事件,widget的事件会先送到QGraphicsLayout进行处理。
layout的构造:
构造一个QGraphicsLayout :
QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent)
parent会被传递给,QGraphicsLayoutItem,并且QGraphicsLayoutItem的 isLayout的标志被设置为true,
如果 parent 是一个 QGraphicsWidget, 则当前的 layout 会被 绑定到 parent代表的 Widget 上(Widget上原来的layout会被delete掉)。
成员函数表:
函数:
说明:
void QGraphicsLayout::activate ()
激活layout, 导致 layout 中的所有item立刻重排,对每个item顺序调用setGeometry(),使得layout 管理的所有item 所占区域匹配layout当前属的widget的contensRect(). 注意 layout 属的 widget 也是 layout 的parent.
当前函数依赖 count() 和 itemAt(),
在顺序或者递归调用 activate()的时候,activate 不做任何事情。比如在相应resized,导致的items重排的时候触发activate。
注意layout 会充分利用缓存的几何数据来优化排版过程,要强制性清空这些缓存的话,可以在activate之前调用 invalidate()
void QGraphicsLayout::addChildLayoutItem ( QGraphicsLayoutItem * layoutItem ) [protected]
这个函数是提供给制作自定义layout类时候的一个非常方便的工具函数,这个函数会遍历layoutItem (当layoutItem本身是一个layout的时候)中所有的item,并且重新定位item的parent为当前layout的最近的QGraphicsWidget。
如果layoutItem已经在另外一个layout中,也回把layoutItem从原layout移除。
如果自定义的layout类想要特殊的行为,可以重载当前函数。
这个函数由Qt 4.6 引入
int QGraphicsLayout::count () const [pure virtual]
纯虚函数,子类必须实现。
void QGraphicsLayout::getContentsMargins ( qreal * left, qreal * top, qreal * right, qreal * bottom ) const [virtual]
重载 QGraphicsLayoutItem::getContentsMargins().
void QGraphicsLayout::invalidate () [virtual]
清空 Geometry 缓存,清空 size hint 缓存,并 post 一个 LayoutRequest 事件到layout所属的widget
bool QGraphicsLayout::isActivated () const
如果layout当前正处理activated 中,返回true,否则返回false。
正处在activated中,意思就是调用activated还没用返回。
Note: 有效防止递归中调用activated导致的死锁问题。
QGraphicsLayoutItem * QGraphicsLayout::itemAt ( int i ) const [pure virtual]
纯虚函数,子类必须实现。
返回索引 i 代表的item的指针。
这个函数加上cout()等于提供了一套layout的迭代访问方法。
子类可以完全自主的决定 i 与 item 的对应关系,和视觉上的 item 排列没用必然联系
void QGraphicsLayout::removeAt ( int index ) [pure virtual]
纯虚函数,子类必须实现。重载实现中,需要根据count()的数量判断index的有效性。
在重载实现中,必须保证,被移除的item的parentLayoutItem()不再指向当前layout,因为这个函数就是用来把item从layout层次树上移除的。
如果移除item的layout还要被再次利用,我们建议在移除item的时候,同事对item指针delete操作。当然graphics view 框架本身并没用依赖这一特性。
void QGraphicsLayout::setContentsMargins ( qreal left, qreal top, qreal right, qreal bottom )
设置内容的边距,默认的边距取决于当前stype的定义,通过pixelMetric查询QStyle::PM_LayoutLeftMargin, QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and QStyle::PM_LayoutBottomMargin可以得到定义的默认值。
对于下级layout,默认的边距为0.
变更内容的边距会倒在layout调用invalidate
void QGraphicsLayout::updateGeometry () [virtual]
重载实现QGraphicsLayoutItem::updateGeometry().
void QGraphicsLayout::widgetEvent ( QEvent * e ) [virtual]
虚函数,事件处理接口,会接收到layout所属的widget的所有事件。 QGraphicsLayout用这个事件处理接口,监听layout关心的widget事件,比如widget的geometry变更,layout 变更,layout方向变更等。
e 是事件指针。
你可以重载这个接口,实现自定义布局类