一、效果预览

  使用过qml的同学都知道,使用qml做动画效果是非常简单的,再也不需要像QWidget那样,自己模拟一个动画,费时又费力,往往还达不到效果。今天我们就来分析下qml的两种动画实现方式,如图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实现,右侧窗口使用opengl scene graph实现,细心的同学就会发现,当Not blocked字样变为Blocked时,左侧的旋转动画每过大概400ms就会停顿下,而右侧窗口丝毫不受影响。好了废话不多说,我们这就来分析下这个示例程序的源码,这个现象就不难理解啦。

图1 旋转动画

二、源码分析

  首先我们先来分析下该示例代码的工程目录,如图1所示,spinner一个C++类,其重写了QQuickItem组件,QQuickItem其实在qml中就相当于Item组件,重写QQuickItem是为了注册到qml系统中,把spinner当qml的一个组件使用,在spinner的实现过程中,使用了opengl的方式来进行绘图。

图2 工程目录

  该示例代码的main.cpp文件与qml demo分析(abstractitemmodel-数据分离)文章中分析的一样,都是注册了一个自定义QQuickItem对象到qml上下文中,并使用QQuickView加载main.qml文件并显示,在此就不贴代码啦。

1、opengl scene graph

  由于这是第一次分析opengl的示例程序,这里我将会把spinner的头文件和实现代码都贴上。首先先来看下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 };

  头文件中声明了一个SPinner类,继承自QQuickItem类,重写该类是为了使用opengl绘制旋转效果。代码中使用了Q_PROPERTY宏将m_spinning成员变量注册到了qml系统中,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类实现中,updatePaintNode接口中构造了一个SpinnerNode类,具体的绘制操作就是在SpinnerNode类中实现。

 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);//创建一个纹理
15         QSGSimpleTextureNode *textureNode = new QSGSimpleTextureNode();//简单纹理节点  加入场景前必须有一个纹理
16         textureNode->setTexture(m_texture);//设置纹理
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轴旋转
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,构造函数中使用图片构造了一个纹理,并将纹理设置到QSGSimpleTextureNode对象中,进而将该对象添加到场景,添加到场景的顺序决定他们在场景中被绘制的顺序。

  SpinnerNode类中使用maybeRotate方法将纹理进行了旋转,代码如上32-41行所示。

2、quick animation

  使用qml属性动画进行旋转操作相对来说比较简单,只需要使用NumberAnimation on rotation即可,具体代码如下64-66行

 1 Rectangle {
 2
 3     width: 320
 4     height: 480
 5
 6     gradient: Gradient {//设置背景色
 7         GradientStop { position: 0; color: "lightsteelblue" }
 8         GradientStop { position: 1; color: "black" }
 9     }
10
11     Rectangle {//与blockingLabel组成一个提示框
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"//根据定时器状态 修改提示框文本信息和字体颜色
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//触发间隔
32         running: false;//默认没有启动
33         repeat: true
34         onTriggered: {//该槽的作用就是为了模拟一个延迟  说明opengl的渲染不在主线程进行  而quick animation在主线程进行渲染
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//定时器状态修改  4s中置反
51         }
52     }
53
54     Spinner {//opengl
55         anchors.centerIn: parent
56         anchors.horizontalCenterOffset: 80
57         spinning: true//通过setSpinning接口设置该属性值
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转动一圈
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 }

总结:使用qml有两种方式实现旋转,往大的说就是动画,有两种方式:opengl scene or quick animation。

opengl scene:实现比较麻烦,但是线程独立,不会因为复杂计算而阻塞ui绘制

quick animation:使用简单,但是是在主线程进行绘制。

qml demo分析(threadedanimation-线程动画)相关推荐

  1. qml demo分析(customgeometry-贝塞尔曲线)

    一.效果展示 本篇文章还是带来一个简单的qt示例分析,且看图1效果. 图1 贝塞尔曲线 二.源码分析 该示例代码所在目录quick\scenegraph\customgeometry,感兴趣的同学可以 ...

  2. qml demo分析(threading-线程任务)

    一.关键类说明 qml内置了WorkerScript组件,该组件有一个source属性,可以加载js文件,含有一个名为message的信号,意味着他有一个默认的onMessage槽函数,除此之外他还有 ...

  3. qml demo分析(clocks-时钟)

    一.效果展示 效果如图1所示,时钟列表支持鼠标左右拖动,带有黑色背景的是晚上时钟,无黑色背景的是白天时钟 二.源码分析 1.main.cpp文件中只包含了一个宏,该宏的具体解释请看qml 示例中的关键 ...

  4. 深入源码分析Java线程池的实现原理

    转载自   深入源码分析Java线程池的实现原理 程序的运行,其本质上,是对系统资源(CPU.内存.磁盘.网络等等)的使用.如何高效的使用这些资源是我们编程优化演进的一个方向.今天说的线程池就是一种对 ...

  5. 分析java 线程占用内存_Java线程:保留的内存分析

    分析java 线程占用内存 本文将为您提供一个教程,使您可以确定活动应用程序Java线程中保留了多少Java堆空间 . 将提供来自Oracle Weblogic 10.0生产环境的真实案例研究,以使您 ...

  6. 消息队列NetMQ 原理分析2-IO线程和完成端口

    目录 前言 介绍 目的 IO线程 初始化IO线程 Proactor 启动Procator线程轮询 处理socket IOObject 总结 前言 介绍 [NetMQ](https://github.c ...

  7. Hbase Compaction 源码分析 - CompactSplitThread 线程池选择

    目录 CompactSplitThread requestCompactionInternal方法 selectCompaction方法 requestCompaction方法 其他相关文章 Hbas ...

  8. 从源码分析创建线程池的4种方式

    摘要:从创建线程池的源码来深入分析究竟有哪些方式可以创建线程池. 本文分享自华为云社区<[高并发]从源码角度分析创建线程池究竟有哪些方式>,作者:冰 河 . 在Java的高并发领域,线程池 ...

  9. go-pitaya学习笔记(9)-rate_limiting demo分析

    学习笔记: 我家别墅靠大海/pitaya-learn 尝试集成功能:我家别墅靠大海/pitaya-game 如果你正在看此笔记,请你左边放笔记,右边放chatdemo的代码!! 我是按代码的顺序记的笔 ...

最新文章

  1. 赠票 | 2019,GMIS归来!杨强、吴恩达等全球重磅嘉宾共话数据智能(上海)
  2. HTTP学习笔记(1)
  3. module ‘brotli‘ has no attribute ‘error
  4. 进程通信学习笔记(System V消息队列)
  5. Dev控件使用 - 皮肤
  6. 使用jMeter对基于SAP ID service进行Authentication的Restful API进行并发测试
  7. 常用的echo和cat,这次让我折在了特殊字符丢失问题上
  8. (转)版本管理工具介绍——SVN篇(一)
  9. 第二章 Mablab语言基础
  10. Delphi版 熊猫烧香源码
  11. psd.js 解析PSD文件
  12. 单片机的c语言程序设计与应用课后答案,单片机的C语言程序设计与应用——基于Proteus仿真(第3版)...
  13. python 根据名称获取pid_python 使用标准库根据进程名获取进程的pid
  14. Tomcat IP访问限制
  15. 2023软件测试最难求职季,哪些测试技能更容易拿到offer?
  16. xiunobbs 4 mysql_轻论坛程序 - Xiuno BBS 4.0
  17. 经典物理建模钢琴-4Front TruePianos v1.9.8 WiN-MAC
  18. MindSpore:【AIR模型导出】导出时提示源码中select_op参数类型转换失败
  19. std:weak_ptr
  20. 用matplotlib.pyplot绘制甜甜圈图

热门文章

  1. 使用C#删除一个字符串数组中的空字符串
  2. UVA 11038 How Many O's?
  3. maven nexus 私服的搭建学习
  4. WebIDE,让开发更简单
  5. 「我去,这也能行!」令人惊叹的8个深度学习应用
  6. linux平滑升级nginx,Nginx的平滑重启和平滑升级,nginx
  7. 线性回归(y=ax+b)
  8. c语言不同类型指针间的强转,C语言中不同的结构体类型的指针间的强制转换详解...
  9. php获取当前整点时间_8.PHP的日期和时间
  10. MySQL高级 - 存储引擎 - 概述