How to animate/change color of QPixmap/QImage/QPicture/whatever on mouseEvents?
-
What I basically want is a button, with svg icon / path data, and without any text which will change color (animate) on enter, leave, press. Previously I tried with QAction, doesn't work, and here I've tried another approach with QWidget:
ActionButton::ActionButton(const QString &fileName, QWidget *parent) :QWidget(parent){ icon = QImage(fileName); // fileName.svg auto label = new QLabel(); label->setPixmap(QPixmap::fromImage(icon)); auto lay = new QVBoxLayout(this); lay->setContentsMargins(0,0,0,0); lay->addWidget(label); setLayout(lay); } void ActionButton::enterEvent(QEnterEvent *event){ qDebug() << "entered"; icon.fill(Qt::blue); } void ActionButton::leaveEvent(QEvent *event){ qDebug() << "left"; icon.fill(Qt::black); } void ActionButton::mousePressEvent(QMouseEvent *event){ qDebug() << "pressed"; icon.fill(Qt::red); }
it detects correctly the enter, leave and press BUT doesn't change color. It also doesn't resize the image when I reset label width and height.
EDIT
Like this:
-
@eyllanesc You have to modify the svg attributes for example using QtXml
#include <QApplication> #include <QDomDocument> #include <QEnterEvent> #include <QFile> #include <QLabel> #include <QPainter> #include <QSvgRenderer> #include <QVBoxLayout> #include <QWidget> #include <QDebug> class ActionButton: public QWidget{ public: ActionButton(const QString & filename, QWidget *parent=nullptr) :QWidget(parent), m_label(new QLabel) { m_label->setAlignment(Qt::AlignCenter); QVBoxLayout *lay = new QVBoxLayout(this); lay->addWidget(m_label); QFile f(filename); if(f.open(QIODevice::ReadOnly | QIODevice::Text)){ m_document.setContent(f.readAll()); } changeColor(QColor("red")); } protected: void enterEvent(QEvent *event) { QWidget::enterEvent(event); qDebug() << "entered"; changeColor(Qt::blue); } void leaveEvent(QEvent *event){ QWidget::leaveEvent(event); qDebug() << "left"; changeColor(Qt::black); } void mousePressEvent(QMouseEvent *event){ QWidget::mousePressEvent(event); qDebug() << "pressed"; changeColor(Qt::red); } private: void changeColor(const QColor & color){ QDomNodeList path_elements = m_document.elementsByTagName("path"); if(path_elements.length() > 0){ QDomNode path_node = path_elements.at(0); if(path_node.isElement()){ QDomElement path_element = path_node.toElement(); path_element.setAttribute("fill", color.name()); } } m_renderer.load(m_document.toByteArray()); QPixmap pixmap(m_renderer.defaultSize()); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); m_renderer.render(&painter); painter.end(); m_label->setPixmap(pixmap); } QSvgRenderer m_renderer; QDomDocument m_document; QLabel *m_label; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QString filename{"/path/of/database-plus.svg"}; ActionButton button(filename); button.show(); return a.exec(); }
-
@Emon-Haque share the .svg
-
@eyllanesc, I use this site for path data/svg icons. Right click on any icon, click
Download .SVG Compressed
on the context menu, you'll get the svg file that works fine in Qt. -
@Emon-Haque said in How to animate/change color of QPixmap/QImage/QPicture/whatever on mouseEvents?:
doesn't resize the image when I reset label width and height
QImage and QPixmap are fixed size. You can update the size by calling
QImage::scaled()
orQPixmap::scaled()
, but you will get pixellation.If you want resizable SVG, use
QSvgWidget
instead. Update the size by callingQSvgWidget::resize()
-- the SVG will be rendered at the new size without pixellation.doesn't change color
2 important notes:
- Your code only updates
icon
. You also need to updatelabel
-- these are 2 separate copies of your image. QImage::fill()
fills the entire image with a solid colour. It doesn't change the colour of an existing image.
Unfortunately, there is no straightforward way to animate colours in a QLabel or QSvgWidget. The alternatives are:
- Put your icon in a QGraphicsView and apply a
QGraphicsColorizeEffect
(see https://doc.qt.io/qt-5/qgraphicseffect.html ) - Implement your GUI in QML and apply a
Colorize
effect (see https://doc.qt.io/qt-5/qml-qtgraphicaleffects-colorize.html )
- Your code only updates
-
@eyllanesc You have to modify the svg attributes for example using QtXml
#include <QApplication> #include <QDomDocument> #include <QEnterEvent> #include <QFile> #include <QLabel> #include <QPainter> #include <QSvgRenderer> #include <QVBoxLayout> #include <QWidget> #include <QDebug> class ActionButton: public QWidget{ public: ActionButton(const QString & filename, QWidget *parent=nullptr) :QWidget(parent), m_label(new QLabel) { m_label->setAlignment(Qt::AlignCenter); QVBoxLayout *lay = new QVBoxLayout(this); lay->addWidget(m_label); QFile f(filename); if(f.open(QIODevice::ReadOnly | QIODevice::Text)){ m_document.setContent(f.readAll()); } changeColor(QColor("red")); } protected: void enterEvent(QEvent *event) { QWidget::enterEvent(event); qDebug() << "entered"; changeColor(Qt::blue); } void leaveEvent(QEvent *event){ QWidget::leaveEvent(event); qDebug() << "left"; changeColor(Qt::black); } void mousePressEvent(QMouseEvent *event){ QWidget::mousePressEvent(event); qDebug() << "pressed"; changeColor(Qt::red); } private: void changeColor(const QColor & color){ QDomNodeList path_elements = m_document.elementsByTagName("path"); if(path_elements.length() > 0){ QDomNode path_node = path_elements.at(0); if(path_node.isElement()){ QDomElement path_element = path_node.toElement(); path_element.setAttribute("fill", color.name()); } } m_renderer.load(m_document.toByteArray()); QPixmap pixmap(m_renderer.defaultSize()); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); m_renderer.render(&painter); painter.end(); m_label->setPixmap(pixmap); } QSvgRenderer m_renderer; QDomDocument m_document; QLabel *m_label; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QString filename{"/path/of/database-plus.svg"}; ActionButton button(filename); button.show(); return a.exec(); }
-
@Emon-Haque what is your Qt version?
-
@eyllanesc, 6.2.0
-
@Emon-Haque Please read the docs: https://doc.qt.io/qt-6/qsvgwidget.html, add
QT += svgwidgets
-
@eyllanesc, hmm not bad:
would be nicer if there's some easy way to add a
QPropertyAnimation
for color. Instead of svg file there should be some way to use path data, to avoid DOM traversing, directly and animate its color.