《qt quick核心编程》笔记三
9 C++和QML混合编程
9.1 在QML中使用C++类和对象
qt提供两种方式在QML环境中使用C++对象:
- 在C++中实现一个类,注册为QML环境的一个类型,在QML环境中使用该类型创建对象
- 在C++中构造一个对象,将这个对象设置为QML的上下文属性,在QML环境中直接使用该属性
9.1.1 前提条件:定义可以导出的C++类
一个C++类必须满足以下条件才能够导出到QML环境中使用:
- 从
QObject
或QObject
的派生类继承 - 使用
Q_OBJECT
宏
QML中使用C++导出的类,只能使用以下特征的属性和方法:
- 信号和槽
- Q_INVOKABLE宏修饰的成员函数
- Q_ENUM宏修饰的枚举类型
- Q_PROPERTY宏修饰的成员属性(常用READ、WRITE、NOTIFY标记)
<colormaker.h>头文件
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include <QObject>
#include <QColor>
class ColorMaker : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QColor timeColor READ timeColor)
public:
explicit ColorMaker(QObject *parent = nullptr);
~ColorMaker();
enum GenerateAlgorithm{ RandomRGB, RandomRed, RandomGreen, RandomBlue, LinearIncrease};
Q_ENUM(GenerateAlgorithm)
Q_INVOKABLE ColorMaker::GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(ColorMaker::GenerateAlgorithm algorithm);
QColor getColor() const;
void setColor(const QColor &color);
QColor timeColor() const;
signals:
void colorChanged(const QColor &color);
void currentTime(const QString &strTime);
public slots:
void start();
void stop();
protected:
void timerEvent(QTimerEvent *e);
private:
GenerateAlgorithm m_algorithm;
QColor m_currentColor;
int m_nColorTimer;
};
#endif // COLORMAKER_H
<colormaker.c>源文件
#include "colormaker.h"
#include <QDateTime>
#include <QRandomGenerator>
#include <QTimerEvent>
ColorMaker::ColorMaker(QObject *parent)
: QObject(parent), m_algorithm(RandomRGB), m_currentColor(Qt::black),
m_nColorTimer(0) {}
ColorMaker::~ColorMaker() {}
QColor ColorMaker::getColor() const { return m_currentColor; }
void ColorMaker::setColor(const QColor &color) {
m_currentColor = color;
emit colorChanged(m_currentColor);
}
QColor ColorMaker::timeColor() const {
QTime time = QTime::currentTime();
int r = time.hour();
int g = time.minute() * 2;
int b = time.second() * 4;
return QColor::fromRgb(r, g, b);
}
ColorMaker::GenerateAlgorithm ColorMaker::algorithm() const {
return m_algorithm;
}
void ColorMaker::setAlgorithm(GenerateAlgorithm algorithm) {
m_algorithm = algorithm;
}
void ColorMaker::start() {
if (m_nColorTimer == 0) {
m_nColorTimer = startTimer(1000);
}
}
void ColorMaker::stop() {
if (m_nColorTimer > 0) {
killTimer(m_nColorTimer);
m_nColorTimer = 0;
}
}
void ColorMaker::timerEvent(QTimerEvent *e) {
if (e->timerId() == m_nColorTimer) {
switch (m_algorithm) {
case RandomRGB:
m_currentColor.setRgb(QRandomGenerator::global()->bounded(255),
QRandomGenerator::global()->bounded(255),
QRandomGenerator::global()->bounded(255));
break;
case RandomRed:
m_currentColor.setRed(QRandomGenerator::global()->bounded(255));
break;
case RandomGreen:
m_currentColor.setGreen(QRandomGenerator::global()->bounded(255));
break;
case RandomBlue:
m_currentColor.setBlue(QRandomGenerator::global()->bounded(255));
break;
case LinearIncrease: {
int r = m_currentColor.red() + 10;
int g = m_currentColor.green() + 10;
int b = m_currentColor.blue() + 10;
m_currentColor.setRgb(r % 255, g % 255, b % 255);
} break;
}
emit colorChanged(m_currentColor);
emit currentTime(
QDateTime::currentDateTime().toString("yyyy-MM-ddhh:mm:ss"));
} else {
QObject::timerEvent(e);
}
}
9.1.2 使用方法1:将一个C++类注册为QML类型使用
首先定义出一个供QML访问的C++类,接下来需要将这个C++类型注册为QML类型,然后才能在QML中使用
注册QML类型的函数:
qmlRegisterSingletonType()
注册表一个单例类型
qmlRegisterType()
注册一个非单例类型
qmlRegisterTypeNotAvailable()
注册一个类型用来占位
qmlRegisterUncreatableType()
注册一个具有附加属性的附加类型
举例qmlRegisterType如下:
//用来注册一个新类型
template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
//为特定的版本注册类型
template<typename T, int metaObjectRevision>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
uri
:指定一个唯一的包名,"import QtQuick.Controls 1.2"中的QtQuick.Controls
就是包名,1.2
则是版本
versionMajor
:包的主版本号,"import QtQuick.Controls 1.2"中的1.2
中的1
versionMinor
:包的子版本号,"import QtQuick.Controls 1.2"中的1.2
中的2
qmlName
:QML中可以使用的类名
<main.cpp>
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml>
#include "colorMaker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<ColorMaker>("an.qt.ColorMaker", 1, 0,"ColorMaker"); //注册ColorMaker类
QQuickView viewer;
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
viewer.setSource(QUrl("qrc:///main.qml"));
viewer.show();
return app.exec();
}
后续即可直接在qml中使用:
<main.qml>
import QtQuick 2.2
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import an.qt.ColorMaker 1.0
Window {
width: 360
height: 360
visible: true
Text {
id: timeLabel
anchors.left: parent.left
anchors.leftMargin: 4
anchors.top: parent.top
anchors.topMargin: 4
font.pixelSize: 26
}
ColorMaker {
id: colorMaker
color: Qt.green
}
Rectangle {
id: colorRect
anchors.centerIn: parent
width: 200
height: 200
color: "blue"
}
Button {
id: start
text: "start"
anchors.left: parent.left
anchors.leftMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
onClicked: {
colorMaker.start()
}
}
Button {
id: stop
text: "stop"
anchors.left: start.right
anchors.leftMargin: 4
anchors.bottom: start.bottom
onClicked: {
colorMaker.stop()
}
}
function changeAlgorithm(button, algorithm) {
switch (algorithm) {
case 0:
button.text = "RandomRGB"
break
case 1:
button.text = "RandomRed"
break
case 2:
button.text = "RandomGreen"
break
case 3:
button.text = "RandomBlue"
break
case 4:
button.text = "LinearIncrease"
break
}
}
Button {
id: colorAlgorithm
text: "RandomRGB"
anchors.left: stop.right
anchors.leftMargin: 4
anchors.bottom: start.bottom
onClicked: {
var algorithm = (colorMaker.algorithm() + 1) % 5
changeAlgorithm(colorAlgorithm, algorithm)
colorMaker.setAlgorithm(algorithm)
}
}
Button {
id: quit
text: "quit"
anchors.left: colorAlgorithm.right
anchors.leftMargin: 4
anchors.bottom: start.bottom
onClicked: {
Qt.quit()
}
}
Component.onCompleted: {
colorMaker.color = Qt.rgba(0, 180, 120, 255)
colorMaker.setAlgorithm(ColorMaker.LinearIncrease)
changeAlgorithm(colorAlgorithm, colorMaker.algorithm())
}
Connections {
target: colorMaker
function onCurrentTime(strTime) {
timeLabel.text = strTime
timeLabel.color = colorMaker.timeColor
}
}
Connections {
target: colorMaker
function onColorChanged(color) {
colorRect.color = color
}
}
}
9.1.3 使用方法2:将一个C++对象注册为QML属性使用
首先要先注册属性
<main.cpp>
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml>
#include "colorMaker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView viewer;
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
viewer.rootContext()->setContextProperty("colorMaker", new ColorMaker); //注册为属性
viewer.setSource(QUrl("qrc:///main.qml"));
viewer.show();
return app.exec();
}
然后修改qml中的内容,无需import类,直接使用注册的属性名即可
<main.qml>
import QtQuick 2.2
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Text {
id: timeLabel
anchors.left: parent.left
anchors.leftMargin: 4
anchors.top: parent.top
anchors.topMargin: 4
font.pixelSize: 26
}
Rectangle {
id: colorRect
anchors.centerIn: parent
width: 200
height: 200
color: "blue"
}
Button {
id: start
text: "start"
anchors.left: parent.left
anchors.leftMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
onClicked: {
colorMaker.start()
}
}
Button {
id: stop
text: "stop"
anchors.left: start.right
anchors.leftMargin: 4
anchors.bottom: start.bottom
onClicked: {
colorMaker.stop()
}
}
function changeAlgorithm(button, algorithm) {
switch (algorithm) {
case 0:
button.text = "RandomRGB"
break
case 1:
button.text = "RandomRed"
break
case 2:
button.text = "RandomGreen"
break
case 3:
button.text = "RandomBlue"
break
case 4:
button.text = "LinearIncrease"
break
}
}
Button {
id: colorAlgorithm
text: "RandomRGB"
anchors.left: stop.right
anchors.leftMargin: 4
anchors.bottom: start.bottom
onClicked: {
var algorithm = (colorMaker.algorithm() + 1) % 5
changeAlgorithm(colorAlgorithm, algorithm)
colorMaker.setAlgorithm(algorithm)
}
}
Button {
id: quit
text: "quit"
anchors.left: colorAlgorithm.right
anchors.leftMargin: 4
anchors.bottom: start.bottom
onClicked: {
Qt.quit()
}
}
Component.onCompleted: {
colorMaker.color = Qt.rgba(0, 180, 120, 255)
colorMaker.setAlgorithm(colorMaker.LinearIncrease)
changeAlgorithm(colorAlgorithm, colorMaker.algorithm())
}
Connections {
target: colorMaker
function onCurrentTime(strTime) {
timeLabel.text = strTime
timeLabel.color = colorMaker.timeColor
}
}
Connections {
target: colorMaker
function onColorChanged(color) {
colorRect.color = color
}
}
}
9.2 在C++中使用QML对象
9.2.1 查找一个对象的孩子
QObject类的构造函数有一个parent参数,指定一个对象的父亲,QML中的对象借助这个组成了以根Item为根的对象树
QObject类中有一个objectName
属性,代表这个对象的名字
通过对象名字可以用于findChild()
和findChildren()
查找孩子
T QObject::findChild(const QString & name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<T> QObject::findChildren(const QString & name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<T> QObject::findChildren(const QRegExp & regExp, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<T> QObject::findChildren(const QRegularExpression & re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
示例:查找parentWidget的名为"button1",类型为QPushButton的孩子
QPushButton *button = parentWidget->findChild<QPushButton*>("button1");
示例:查找parentWidget的所有名为"widgetname"的QWidget类型的孩子列表
QList<QWidget *> widgets = parentWidget->findChildren<QWidget *>("widgetname");
9.2.2 使用元对象调用QML对象的方法
QMetaObject的invokeMethod()
静态方法用来调用一个对象的信号、槽、可调用方法.
bool QMetaObject::invokeMethod(
QObject * obj,
const char * member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument( 0 ),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()
)
//QGenericReturnArgument 原型为:
QGenericReturnArgument Q_RETURN_ARG( Type, Type & value)
//QGenericArgument 原型为:
QGenericArgument Q_ARG( Type, const Type & value)
返回值
:true调用成功,false调用失败
obj
:被调用对象的指针
member
:方法名字
type
:连接类型
- Qt::DirectConnection
- Qt::AutoConnection
- Qt::QueuedConnection
ret
:接收调用方法的返回值
val0-val10
:用于传递给调用方法的参数
调用方法:
假设一个对象有一个槽compute(QString, int, double),返回一个QString对象
QString retVal;
QMetaObject::invokeMethod(
obj,
"compute",
Qt::DirectConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QString, "sqrt"),
Q_ARG(int, 42),
Q_ARG(double, 9.7)
);
示例代码如下:
<changecolor.h>
#ifndef CHANGECOLOR_H
#define CHANGECOLOR_H
#include <QObject>
#include <QTimer>
class ChangeQmlColor : public QObject {
Q_OBJECT
public:
ChangeQmlColor(QObject *target, QObject *parent = 0);
~ChangeQmlColor();
protected slots:
void onTimeout();
private:
QTimer m_timer;
QObject *m_target;
};
#endif // CHANGECOLOR_H
<changecolor.cpp>
#include "changeColor.h"
#include <QColor>
#include <QDateTime>
#include <QVariant>
ChangeQmlColor::ChangeQmlColor(QObject *target, QObject *parent)
: QObject(parent), m_timer(this), m_target(target) {
qsrand(QDateTime::currentDateTime().toTime_t());
connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
m_timer.start(1000);
}
ChangeQmlColor::~ChangeQmlColor() {}
void ChangeQmlColor::onTimeout() {
QColor color = QColor::fromRgb(qrand() % 256, qrand() % 256, qrand() % 256);
m_target->setProperty("color", color);
}
<main.cpp>
#include "changecolor.h"
#include <QApplication>
#include <QColor>
#include <QQmlApplicationEngine>
#include <QtQml>
int main(int argc, char *argv[]) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
QObject *root = NULL;
QList<QObject *> rootObjects = engine.rootObjects();
int count = rootObjects.size();
for (int i = 0; i < count; i++) {
if (rootObjects.at(i)->objectName() == "rootObject") {
root = rootObjects.at(i);
break;
}
}
new ChangeQmlColor(root);
QObject *quitButton = root->findChild<QObject *>("quitButton");
if (quitButton) {
QObject::connect(quitButton, SIGNAL(clicked()), &app, SLOT(quit()));
}
QObject *textLabel = root->findChild<QObject *>("textLabel");
if (textLabel) {
// this will failed
bool bRet = QMetaObject::invokeMethod(textLabel, "setText",Q_ARG(QString, "world hello"));
qDebug() << "call setText return - " << bRet;
textLabel->setProperty("color", QColor::fromRgb(255, 0, 0));
bRet = QMetaObject::invokeMethod(textLabel, "doLayout");
qDebug() << "call doLayout return - " << bRet;
}
return app.exec();
}
<main.qml>
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
objectName: "rootObject"
width: 360
height: 360
visible: true
Text {
objectName: "textLabel"
text: "Hello World"
anchors.centerIn: parent
font.pixelSize: 26
}
Button {
anchors.right: parent.right
anchors.rightMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
text: "quit"
objectName: "quitButton"
}
}
10 动画
基本动画对象
PropertyAnimation
:可以改变各种property产生动画
NumberAnimation
:PropertyAnimation的派生类,专门改变数字类型的property产生动画
ColorAnimation
:PropertyAnimation的派生类,专门改变color类型的property产生动画
RotationAnimation
:PropertyAnimation的派生类,专门改变rotation的值产生动画
Vector3dAnimation
:PropertyAnimation的派生类,再一个Vector3d发生变化时使用
PathAnimation
:让对象沿着一个给定的路径运动
SmoothAnimation
:允许一个property跟踪一个值,产生平滑动画
SpringAnimation
:允许一个property跟踪一个值,动画效果类似于弹簧运动
组合动画对象:
SequentialAnimation
:顺序执行一系列动画
ParallelAnimation
:并行执行一系列动画
动画搭档:
Behavior
:为Item的property变化绑定一个默认的动画对象
ParentAnimation
:在改变一个Item的parent时使用,使得该Item从旧parent移动到新parent的过程更平滑,通常和Transition、State、ParentChange联合使用
AnchorAnimation
:在改变一个Item的anchor时使用,平滑变化过程,通常和Transition、State、ParentChange联合使用
PauseAnimation
:在动画过程中插入它,可以将动画过程暂停一段时间
PropertyAction
:在动画执行过程中立即改变某个属性
ScriptAction
:在动画执行过程中运行一段ECMAScript脚本
10.1 基本动画元素
Animation
是Qt Quick中所有动画类的基类
有以下属性
running
:布尔值,指示动画是否在运行,默认false,start()方法置为true,stop()方法置为false
loops
:动画的执行次数,默认值1,赋值Animation.Infinite导致动画永远的循环执行下去
paused
:布尔值,默认false,指定动画当前是否处于暂停状态,pause()方法置为true
alwaysRunToEnd
:布尔值,默认false,是否即使stop()或设置running为false,动画也要执行完
有以下方法:
start()
:启动一个动画,如果动画已经执行,则什么也不干
stop()
:终止一个动画,如果动画没执行完,那么动画操作的属性可能就是某个中间值
restart()
:等同于先调用stop(),在调用start()
pause()
:暂停一个动画,如果动画已经暂停,则什么也不干
resume()
:和pause()对应,让一个动画继续执行
complete()
:完成一个动画,如果动画执行到某个中间步骤,将使动画直接跳到结束状态
有以下信号:
started()
:动画开始时触发(只有顶层的动画才会触发信号,动画位于某个动画分组或Behavior、Transition中,则不会触发)
stopped()
:动画(手动或自动)执行完毕进入停止状态时触发,只有单独的顶层动画才会触发
启动动画的方法:
- 调用start()方法
- 设置running为true
- 为running绑定表达式,表达式求值结果为true
停止动画的方法:
- 调用stop()方法
- 设置running为false
- 触发running绑定表达式重新求值,且返回值为false
- 调用complete()方法
10.1.2 PropertyAnimation(属性变化动画基类)
单独使用:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
width: 360
height: 240
color: "#EEEEEE"
id: rootItem
Rectangle {
id: rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
PropertyAnimation {
id: animation
target: rect
property: "width"
to: 150
duration: 1000
}
MouseArea {
anchors.fill: parent
onClicked: animation.running = true
}
}
}
}
//同时改变两个属性
PropertyAnimation {
id: animation;
target: rect;
properties: "width,height";
to: 220;
duration: 1000;
}
//同时改变两个对象
PropertyAnimation {
id: animation;
targets: [rectA , rectB];
properties: "width";
to: 150;
duration: 1000;
}
target
:要操作的对象
property
:要改变目标的哪个属性
from
:要改变目标的初始值(默认,目标的当前值)
to
:指定目标的目标值
duration
:动画时间
easing
:动画的松弛曲线
在信号处理器中使用
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: PropertyAnimation {
target: rect
properties: "width"
to: 150
duration: 1000
}
}
}
}
使用Animation on <property>
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
MouseArea {
anchors.fill: parent
id: mouseArea
}
PropertyAnimation on width {
to: 150
duration: 1000
running: mouseArea.pressed
}
}
}
如果不设置running属性,则动画对象将立即执行
使用start()和stop()信号
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
property var animation
PropertyAnimation {
id: toSquare
target: rect
property: "width"
to: 150
duration: 1000
onStarted: {
rect.animation = toSquare
rect.color = "red"
}
onStopped: {
rect.color = "blue"
}
}
PropertyAnimation {
id: toRect
target: rect
property: "width"
to: 50
duration: 1000
onStarted: {
rect.animation = toRect
rect.color = "red"
}
onStopped: {
rect.color = "blue"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.animation === toRect || rect.animation === undefined) {
toSquare.start()
} else {
toRect.start()
}
}
}
}
}
10.1.3 NumberAnimation
NumberAnimation是PropertyAnimation的派生类,专门用于处理数字类型的property
NumberAnimation重写了from和to两个属性类型为real,因此动画将会更加精细顺滑
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
color: "blue"
width: 50
height: 50
x: 0
y: 95
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
animationX.start()
animationRotation.running = true
animationRadius.start()
}
}
NumberAnimation {
id: animationX
target: rect
property: "x"
to: 310
duration: 3000
easing.type: Easing.OutCubic
}
NumberAnimation {
id: animationRotation
target: rect
property: "rotation"
to: 1080
duration: 3000
running: false
easing.type: Easing.OutInQuad
}
NumberAnimation on radius {
id: animationRadius
to: 25
duration: 3000
running: false
}
}
}
10.1.4 ColorAnimation
ColorAnimation是PropertyAnimation的派生类,专门用于处理Color类型的property
ColorAnimation重写了from和to两个属性类型为color
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
color: "red"
width: 60
height: 60
radius: 30
anchors.centerIn: parent
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: ColorAnimation {
target: rect
property: "color"
to: "green"
duration: 3000
}
}
}
}
10.1.5 RotationAnimation
RotationAnimation是PropertyAnimation的派生类,专门处理rotation和angle属性
RotationAnimation重写了from和to两个属性类型为real
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
color: "red"
width: 120
height: 60
anchors.left: parent.left
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onClicked: RotationAnimation {
target: rect
to: 90
duration: 1500
direction: RotationAnimation.Counterclockwise
}
}
}
Rectangle {
id: blueRect
color: "blue"
width: 120
height: 60
anchors.right: parent.right
anchors.rightMargin: 40
anchors.verticalCenter: parent.verticalCenter
transformOrigin: Item.TopRight
MouseArea {
anchors.fill: parent
onClicked: anim.start()
}
RotationAnimation {
id: anim
target: blueRect
to: 60
duration: 1500
}
}
}
RotationAnimation新增一个direction
属性,指定旋转方向
RotationAnimation.Numerical
:默认值,在from和to之间进行线性插值旋转RotationAnimation.Clockwise
:在两个角度之间顺时间旋转RotationAnimation.Counterclockwise
:在两个角度之间逆时针旋转RotationAnimation.Shortest
:选取两个角度之间的最短路径旋转
RotationAnimation旋转一个Item时以Item的transformOrigin属性指定的点为中心旋转
使用RotationAnimation时不需要指定property属性,因为是固定的
10.1.6 PathAnimation
PathAnimation是Animation继承而来,它让目标对象沿着一个既定的路径运动
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
ctx.lineWidth = 4
ctx.strokeStyle = "red"
ctx.beginPath()
ctx.arc(200, 0, 160, Math.PI * 2, 0, false)
ctx.stroke()
}
Rectangle {
id: rect
width: 40
height: 40
color: "blue"
x: 20
y: 0
MouseArea {
anchors.fill: parent
id: mouseArea
onClicked: pathAnim.start()
}
PathAnimation {
id: pathAnim
target: rect
duration: 6000
anchorPoint: "20,20"
orientationEntryDuration: 200
orientationExitDuration: 200
easing.type: Easing.InOutCubic
orientation: PathAnimation.TopFirst
path: Path {
startX: 40
startY: 0
PathArc {
x: 360
y: 0
useLargeArc: true
radiusX: 160
radiusY: 160
direction: PathArc.Counterclockwise
}
}
}
}
}
}
path
:构造的路径
easing
:指定动画的松弛曲线
anchorPoint
:指定目标对象的那个点锚定在路径上
orienttation
:控制目标沿着路径移动的旋转策略
PathAnimation.Fixed
,orientation的默认值,在运动过程中保持物体方位不旋转。PathAnimation.RightFirst
,旋转目标对象时努力使目标对象的右侧贴合路径。PathAnimation.LeftFirst
,旋转目标对象时努力使目标对象的左侧贴合路径。PathAnimation.BottomFirst
,旋转目标对象时努力使目标对象的底部贴合路径。PathAnimation.TopFirst
,旋转目标对象时努力使目标对象的顶部贴合路径。
10.1.7 SmoothedAnimation
SmoothedAnimation是NumberAnimation的派生类
默认将easing.type设置为Easing.InOutQuad,在from和to之间产生平滑的动画效果
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
width: 80
height: 60
x: 20
y: 20
color: "red"
}
SmoothedAnimation {
id: smoothX
target: rect
property: "x"
duration: 1000
velocity: -1
}
SmoothedAnimation {
id: smoothY
target: rect
property: "y"
velocity: 100
}
MouseArea {
anchors.fill: parent
onClicked: {
smoothX.from = rect.x
smoothX.to = mouse.x + 4
smoothX.start()
smoothY.from = rect.y
smoothY.to = mouse.y + 4
smoothY.start()
}
}
}
easing.type
:默认设置为Easing.InOutQuad
duration
:动画周期,单位毫秒,默认为-1
velocity
:速率,默认速率200units/秒,设置为-1时禁用速率,如果from和to之间很小,则自行调整
当
duration
和velocity
同时设置时,自动根据from、to之间举例和速率计算动画完成所需时间,用这个时间和duration
比较,如果duration
段,则用duration,否则使用velocity
10.1.8 SpringAnimation
SpringAnimation用于模仿弹簧的震荡效果
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
width: 40
height: 40
x: 20
y: 20
color: "red"
}
SpringAnimation {
id: springX
target: rect
property: "x"
spring: 3
damping: 0.06
epsilon: 0.25
}
SpringAnimation {
id: springY
target: rect
property: "y"
spring: 3
damping: 0.06
epsilon: 0.25
}
MouseArea {
anchors.fill: parent
onClicked: {
springX.from = rect.x
springX.to = mouse.x - 20
springX.start()
springY.from = rect.y
springY.to = mouse.y - 20
springY.start()
}
}
}
spring
:控制动画的加速度,默认0,0-5.0之间取值
damping
:衰减系数,值越大震荡越快平复,默认0,0-1.0之间取值
epsilon
:设定一个最接近0的阈值来代替0,默认0.01,调整可带来性能提升
velocity
:设定动画的最大速率,默认值为0,没有限制
10.2 组合动画
组合动画包括:ParallelAnimation和SequentialAnimation
10.2.1 ParallelAnimation
ParallelAnimation允许将多个动画元素组合在一起执行
ParallelAnimation中定义的多个动画将并行执行
ParallelAnimation从Animation继承而来,没有额外的属性,本身单独使用没有意义
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
color: "blue"
width: 50
height: 50
x: 0
y: 95
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (anim.paused) {
anim.resume()
} else if (anim.running) {
anim.pause()
} else {
anim.start()
}
}
}
ParallelAnimation {
id: anim
loops: Animation.Infinite
NumberAnimation {
target: rect
property: "x"
to: 310
duration: 3000
}
NumberAnimation {
target: rect
property: "rotation"
to: 360
duration: 1000
loops: 3
}
NumberAnimation {
target: rect
property: "radius"
to: 25
duration: 3000
}
}
}
}
10.2.2 SequentialAnimation
SequentialAnimation中定义的动画将顺序执行
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
Rectangle {
id: rect
color: "blue"
width: 50
height: 50
x: 0
y: 95
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (anim.paused) {
anim.resume()
} else if (anim.running) {
anim.pause()
} else {
rect.radius = 0
rect.x = 0
rect.rotation = 0
anim.start()
}
}
}
SequentialAnimation {
id: anim
NumberAnimation {
target: rect
property: "x"
to: 310
duration: 3000
}
NumberAnimation {
target: rect
property: "rotation"
to: 360
duration: 1000
loops: 3
}
NumberAnimation {
target: rect
property: "radius"
to: 25
duration: 3000
}
}
}
}
10.3 State
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
color: "#EEEEEE"
Text {
id: centerText
text: "A Single Text."
anchors.centerIn: parent
font.pixelSize: 24
MouseArea {
id: mouseArea
anchors.fill: parent
onReleased: {
centerText.state = "redText"
}
}
states: [
State {
name: "redText"
changes: [
PropertyChanges {
target: centerText
color: "red"
}
]
},
State {
name: "blueText"
when: mouseArea.pressed
PropertyChanges {
target: centerText
color: "blue"
font.bold: true
font.pixelSize: 32
}
}
]
state: "redText"
}
}
Item有一个state属性,字符串,默认是空串
Item还有一个states属性,list<State>,保存为这个Item定义的所有的状态
应用一种State的两种方式:
- 显式改变Item的state属性
- 将State的when属性绑定到一个表达式上
State类型:
name
:字符串,保存状态的名字
when
:布尔值,描述状态在什么时候应用,一般绑定在ECMAScript表达式上,true时应用
extened
:字符串,指向当前状态的"基类"的名字
changes
:list<Change>,保存应用于这种状态的所有变化,当进入一种State后,这个列表中的Change对象会依次顺序执行
Change对象的类型有以下几种:
PropertyChanges
,用来改变一个对象的属性,对应的C++类为QQuickPropertyChanges,是- QQuickStateOperation的派生类。ParentChange
,用来改变一个对象的父,对应的C++类为QQuickParentChange,是QQuickStateOperation的派生类。AnchorChanges
,用来改变一个对象的锚布局参数,对应的C++类为QQuickAnchorChanges,是QQuickStateOperation的派生类。StateChangeScript
,用来执行一个ECMAScript脚本,对应的C++ 类 为 QQuickStateChangeScript , 是QQuickStateOperation的派生类。
10.3.1 PropertyChanges
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 360
height: 360
visible: true
color: "#EEEEEE"
Rectangle {
id: rect
color: "blue"
width: 200
height: 200
anchors.centerIn: parent
MouseArea {
id: mouseArea
anchors.fill: parent
}
states: [
State {
name: "resetwidth"
when: mouseArea.pressed
PropertyChanges {
target: rect
restoreEntryValues: false
color: "red"
width: parent.width
}
}
]
}
}
target
:指向要改变的的目标对象
restoreEntryValues
:布尔值,离开本状态时,是否将本状态改变的那些属性的值重置为进入本状态之前的值,默认true
explicit
:如果设定目标属性时,使用表达式,是否将表达式视为一次性的赋值行为,还是每次都计算表达式。默认false,每次都计算
10.3.2 ParentChange
ParentChange用于改变一个对象的parent
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Rectangle {
width: 360
height: 360
visible: true
color: "#EEEEEE"
id: "rootItem"
Rectangle {
id: blueRect
width: 200
height: 200
color: "blue"
x: 8
y: 8
}
Rectangle {
id: redRect
color: "red"
width: 100
height: 100
x: blueRect.x + blueRect.width + 8
y: blueRect.y
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (redRect.state == "" || redRect.state == "default") {
redRect.state = "reparent"
} else {
redRect.state = "default"
}
}
}
states: [
State {
name: "reparent"
ParentChange {
target: redRect
parent: blueRect
width: 50
height: 50
x: 30
y: 30
rotation: 45
}
},
State {
name: "default"
ParentChange {
target: redRect
parent: rootItem
width: 100
height: 100
x: blueRect.x + blueRect.width + 8
y: blueRect.y
}
}
]
}
}
target
,指定要操作的目标对象
parent
,指定目标对象的新parent
x
,指定目标对象相对于新parent的x 位置
y
,指定目标对象相对于新parent的y 位置
width
,指定目标对象的宽度
height
,指定目标对象的高度
rotation
,指定目标对象的旋转角度
scale
,指定目标对象的放大系数
10.3.3 AnchorChanges
AnchorChanges用于改变一个Item的锚布局属性
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Rectangle {
id: rootItem
width: 360
height: 360
visible: true
color: "#EEEEEE"
Rectangle {
id: blueRect
width: 200
height: 180
color: "blue"
x: 8
y: 8
}
Rectangle {
id: redRect
color: "red"
width: 100
height: 100
anchors.left: blueRect.right
anchors.leftMargin: 10
anchors.top: blueRect.top
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (redRect.state == "" || redRect.state == "default") {
redRect.state = "reanchor"
} else {
redRect.state = "default"
}
}
}
states: [
State {
name: "reanchor"
changes: [
AnchorChanges {
target: redRect
anchors.top: blueRect.bottom
anchors.left: rootItem.left
},
PropertyChanges {
target: redRect
height: 40
anchors.topMargin: 4
}
]
},
State {
name: "default"
AnchorChanges {
target: redRect
anchors.left: blueRect.right
anchors.top: blueRect.top
}
}
]
}
}
target
,指向目标对象。
anchors.left
anchors.right
anchors.top
anchors.bottom
anchors.horizontalCenter
anchors.verticalCenter
anchors.baseline
10.3.4 StateChangeScript
StateChangeScript用于在状态改变时执行ECMAScript脚本
<colorMaker.js>
function changeColor(obj){
obj.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1.0);
}
<main.qml>
import QtQuick 2.15
import QtQuick.Window 2.15
import "colorMaker.js" as ColorMaker
Window {
width: 360
height: 240
color: "#EEEEEE"
visible: true
id: rootItem
Rectangle {
id: colorRect
color: "red"
width: 150
height: 130
anchors.centerIn: parent
MouseArea {
id: mouseArea
anchors.fill: parent
}
states: [
State {
name: "default"
when: mouseArea.pressed
StateChangeScript {
name: "changeColor"
script: ColorMaker.changeColor(colorRect)
}
}
]
}
}
name
:脚本名称,可以被ScriptAction对象引用,以便服复用代码
script
:实际的脚本代码
10.4 Transition
Item的Transition属性是一个列表,保存为这个Item定义的所有Transition
当一个Item从一个State切换到另一个State时,Transition定义的动画会自动在两个State之间运行,从而使得State状态切换的更加平滑
//举例如下:
Item {
...
transitions: [
Transition {
from: "stateA";
to: "stateB";
NumberAnimation {
properties: "x,y";
duration: 2000;
}
},
Transition {
from: "stateB";
to: "stateA";
NumberAnimation {
properties: "x,y";
duration: 2000;
}
}
]
...
}
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 360
height: 240
color: "#EEEEEE"
visible: true
id: rootItem
Rectangle {
id: rect
color: "gray"
width: 50
height: 50
anchors.centerIn: parent
MouseArea {
id: mouseArea
anchors.fill: parent
}
states: State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges {
target: rect
color: "green"
scale: 2.0
}
}
//begin, 增加过渡动画
transitions: Transition {
NumberAnimation {
property: "scale"
easing.type: Easing.InOutQuad
duration: 1000
}
ColorAnimation {
duration: 600
}
}
//end, 增加过渡动画
}
}
Transition的属性及成员函数如下:
from
:用来指定触发过度状态名称,默认值为"“,匹配所有状态
to
:用来指定过滤的目标状态名称,默认值为”",匹配所有状态
running
:这个Transition是否正在运行
animations
:列表,保存为一个Transition定义的所有Animations
reversible
:指定触发Transition的条件反转时Transition是否自动反转,默认false
10.4.1 Transition综合示例
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 360
height: 240
color: "#EEEEEE"
visible: true
id: rootItem
Text {
id: linkText
text: "I\'m web link."
anchors.centerIn: parent
font.pixelSize: 24
property var hadClicked: false
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
linkText.state = linkText.hadClicked == true ? "clickedHover" : "hover"
}
onExited: {
linkText.state = linkText.hadClicked == true ? "clicked" : "initial"
}
onClicked: {
if (linkText.hadClicked == false) {
linkText.hadClicked = true
}
linkText.state = "clicked"
}
}
states: [
State {
name: "initial"
changes: [
PropertyChanges {
target: linkText
color: "blue"
}
]
},
State {
name: "hover"
PropertyChanges {
target: linkText
color: "#87CEFA"
font {
italic: true
pixelSize: 36
underline: true
}
}
},
State {
name: "clicked"
PropertyChanges {
target: linkText
color: "#8B4513"
font {
pixelSize: 24
}
}
},
State {
name: "clickedHover"
PropertyChanges {
target: linkText
color: "#D2691E"
font {
italic: true
pixelSize: 36
underline: true
}
}
}
]
state: "initial"
transitions: [
Transition {
from: "initial"
to: "hover"
reversible: true
NumberAnimation {
property: "font.pixelSize"
duration: 800
}
ColorAnimation {
duration: 800
}
},
Transition {
from: "hover"
to: "clicked"
NumberAnimation {
property: "font.pixelSize"
duration: 800
}
ColorAnimation {
duration: 800
}
},
Transition {
from: "clicked"
to: "clickedHover"
reversible: true
SequentialAnimation {
NumberAnimation {
property: "font.pixelSize"
duration: 800
}
ColorAnimation {
duration: 800
}
}
}
]
}
}
10.5 协同动画元素
Behavior对象用于给Item的某个属性绑定默认动画
ParentAnimation 、 AnchorAnimation通 常 需要和 Transition 、State联合使用
PauseAnimation可以插入在多个动画之间产生暂停效果
PropertyAction可以插入在多个动画之间来立即改变某个属性
ScriptAction用于在动画执行过程中运行一段ECMAScript脚本
10.5.1 Behavior
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 360
height: 240
color: "#EEEEEE"
visible: true
id: rootItem
Rectangle {
id: rect
width: 160
height: 100
color: "red"
anchors.centerIn: parent
Behavior on width {
NumberAnimation {
duration: 1000
}
}
Behavior on height {
NumberAnimation {
duration: 1000
easing.type: Easing.InCubic
}
}
MouseArea {
anchors.fill: parent
onClicked: {
rect.width = Math.random() * rootItem.width
rect.height = Math.min(Math.random() * rootItem.height, rect.height * 1.5)
}
}
}
}
10.5.2 ParentAnimation
ParentAnimation是Animation的派生类
ParentAnimation在改变一个Item的parent时使用,使得旧parent移动到新parent的过程更平滑
import QtQuick 2.2
Rectangle {
width: 360
height: 240
color: "#EEEEEE"
id: rootItem
Rectangle {
id: blueRect
width: 200
height: 200
color: "blue"
x: 8
y: 8
}
Rectangle {
id: redRect
color: "red"
state: "default"
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (redRect.state == "" || redRect.state == "default") {
redRect.state = "reparent"
} else {
redRect.state = "default"
}
}
}
states: [
State {
name: "reparent"
ParentChange {
target: redRect
parent: blueRect
width: 50
height: 50
x: 30
y: 30
rotation: 45
}
},
State {
name: "default"
ParentChange {
target: redRect
parent: rootItem
width: 100
height: 100
x: blueRect.x + blueRect.width + 8
y: blueRect.y
}
}
]
transitions: Transition {
ParentAnimation {
NumberAnimation {
properties: "x,y"
duration: 1000
}
}
}
}
}
newParent
:指定目标对象的新parent
target
:指定目标对象
via
:动画过程中参考的其他对象
10.5.3 AnchorAnimation
AnchorAnimation只能和Transition、AnchorChanges联合使用,不能在Behavior或其他动画元素中用
import QtQuick 2.2
Rectangle {
width: 360
height: 240
color: "#EEEEEE"
id: rootItem
Rectangle {
id: blueRect
width: 200
height: 180
color: "blue"
x: 8
y: 8
}
Rectangle {
id: redRect
color: "red"
width: 100
height: 100
anchors.leftMargin: 10
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (redRect.state == "" || redRect.state == "default") {
redRect.state = "reanchor"
} else {
redRect.state = "default"
}
}
}
states: [
State {
name: "reanchor"
changes: [
AnchorChanges {
target: redRect
anchors.top: blueRect.bottom
anchors.left: rootItem.left
},
PropertyChanges {
target: redRect
height: 40
anchors.topMargin: 4
}
]
},
State {
name: "default"
AnchorChanges {
target: redRect
anchors.left: blueRect.right
anchors.top: blueRect.top
}
}
]
state: "default"
transitions: Transition {
AnchorAnimation {
duration: 1000
easing.type: Easing.OutInCubic
}
}
}
}
使用AnchorAnimation可以设定duration、easing、targets属性,和Transition结合使用时,无需target属性