8

I'm trying to change the Icon of a QpushButton on hover and pressed, I'm using QtDesigner with stylesheets. I tried this

QpushButton{
       qproperty-icon:url(:/images/start.png);
}

QPushButton:hover
{
       qproperty-icon:url(:/images/start_hov.png);
}

But it doesn't work.

I tried setting it from QtDesigner Menu but it didn't work as well.

Zeyad Obaia
  • 686
  • 1
  • 6
  • 21

6 Answers6

14

Unfortunately, it is a bug of Qt which is still not fixed. There's a workaround suggestion within the comments to that bug, basically you could use empty qproperty-icon and reserve the space necessary for it while actually changing background-image property instead:

QPushButton {
    qproperty-icon: url(" "); /* empty image */
    qproperty-iconSize: 16px 16px; /* space for the background image */
    background-image: url(":/images/start.png");
    background-repeat: no-repeat;
}

QPushButton:hover {
    background-image: url(":/images/start_hov.png");
    background-repeat: no-repeat;
}

But the end result looks... not very satisfactory really. You can get much better results if you use C++ to change the button's icon at runtime, here's a simple example using event filter:

#include <QObject>
#include <QPushButton>
#include <QEvent>

class ButtonHoverWatcher : public QObject
{
    Q_OBJECT
public:
    explicit ButtonHoverWatcher(QObject * parent = Q_NULLPTR);
    virtual bool eventFilter(QObject * watched, QEvent * event) Q_DECL_OVERRIDE;
};

ButtonHoverWatcher::ButtonHoverWatcher(QObject * parent) :
    QObject(parent)
{}

bool ButtonHoverWatcher::eventFilter(QObject * watched, QEvent * event)
{
    QPushButton * button = qobject_cast<QPushButton*>(watched);
    if (!button) {
        return false;
    }

    if (event->type() == QEvent::Enter) {
        // The push button is hovered by mouse
        button->setIcon(QIcon(":/images/start_hov.png"));
        return true;
    }

    if (event->type() == QEvent::Leave){
        // The push button is not hovered by mouse
        button->setIcon(QIcon(":/images/start.png"));
        return true;
    }

    return false;
}

Then somewhere in your code setting up the UI you do something like this:

ButtonHoverWatcher * watcher = new ButtonHoverWatcher(this);
ui->pushButton->installEventFilter(watcher);

And bingo - you get the button's icon changing on hover and unhover!

Dmitry
  • 3,063
  • 2
  • 22
  • 32
  • QIcon objects can contain up to 4 images depending on the mode. Wouldn't it be easier to just set those images at creation and then let Qt do the rest? – RobbieE Oct 30 '16 at 07:07
  • 1
    I wasn't able to get this approach to work and neither was the topicstarter trying to set icon images from Qt Designer. Could you please provide a working example of this approach via another answer? – Dmitry Oct 30 '16 at 09:11
3

After reading this article and encountering similar issues. This is my work around in c++ not using style sheet from designer.

1>I create Icons one for being pressed and one for normal. In your case we would address it as the hover condition.

2>Add the icons to the resource file.

3>Use the following code for reference...

Where Add_PB is a QPushButton.

Add_PB->setStyleSheet( "*{border-image: url(:/icons/maximize.bmp);}"  
":pressed{ border-image: url(:/icons/maximize_pressed.bmp);}"); 

The key take away here is you can use setStyleSheet to set diffrent icons for different conditons. I couldnt get the above code to work until I used the * operator or "Universal Selector" in the CSS string.

Reference: http://doc.qt.io/qt-5/stylesheet-syntax.html

2

I maked it in designer, from ui_...h file:

QIcon icon;
icon.addFile(QStringLiteral(":/unpressed.png"), QSize(), QIcon::Normal, QIcon::Off);
icon.addFile(QStringLiteral(":/pressed.png"), QSize(), QIcon::Normal, QIcon::On);
pushButton->setIcon(icon);
deMax
  • 86
  • 7
2

In c++ , we can achieve it using the following code:

ui->button->setStyleSheet("QPushButton{border-image : url(./default_Img.png);} QPushButton:hover{border-image : url(./hover_Img.png); }"
                               "QPushButton:focus{border-image : url(./focus_Img.png);}");
george
  • 339
  • 3
  • 12
  • this is not a c++, but rather a workaround of the missing Qt feature. Those mode bitmaps are not styles and shuoldn't be set thru the StyleSheet at all. – Igor Oct 29 '18 at 17:01
2

To get a button with only an image showing by default, then a different image on hover, I tried having an icon set in the editor and playing around with the onSelected, onActive, etc. but naturally, it didn't work.

What did work is inspired from JosephFarrish's answer and goerge's.

For the particular push button, I have 2 images:

  • One shown by default.

enter image description here

  • one for a hover effect.

enter image description here

My solution for a specific QPushButton is:

QPushButton {
    border-image: url(:/icons/ic-explore);
    background-repeat: no-repeat;
    width: 32px;
    height: 32px;
}

QPushButton:hover {
    border-image: url(:/icons/ic-explore-hover);
    background-repeat: no-repeat;
}

as you can see, the ic-explore and ic-explore-hover are added to my resource file as shown below:

enter image description here

where the actual icons are in the root project folder, in a folder named icons. The prefix for the icons is given by :/icons/ and this coincidentally happens to be the same name as the icons folder name.

Note: with the CSS that I set the width and height of the QPushButton.

CybeX
  • 2,060
  • 3
  • 48
  • 115
1

I think it's worth mentioning that as of the time of posting this answer, the bug mentioned in the approved answer appears to have been fixed. I have the following stylesheet macro for my buttons. This makes the button icons change correctly when they are hovered over and pressed.

#define BUTTON_STYLESHEET_TEMPLATE(not_pressed, hovered, pressed) "QPushButton {"\
"border-image: url(:icons/" not_pressed ");"\
"background-repeat: no-repeat;"\
"width: 65px;"\
"height: 56px;"\
"}"\
"QPushButton:hover {"\
"border-image: url(:icons/" hovered ");"\
"}"\
"QPushButton:pressed {"\
"border-image: url(:icons/" pressed ");"\
"}"

I applied this to each of my QPushButtons with the setStyleSheet function, passing the three different images for each state into the macro.

button.setStyleSheet(BUTTON_STYLESHEET_TEMPLATE("not_pressed.png", "hovered.png", "pressed.png"));

Hopefully this helps!

Ben
  • 116
  • 1
  • 7