Skip to content

Instantly share code, notes, and snippets.

@lyarbean
Created September 20, 2015 10:55
Show Gist options
  • Save lyarbean/09f6eeb0b6aadeca4d42 to your computer and use it in GitHub Desktop.
Save lyarbean/09f6eeb0b6aadeca4d42 to your computer and use it in GitHub Desktop.
Qt Graphics View Framework 实践
Qt Graphics View Framework 简介
Qt Graphics View Framework (以下简称GVF)是一套成熟的基于Qt Widgets的框架,为大量二维物件的交互提供了优化的管理方法和渲染机制。 这个框架内建了专用事件传递架构,使得物件可以处理键盘时间和鼠标时间,从而允许被选取,移动和旋转等。 此外这个框架提供了BSP(Binary Space partitioning)树索引模式,以快速地定位物件,从而也加速了区域的渲染,为实时显示提供了支持。
GVF首次亮相于Qt 4.2,替换了QCanvas。
架构
GVF由三部分组成,场景、视图和物件, 类似于Model View Framework中的模型、视图和代表元。
物件
物件即为被观察的独立个体,在GVF中,均为QGraphicsItem(抽象类)的子类。每个物体拥有x、y和z三个坐标,每类物件有唯一的Type。一个物件只能属于一个场景,如果它有父物件(parentItem),则属于父物件所属的场景。
GVF提供了四类继承自QGraphicsItem的物件:
QAbstractGraphicsShapeItem
QGraphicsItemGroup
QGraphicsLineItem
QGraphicsObject
QGraphicsPixmapItem
QAbstractGraphicsShapeItem附加了画刷(Brush)和画笔(Pen)属性,GVF中提供了5个具体子类:
QGraphicsEllipseItem
QGraphicsPathItem
QGraphicsPolygonItem
QGraphicsRectItem
QGraphicsSimpleTextItem
QGraphicsObject则也继承自QObject,从而附加了十一个信号,其中两个来自QObject,以下是新加的信号:
void enabledChanged()
void opacityChanged()
void parentChanged()
void rotationChanged()
void scaleChanged()
void visibleChanged()
void xChanged()
void yChanged()
void zChanged()
场景
GVF提供QGraphicsScene作为场景类。 QGraphicsScene作为物件的容器,用于组织和管理被观察的物件,并负责传递事件给每个物件,维护各个物件的状态。
视图
GVF提供QGraphicsView作为视图类,用于可视化场景的内容。QGraphicsView继承自QAbstractScrollArea而获得滚动的能力,同时也提供了视图变化,场景区域选取和各种预设的事件处理能力。
GVF实践
在GDS中,所有的物件都是多边形,即使是线型,都可被视作宽度极小的矩形。因此,直觉上会采用QGraphicsPolygonItem作为物件的类型,然而考虑到具体使用的情形是,不是为每个物价设置画笔,而是为一层!因此,可以使用z坐标作为层次,而物件也无需自己拥有渲染相关的属性,只需要在绘制时从场景去获取。
既然如此,需要扩展QGraphicsScene,为其添加画笔和画刷的访问方法:
QPen pen ( quint64 index ) const;
QBrush brush ( quint64 index ) const;
既然不使用QGraphicsPolygonItem,就直接从QGraphicsItem派生一个新的物件类型(GraphicsPolygonItem),记得添加类型标记:
enum {
Type = UserType + 8
};
然后实现paint方法:
Q_D(GraphicsPolygonItem);
const QRectF &rect = d->m_data.constData()->m_rect;
qreal level = option->levelOfDetailFromTransform(painter->worldTransform());
qreal thresholdMin = qMin(rect.width(), rect.height()) * level;
qreal thresholdMax = qMax(rect.width(), rect.height()) * level;
// The scene must be a GraphicsScene
auto s = static_cast<GraphicsScene *>(scene());
quint64 stack = quint64(zValue());
// The pen for this item
auto p = s->pen(stack);
// Adjust screen size to scene size
p.setWidthF(1 / level);
painter->setCompositionMode(QPainter::CompositionMode(s->compositionMode()));
if (thresholdMin >= 5) { // Detail mode
// The brush for this item
auto b = s->brush(stack);
// Adjust screen size to scene size
b.setTransform(QTransform(painter->worldTransform().inverted()));
painter->setBrush(b);
painter->setPen(p);
// Apply composition mode
painter->drawPath(d->m_data.constData()->m_painterPath);
// ...
这里使用了一个小伎俩,如果物件看得很清晰,那就画清晰点,如果看起来很细,那就画条线,如果真的看不到一个点,那就算了。
一个物件有多种状态,如ItemIsSelectable和ItemIsMovable,根据不同的状态来调节渲染的参数可以丰富物件的外观。
为了实现缩放功能,派生了视图类并覆写了wheelEvent;为了统计绘制的时间,也覆写了paintEvent:
void GraphicsView::paintEvent(QPaintEvent *event)
{
static QTime tickTime;
tickTime.restart();
QGraphicsView::paintEvent(event);
qDebug() << "Paint view:" << tickTime.elapsed() << "milliseconds";
}
性能
显式共享
考虑到许多物件拥有一致的外形,只是所在的位置和层次的不同,如果它们共享同一个外形数据,将节约不少内存。事实确实如此,发现不同外形的个数大约是总物件数的十分之一!然而,由于采用线性匹配,所耗时间非常大。
BSP树构建
相比于QGraphicsSceneNoIndex模式,物件的插入同时也需要更新BSP树,代价是颇高的,包括时间开销和内存开销,其中设计的矩形比较贡献了不小的常数!下面的实验使用此模式。
实验
实验的样本是/home/yyuan/tflexjobs/gds//Ferrari_crop_with_all_layers_400.gds(288MB), 当然,目前不能直接解析GDS,只能导出为内部格式:/home/beyan/tflexjob/crrr.txt(498M)
输出结果如下
0 0 8 1261824
Item creation: 6349
Detach views: 0
Paint view: 0 milliseconds
Item insertion: 21400
Attach views: 2922
Paint view: 2912 milliseconds
Paint view: 3068 milliseconds
Paint view: 3031 milliseconds
Paint view: 2486 milliseconds
Paint view: 1438 milliseconds
Paint view: 836 milliseconds
Paint view: 467 milliseconds
Paint view: 276 milliseconds
Paint view: 158 milliseconds
Paint view: 116 milliseconds
Paint view: 100 milliseconds
Paint view: 91 milliseconds
Paint view: 88 milliseconds
Paint view: 85 milliseconds
Paint view: 82 milliseconds
Paint view: 82 milliseconds
Paint view: 83 milliseconds
Paint view: 81 milliseconds
Paint view: 80 milliseconds
Paint view: 79 milliseconds
Item Creation是指专用的线程负责读入文件并构造为物件,一共为1261824个
Detach views是为了更准确地统计物件加入场景的时间而特意分离视图
Item insertion的时间为21.4秒,相比于NoIndex模式,大约多花费了15秒!
Attach views 重新关联视图和场景
Paint view:初始化时绘制的结果是一个um一个像素,然后按1.5倍进行放大。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment