How to add a hover transition to QPushButton?

前端 未结 2 1067
情歌与酒
情歌与酒 2021-01-06 17:31

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.

相关标签:
2条回答
  • 2021-01-06 17:46

    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:

    1. Create a subclass of QPushButton, e.g. AnimatedHoverButton

    2. 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);
      }
      
    3. 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);
      }
      
    4. 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:

    0 讨论(0)
  • 2021-01-06 17:57

    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.

    0 讨论(0)
提交回复
热议问题