QT属性动画--设置样式属性(其他属性)

故事背景

  最近在制作一个按钮切换的动画特效中接触了属性动画这部分内容,并由此产生了一些思考。
切换按钮动画
  开始也是不会使用 QPropertyAnimation 这个类,然后就在网上查资料总算是完成自己想要的效果了,但是发现了一个问题,网上我查到的资料在介绍使用QPropertyAnimation类时都是只使用 geometry 这一个属性。

          porpAnim->setPropertyName("geometry");

  这就令我很奇怪了,按理说这个QPropertyAnimation(属性动画)应该可以控制很多属性的,为什么大家都知识介绍这一个属性,以这个为契机,我开始研究如何进行样式动画属性.

遇到的问题

  既然是设置样式动画属性,那属性肯定不是geometry了,我们需要到头文件里面看看。我这里要设置的控件是QPushButton,我们可以直接去它的基类QWidget里面看样式属性。可以看到属性名称是styleSheet,是一个QString类型变量。

             Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet)

  接着仿照设置geometry来设置styleSheet。我是想着设置btn_A按钮的背景颜色进行一个由黑变白的动画效果。但是很遗憾,这样的操作并不奏效。于是我去网上找相关的资料,希望可以找到一点线索。

    QPropertyAnimation *animation = new QPropertyAnimation(ui->btn_A, "styleSheet");
    animation->setDuration(100);
    animation->setStartValue(QString("background-color:#000000"));
    animation->setEndValue(QString("background-color:#ffffff"));
    animation->start();

解决过程

  在网上转了一圈,还是没有找到什么好的方法,于是我决定自己来看看到底怎么解决这个问题。首先我想到的是可能是我传入setStartValue的值不对,既然是样式属性,那么传入的参数应该是样式表这样的字符串吧,于是我进行了如下修改。为了能看到明显变化,我在界面的构造函数中还设置btn_A按钮的样式表为红色。但还是不生效。

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setStyleSheet("#btn_A{ background-color:red} ");  //首先设置样式表
}

MainWindow::~MainWindow()
{
    delete ui;
}



void MainWindow::on_btn_B_clicked()
{
    QPropertyAnimation *animation = new QPropertyAnimation(ui->btn_A, "styleSheet");
    animation->setDuration(100);
    animation->setStartValue(QString("QPushButton{ background-color:#000000} "));     //这里是我的改动
    animation->setEndValue(QString("QPushButton{ background-color:#ffffff} "));
    animation->start();

}

失败1

  接着我又试了几种其他传入的参数,都不行,这让我一度陷入绝望。难道属性动画只能是修改geometry这一个属性吗?实在没有想法了我就去Qt Helper看看。
  看完了QPropertyAnimation这个类之后,没什么收获,接着我就又去它的父类QVariantAnimation去看看。果然皇天不负有心人,我还是找到了一些东西

Not all QVariant types are supported. Below is a list of currently supported QVariant types:
Int
UInt
Double
Float
QLine
QLineF
QPoint
QPointF
QSize
QSizeF
QRect
QRectF
QColor
If you need to interpolate other variant types, including custom types, you have to implement interpolation for these yourself. To do this, you can register an interpolator function for a given type. This function takes 3 parameters: the start value, the end value, and the current progress.
Example:

      QVariant myColorInterpolator(const QColor &start, const QColor &end, qreal progress)
      {
          ...
          return QColor(...);
      }
      ...
      qRegisterAnimationInterpolator<QColor>(myColorInterpolator);

Another option is to reimplement interpolated(), which returns interpolation values for the value being interpolated.

  上面这段话的意思是,目前支持的属性变量的类型是Int UInt Double Float QLine QLineF QPoint QPointF QSize QSizeF QRect QRectF QColor。没有QString,这也解释了为什么我上面传入的参数是无效的。不过Qt也为我们提供了设置其他类型的方法。类如注册动画属性qRegisterAnimationInterpolator(myColorInterpolator);(这个函数是可以自定义的),还有一个是重新实现 interpolated(),我决定试一下第一种方法。

最终方法

  下面说一下我是怎么用的,mySheetStyle函数是要注册使用的,这里我先没有使用传入的参数,是自己在函数中造了一些值,progress这个值就是一个进度从0到1,所以我设置的rgb颜色会随着进度改变。然后返回一个QString样式,效果如下。由于对颜色渐变值设置的原因,颜色变化不是很明显,但是绝对变化了。mySheetStyle函数中的逻辑可以依照自己的需要来改变。

QVariant mySheetStyle(const QString & start, const QString & end, qreal progress)
{
    int red   = (int)(40 * progress) + 50;
    int green = (int)(50 * progress) + 50;
    int blue  = (int)(200 * progress) + 50;

    QString style = QString("background-color: rgb(%1,%2,%3)").arg(red).arg(green).arg(blue);
    qDebug()<<"style = "<<style;
    return style;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setStyleSheet("#btn_A{ background-color:red} ");
    qRegisterAnimationInterpolator<QString>(mySheetStyle);   //这里进行了注册
}

void MainWindow::on_btn_B_clicked()
{
    QPropertyAnimation *animation = new QPropertyAnimation(ui->btn_A, "styleSheet");
    animation->setDuration(1000);
    animation->setStartValue(QString("QPushButton{ background-color:#000000} "));   //这里的值目前没有使用
    animation->setEndValue(QString("QPushButton{ background-color:#ffffff} "));
    animation->start();
}

成功1

总结

  大家需要注意的是,setStartValue传入的参数类型、qRegisterAnimationInterpolator(myColorInterpolator)最后设定类型、需要一致,并且类型需要和你设置的属性匹配。比如我设置的是styleSheet属性,它对应的变量类型是QString,所以上述的参数类型都是QString,换成其他类型是没有动画效果的。
  另外,关于上面我返回的样式字符串,为什么我没有指定选择器按钮也更改了,可能是因为创建属性动画对象时已经选择了控件

new QPropertyAnimation(ui->btn_A, "styleSheet");

  当然加上选择器也可以生效,但要注意前后设置的控件要保持一致性。

QString style = QString("background-color: rgb(%1,%2,%3)").arg(red).arg(green).arg(blue);

QString style = QString("#btn_A { background-color: rgb(%1,%2,%3)} ").arg(red).arg(green).arg(blue);