Qt动态修改svg文件属性

本文介绍Qt中SVG文件的加载、动态属性修改及自定义图元添加方法。通过QGraphicsSvgItem类加载SVG,并可通过XML解析器修改图元属性,实现动态更新。此外,还介绍了如何利用QGraphicsItem子类在视图中添加自定义图元。

前言

Qt支持加载svg格式的图片,svg是Scalable Vector Graphics的缩写,即可缩放的矢量图像,它是一种基于XML的语言,用于描述二维矢量图形,这种格式的文件具有边缘清晰、文件体积小、传输方便的特点。我们可以通过svg展示复杂的图形场景,这些场景一般由美术设计人员提供编辑好的svg文件,有时还需要根据实际业务变化动态地改变场景中某个图元的位置、颜色、大小等属性。本文总结了加载svg文件,动态修改svg文件属性,及向场景中添加新的图元的方法。

1、加载svg文件

Qt提供了在QWidget和其他绘制设备上渲染和显示SVG图形的类QGraphicsSvgItem。将 QGraphicsSvgItem 对象添加到QGraphicsView 中来显示svg文件。QGraphicsView可以使用QWidget或QGLWidget作为其视口。这里我们继承QGraphicsView,来实现一个SvgViewer类,
其中openFile接口用来设置要显示的svg文件。

bool SvgViewer::openFile(const QString &fileName)
{
    QGraphicsScene *s = scene();
    QScopedPointer<QGraphicsSvgItem> svgItem(new QGraphicsSvgItem(fileName));
    if (!svgItem->renderer()->isValid())
        return false;

    s->clear();
    resetTransform();
    
    m_svgItem = svgItem.take();
    m_svgItem->setFlags(QGraphicsItem::ItemClipsToShape);
    m_svgItem->setCacheMode(QGraphicsItem::NoCache);
    m_svgItem->setZValue(0);
    
    s->addItem(m_svgItem);
	return true;
}

2、动态修改svg属性

svg文件已经可以加载并显示了,那么如何修改svg中图元的属性呢?在前面的介绍中,我们知道svg文件是用xml语言来描述的,要改其中的属性值,其实就是改xml文件中某个元素的属性。下面是一个svg文件的xml源码。

<?xml version="1.0" standalone="no"?>
<svg width="10cm" height="10cm" viewBox="0 0 1000 1000"
     xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny">
  <title>Example cubic02 - cubic Bezier commands in path data</title>
  <desc>Picture showing examples of "C" and "S" commands,
        along with annotations showing the control points
        and end points</desc>

  <rect fill="none" stroke="blue" stroke-width="1" x="1" y="1" width="998" height="998" />

  <!-- Path 1 -->
  <polyline fill="none" stroke="#888888" stroke-width="2" points="100,200 100,100" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="400,100 400,200" />
  <path fill="none" stroke="red" stroke-width="5" d="M100,200 C100,100 400,100 400,200" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="100" cy="200" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="400" cy="200" r="10" />
  <circle class="CtlPoint" cx="100" cy="100" r="10" />
  <circle class="CtlPoint" cx="400" cy="100" r="10" />
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="250" y="275">M100,200 C100,100 400,100 400,200</text>

  <!-- Path 2 -->
  <polyline fill="none" stroke="#888888" stroke-width="2" points="100,500 25,400" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="475,400 400,500" />
  <path fill="none" stroke="red" stroke-width="5" d="M100,500 C25,400 475,400 400,500" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="100" cy="500" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="400" cy="500" r="10" />
  <circle fill="#888888" stroke="none" cx="25" cy="400" r="10" />
  <circle fill="#888888" stroke="none" cx="475" cy="400" r="10" />
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="250" y="575">M100,500 C25,400 475,400 400,500</text>

  <!-- Path 3 -->
  <polyline fill="none" stroke="#888888" stroke-width="2" points="100,800 175,700" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="325,700 400,800" />
  <path fill="none" stroke="red" stroke-width="5" d="M100,800 C175,700 325,700 400,800" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="100" cy="800" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="400" cy="800" r="10" />
  <circle fill="#888888" stroke="none" cx="175" cy="700" r="10" />
  <circle fill="#888888" stroke="none" cx="325" cy="700" r="10" />
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="250" y="875">M100,800 C175,700 325,700 400,800</text>

  <!-- Path 4 -->
  <polyline fill="none" stroke="#888888" stroke-width="2" points="600,200 675,100" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="975,100 900,200" />
  <path fill="none" stroke="red" stroke-width="5" d="M600,200 C675,100 975,100 900,200" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="600" cy="200" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="900" cy="200" r="10" />
  <circle fill="#888888" stroke="none" cx="675" cy="100" r="10" />
  <circle fill="#888888" stroke="none" cx="975" cy="100" r="10" />
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="275">M600,200 C675,100 975,100 900,200</text>

  <!-- Path 5 -->
  <polyline fill="none" stroke="#888888" stroke-width="2" points="600,500 600,350" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="900,650 900,500" />
  <path fill="none" stroke="red" stroke-width="5" d="M600,500 C600,350 900,650 900,500" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="600" cy="500" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="900" cy="500" r="10" />
  <circle fill="#888888" stroke="none" cx="600" cy="350" r="10" />
  <circle fill="#888888" stroke="none" cx="900" cy="650" r="10" />
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="575">M600,500 C600,350 900,650 900,500</text>

  <!-- Path 6 (C and S command) -->
  <polyline fill="none" stroke="#888888" stroke-width="2" points="600,800 625,700" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="725,700 750,800" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="750,800 775,900" />
  <polyline fill="none" stroke="#888888" stroke-width="2" points="875,900 900,800" />
  <path fill="none" stroke="red" stroke-width="5" d="M600,800 C625,700 725,700 750,800
                                       S875,900 900,800" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="600" cy="800" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="750" cy="800" r="10" />
  <circle fill="none" stroke="#888888" stroke-width="2" cx="900" cy="800" r="10" />
  <circle fill="#888888" stroke="none" cx="625" cy="700" r="10" />
  <circle fill="#888888" stroke="none" cx="725" cy="700" r="10" />
  <circle fill="#888888" stroke="none" cx="875" cy="900" r="10" />
  <circle fill="none" stroke="blue" stroke-width="4" cx="775" cy="900" r="9" />
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="945">M600,800 C625,700 725,700 750,800</text>
  <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="975">S875,900 900,800</text>
</svg>

从上面的源码我们可以看到每个元素都有几个属性,而这些属性都可以通过xml解析器来访问。如果我们给每一个元素分配一个id,再通过id查找到对应的元素,然后修改它的属性,就可以实现动态修改属性的目的,最后重新加载svg源码就可以看到更新后的效果了。伪代码如下:

	QDomDocument doc;
	QFile file();
	doc.setContent(file);
	QDomElement ele = doc.documentElement(); //通过遍历节点根据节点id找到需要操作的节点,修改其属性。
	QString svgXml = doc.toString();//将修改后的xml对象转为字符串
	m_svgItem->renderer()->load(xml);//用QGraphicsSvgItem的渲染器重新加载xml源码,更新显示

为了方便修改任意元素的属性,可以添加新的方法,如这里修改元素的颜色添加下面一组方法

	setColorById(id, color);
	setColorByClass(className, color);
	setColorByGroupId(groupId, color);

查找目标节点时需要递归遍历所有节点来找出目标节点。

3、添加自定义图元

为了满足特定业务需求,只是展示和修改svg文件还不够,还要能动态的在视图中添加和删除图元,因为svg文件是通过QGraphicsView显示的,因此我们可以利用QGraphicsItem的子类来实现自定义图元,然后添加到场景中。为了方便可以定义一组接口添加特定图元,接口如下

addLine();
addRect();
addEllipse();
addText();
...

QGraphicsItem的子类常用的有下面几种:

  • QGraphicsRectItem //用于添加矩形
  • QGraphicsEllipseItem//用于添加圆形
  • QGraphicsLineItem//用于添加线
  • QGraphicsPixmapItem//用于添加图片
  • QGraphicsTextItem//用于添加文本

以上为本文全部内容,有疑问的朋友欢迎留言讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凝望星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值