qml demo分析(threadedanimation-线程动画)
一、效果預(yù)覽
使用過qml的同學(xué)都知道,使用qml做動(dòng)畫效果是非常簡單的,再也不需要像QWidget那樣,自己模擬一個(gè)動(dòng)畫,費(fèi)時(shí)又費(fèi)力,往往還達(dá)不到效果。今天我們就來分析下qml的兩種動(dòng)畫實(shí)現(xiàn)方式,如圖1所示,窗口底部的提示信息文本“This application shows two spinners. The one to the right is animated on the scene graph thread (when applicable) and the left one is using the normal Qt Quick animation system.”意思就是左邊的窗口使用quick animation實(shí)現(xiàn),右側(cè)窗口使用opengl scene graph實(shí)現(xiàn),細(xì)心的同學(xué)就會發(fā)現(xiàn),當(dāng)Not blocked字樣變?yōu)锽locked時(shí),左側(cè)的旋轉(zhuǎn)動(dòng)畫每過大概400ms就會停頓下,而右側(cè)窗口絲毫不受影響。好了廢話不多說,我們這就來分析下這個(gè)示例程序的源碼,這個(gè)現(xiàn)象就不難理解啦。
圖1 旋轉(zhuǎn)動(dòng)畫
二、源碼分析
首先我們先來分析下該示例代碼的工程目錄,如圖1所示,spinner一個(gè)C++類,其重寫了QQuickItem組件,QQuickItem其實(shí)在qml中就相當(dāng)于Item組件,重寫QQuickItem是為了注冊到qml系統(tǒng)中,把spinner當(dāng)qml的一個(gè)組件使用,在spinner的實(shí)現(xiàn)過程中,使用了opengl的方式來進(jìn)行繪圖。
圖2 工程目錄
該示例代碼的main.cpp文件與qml demo分析(abstractitemmodel-數(shù)據(jù)分離)文章中分析的一樣,都是注冊了一個(gè)自定義QQuickItem對象到qml上下文中,并使用QQuickView加載main.qml文件并顯示,在此就不貼代碼啦。
1、opengl scene graph
由于這是第一次分析opengl的示例程序,這里我將會把spinner的頭文件和實(shí)現(xiàn)代碼都貼上。首先先來看下spinner類頭文件
1 #ifndef SPINNER_H 2 #define SPINNER_H 3 4 #include <QtQuick/QQuickItem> 5 6 class Spinner : public QQuickItem 7 { 8 Q_OBJECT 9 10 Q_PROPERTY(bool spinning READ spinning WRITE setSpinning NOTIFY spinningChanged) 11 12 public: 13 Spinner(); 14 15 bool spinning() const { return m_spinning; } 16 void setSpinning(bool spinning); 17 18 protected: 19 QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); 20 21 signals: 22 void spinningChanged(); 23 24 private: 25 bool m_spinning; 26 };頭文件中聲明了一個(gè)SPinner類,繼承自QQuickItem類,重寫該類是為了使用opengl繪制旋轉(zhuǎn)效果。代碼中使用了Q_PROPERTY宏將m_spinning成員變量注冊到了qml系統(tǒng)中,qml代碼直接可以通過spinning:值的方式修改該變量,該宏在之前的文章中也有簡單的介紹,具體參見qml demo分析(customgeometry-貝塞爾曲線)。
1 Spinner::Spinner() 2 : m_spinning(false) 3 { 4 setSize(QSize(64, 64)); 5 setFlag(ItemHasContents); 6 } 7 8 void Spinner::setSpinning(bool spinning) 9 { 10 if (spinning == m_spinning) 11 return; 12 m_spinning = spinning; 13 emit spinningChanged(); 14 update(); 15 } 16 17 QSGNode *Spinner::updatePaintNode(QSGNode *old, UpdatePaintNodeData *) 18 { 19 SpinnerNode *n = static_cast<SpinnerNode *>(old); 20 if (!n) 21 n = new SpinnerNode(window()); 22 23 n->setSpinning(m_spinning); 24 25 return n; 26 }在Spinner類實(shí)現(xiàn)中,updatePaintNode接口中構(gòu)造了一個(gè)SpinnerNode類,具體的繪制操作就是在SpinnerNode類中實(shí)現(xiàn)。
1 class SpinnerNode : public QObject, public QSGTransformNode 2 { 3 Q_OBJECT 4 public: 5 SpinnerNode(QQuickWindow *window) 6 : m_rotation(0) 7 , m_spinning(false) 8 , m_window(window) 9 { 10 connect(window, &QQuickWindow::beforeRendering, this, &SpinnerNode::maybeRotate); 11 connect(window, &QQuickWindow::frameSwapped, this, &SpinnerNode::maybeUpdate); 12 13 QImage image(":/scenegraph/threadedanimation/spinner.png"); 14 m_texture = window->createTextureFromImage(image);//創(chuàng)建一個(gè)紋理 15 QSGSimpleTextureNode *textureNode = new QSGSimpleTextureNode();//簡單紋理節(jié)點(diǎn) 加入場景前必須有一個(gè)紋理 16 textureNode->setTexture(m_texture);//設(shè)置紋理 17 textureNode->setRect(0, 0, image.width(), image.height()); 18 textureNode->setFiltering(QSGTexture::Linear); 19 appendChildNode(textureNode); 20 } 21 22 ~SpinnerNode() { 23 delete m_texture; 24 } 25 26 void setSpinning(bool spinning) 27 { 28 m_spinning = spinning; 29 } 30 31 public slots: 32 void maybeRotate() { 33 if (m_spinning) { 34 m_rotation += (360 / m_window->screen()->refreshRate()); 35 QMatrix4x4 matrix; 36 matrix.translate(32, 32); 37 matrix.rotate(m_rotation, 0, 0, 1);//繞z軸旋轉(zhuǎn) 38 matrix.translate(-32, -32); 39 setMatrix(matrix); 40 } 41 } 42 43 void maybeUpdate() { 44 if (m_spinning) { 45 m_window->update(); 46 } 47 } 48 49 private: 50 qreal m_rotation; 51 bool m_spinning; 52 QSGTexture *m_texture; 53 QQuickWindow *m_window; 54 };SpinnerNode類繼承自QSGTransformNode,構(gòu)造函數(shù)中使用圖片構(gòu)造了一個(gè)紋理,并將紋理設(shè)置到QSGSimpleTextureNode對象中,進(jìn)而將該對象添加到場景,添加到場景的順序決定他們在場景中被繪制的順序。
SpinnerNode類中使用maybeRotate方法將紋理進(jìn)行了旋轉(zhuǎn),代碼如上32-41行所示。
2、quick animation
使用qml屬性動(dòng)畫進(jìn)行旋轉(zhuǎn)操作相對來說比較簡單,只需要使用NumberAnimation on rotation即可,具體代碼如下64-66行
1 Rectangle { 2 3 width: 320 4 height: 480 5 6 gradient: Gradient {//設(shè)置背景色 7 GradientStop { position: 0; color: "lightsteelblue" } 8 GradientStop { position: 1; color: "black" } 9 } 10 11 Rectangle {//與blockingLabel組成一個(gè)提示框 12 color: Qt.rgba(1, 1, 1, 0.7); 13 radius: 10 14 border.width: 1 15 border.color: "white" 16 anchors.fill: blockingLabel; 17 anchors.margins: -10 18 } 19 20 Text { 21 id: blockingLabel 22 color: blocker.running ? "red" : "black"//根據(jù)定時(shí)器狀態(tài) 修改提示框文本信息和字體顏色 23 text: blocker.running ? "Blocked!" : "Not blocked" 24 anchors.horizontalCenter: parent.horizontalCenter 25 anchors.top: parent.top 26 anchors.topMargin: 100 27 } 28 29 Timer { 30 id: blocker 31 interval: 357//觸發(fā)間隔 32 running: false;//默認(rèn)沒有啟動(dòng) 33 repeat: true 34 onTriggered: {//該槽的作用就是為了模擬一個(gè)延遲 說明opengl的渲染不在主線程進(jìn)行 而quick animation在主線程進(jìn)行渲染 35 var d = new Date(); 36 var x = 0; 37 var wait = 50 + Math.random() * 200; 38 while ((new Date().getTime() - d.getTime()) < 100) { 39 x += 1; 40 } 41 } 42 } 43 44 Timer { 45 id: blockerEnabler 46 interval: 4000 47 running: true 48 repeat: true 49 onTriggered: { 50 blocker.running = !blocker.running//定時(shí)器狀態(tài)修改 4s中置反 51 } 52 } 53 54 Spinner {//opengl 55 anchors.centerIn: parent 56 anchors.horizontalCenterOffset: 80 57 spinning: true//通過setSpinning接口設(shè)置該屬性值 58 } 59 60 Image {//quick animation 61 anchors.centerIn: parent 62 anchors.horizontalCenterOffset: -80 63 source: "spinner.png" 64 NumberAnimation on rotation { 65 from: 0; to: 360; duration: 1000; loops: Animation.Infinite//1s轉(zhuǎn)動(dòng)一圈 66 } 67 } 68 69 Rectangle {//與label文本組成底部提示框 70 color: Qt.rgba(1, 1, 1, 0.7) 71 radius: 10 72 border.width: 1 73 border.color: "white" 74 anchors.fill: label 75 anchors.margins: -10 76 } 77 78 Text { 79 id: label 80 color: "black" 81 wrapMode: Text.WordWrap 82 text: "This application shows two spinners. The one to the right is animated on the scene graph thread (when applicable) and the left one is using the normal Qt Quick animation system." 83 anchors.right: parent.right 84 anchors.left: parent.left 85 anchors.bottom: parent.bottom 86 anchors.margins: 20 87 } 88 }?
總結(jié):使用qml有兩種方式實(shí)現(xiàn)旋轉(zhuǎn),往大的說就是動(dòng)畫,有兩種方式:opengl scene or quick animation。
opengl scene:實(shí)現(xiàn)比較麻煩,但是線程獨(dú)立,不會因?yàn)閺?fù)雜計(jì)算而阻塞ui繪制
quick animation:使用簡單,但是是在主線程進(jìn)行繪制。
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的qml demo分析(threadedanimation-线程动画)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# HTTP请求后对gzip页面实现解
- 下一篇: 二叉树(先序遍历)非递归