Created
September 20, 2015 10:55
-
-
Save lyarbean/09f6eeb0b6aadeca4d42 to your computer and use it in GitHub Desktop.
Qt Graphics View Framework 实践
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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