chacham15
chacham15

Reputation: 14251

Change the color of an svg in qt

I have a svg loaded in the resources, but it is black. How do I change the color to white?

Upvotes: 18

Views: 26440

Answers (9)

VictorCanti
VictorCanti

Reputation: 13

Pretty old thread but also so viewed so I have the need to answer for those like me that still are using qt5 and the answer is not working because in qt5 doc.documentElement() returns a const element. There is the simple changes you need to make to make it work:

void SetAttrRecur(QDomElement &elem, QString strtagname, QString strattr, 
QString strattrval)
{
    // if it has the tagname then overwritte desired attribute
    if (elem.tagName().compare(strtagname) == 0)
    {
        elem.setAttribute(strattr, strattrval);
    }
    // loop all children
    for (int i = 0; i < elem.childNodes().count(); i++)
    {
        if (!elem.childNodes().at(i).isElement())
        {
            continue;
        }
        QDomElement docElem = elem.childNodes().at(i).toElement(); //<-- make const "variable"
        SetAttrRecur(docElem, strtagname, strattr, strattrval);
    }
}

QIcon ChangeSVGColor(QString iconPath, QString color)
{
    // open svg resource load contents to qbytearray
    QFile file(iconPath);
    if(!file.open(QIODevice::ReadOnly))
        return {};
    QByteArray baData = file.readAll();
    // load svg contents to xml document and edit contents
    QDomDocument doc;
    doc.setContent(baData);
    // recurivelly change color
    QDomElement docElem = doc.documentElement(); //<-- make const "variable"
    SetAttrRecur(docElem, "path", "fill", color);
    // create svg renderer with edited contents
    QSvgRenderer svgRenderer(doc.toByteArray());
    // create pixmap target (could be a QImage)
    QPixmap pix(svgRenderer.defaultSize());
    pix.fill(Qt::transparent);
    // create painter to act over pixmap
    QPainter pixPainter(&pix);
    // use renderer to render over painter which paints on pixmap
    svgRenderer.render(&pixPainter);
    QIcon myicon(pix);
    return myicon;
}

Upvotes: 0

iperov
iperov

Reputation: 514

solution for pyqt5. You can easily convert it to c++

def QIcon_from_svg(svg_filepath, color='black'):
    img = QPixmap(svg_filepath)
    qp = QPainter(img)
    qp.setCompositionMode(QPainter.CompositionMode_SourceIn)
    qp.fillRect( img.rect(), QColor(color) )
    qp.end()
    return QIcon(img)

Upvotes: 9

javad
javad

Reputation: 19

#ifndef SVG_ITEM_H
#define SVG_ITEM_H

#include <QObject>
#include <QPen>
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QSvgRenderer>


class SVG_Item : public QQuickPaintedItem
{

    Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    Q_PROPERTY(QPen stroke READ stroke WRITE setStroke NOTIFY strokeChanged)
    Q_PROPERTY(bool debuging READ debuging WRITE setdebuging)
    Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged)

    Q_OBJECT

public:
    explicit SVG_Item(QQuickItem *parent = nullptr);
    void paint(QPainter *painter) Q_DECL_OVERRIDE;
    QString source() const;
    QColor color() const;
    QPen stroke() const;
    bool debuging() const;

    QColor backgroundColor() const;

signals:
    void sourceChanged(QString source);
    void colorChanged(QColor color);
    void strokeChanged(QPen stroke);
    void backgroundColorChanged(QColor backgroundColor);

public slots:
    void setSource(QString source);
    void setColor(QColor color);
    void setStroke(QPen stroke);
    void setdebuging(bool debuging);    
    void setBackgroundColor(QColor backgroundColor);

private:
    QString m_source;
    QColor m_color;
    QPen m_stroke;
    QString svgContent;
    QSvgRenderer *renderer;
    bool changed;
    bool m_debuging;
    QColor m_backgroundColor;
    QColor m_test;
};

#endif // SVG_ITEM_H





#include "svg_item.h"

#include <QSvgRenderer>
#include <QDebug>
#include <QPainter>
#include <QSvgGenerator>

SVG_Item::SVG_Item(QQuickItem *parent) : QQuickPaintedItem(parent)
{
    changed = false;
    renderer = NULL;
    m_debuging = false;
    m_backgroundColor = Qt::transparent;
}


void SVG_Item::paint(QPainter *painter)
{
    painter->fillRect(0,0,this->width(),this->height(),m_backgroundColor);
    if(m_source != "")
    {
        if(changed)
        {
            if(renderer != NULL)
            {
                renderer->deleteLater();
            }
            renderer = new QSvgRenderer(svgContent.toLocal8Bit());
        }
        if(renderer != NULL)
            renderer->render(painter);
    }

}


void SVG_Item::setSource(QString source)
{
    if(source.startsWith("qrc"))
        source = source.remove(0,3);
    if (m_source == source)
        return;

    QFile readFile(source);
    if(!readFile.exists())
    {
        qWarning("file not found");
    }
    readFile.open(QFile::ReadOnly);
    svgContent = readFile.readAll();

    setColor(color());

    //readData.replace()

    m_source = source;
    emit sourceChanged(m_source);
}

void SVG_Item::setColor(QColor color)
{
    changed = true;
    QString fillStr = "fill:%1";
    fillStr = fillStr.arg(color.name());
    svgContent = svgContent.replace(QRegExp("fill:[# 0-9 a b c d e f A B C D E F]+"), fillStr);

    if(!svgContent.contains(QRegExp("fill:[# 0-9 a b c d e f A B C D E F]+")))
    {
        QString style = "<path \n style=\"fill:%1;fill-opacity:1\"";
        style = style.arg(color.name());
        svgContent = svgContent.replace("<path",style);

        style = "<rect \n style=\"fill:%1;fill-opacity:1\"";
        style = style.arg(color.name());
        svgContent = svgContent.replace("<rect",style);

        style = "<circle \n style=\"fill:%1;fill-opacity:1\"";
        style = style.arg(color.name());
        svgContent = svgContent.replace("<circle",style);

        style = "<ellipse \n style=\"fill:%1;fill-opacity:1\"";
        style = style.arg(color.name());
        svgContent = svgContent.replace("<ellipse",style);

        style = "<polygon \n style=\"fill:%1;fill-opacity:1\"";
        style = style.arg(color.name());
        svgContent = svgContent.replace("<polygon",style);
    }

    //

    this->update();

    if (m_color == color)
        return;

    m_color = color;
    emit colorChanged(m_color);
}

void SVG_Item::setStroke(QPen stroke)
{
    changed = true;
    if (m_stroke == stroke)
        return;

    m_stroke = stroke;
    emit strokeChanged(m_stroke);
}

void SVG_Item::setdebuging(bool debuging)
{
    m_debuging = debuging;
}

void SVG_Item::setBackgroundColor(QColor backgroundColor)
{
    if (m_backgroundColor == backgroundColor)
        return;

    m_backgroundColor = backgroundColor;
    emit backgroundColorChanged(m_backgroundColor);
}


QString SVG_Item::source() const
{
    return m_source;
}

QColor SVG_Item::color() const
{
    return m_color;
}

QPen SVG_Item::stroke() const
{
    return m_stroke;
}

bool SVG_Item::debuging() const
{
    return m_debuging;
}


QColor SVG_Item::backgroundColor() const
{
    return m_backgroundColor;
}

Upvotes: 0

HL94
HL94

Reputation: 51

If your SVG is black, there is an extremely easy way to do it: QGraphicsEffect

#include <QGraphicsItem>
#include <QGraphicsColorizeEffect>

QGraphicsItem *item;
QGraphicsColorizeEffect *effect;    

item = new QGraphicsItem;
effect = new QGraphicsColorizeEffect;

effect->setColor(Qt::white);
effect->setStrength(1);

item->setGraphicsEffect(effect)    

This doesn't work with white SVGs, but given that almost any website that provides icons does so in black, this is pretty neat.

Upvotes: 5

Will59
Will59

Reputation: 1883

You can use a ColorOverlay, as described in the end of this question:

Qt QML LevelAdjust shows strange edge effects when applied to svg source

It doesn't change the SVG per say, but it creates a colored layer which will have the same shape as your drawing (assuming your drawing's background is transparent).

Upvotes: 1

Juan Gonzalez Burgos
Juan Gonzalez Burgos

Reputation: 982

This is how you can do it in Qt, don´t forget to add the xml and svg modules to your qt project (*.pro file). This code snippet changes the color by modifying the "fill" attribute of any "path" element but you could use it to modify any attribute of any element.

void SetAttrRecur(QDomElement &elem, QString strtagname, QString strattr, QString strattrval);

void ChangeSVGColor()
{

    // open svg resource load contents to qbytearray
    QFile file("myfile.svg");
    file.open(QIODevice::ReadOnly);
    QByteArray baData = file.readAll();
    // load svg contents to xml document and edit contents
    QDomDocument doc;
    doc.setContent(baData);
    // recurivelly change color
    SetAttrRecur(doc.documentElement(), "path", "fill", "white");
    // create svg renderer with edited contents
    QSvgRenderer svgRenderer(doc.toByteArray());
    // create pixmap target (could be a QImage)
    QPixmap pix(svgRenderer.defaultSize());
    pix.fill(Qt::transparent);
    // create painter to act over pixmap
    QPainter pixPainter(&pix);
    // use renderer to render over painter which paints on pixmap
    svgRenderer.render(&pixPainter);
    QIcon myicon(pix);
    // Use icon ....    

}


void SetAttrRecur(QDomElement &elem, QString strtagname, QString strattr, QString strattrval)
{
    // if it has the tagname then overwritte desired attribute
    if (elem.tagName().compare(strtagname) == 0)
    {
        elem.setAttribute(strattr, strattrval);
    }
    // loop all children
    for (int i = 0; i < elem.childNodes().count(); i++)
    {
        if (!elem.childNodes().at(i).isElement())
        {
            continue;
        }
        SetAttrRecur(elem.childNodes().at(i).toElement(), strtagname, strattr, strattrval);
    }
}

Upvotes: 11

Jeremy Friesner
Jeremy Friesner

Reputation: 73081

Since the SVG format is XML based, and XML is just ASCII text... you could load the SVG resource in to a QString, call QString::replace("\"#000000\"", "\"#ffffff\""), and then pass the modified QString in to your QSVGRenderer.

Upvotes: 8

user362638
user362638

Reputation:

If white is the only color you need, then a simple solution would be to change the color of the original SVG image with an image editor (e.g. Inkscape). Of course, if you need use the image with many different colors, that wont be a reasonable solution.

Upvotes: -2

phyatt
phyatt

Reputation: 19112

As long as you don't need it on Mac, this should work:

http://doc-snapshot.qt-project.org/4.8/qwidget.html#setGraphicsEffect

http://doc-snapshot.qt-project.org/4.8/qgraphicscolorizeeffect.html

EDIT: Or if you need to support Mac, do the svg rendering and effects inside a QGraphicsView.

http://doc-snapshot.qt-project.org/4.8/qgraphicsitem.html#setGraphicsEffect

Setup your colorize effect to color it white, and set it to the svgWidget.

Hope that helps.

Upvotes: 3

Related Questions