How do I change the state of a QProgressBar? - c++

I have a custom dialog that imitates and embellishes QProgressDialog with some additional application-specific information and enables pausing/resuming the worker thread running in the background. Ultimately what I'm looking for is the ability to change the appearance of the QProgressBar to reflect the paused state as described in this question.
Oddly enough QWinTaskbarProgress seems to support this exact functionality as of Qt 5.2, but a plain QProgressBar does not, in Qt4 or Qt5. I'm okay with fiddling around in the Qt source code, but I've dug around in the Qt source code and I can't figure out where the actual state of the form control is queried/provided, so I can't figure out what I need to change. Maybe it's not there because this is strictly a Windows thing, but maybe not?
Using CSS to override the StyleSheet as recommended in the docs (and here) results in a very ugly progress bar, completely different in appearance from the stock Windows 7 progress bar.
Normal:
Stylesheet:
I don't want to use this option.

The simple way is setting QGraphicsColorizeEffect to the progress bar.
Like this:
QProgressBar* progress = new QProgressBar;
progress->setGraphicsEffect(new QGraphicsColorizeEffect);
Result on Win7:
Umm...the result looks like fine, but we can make it better, only change the chunk colour.
Here is the final result:
Reimplement QGraphicsEffect::draw to specific & customize the colourize effect area:
class Colorize : public QGraphicsEffect {
public:
explicit Colorize(QObject *parent = Q_NULLPTR) :
QGraphicsEffect(parent),
strength(1),
color(Qt::red),
effectRect()
{ }
quint32 strength;
QColor color;
QRectF effectRect;
protected:
void draw(QPainter* painter) {
QPoint offset;
const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset);
draw(painter, offset, pixmap, QRect());
}
void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
{
if (src.isNull())
return;
QImage srcImage;
QImage destImage;
if (srcRect.isNull()) {
srcImage = src.toImage();
srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
destImage = QImage(srcImage.size(), srcImage.format());
} else {
QRect rect = srcRect.toAlignedRect().intersected(src.rect());
srcImage = src.copy(rect).toImage();
srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
destImage = QImage(rect.size(), srcImage.format());
}
destImage.setDevicePixelRatio(src.devicePixelRatioF());
// do colorizing
QPainter destPainter(&destImage);
grayscale(srcImage, destImage, srcImage.rect());
destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
destPainter.fillRect(effectRect, color);
destPainter.end();
// alpha blending srcImage and destImage
if(0 < strength && strength < 1){
QImage buffer = srcImage;
QPainter bufPainter(&buffer);
bufPainter.setOpacity(strength);
bufPainter.drawImage(0, 0, destImage);
bufPainter.end();
destImage = buffer;
}
if (srcImage.hasAlphaChannel())
destImage.setAlphaChannel(srcImage.alphaChannel());
painter->drawImage(dest, destImage);
}
};
Calculate the grove rect of the progress bar:
QRectF getGrooveRect() const {
StyleOptionProgressBar option;
option.initFrom(this); // this ⇒ progress bar
return style()->subElementRect(QStyle::SE_ProgressBarGroove, &option, this);
}
...
class StyleOptionProgressBar : public QStyleOptionProgressBar {
public:
using QStyleOptionProgressBar::QStyleOptionProgressBar;
void initFrom(const ColorizeProgressBar* w) {
init(w);
minimum = w->minimum();
maximum = w->maximum();
progress = w->value();
text = w->text();
textAlignment = w->alignment();
textVisible = w->isTextVisible();
orientation = w->orientation();
invertedAppearance = w->invertedAppearance();
}
};
Complete source on Github.
BTW, Referred Qt source code

Related

Translate screen drawing to another part of the screen

Is it possible to make it so that all drawing to an area "A" is translated to an area "B"?
For example drawing to the area(0,0)(100,100) and have it appear in area(200,200)(300,300).
The question is actually tagged with windows and graphics. This might have been targeted to Win32 and GDI (where I've unfortunately nearly no experience). So, the following might be seen as proof of concept:
I couldn't resist to implement the idea / concept using QWindow and QPixmap.
The concept is:
open a window fullscreen (i.e. without decoration)
make a snapshot and store it internally (in my case a )
display the internal image in window (the user cannot notice the difference)
perform a loop where pixmap is modified and re-displayed periodically (depending or not depending on user input).
And this is how I did it in Qt:
I opened a QWindow and made it fullscreen. (Maximum size may make the window full screen as well but it still will have decoration (titlebar with system menu etc.) which is unintended.)
Before painting anything, a snapshot of this window is done. That's really easy in Qt using QScreen::grabWindow(). The grabbed contents is returned as QPixmap and stored as member of my derived Window class.
The visual output just paints the stored member QPixmap.
I used a QTimer to force periodical changes of the QPixmap. To keep the sample code as short as possible, I didn't make the effort of shuffling tiles. Instead, I simply scrolled the pixmap copying a small part, moving the rest upwards, and inserting the small stripe at bottom again.
The sample code qWindowRoll.cc:
#include <QtWidgets>
class Window: public QWindow {
private:
// the Qt backing store for window
QBackingStore _qBackStore;
// background pixmap
QPixmap _qPixmap;
public:
// constructor.
Window():
QWindow(),
_qBackStore(this)
{
showFullScreen();
}
// destructor.
virtual ~Window() = default;
// disabled:
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
// do something with pixmap
void changePixmap()
{
enum { n = 4 };
if (_qPixmap.height() < n) return; // not yet initialized
const QPixmap qPixmapTmp = _qPixmap.copy(0, 0, _qPixmap.width(), n);
//_qPixmap.scroll(0, -n, 0, n, _qPixmap.width(), _qPixmap.height() - n);
{ QPainter qPainter(&_qPixmap);
qPainter.drawPixmap(
QRect(0, 0, _qPixmap.width(), _qPixmap.height() - n),
_qPixmap,
QRect(0, n, _qPixmap.width(), _qPixmap.height() - n));
qPainter.drawPixmap(0, _qPixmap.height() - n, qPixmapTmp);
}
requestUpdate();
}
protected: // overloaded events
virtual bool event(QEvent *pQEvent) override
{
if (pQEvent->type() == QEvent::UpdateRequest) {
paint();
return true;
}
return QWindow::event(pQEvent);
}
virtual void resizeEvent(QResizeEvent *pQEvent)
{
_qBackStore.resize(pQEvent->size());
paint();
}
virtual void exposeEvent(QExposeEvent*) override
{
paint();
}
// shoot screen
// inspired by http://doc.qt.io/qt-5/qtwidgets-desktop-screenshot-screenshot-cpp.html
void makeScreenShot()
{
if (QScreen *pQScr = screen()) {
_qPixmap = pQScr->grabWindow(winId());
}
}
private: // internal stuff
// paint
void paint()
{
if (!isExposed()) return;
QRect qRect(0, 0, width(), height());
if (_qPixmap.width() != width() || _qPixmap.height() != height()) {
makeScreenShot();
}
_qBackStore.beginPaint(qRect);
QPaintDevice *pQPaintDevice = _qBackStore.paintDevice();
QPainter qPainter(pQPaintDevice);
qPainter.drawPixmap(0, 0, _qPixmap);
_qBackStore.endPaint();
_qBackStore.flush(qRect);
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// setup GUI
Window win;
win.setVisible(true);
// setup timer
QTimer qTimer;
qTimer.setInterval(50); // 50 ms -> 20 Hz (round about)
QObject::connect(&qTimer, &QTimer::timeout,
&win, &Window::changePixmap);
qTimer.start();
// run application
return app.exec();
}
I compiled and tested with Qt 5.9.2 on Windows 10. And this is how it looks:
Note: On my desktop, the scrolling is smooth. I manually made 4 snapshots and composed a GIF in GIMP – hence the image appears a bit stuttering.

How do I reuse Qt's QTextCursor in my C++ application?

I am implementing a text editor on Windows using Qt in C++ and am using a QWidget as the surface on which I am displaying text using OpenGL. So far, I have my own layout engine and document model and am able to get the text to display on the widget.
Now I am trying to implement a text cursor that will be used similar to the one Qt provides, but QTextCursor is closely tied to its QTextDocument model and I am not able to subclass it for reuse using my model. Is there any way to reuse just the cursor without the model?
If not, how do I go about implementing a text cursor using Qt?
Note: I did go through the Caret methods that Windows provides here, but am hoping to avoid using them directly.
TL;DR: You can't. It's not a caret.
QTextCursor is an iterator, it has nothing to do whatsoever with the on-screen cursor. You can certainly reuse it if it is useful as an iterator, and iff your own text representation is built on top of QTextDocument. But it's not a caret.
The visible cursor control is provided by the QTextEdit implementation. Recall that QTextEdit is a view for a QTextDocument - it is entirely devoted to the graphical rendering of the text, controlling the visible cursor, etc.
There is no public Qt API that you can use for the caret. The WINAPI caret methods are completely useless when you're using Qt for rendering. You need your own caret implementation. Given that you already have a text representation and a renderer, you presumably have an iterator that works on the text representation, so implementing the caret should be a trivial affair.
The WINAPI caret is very simple and trivial to reimplement with the power of Qt:
class Caret : public QWidget {
Q_OBJECT
Q_PROPERTY(int period READ period WRITE setPeriod)
QPicture m_shape;
BasicTimer m_timer;
int m_period;
void updateSize() {
auto size = m_shape.boundingRect().size();
setFixedSize(size);
resize(size);
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
if (isVisible()) hide(); else show();
}
public:
QPicture defaultShape(int w, int h) {
QPicture pic;
QPainter p(&pic);
p.fillRect(0, 0, w, h, Qt::black);
return pic;
}
Caret(QWidget * parent = 0, const QPicture & pic = defaultShape()) :
QWidget(parent), m_shape(pic), m_period(250) {
setAttribute(Qt::WA_TransparentForMouseEvents);
setAttribute(Qt::WA_TranslucentBackground);
m_timer.start(m_period);
updateSize();
}
void setShape(const QPicture & pic) {
m_shape = pic;
updateSize();
update();
}
void setPeriod(int period) {
if (period < 1) {
m_timer.stop();
if (m_period > 0) show();
} else
m_timer.start(period);
m_period = period;
}
int period() const { return m_period; }
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE {
auto painter = QPainter(this);
painter.fillRect(rect(), Qt::transparent);
painter.drawPicture(QPoint(), m_shape);
}
};

Change color of placeholder text in QLineEdit

When I set the placeholder text with QLineEdit::setPlaceholderText(), it appears gray.
Is there any way to change the color to something else, for example red?
You'll have to subclass QLineEdit and paint your own placeholder in the paintEvent().
class CustomColorPlaceholderLineEdit : public QLineEdit
{
public:
CustomColorPlaceholderLineEdit(QWidget * parent = 0) : QLineEdit(parent) { color = QColor(0,0,0,128); }
void setCustomPlaceholderText(const QString &text) { this->mText = text; }
const QString &customPlaceholderText() const { return mText; }
void setCustomPlaceholderColor(const QColor &color) { this->color = color; }
const QColor &customPlaceholderColor() const { return color; }
void paintEvent(QPaintEvent *event) {
QLineEdit::paintEvent(event);
if (!hasFocus() && text().isEmpty() && !mText.isEmpty()) {
// QLineEdit's own placeholder clashes with ours.
Q_ASSERT(placeholderText().isEmpty());
QPainter p(this);
p.setPen(color);
QFontMetrics fm = fontMetrics();
int minLB = qMax(0, -fm.minLeftBearing());
QRect lineRect = this->rect();
QRect ph = lineRect.adjusted(minLB + 3, 0, 0, 0);
QString elidedText = fm.elidedText(mText, Qt::ElideRight, ph.width());
p.drawText(ph, Qt::AlignVCenter, elidedText);
}
}
private:
QString mText;
QColor color;
};
There is another a bit hacky but simple and reliable way.
connect(lineEdit, &QLineEdit::textChanged, this, &YourClass::updateLineEditStyleSheet);
void YourLineEdit::updateLineEditStyleSheet()
{
if (lineEdit->text().isEmpty()) {
lineEdit->setStyleSheet("#lineEdit { color: lightGray;"); // Set your color but remember that Qt will reduce alpha
} else {
lineEdit->setStyleSheet("#lineEdit { color: black;"); // usual color
}
}
also you can use this way to derived from QLineEdit class
If you want to use QSS instead of QPalette, try the following:
setStyleSheet("QLineEdit{"
" color: red;" //TEXT COLOR
"}"
"QLineEdit[text=\"\"]{"
" color: gray;" //TEXTHOLDER COLOR
"}");
connect(ui->lineEdit, &QLineEdit::textChanged, [=]{ style()->polish(ui->lineEdit); });
You can change the color, but bare in mind there is an alpha factor set in the placeholder from the source code (as mentioned in another comment) that cannot be removed. Therefore you will always see the placeholder darker (no white possible with this option).
You can't, at least with the current QLineEdit code.
As you can see from the source code, the placeholder text is simply taking the foreground brush of the palette and making it partially transparent, see QLineEdit::paintEvent:
if (d->shouldShowPlaceholderText()) {
if (!d->placeholderText.isEmpty()) {
QColor col = pal.text().color();
col.setAlpha(128);
QPen oldpen = p.pen();
p.setPen(col);
QRect ph = lineRect.adjusted(minLB, 0, 0, 0);
QString elidedText = fm.elidedText(d->placeholderText, Qt::ElideRight, ph.width());
p.drawText(ph, va, elidedText);
p.setPen(oldpen);
}
}
You can work with upstream into a more general solution, though. In particular I one would expect that color to be added to the palette, or in general provided by the current QStyle (for instance as a style hint).
If you want to change placeholder text color for a QLineEdit you have to customize the component's QPalette object.
QPalette p = lineEdit->palette();
p.setColor(QPalette::Mid, Qt::red); // assuming Mid is the color you want to change.
lineEdit->setPalette(p);
I don't recall exactly which QPalette::ColorRole is appropriate for changing QLineEdit's placeholder text color though.
#Meefte solution is quite good given the situation that Qt gives placeholder the same color as for the text, except it adds 50% opacity. So, there is little choice to set placeholder color to be different than the text. However, even this solution could be improved by making sure that you would not need to set some other variable than the default one Qt provides you.
The need to use default placeholderText() might arise from the situation when you have lots of QLineEdit controls which are already promoted to some control overriding QLineEdit behavior, and placeholderText() is already set through code or through Qt Creator, i.e. it would be a bit painful to introduce another dynamic property. However, if you did not promote to some child control, then it would be a necessity to do so in order to use such solution.
class CustomColorPlaceholderLineEdit : public QLineEdit
{
public:
CustomColorPlaceholderLineEdit(QWidget * parent = 0) : QLineEdit(parent) { color = QColor(0,0,0,128); }
const QString &customPlaceholderText() const { return mText; }
void setCustomPlaceholderColor(const QColor &color) { this->color = color; }
const QColor &customPlaceholderColor() const { return color; }
void paintEvent(QPaintEvent *event)
{
if(color.isValid() && text().isEmpty() && (!placeholderText().isEmpty() || !mText.isEmpty()))
{
if(!placeholderText().isEmpty())
{
// In this way, placeholderText() is taken into local variable 'mText' care. Whenever placeholderText() will change, there it will be taken care of.
mText = placeholderText();
// This will ensure Qt will not draw placeholder for us.
setPlaceholderText("");
}
// By this, we make sure Qt will paint QLineEdit default parts properly.
QLineEdit::paintEvent(e);
// And now #Meefte code is reused here.
QPainter p(this);
p.setPen(color);
QFontMetrics fm = fontMetrics();
int minLB = qMax(0, -fm.minLeftBearing());
QRect lineRect = this->rect();
QRect ph = lineRect.adjusted(minLB + 3, 0, 0, 0);
QString elidedText = fm.elidedText(mText, Qt::ElideRight, ph.width());
p.drawText(ph, Qt::AlignVCenter, elidedText);
return; // No need to paint again.
}
// Default Qt's painting behavior for QLineEdit.
QLineEdit::paintEvent(e);
}
private:
QString mText;
QColor color;
};
QT still has this problem)
I solved it like this:
bool CustomLineEdit::event(QEvent *event)
{
bool eventResult = QLineEdit::event(event);
if (event->type() == QEvent::StyleChange) {
QPalette pal = palette();
pal.setColor(QPalette::PlaceholderText, Qt::red);
setPalette(pal);
}
return eventResult;
}

Qt Drawing pixel by pixel zoomed

I tried to find some helps around internet, i had some codes to test but none works like i want.
I would draw with Qt something pixel by pixel.
I tried with a QImage within a QLabel with the protected event mousePressEvent it worked, but the pixels are too small to see them.
I tried to scale my Image, it's much better, but the position X, Y are not the same position of the pixels now or they're not synchronized with the pixels locations.
Here a screenshot of what i want do, if someone has an idea to recreate this...
If i can exactly done this via Qt i'll save a lots of time, just a basic drawing pixel by pixel, i created this example with Paint, Black and White format zoomed to 800%.
Thanks.
This is a solution hard-coded for a specific size - just implement relative coordinate mapping between pixmap and draw widget and you are set.
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent), pressed(0) {
color = Qt::black;
pixmap = new QPixmap("h:/small.png");
resize(240, 240);
}
~Widget() { if (pixmap) delete pixmap; }
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.drawPixmap(0, 0, pixmap->scaled(240, 240));
painter.drawPixmap(0, 0, *pixmap);
}
void mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton)
color = color == Qt::black ? Qt::white : Qt::black;
else {
pressed = 1;
draw(e);
}
}
void mouseReleaseEvent(QMouseEvent *) { pressed = 0; }
void mouseMoveEvent(QMouseEvent *e) { draw(e); }
private:
void draw(QMouseEvent *e) {
if (pressed) {
QPainter painter(pixmap);
painter.setPen(color);
int x = e->pos().x() / 12;
int y = e->pos().y() / 12;
painter.drawPoint(x, y);
repaint();
}
}
QColor color;
QPixmap *pixmap;
bool pressed;
};

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?
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);
}
}
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.
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.
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.
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)
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).
#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;
}
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;
}
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.