浅谈Qt QGraphics体系及刷新机制介绍

所属分类: 软件编程 / C 语言 阅读数: 112
收藏 0 赞 0 分享

概述

Qt的三大体系:QWidget、QGraphics、Quick,其中QGraphics图形框架算是这三个中比较高级的一种用法了,并且使用起来相比另外两个体系会更加的复杂一些,不过它能实现的功能却非常强大,主要体现在对图元的管理,它独特的刷新机制可以在众多的图元中都能够很好的管理,保证整个交互的流畅度。

而这里要描述的就是QGraphics体系的刷新机制以及该体系中相关元素的使用方式及特点。

QGraphics体系的三大元素

QGraphics体系中最重要的三大元素:QGraphicsView、QGraphicsScene、QGraphicsItem,这三者构成了QGraphics体系最基础的模型框架,也是在使用过程中必不可少的元素。

  • QGraphicsScene :场景。场景用于装载所有item元素,它是一个无限大的空间,但是我们在使用的时候通常会指定一块区域(setSceneRect)用于安放所有的item元素,并且item之间的逻辑,以及消息传递都是从场景中进行统一管理,比如我门要捕捉鼠标消息,或者触控消息,统一在Scene中获取,然后分发给需要的item,可以说Scene就是一个大管家;
  • QGraphicsView:视图。视图就好比一个窗口,用于展示当前Scene中的元素,上面说到,Scene是一个无限大的空间,当view移动到Scene某个位置,就能看到该位置上的Item元素。
  • QGraphicsItem:每一个单独的图元,QGraphicsItem是一个基类,还有很多子类继承于它,也就是这一系列的item行程了整个QGraphics体系中的每一个图元。

看一下这三者的关系:

再用一个非常形象的类比应该就会很明白这三者的关系了:
Scene就好比天空,无限大,而Item就是天空中的云朵,可以有很多云,而view就好比一扇窗户,透过窗户可以看到天空中的云,而一片天空可以通过很多扇窗户去看。所以一个Scene可以同时对应多个View,但是一个View只能对应一个Scene。

刷新机制

OK ,有了以上铺垫,终于可以进入都今天的主题,QGraphics体系中的刷新机制到底是怎样的呢?

我们都知道,QWidget是以窗口式刷新,每次会渲染整个窗口达到刷新目的,而QGraphics中可以局部刷新,也就是说可以只刷新某一个图元,而其他的元素保持不动,这是二者在刷新机制上很大的不同,以致于QGraphics在渲染大量图元的时候也能很流畅。

看以下图示:

这里的itemA在刷新的时候,ItemB是不会刷新的,这是两个独立的Item,但是考虑以下这种情况:

当两个item有交集的时候,这时候如果刷新ItemA,那么ItemB也会相应的刷新,同样,刷新ItemB的时候,ItemA也会触发刷新。

并且要注意的是,上面说到的ItemA和ItemB的交集,并不局限于这两者只是在同一平面上真实的交集,也就是说,即便是二者的ZValue不同, 但是从Z轴俯视的角度看到二者有交集也会触发对方相应的刷新。还有一种情况,如果两个Item是父子关系,也会全部刷新。

所以上面图示,即便ItemA和ItemB的ZValue不同,还是会触发刷新。这是QGraphicsItem默认的行为。

那么,这样会带来什么问题呢,如果我们做的是一个实时性非常高的动作,比如在屏幕上画线,线条要实时刷新,而这时候如果同时触发了其他Item的刷新,并且该Item刷新比较耗时,那么就会直接影响我们画线item的刷新,直观的感觉就是卡顿,线条折线严重,因为刷新界面都是在主线程中执行的,耗时操作将会阻塞。

避免重复刷新

那么该怎么解决这个问题呢?还真有办法。

我们的目的就是即便是多个Item重叠,那在刷新其中一个的时候不要让其他Item也跟着刷新,OK,QGraphicsItem中提供了一个枚举:

enum QGraphicsItem::CacheMode

设置Item的缓存模式,我们来看一下缓存的类型:

默认就是不做缓存,然后每次都会重新绘制。

QGraphicsItem::ItemCoordinateCache模式, QGraphicsItem会创建一个具有可配置大小/分辨率的屏幕外像素缓冲区,但是渲染质量通常会降低,具体取决于缓存的分辨率和项目转换。 第一次重绘项时,它会将自身渲染到缓存中,然后缓存将在每次后续曝光中重复使用。

QGraphicsItem::DeviceCoordinateCache模式,此模式适用于可以移动但不旋转,缩放或剪切的项目。 如果直接或间接转换项目,将自动重新生成缓存。 与ItemCoordinateCacheMode不同,DeviceCoordinateCache始终以最高质量呈现。

可以根据实际需要选择使用哪种缓存模式,然后通过调用函数setCacheMode来设置。

函数原型为:

void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize = QSize())

可选的logicalCacheSize参数仅由ItemCoordinateCache模式使用,并描述缓存缓冲区的分辨率,如果logicalCacheSize是(100,100),QGraphicsItem将使项目适合图形内存中的100x100像素,而不管项目本身的逻辑大小。

默认情况下,QGraphicsItem使用boundingRect()的大小。对于除ItemCoordinateCache之外的所有其他缓存模式,将忽略logicalCacheSize。
如果项目花费大量时间重绘自身,则缓存可以加快渲染速度。在某些情况下,缓存也会降低渲染速度,特别是当项目花费的时间少于重绘时间时,QGraphicsItem会从缓存中重新绘制。

启用缓存后,项目的paint()函数通常会绘制到屏幕外的pixmap缓存中,对于任何后续重绘请求,Graphics View框架将从缓存中重绘。这种方法特别适用于QGLWidget,它将所有缓存存储为OpenGL纹理。

注意:启用缓存并不意味着只有在响应显式update()调用时才会调用item的paint()函数。例如,在内存压力下,Qt可能决定丢弃一些缓存信息;在这种情况下,即使没有update()调用(也就是说,没有启用缓存),也会调用item的paint()函数。

那么,既然会绘制到pixmap缓存中,如果数据量特别多,导致pixmap缓存不够怎么办,这时候就需要通过更改QPixmapCache的缓存限制以获得最佳性能。

QPixmapCache

QPixmapCache类为pixmaps提供应用程序范围的缓存。
此类是使用QPixmap优化绘图的工具。

QPixmapCache不包含任何成员数据,只包含访问全局像素图缓存的静态函数。它创建了一个内部QCache对象来缓存pixmaps。

默认的pixmap缓存空间为10MB,如果我们需要缓存的数据量很大,那么就需要修改这个值,通过调用静态函数setCacheLimit来进行设置即可。

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多