Change icon on state change using Qt stylesheet

4.9k views Asked by At

I'm trying to support theming for my application, and I'm coming across a problem where I have to change icons based on the state of the QPushButton (default, hover, pressed, etc.). Here is what I use to set the icon for a QPushButton:

QPushButton#playButton {
    qproperty-icon: url(":/light/icons/play_light.png");
}

Because the hover state uses a background that requires an icon from my dark theme, I tried to change it to the other using this:

QPushButton#playButton:hover {
    qproperty-icon: url(":/dark/icons/play_dark.png");
}

When I do this, the play_light.png displays as it should, but it doesn't change to play_dark.png on state change.

In my Python code, the play button changes to a stop button on playback, so in my style, I set it to that icon using a custom property:

QPushButton#playButton[isPlaying="true"] {
    qproperty-icon: url(":/light/icons/stop_light.png");
}

This wouldn't change for me either. So, I found some code online to reset the style for the button, which looks like this:

self.ui.playButton.setProperty('isPlaying', not isEnable)
self.ui.playButton.setStyle(qApp.style())

I don't want to use this workaround for every single button for every single state change. Have you guys ran into this problem before?

Thanks for your time looking at this.

3

There are 3 answers

1
Spen-ZAR On

Figured it out. I kept the icons on my form so that I knew which icons went to which one, but in my stylesheet, I did something like this:

QPushButton#searchNext {
    qproperty-icon: none;
    image: url(":/light/icons/down_light.png"); 
}

Then on my hover:

QPushButton#searchNext:hover {
    image: url(":/dark/icons/down_dark.png");   
}

I still needed the self.ui.playButton.setStyle(qApp.style()) code when I change a custom property, but for everything else, this works fine.

2
DmitryARN On

Setting qApp.style() is not a good idea. Try this (convert from C++):

button->style()->unpolish(button);
button->style()->polish(button);
button->update();
0
VerySimple On

From experience, all variables which could be set with Q_PROPERTY in stylesheet appear to be a persistent state and can only be set once.

For example, if you add a custom border-radius and set such stylesheet, you will see the rounded edge, and then you set an empty stylesheet, you will discover the widget changes back to what it used to be. But for a "qproperty-xxx", once it's set and then if it's not explicitly updated, it won't change. Moreover, the "qproperty-xxx" only can be set in default selector.

Inherit QToolButton as MyToolButton, here are the main codes:

Q_PROPERTY(QIcon iconUp READ iconUp WRITE setIconUp NOTIFY iconChanged)
Q_PROPERTY(QIcon iconOver READ iconOver WRITE setIconOver NOTIFY iconChanged)
Q_PROPERTY(QIcon iconDown READ iconDown WRITE setIconDown NOTIFY iconChanged)
Q_PROPERTY(QIcon iconUpChecked READ iconUpChecked WRITE setIconUpChecked NOTIFY iconChanged)
Q_PROPERTY(QIcon iconOverChecked READ iconOverChecked WRITE setIconOverChecked NOTIFY iconChanged)
Q_PROPERTY(QIcon iconDownChecked READ iconDownChecked WRITE setIconDownChecked NOTIFY iconChanged)

protected:
    QIcon m_iconUp;
    QIcon m_iconOver;
    QIcon m_iconDown;
    QIcon m_iconUpChecked;
    QIcon m_iconOverChecked;
    QIcon m_iconDownChecked;

signals:
    void iconChanged();

bool event(QEvent *event) override {
    switch (event->type()) {
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
    case QEvent::Enter:
    case QEvent::Leave:
        reloadIcon();
        break;
    default:
        break;
    }
    return QToolButton::event(event);
}

void checkStateSet() override {
    reloadIcon();
}

void reloadIcon() {
    if (isChecked()) {
        if (isDown() && !m_iconDownChecked.isNull()) {
            setIcon(m_iconDownChecked);
            return;
        } else if (underMouse() && !m_iconOverChecked.isNull()) {
            setIcon(m_iconOverChecked);
            return;
        } else if (!m_iconUpChecked.isNull()) {
            setIcon(m_iconUpChecked);
            return;
        }
    }
    if (isDown() && !m_iconDown.isNull()) {
        setIcon(m_iconDown);
        return;
    } else if (underMouse() && !m_iconOver.isNull()) {
        setIcon(m_iconOver);
        return;
    } else if (!m_iconUp.isNull()) {
        setIcon(m_iconUp);
        return;
    } else {
        setIcon(QIcon());
    }
}

Stylesheet example:

MyToolButton {
    qproperty-iconUp: url(:/images/up.svg);
    qproperty-iconOver: url(:/images/hover.svg);
    qproperty-iconDown: url(:/images/pressed.svg);
}