如何向 QPushButton 添加悬停过渡?
How to add a hover transition to QPushButton?
我尝试使用样式表自定义 QPushButton
。当我们将鼠标悬停在按钮上时,我想自定义按钮的颜色。它有效,但我想设置一个过渡持续时间。
但是在 Qt 中这个选项不可用。
这是我的自定义按钮:
#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; }");
}
参数"transition 0.9s"不成立。
这是一个example in CSS。
还有其他方法吗?
您可以使用 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();
}
会和外部qss设置冲突。所以把所有的qss都设置在SetColor
.
更新
出于某种原因,建议的解决方案在 Windows 10 上无法按预期工作。我已使用 painter.setOpacity(0.25);
和 painter.fillRect(rect(), m_currentColor);
更新了答案作为解决方法。 GitHub 存储库中的代码也已更新。
原因
QSS 不是 CSS。没有过渡 属性。 Here 是所有可用属性的列表。
解决方案
我建议您不使用样式表,而是选择另一条路,这条路更长,但更灵活。这是解决方案:
创建 QPushButton
的子class,例如AnimatedHoverButton
通过重新实现 QPushButton::event
获得有关 QEvent::HoverEnter
和 QEvent::HoverLeave
events 的通知
bool AnimatedHoverButton::event(QEvent *event)
{
switch (event->type()) {
case QEvent::HoverEnter:
animateHover(true);
break;
case QEvent::HoverLeave:
animateHover(false);
break;
default:
break;
}
return QPushButton::event(event);
}
- 创建
in
和 out
转换
void AnimatedHoverButton::animateHover(bool in)
{
if (m_transition)
m_transition->stop();
m_transition = new QVariantAnimation(this);
m_transition->setDuration(m_duration);
m_transition->setStartValue(m_currentColor);
m_transition->setEndValue(in ? palette().highlight().color()
: Qt::transparent);
connect(m_transition, &QVariantAnimation::valueChanged,
this, [this](const QVariant &value){
m_currentColor = value.value<QColor>();
repaint();
});
connect(m_transition, &QVariantAnimation::destroyed,
this, [this](){
m_transition = nullptr;
repaint();
});
m_transition->start(QAbstractAnimation::DeleteWhenStopped);
}
通过重新实现 QPushButton::paintEvent
事件处理程序并考虑动画的当前值来绘制按钮
void AnimatedHoverButton::paintEvent(QPaintEvent */*event*/)
{
QStylePainter painter(this);
QStyleOptionButton option;
initStyleOption(&option);
option.state &= ~QStyle::State_MouseOver;
painter.drawControl(QStyle::CE_PushButton, option);
painter.setOpacity(0.25);
painter.fillRect(rect(), m_currentColor);
}
注意:此解决方案使用小部件的调色板来设置动画的开始值和结束值。
例子
解决方案可能看起来很复杂,但幸运的是,我已经为您准备了一个工作示例,说明如何实施和使用 AnimatedHoverButton
class.
以下代码片段使用 AnimatedHoverButton
class 生成结果,类似于您提供的 CSS 示例:
#include <QApplication>
#include "AnimatedHoverButton.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
AnimatedHoverButton button(QObject::tr("Hover Over Me"));
button.setTransitionDuration(300);
button.resize(300, 150);
button.show();
return a.exec();
}
示例的完整代码可在 GitHub 上找到。
结果
给出的示例产生以下结果:
我尝试使用样式表自定义 QPushButton
。当我们将鼠标悬停在按钮上时,我想自定义按钮的颜色。它有效,但我想设置一个过渡持续时间。
但是在 Qt 中这个选项不可用。
这是我的自定义按钮:
#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; }");
}
参数"transition 0.9s"不成立。
这是一个example in CSS。
还有其他方法吗?
您可以使用 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();
}
会和外部qss设置冲突。所以把所有的qss都设置在SetColor
.
更新
出于某种原因,建议的解决方案在 Windows 10 上无法按预期工作。我已使用 painter.setOpacity(0.25);
和 painter.fillRect(rect(), m_currentColor);
更新了答案作为解决方法。 GitHub 存储库中的代码也已更新。
原因
QSS 不是 CSS。没有过渡 属性。 Here 是所有可用属性的列表。
解决方案
我建议您不使用样式表,而是选择另一条路,这条路更长,但更灵活。这是解决方案:
创建
QPushButton
的子class,例如AnimatedHoverButton
通过重新实现
获得有关QPushButton::event
QEvent::HoverEnter
和QEvent::HoverLeave
events 的通知bool AnimatedHoverButton::event(QEvent *event) { switch (event->type()) { case QEvent::HoverEnter: animateHover(true); break; case QEvent::HoverLeave: animateHover(false); break; default: break; } return QPushButton::event(event); }
- 创建
in
和out
转换void AnimatedHoverButton::animateHover(bool in) { if (m_transition) m_transition->stop(); m_transition = new QVariantAnimation(this); m_transition->setDuration(m_duration); m_transition->setStartValue(m_currentColor); m_transition->setEndValue(in ? palette().highlight().color() : Qt::transparent); connect(m_transition, &QVariantAnimation::valueChanged, this, [this](const QVariant &value){ m_currentColor = value.value<QColor>(); repaint(); }); connect(m_transition, &QVariantAnimation::destroyed, this, [this](){ m_transition = nullptr; repaint(); }); m_transition->start(QAbstractAnimation::DeleteWhenStopped); }
通过重新实现
QPushButton::paintEvent
事件处理程序并考虑动画的当前值来绘制按钮void AnimatedHoverButton::paintEvent(QPaintEvent */*event*/) { QStylePainter painter(this); QStyleOptionButton option; initStyleOption(&option); option.state &= ~QStyle::State_MouseOver; painter.drawControl(QStyle::CE_PushButton, option); painter.setOpacity(0.25); painter.fillRect(rect(), m_currentColor); }
注意:此解决方案使用小部件的调色板来设置动画的开始值和结束值。
例子
解决方案可能看起来很复杂,但幸运的是,我已经为您准备了一个工作示例,说明如何实施和使用 AnimatedHoverButton
class.
以下代码片段使用 AnimatedHoverButton
class 生成结果,类似于您提供的 CSS 示例:
#include <QApplication>
#include "AnimatedHoverButton.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
AnimatedHoverButton button(QObject::tr("Hover Over Me"));
button.setTransitionDuration(300);
button.resize(300, 150);
button.show();
return a.exec();
}
示例的完整代码可在 GitHub 上找到。
结果
给出的示例产生以下结果: