Toggle svg icon on Button click - QT C++
-
wrote on 30 Dec 2019, 16:54 last edited by
Hi,
I am relatively new to svg + QT concepts.
I have created an svg icon which has 2 states.
I want to add this svg icon to a QPushButton. When the button is clicked,the icon should get toggled ( show the other state ).Can anybody pls give me a heads up on how to do this in QT? QT with C++!
-
Hi,
Take a look at QIcon.
It's what you need to setup in order to have the various icons used automatically when the button state changes.
-
wrote on 2 Jan 2020, 14:03 last edited by
Hi @SGaist ,
I have tried something using the QSvgRenderer and QIcon.
I have achieved what I was looking for.But I am not sure if this is how it should be done.
Please have a look and let me know if this is the preferred way or not.
I have created a svg icon ( using 2 other svg icons ) from the https://nucleoapp.com/tool/icon-transition
The svg icon has 2 svg icons embedded into it. My objective now is to toggle the icons when button is clicked.Checkout my files below
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QSvgRenderer> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; bool isDefaultIconLoaded; QString otherIconSvgContent; QString defaultIconSvgContent; QSvgRenderer m_svgRenderer; void loadCurrentSvgIcon(QByteArray ba); private slots: void toolBtnClicked(); }; #endif // MAINWINDOW_H
The cpp file
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QFile> #include <QPainter> #include <QtSvg/QSvgRenderer> #include <QPixmap> #include <QDomDocument> #include <QDomNodeList> #include <QTextStream> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), isDefaultIconLoaded(true) { ui->setupUi(this);; connect(ui->toolButton, &QToolButton::clicked, this, &MainWindow::toolBtnClicked); ui->toolButton->setStyleSheet("border:none;"); // open svg icon which has multiple states QFile file(":/icon/theSvg.svg"); file.open(QIODevice::ReadOnly); const QByteArray baData = file.readAll(); QDomDocument doc; doc.setContent(baData, false); // Read both the <g> elements from the .svg xml and store each 'icon content' in member var. QDomNodeList gList = doc.elementsByTagName("g"); for(int i = 0; i < gList.size(); i++) { QString aContent; QTextStream ts(&aContent); gList.at(i).save(ts,0); qDebug("%s", qPrintable(aContent)); if( isDefaultIconLoaded ) { defaultIconSvgContent.insert(0, "<svg>"); defaultIconSvgContent += aContent;; defaultIconSvgContent.append("</svg>"); isDefaultIconLoaded = false; qDebug() <<defaultIconSvgContent; } else { otherIconSvgContent.insert(0, "<svg>"); otherIconSvgContent += aContent; otherIconSvgContent.append("</svg>"); qDebug() <<otherIconSvgContent; } } // Load the default svg icon first QByteArray ba = defaultIconSvgContent.toUtf8(); isDefaultIconLoaded = true; loadCurrentSvgIcon(ba); } MainWindow::~MainWindow() { delete ui; } void MainWindow::loadCurrentSvgIcon(QByteArray ba) { m_svgRenderer.load(ba); QPixmap pix(m_svgRenderer.defaultSize()); QPainter pixPainter(&pix); m_svgRenderer.render(&pixPainter); QIcon myicon(pix); ui->toolButton->setIcon(myicon); } void MainWindow::toolBtnClicked() { if ( isDefaultIconLoaded == true ) { // Toggle the icon to "other" QByteArray ba = otherIconSvgContent.toUtf8(); loadCurrentSvgIcon(ba); isDefaultIconLoaded = false; } else { QByteArray ba = defaultIconSvgContent.toUtf8(); loadCurrentSvgIcon(ba); isDefaultIconLoaded = true; } }
Content of the svg file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16" width="16"> <g class="nc-icon-wrapper" stroke-width="1" fill="#111111" stroke="#111111"> <title>default</title> <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" data-color="color-2" d="M1.5 1.5h2"> </path> <circle fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" cx="9" cy="9" r="3.5" data-color="color-2"> </circle> <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" data-color="color-2" d="M2.5 5.5h1"> </path> <path fill="none" stroke="#111111" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M14.5 3.5h-2l-1-2h-5l-1 2h-4a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1v-9a1 1 0 0 0-1-1z"> </path> </g> <g class="nc-icon-wrapper" fill="#111111"> <title>other</title> <path fill="#111111" d="M15,3h-2.5l-1.7-2.6C10.6,0.2,10.3,0,10,0H6C5.7,0,5.4,0.2,5.2,0.4L3.5,3H1C0.4,3,0,3.4,0,4v11 c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V4C16,3.4,15.6,3,15,3z M14,14H2V5h2c0.3,0,0.6-0.2,0.8-0.4L6.5,2h2.9l1.7,2.6 C11.4,4.8,11.7,5,12,5h2V14z"> </path> <circle data-color="color-2" cx="8" cy="9" r="3"> </circle> </g> </svg>
basically, each <g> element represents an icon.
I am reading the <g> element contents in C'tor and storing them into member variables ( in order to avoid reading again and again ).Then I am "loading" the icon based on the click event using the "QSvgRenderer".
This works as expected, but is it the best practice or is there a better way I can acheive this ?
-
I understand your goal but what escapes me here is why you don't take advantage of QIcon's API. You should just have to assign each image of your SVG to the correct state on the icon and then it will be automatically be used by the button.
-
I understand your goal but what escapes me here is why you don't take advantage of QIcon's API. You should just have to assign each image of your SVG to the correct state on the icon and then it will be automatically be used by the button.
wrote on 3 Jan 2020, 01:56 last edited by@SGaist
Thanks for the reply.Do You mean to say something like this ?
@
toolButton = new QToolButton;
toolButton->setCheckable(true);
QIcon qIcon;
qIcon.addFile(":/Icons/images/first.svg",QSize(32,32),QIcon::Normal,QIcon::On);
qIcon.addFile(":/Icons/images/second.svg",QSize(32,32),QIcon::Normal,QIcon::Off);
toolButton->setIcon(qIcon);
@But here I need to have 2 seperate icons for on and off states.
But What I am trying to do is that only single svg icon which has 2 states should be toggled on click ?
Can that be done as per your above comment ?If my understanding of your above comment is wrong, would you please provide a minimal example ?
-
@Rizwan94 said in Toggle svg icon on Button click - QT C++:
But What I am trying to do is that only single svg icon which has 2 states should be toggled on click ?
The Qt svg handle can not handle more than one image in a svg.
-
@Rizwan94 said in Toggle svg icon on Button click - QT C++:
But What I am trying to do is that only single svg icon which has 2 states should be toggled on click ?
The Qt svg handle can not handle more than one image in a svg.
wrote on 3 Jan 2020, 16:24 last edited by@Christian-Ehrlicher said in Toggle svg icon on Button click - QT C++:
The Qt svg handle can not handle more than one image in a svg.
Oh, fine then.
I will go ahead with the approach suggested by @SGaist and make use of QIcon for toggling "each" svg icon.
1/7