Qt视图场景框架之SVG

来源:互联网 发布:经典书籍推荐知乎 编辑:程序博客网 时间:2024/06/13 14:33

Qt提供了对SVG图片的基本支持,对外提供了4个基本的类封装,支持SVG图片渲染展示,可以在GUI widget中和视图场景模型中加载使用SVG图片。由于Qt对SVG封装类较少提供的功能接口也较少,对于复杂的SVG操作还需要开发人员自己实现。另外,Qt只支持SVG 1.2 Tiny版本(SVG的一个子集标准),相对于浏览器对SVG标准的全量支持,Qt在渲染超出SVG1.2 Tiny版本的SVG图片是会出现问题,这也是在用Qt Svg开发过程中常见的问题而非Qt bug,所以在使用Qt Svg开发时要求SVG版本不能高于1.2 Tiny。Qt官方也暂无计划更新SVG模块,所以如果要使用的SVG图片版本较高,就只能考虑使用其他第三方类库或QtWebEngine来渲染实现了。


直接与SVG操作相关的类Qt只提供了4个:

QGraphicsSvgItem

QSvgGenerator

QSvgRenderer

QSvgWidget

这无疑满足不了我们复杂的需求开发,针对博主本人在开发过程中遇到的问题和解决方法总结如下:

1.     视图场景中SVG内部元素打散和重新组合

首先说组合,组合在视图场景中有两种通用实现方法:QGraphicsItemGroup方式和parentItem方式。

利用QGraphicsItemGroup比较简单,将Item添加到QGraphicsItemGroup中即可实现多个item组合,只需对 QGraphicsItemGroup这个Item操作即可。

auto group =scene->createItemGroup(scene->selecteditems());

scene->destroyItemGroup(group);

使用parentItrem方式,其实QGraphicsItemGroup就是对parentItrem方式的封装。

这里的SVG内部元素打散是说单独渲染绘制SVG内部元素,多个元素之间是独立显示。内部元素单独绘制显示有个限制,就是这个元素必须拥有唯一的id,QGraphicsSvgItem通过setElementId设置需要绘制元素的id,该元素就会以独立的item绘制显示出来。

处理过程如下:

1、 使用QSvgRenderer加载SVG文件

2、 创建空的QGraphicsSvgItem实例

3、 给QGraphicsSvgItem实例使用setSharedRenderer设置1中的QSvgRenderer为共享Renderer

4、 给QGraphicsSvgItem实例设置要绘制元素的id,则当前item只绘制该item

所有元素都单独绘制出来后,可以使用组合组合所有元素为一个完整的SVG,同时可以再次布局SVG中不同元素的位置。

2.     视图场景中SVG元素属性修改显示

SVG图形加载到视图场景中后,是通过QT Painter绘制图形到窗口,已经与SVG文件脱离了关系,窗口中的图形只能显示,无法再次编辑。只是显示SVG图形无法满足博主的需求,博主需要在视图场景中再次编辑SVG图形的某些样式,QT没有提供类似功能接口,只能自行实现。通过分析以上QT提供的几个SVG操作类,我们可以采用变通方式实现编辑SVG图形:修改SVG文件中xml内容,保存再次加载到视图场景中,实现视图场景中SVG图形变化,实现二次编辑功能效果。

具体实现方法如下:

1、 读取SVG文件到内存,并使用QGraphicsSvgItem加载展示SVG图形

2、 通过XML读写类编辑内存中SVG文件内容

3、 获取QGraphicsSvgItem的QSVGRenderer指针,QSVGRenderer重新加载渲染SVG文件即可

3.     视图场景中SVG图形的大小调整

在视图场景中通过拖拽实现SVG图形整体大小调整,由于SVG是矢量图形,这里的大小调整就是拉伸或者缩放SVG图形。通过交互式鼠标拖拽SVG图形实现水平或垂直拉伸,Qt视图场景框架本身并没有现成接口支持鼠标拉伸item功能,需要自行实现。这里我们自行实现了GraphicsItemResizer类,实现item选中虚线边框和可拖拽小矩形热点。针对SVG图形Item的拖拽我们子类SVGItemResizer实现,我们使用一种简单的方式,使用水平或垂直方向缩放功能实现拖拽效果,也就是说拖拽SVG图形Item实际上是在放大或缩小SVG图形,缩放比例记录在item的Transform中,可以方便获取。这种方式也即boundingRect大小不变,改变的是场景中sceneBoundingRect大小。

4.     视图场景中多个SVG图形合并生成新的SVG图形文件

在视图场景中改变SVG图形Item大小不止Transform缩放方法这一种,首先是因为它实现简单且可以实现拖拽大小效果。其次,更重要的一个原因是多SVG合并功能需要用到Transform。

在视图场景中编辑SVG图形最重要的一个功能场景就是导入的多个SVG图形重新布局位置,然后合并成为一个新的SVG图形画面,并保存成为一个SVG文件。实现该功能的思路是:提取单个SVG文件中svg标签中的xml内容,将内容放入g标签内作为新SVG文件中的一个子节点,然后按照SVG item层级由低到顶在新SVG文件中从前到后排列g标签,最后保存为一个SVG文件。

这里有几点说明:

1、 SVG图形是根据文件中元素顺序从前往后绘制的,即xml文件中排在前面的元素先绘制,处在底层,排在后面的元素后绘制,处在上层,SVG中本身没有设置元素层级的属性,是通过xml文件中元素顺序实现的,这一点与视图场景中的item不同,item本身有ZValue值可以设置,所以在合并多个SVG图形item时,先对item依据ZValue值有小到大排序,按顺序合并到新SVG文件中。

2、 视图场景中重新布局的SVG位置和大小等信息如何写入新SVG文件中?这里就用到了Transform,熟悉SVG的同学应该都知道SVG中元素布局用到的transform属性,同时视图场景中item的位置变换信息也是在QTransform中,SVG的transform属性和item的QTransform是对应的,这就完美解决了我们的问题。我们保存的新SVG是以Scene大小为SVG实际大小,这样item在Scene中布局信息与新SVG布局信息是对应的,这里SVG外包的g标签的transform属性就和item的sceneTransform完全对应,最后我们只需将sceneTransform转换为transform属性字符串写入新SVG文件中即可。

经测试验证该方法可以实现多SVG图形合并,已经应用到作者开发项目当中。

5.     开发中遇到的问题

1、最大的一个坑就是QtSvg本身支持的SVG版本低,如文章开头所诉,加载某些SVG图形会出现绘制问题,这个没办法,目前qt新版本依然没有升级该模块,有坑请绕行!

2、合并多个SVG图形文件时,需要避免不同SVG文件中元素id相同,相同的id可能会导致引用的样式发生冲突,进而导致合并后的SVG图形出现意料之外的显示效果。

3、合并操作SVG文件内容时,内容字符串编码格式一定要保证为UTF-8,否则中文会出现乱码,进而导致合并SVG时会出现各种奇怪的问题。字符串读写中间操作可以使用QTextStream设置编码再操作。

原创粉丝点击