I try to make a custom QPushButton
with a stylesheet. I want to custom color of button when we mouse over it. It works, but I want to put a transition duration.
But in Qt this option is not available.
Here is my custom button:
#include "bouton.h"
Bouton::Bouton(QString title, QWidget *parent) : QPushButton()
{
setGeometry(50,50,120,40);
setText(title);
setMinimumHeight(30);
setParent(parent);
setStyleSheet(" QPushButton {"
"border-radius: 5px; "
"border: 1.5px solid rgb(91,231,255); "
"background-color: white; }"
"QPushButton:pressed {"
"border: 1.4px solid rgb(73,186,205); }"
"QPushButton:hover {"
"font-size: 16px;"
"transition: 0.9s; }");
}
The argument "transition 0.9s" doesn't work.
Here is an example in CSS.
Are there other ways to do this?
Cause
QSS is not CSS. There is no transition property. Here is a list of all available properties.
Solution
Instead of using stylesheets, I would suggest you to take another path, which is longer, but gives you more flexibility. Here is the solution:
Create a subclass of QPushButton
, e.g. AnimatedHoverButton
Get notified about QEvent::HoverEnter
and QEvent::HoverLeave
events by reimplementing QPushButton::event
bool AnimatedHoverButton::event(QEvent *event)
{
switch (event->type()) {
case QEvent::HoverEnter:
animateHover(true);
break;
case QEvent::HoverLeave:
animateHover(false);
break;
}
return QPushButton::event(event);
}
Create the in
and out
transition by using QVariantAnimation
void AnimatedHoverButton::animateHover(bool in)
{
const QColor &baseColor(palette().brush(QPalette::Button).color());
const QColor &highlightColor(palette().brush(QPalette::Highlight).color());
QColor startValue(in ? baseColor : highlightColor);
if (m_transition) {
startValue = m_transition->currentValue().value<QColor>();
m_transition->stop();
}
m_transition = new QVariantAnimation(this);
m_transition->setStartValue(startValue);
m_transition->setEndValue(in ? highlightColor : baseColor);
m_transition->setDuration(m_duration);
connect(m_transition, &QVariantAnimation::valueChanged, [this](const QVariant &value){
m_currentColor = value.value<QColor>();
repaint();
});
connect(m_transition, &QVariantAnimation::destroyed, [this](){
m_transition = nullptr;
});
m_transition->start(QAbstractAnimation::DeleteWhenStopped);
}
Paint the button by reimplementing the QPushButton::paintEvent
event handler and taking into account the current value of the animation
void AnimatedHoverButton::paintEvent(QPaintEvent * /*event*/)
{
QStylePainter painter(this);
QStyleOptionButton option;
QPalette p(palette());
initStyleOption(&option);
p.setBrush(QPalette::Button, m_currentColor);
option.palette = p;
option.state |= QStyle::State_MouseOver;
painter.drawControl(QStyle::CE_PushButton, option);
}
Note: This solution uses the widget's palette to set the start and end values of the animation.
Example
The solution might seem complicated, but fortunatelly I have prepared a working example for you of how to implement and use the AnimatedHoverButton
class.
The following code fragment uses the AnimatedHoverButton
class to produce a result, similar to the CSS example you have provided:
auto *button = new AnimatedHoverButton(tr("Hover Over Me"), this);
QPalette p(button->palette());
p.setBrush(QPalette::Button, QColor("#F89778"));
p.setBrush(QPalette::ButtonText, QColor("#FFFFFF"));
p.setBrush(QPalette::Highlight, QColor("#F4511E"));
button->setPalette(p);
button->setTransitionDuration(300);
setCentralWidget(button);
setContentsMargins(10, 10, 10, 10);
The full code of the example is available on GitHub.
Result
The given example produces the following result:
You can use Animation.
MyButton.h
#include <QPushButton>
#include <QColor>
#include <QPropertyAnimation>
class MyButton : public QPushButton
{
Q_OBJECT
Q_PROPERTY(QColor color READ GetColor WRITE SetColor)
public:
explicit MyButton(QWidget *parent = 0);
void SetColor(const QColor& color);
const QColor& GetColor() const;
protected:
bool eventFilter(QObject *obj, QEvent *e);
private:
QColor m_currentColor;
QPropertyAnimation m_colorAnimation;
void StartHoverEnterAnimation();
void StartHoverLeaveAnimation();
};
MyButton.cpp
#include "MyButton.h"
#include <QEvent>
#include <QDebug>
MyButton::MyButton(QWidget *parent) :
QPushButton(parent),
m_colorAnimation(this, "color")
{
this->installEventFilter(this);
}
void MyButton::SetColor(const QColor& color)
{
m_currentColor = color;
QString css = "QPushButton { border-radius: 5px; ";
css.append("border: 1.5px solid rgb(91,231,255); ");
QString strColor = QString("rgb(%1, %2, %3)").arg(color.red()).arg(color.green()).arg(color.blue());
css.append("background-color: " + strColor + "; }");
setStyleSheet(css);
}
const QColor& MyButton::GetColor() const
{
return m_currentColor;
}
bool MyButton::eventFilter(QObject *obj, QEvent *e)
{
if (e->type() == QEvent::HoverEnter) {
StartHoverEnterAnimation();
}
if (e->type() == QEvent::HoverLeave) {
StartHoverLeaveAnimation();
}
return false;
}
void MyButton::StartHoverEnterAnimation()
{
m_colorAnimation.stop();
m_colorAnimation.setDuration(900); //set your transition
m_colorAnimation.setStartValue(GetColor()); //starts from current color
m_colorAnimation.setEndValue(QColor(100, 100, 100));//set your hover color
m_colorAnimation.setEasingCurve(QEasingCurve::Linear);//animation style
m_colorAnimation.start();
}
void MyButton::StartHoverLeaveAnimation()
{
m_colorAnimation.stop();
m_colorAnimation.setDuration(900); //set your transition
m_colorAnimation.setStartValue(GetColor()); //starts from current color
m_colorAnimation.setEndValue(QColor(255, 0, 0));//set your regular color
m_colorAnimation.setEasingCurve(QEasingCurve::Linear);//animation style
m_colorAnimation.start();
}
It will conflict with external qss setting. So set all qss in SetColor
.