原文鏈接:https://www.cnblogs.com/lvdongjie/p/4366092.html
https://blog.csdn.net/wzs250969969/article/details/78418124
https://blog.csdn.net/kaida1234/article/details/82896611
Qt動畫架構圖:

動畫框架由基類QAbstractAnimation和它的兩個兒子QVariantAnimation和QAnimationGroup組成。QAbstractAnimation是所有動畫類的祖宗。它包含了所有動畫的基本屬性。比如開始,停止和暫停一個動畫的能力。它也可以接收時間改變通知。
動畫框架又進一步提供了QProertyAnimation類。它繼承自QVariantAnimation並對某個Qt屬性(它須是Qt的”元數據對象系統”的一部分,見http://blog.csdn.net/nkmnkm/article/details/8225089)執行動畫。此類對屬性執行一個寬松曲線插值。所以當你想去動畫一個值時,你可以把它聲明為一個屬性,並且讓你的類成為一個QObject。這給予我們極大的自由度來動畫那些已存在的widget和其它QObject。
復雜的動畫可以通過建立一個QAbstractAnimation的樹來構建。這個樹通過使用QAnimationGroups來創建,QAnimationGroups作為其它動畫的容器。注意動畫組也是從QAbstractAnimation派生的,所以動畫組可以再包含其它動畫組。
動畫框架可以單獨使用,同時也被設計為狀態機框架的一部分。狀態機提供了一個特定的狀態可以用來播放動畫。在進入或退出某個狀態時QState也可以設置屬性們,並且這個特定的動畫狀態將在指定QPropertyAnimation時給予的值之間做插值運算。后面我們要進一步介紹此問題。
在場景的背后,動畫被一個全局定時器收集,這個定時器發送update到所有的正在播放的動畫中。
-
動畫框架中的類們
QAbstractAnimation 所有動畫類的基類
QAnimationGroup 動畫組的基類
QEasingCurve 控制動畫的寬松曲線類
QParallelAnimationGroup 並行動畫組類
QPauseAnimation 串行動畫組類的暫停類
QPropertyAnimation 動畫Qt屬性的類
QSequentialAnimationGroup 串行動畫組類
QTimeLine 控制動畫的時間線類
QVariantAnimation 各動畫類的虛基類
QPropertyAnimation 類就是實現屬性以動畫形式改變的類,示例如下:
1、我們選擇動畫Qt屬性的一個主要理由是Qt屬性為我們提供了自己動畫已存在的類的自由度。尤其是QWidget類(我們也可以把它嵌入到一個QGraphicsView中)具有很多屬性表示其bounds,colors等等。讓我們看一個小例子:
QPushButton button("Animated Button"); button.show(); QPropertyAnimation animation(&button, "geometry"); //geometry就是說明要對這個屬性以動畫形式變化 animation.setDuration(10000); animation.setStartValue(QRect(0, 0, 0, 0)); animation.setEndValue(QRect(250, 250, 100, 30)); animation.start();
這段代碼將把按鈕在10秒種內從屏幕的左上角移動到(250,250)處,而且是逐漸變大。見下圖效果:

2、上面的例子舉在開始值和結束值之間做線性插值。還可以在開始和結束值之間設置值,插值運算就會經過這些點。
animation1 = new QPropertyAnimation(ui.pushButton, "geometry"); animation1->setDuration(10000); animation1->setKeyValueAt(0, QRect(0, 0, 00, 00)); animation1->setKeyValueAt(0.4, QRect(20, 250, 20, 30)); animation1->setKeyValueAt(0.8, QRect(100, 250, 20, 30)); animation1->setKeyValueAt(1, QRect(250, 250, 100, 30)); animation1->setEndValue(QRect(250, 250, 100, 30));
在此例中,動畫將按鈕在8秒中內弄到(250,250)處,然后在2秒種內又弄回原位。移位是在這些點中間以線性插值進行的。

3、你也有可能動畫一個QObject的值,雖然這些值並沒有被聲明為Qt屬性。唯一的要求就是這個值具有一個setter。之后你可以從這個類派生子類從而包含這些值並且聲明一個使用這個setter的屬性。注意每個Qt屬性都需要有一個getter,所以你需要提供一個getter,如果它不存在的話。
class MyGraphicsRectItem : public QObject, public QGraphicsRectItem { Q_OBJECT Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) //這個相當於給這個類聲明了一個geometry的屬性,獲取這個屬性值通過geometry方法,更改這個屬性的值通過setGeometry方法
};
在上例中,我們派生了QGraphicsRectItem並定義了一個geometry屬性。我們現在可以動畫這個widget的geometry了,即使QGraphicsRectItem沒有提供geometry屬性。
4、寬松曲線
QPropertyAnimation在屬性的開始值和結束值之間執行一個插值運算。除了向動畫添加更多的關鍵值外,你還可以使用一個寬松曲線。寬松曲線描述了一個在0和1之間插值的速度變化的函數,如果你想控制一個動畫的速度而不改變插值的路徑時,就非常有用。
animation1 = new QPropertyAnimation(ui.pushButton, "geometry"); animation1->setDuration(10000); animation1->setStartValue(QRect(0, 0, 0, 0)); animation1->setEndValue(QRect(250, 250, 100, 30)); animation1->setEasingCurve(QEasingCurve::OutBounce);
這里,動畫將按照一個曲線進行,這個曲線使得動畫像一個跳動的皮球從開始位置跳到結束位置。QEasingCurve具有一個大曲線集合,你可以從里面選擇一個。它們被定義為QEasingCurve::Type枚舉。如果你需要不一樣的曲線,你也可以自己實現一個,然后注冊到QEasingCurve。
5、下面我們自定義一個屬性alpha(畢竟上面的geometry屬性已經存在,屬性對應的讀寫方法也存在)來實現我們的屬性動畫

#ifndef MAIN_WINDOW_H #define MAIN_WINDOW_H ... class MainWindow : public CustomWindow { Q_OBJECT Q_PROPERTY(int alpha READ alpha WRITE setAlpha) public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: int alpha() const; void setAlpha(const int alpha); private: int m_nAlpha; QLabel *m_pLabel; }; #endif // MAIN_WINDOW_H
#include "main_window.h" MainWindow::MainWindow(QWidget *parent) : CustomWindow(parent) { ... QPushButton *pStartButton = new QPushButton(this); pStartButton->setText(QString::fromLocal8Bit("開始動畫")); m_pLabel = new QLabel(this); m_pLabel->setText(QString::fromLocal8Bit("一去丶二三里")); m_pLabel->setAlignment(Qt::AlignCenter); m_pLabel->setStyleSheet("color: rgb(0, 160, 230);"); QPropertyAnimation *pAnimation = new QPropertyAnimation(); pAnimation->setTargetObject(this); pAnimation->setPropertyName("alpha"); pAnimation->setDuration(1000); pAnimation->setKeyValueAt(0, 255); pAnimation->setKeyValueAt(0.5, 100); pAnimation->setKeyValueAt(1, 255); pAnimation->setLoopCount(-1); //永遠運行,直到stop connect(pStartButton, SIGNAL(clicked(bool)), pAnimation, SLOT(start())); ... } int MainWindow::alpha() const { return m_nAlpha; } void MainWindow::setAlpha(const int alpha) { m_nAlpha = alpha; QString strQSS = QString("color: rgb(0, 160, 230); background-color: rgba(10, 160, 105, %1);").arg(m_nAlpha); m_pLabel->setStyleSheet(strQSS); }
6、接下來我們就必須介紹一下自定義屬性Q_PROPERTY
/* Qt提供了一個絕妙的屬性系統。跟那些由編譯器提供的屬性差不多。然而,作為一個獨立於編譯器和平台的庫,Qt不依賴於非標准的編譯特性,
比如__property 或[property]。Qt可以在任何平台上的標准編譯器下編譯。Qt屬性系統基於元數據對象系統--就是那個提供了對象內置信號和槽通訊機制的家伙。 Q_PROPERTY()是一個宏,用來在一個類中聲明一個屬性property,由於該宏是qt特有的,需要用moc進行編譯,故必須繼承於QObject類。 */ Q_PROPERTY(type name READ getFunction [WRITE setFunction] [RESET resetFunction] [NOTIFY notifySignal] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL])
//示例
Q_PROPERTY(double minValue READ getMinValue WRITE setMinValue)
Q_PROPERTY(bool animation READ getAnimation WRITE setAnimation)
Q_PROPERTY(QColor barColor READ getBarColor WRITE setBarColor)
- 一個屬性的行為就像類的數據成員,但是它還具有附加的特性,這些特性可以被元數據對象系統操作。這些特性是:
需要一個READ訪問器函數。用於讀屬性的值。理想情況下,有一個不變的函數用於此目的,並且它必須返回屬性的類型的值或指針或引用。例如,QWidget::focus是一個只讀的屬性,它對應一個讀函數:QWidget::hasFocus()。- 一個可選的WRITE訪問器函數。它用於設置屬性的值。它必須返回空並且至少具有一個參數,參數是屬性類型的值或指針或引用。例如:QWidget::enabled具有WRITE函數QWidget::setEnable()。只讀屬性不需要寫函數。例如,QWidget::focus沒有對應的寫函數。
- 一個可選的RESET函數。用於設置屬性的值到它的默認值。例如:QWidget::cursor具有典型的READ和WRITE函數,QWidget::cursor()和QWidget::setCursor(),並且它也具有一個RESET函數,QWidget::unsetCursor()。RESET函數必須返回void並且不帶有任何參數。
- 一個可選的NOTIFY信號。如果被定義了,信號將在屬性的值改變時發出。信號必須帶有一個參數,這個參數的類型必須與屬性相同;參數保存的是屬性的新值。
- 一個DESIGNABLE變量表明此屬性是否在界面設計器的屬性編輯器中出現。大多數屬性是可見的,除了為這個變量傳入true或false,你還可以指定一個bool型的成員函數。
- SCRIPTABLE變量表明這個屬性是否可以被一個腳本引擎操作(默認是true)。你也可以賦予它true或false或bool型函數。
- STORED變量表明了屬性是否被認為是獨立存在還是依賴於其它的值而存在。它也表明是否在保存對象狀態時保存此屬性的值。大多數屬性都是需要保存的,但是,如QWidget::minimumWidth()就是不被保存的,因為它的值是從另一個屬性QWidget::minimumSize()得來的。
- USER變量表明屬性是否被設計為面向用戶的或用戶可修改的類屬性。通常,每個類只有一個USER屬性。例如,QAbstractButton::checked是按鈕類的用戶可修改屬性。注意QItemDelegate獲取和設置widget的USER屬性。
- CONSTANT的出現表明屬性的值是不變的。對於一個object實例,常量屬性的READ方法在每次被調用時必須返回相同的值。此常量值可能在不同的object實例中不相同。一個常量屬性不能具有WRITE方法或NOYIFY信號。
- FINAL變量的出現表明屬性不能被派生類所重寫。有些情況下,這可以用於效率優化,但不是被moc強制的。程序員必須永遠注意不能重寫一個FINAL屬性。
READ,WRITE和RESET函數都可以被繼承。它們也可以是虛函數。當它們在被多重繼承中被繼承時,它們必須出現在第一個被繼承的類中。
屬性的類型可以是被QVariant支持的所有類型,也可以是用戶定義的類型。在下面的例子中,類QDate被當作用戶自定義類型。
Q_PROPERTY(QDate data READ getDate WRITE setDate)因為QDate是用戶定義的,你必須包含<QDate>頭文件。
對於QMap,QList和QValueList屬性,屬性的值是一個QVariant,它包含整個list或map。注意Q_PROPERTY字符串不能包含逗號,因為逗號會划分宏的參數。因此,你必須使用QMap作為屬性的類型而不是QMap<QString,QVariant>。為了保持一致性,也需要用QList和QValueList而不是QList<QVariant>和QValueList<QVariant>。
代碼調用的例子:
class Test : public QObject { Q_OBJECT Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) public: Test(QObject *parent = 0) : QObject(parent) {} virtual ~Test(){} void setEnabled(bool e) { enabled = e; } bool isEnabled() const { return enabled; } private: bool enabled; };然后在主函數中添加:
Test *test = new Test; test->setProperty("enabled", true); //test->setEnabled(true); //ok also work if(test->property("enabled").toBool()) .....Qt Creator Designer插件的 例子:
頭文件中定義了一個minValue 的屬性,如下:
class BarRuler : public QWidget { Q_OBJECT Q_PROPERTY(double minValue READ getMinValue WRITE setMinValue) public: explicit BarRuler(QWidget *parent = 0); ~BarRuler(); private: double minValue; public: double getMinValue() const; public slots: void setRange(double minValue, double maxValue); };cpp文件實現如下:
#include "barruler.h" BarRuler::BarRuler(QWidget *parent) : QWidget(parent) { minValue = 0; } BarRuler::~BarRuler() { } double BarRuler::getMinValue() const { return this->minValue; } void BarRuler::setMinValue(double minValue) { this->minValue = minValue; update(); }在設計模式界面調用如下:
1、先拖入一個widget控件
2、在其上右鍵選擇“提升為”BarRuler
3、點擊屬性欄的加號,選擇其它類型,如圖

