从内存中的 SVG 内容创建 QIcon

Creating QIcon from SVG contents in memory

本文关键字:创建 QIcon SVG 内存      更新时间:2023-10-16

我们如何在内存缓冲区中创建具有 SVG 图标内容的QIcon对象?

附言最初想创建QSvgIconEngine,但它隐藏在插件层上,我无法明确创建它。如何从插件加载(考虑到该插件已加载)?

在这里和那里挖掘了一段时间,并深入研究了 QIcon 本身如何使用 svg 文件来加载图标,以下是我学到的:

  • QIcon,当使用 svg 文件(或任何其他图像类型)调用时,它会随后调用addFile(),它仅使用文件的扩展名(在 Qt 中称为QFileInfo::suffix)来确定将图像文件转换为图标的方法。

  • 该方法(语义上)由QIconEngine实例确定

  • 每种图像类型的QIconEngine类显然不是我们(Qt开发人员)可以简单地访问的;显然有一个插件系统可以使用,并且在编译时不可用(至少不是简单地)。

另一方面;QIcon如何工作?当从 QIcon 请求图标时,它会使用传递给它的信息来确定要使用的引擎,并创建引擎的实例。然后,每次图标需要绘制一些东西时,它都会要求引擎绘制一个给定一定大小的图标。大小用于函数QIconEngine::pixmap(),该函数创建具有所需大小的像素图,然后使用QIconEngine::paint()方法在该像素图上绘制。

因此,鉴于这些信息,解决方案只是编写一个图标引擎,QIcon 将使用该引擎根据传递给它的大小生成图标。具体操作方法如下:

所以这是头文件 SvgIconEngine.h

#ifndef SVGICONENGINE_H
#define SVGICONENGINE_H
#include <QIconEngine>
#include <QSvgRenderer>
class SVGIconEngine : public QIconEngine {
QByteArray data;
public:
explicit SVGIconEngine(const std::string &iconBuffer);
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode,
QIcon::State state) override;
QIconEngine *clone() const override;
QPixmap pixmap(const QSize &size, QIcon::Mode mode,
QIcon::State state) override;
signals:
public slots:
};
#endif // SVGICONENGINE_H

这是实现,SvgIconEngine.cpp

#include "SvgIconEngine.h"
#include <QPainter>
SVGIconEngine::SVGIconEngine(const std::string &iconBuffer) {
data = QByteArray::fromStdString(iconBuffer);
}
void SVGIconEngine::paint(QPainter *painter, const QRect &rect,
QIcon::Mode mode, QIcon::State state) {
QSvgRenderer renderer(data);
renderer.render(painter, rect);
}
QIconEngine *SVGIconEngine::clone() const { return new SVGIconEngine(*this); }
QPixmap SVGIconEngine::pixmap(const QSize &size, QIcon::Mode mode,
QIcon::State state) {
// This function is necessary to create an EMPTY pixmap. It's called always
// before paint()
QImage img(size, QImage::Format_ARGB32);
img.fill(qRgba(0, 0, 0, 0));
QPixmap pix = QPixmap::fromImage(img, Qt::NoFormatConversion);
{
QPainter painter(&pix);
QRect r(QPoint(0.0, 0.0), size);
this->paint(&painter, r, mode, state);
}
return pix;
}

注意:你必须覆盖clone(),因为它是一个抽象方法,你必须覆盖pixmap(),因为没有它,你将没有一个空的像素图来绘制svg。

要使用它,只需执行以下操作:

std::string iconSvgData = GetTheSvgPlainData();
QIcon theIcon(new SVGIconEngine(iconSvgData));
//Use the icon!

请注意,QIcon 拥有引擎对象的所有权。当它被摧毁时,它会摧毁它。

玩得愉快!

我手头没有 c++,但应该很容易转换:

QtGui.QIcon(
QtGui.QPixmap.fromImage(
QtGui.QImage.fromData(
b'<svg version="1.1" viewBox="0 0 32 32"'
b' xmlns="http://www.w3.org/2000/svg">'
b'<circle cx="16" cy="16" r="4.54237"/></svg>')))

此代码将创建带有黑色圆圈的透明 32 x 32 像素图标。

我们如何SVGQIcon在 内存缓冲区?

为此,所有必需的功能都通过QSvgRenderer类的外部接口提供。要构造这种类型的渲染器,我们需要使用以下任一:

QSvgRenderer(const QByteArray &contents, QObject *parent = Q_NULLPTR);
QSvgRenderer(QXmlStreamReader *contents, QObject *parent = Q_NULLPTR);

或者我们可以加载内容:

bool QSvgRenderer::load(const QByteArray &contents)
bool QSvgRenderer::load(QXmlStreamReader *contents)

并创建实际图标:

QIcon svg2Icon(const QByteArray& svgContent, QPainter::CompositionMode mode = 
QPainter::CompositionMode_SourceOver)
{
return QIcon(util::svg2Pixmap(svgContent, QSize(128, 128), mode));
}
QPixmap svg2Pixmap(const QByteArray& svgContent,
const QSize& size,
QPainter::CompositionMode mode)
{
QSvgRenderer rr(svgContent);
QImage image(size.width(), size.height(), QImage::Format_ARGB32);
QPainter painter(&image);
painter.setCompositionMode(mode);
image.fill(Qt::transparent);
rr.render(&painter);
return QPixmap::fromImage(image);
}

我们也可以使用其他构图模式,例如,QPainter::RasterOp_NotSourceOrDestination反转图标颜色。