Zeyad Obaia
Zeyad Obaia

Reputation: 736

Change QPushButton Icon on hover and pressed

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.

Upvotes: 9

Views: 32025

Answers (6)

CybeX
CybeX

Reputation: 2406

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.

Upvotes: 3

Ben
Ben

Reputation: 136

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!

Upvotes: 2

Dmitry
Dmitry

Reputation: 3143

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!

Upvotes: 16

JoeJoe909
JoeJoe909

Reputation: 41

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

Upvotes: 3

george
george

Reputation: 339

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);}");

Upvotes: 2

deMax
deMax

Reputation: 86

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);

Upvotes: 2

Related Questions