# Quick 学习笔记 ## qml 语言基础 ### 对象 例如下面的代码就是一个qml内置的Button对象。内置对象还有很多:Rectangle、Lable等等。 ```qml Button { text: "button1" style: btnStyle } ``` ### 表达式 例如下面的代码,边框颜色,可以根据`control.hovered`不同而不同。 ```qml Component { id: btnStyle ButtonStyle { background: Rectangle { border.color: (control.hovered || control.pressed) ? "red" : "green" // 表达式 } } } ``` ### 属性 属性类似于C++的成员变量。 #### 属性类型 可以在QML中使用的属性有大致三种: - 有QML语言本身提供的类型; - 有QML模块(比如Qt Quick)提供的类型; - 导出到QML环境中的C++类型。 先看QML提供的基本类型 ##### 基本类型 QML支持 int、real、double、bool、string、color、list、font等。例如下面的代码,有`string`和`bool`类型的。并且QML提供这些类型检测,如果不正确,会自动报错。QML还导入了QT的很多类型:Qtobject、Component、Connections、Binding等。 ```qml Button { text: "button1" // string style: btnStyle visible:true // bool } ``` ##### id属性 一个对象只有一个id,且首字母必须小写,不能包含字母数字下划线以外的字符。 ```qml Component { id: btnStyle // id 属性 } ``` ##### 列表属性 列表属性是list类型的,length提供列表的数量,可以用下标访问列表项。 ```qml Item{ children:[ Text{text:"on"}, Text{text:"off"}, Text{text:"open"} ] Component.onCompleted:{ for(var i=0;i #include #include ColorMaker::ColorMaker(QObject *parent) : QObject(parent) { } ColorMaker::~ColorMaker() { } QColor ColorMaker::getColor() const { qDebug()<<"ColorMaker::getColor() is Called "; return this->m_currentColor; } void ColorMaker::setColor(const QColor &color) { qDebug()<<"ColorMaker::setColor emit colorChanged "; this->m_currentColor = color; emit colorChanged(m_currentColor); } ColorMaker::COLOR_MAKE_PACKET_TYPE ColorMaker::getPacketType() const { qDebug()<<"ColorMaker::getPacketType is callded"; return this->m_packet_type; } void ColorMaker::setPacketType(COLOR_MAKE_PACKET_TYPE packetType) { this->m_packet_type = packetType; qDebug()<<"ColorMaker::setPacketType is callded:"< #include class ColorMaker : public QObject { Q_OBJECT Q_ENUMS(COLOR_MAKE_PACKET_TYPE) Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY colorChanged) // 定义个color的属性,且添加得到和设置该属性的函数接口 属性信号 public: explicit ColorMaker(QObject *parent = nullptr); ~ColorMaker(); enum COLOR_MAKE_PACKET_TYPE{ COLOR_MAKE_PACKET_1, COLOR_MAKE_PACKET_2, COLOR_MAKE_PACKET_3, COLOR_MAKE_PACKET_4, COLOR_MAKE_PACKET_5 }; QColor getColor() const; void setColor(const QColor & color); Q_INVOKABLE COLOR_MAKE_PACKET_TYPE getPacketType() const; Q_INVOKABLE void setPacketType(COLOR_MAKE_PACKET_TYPE packetType); signals: void colorChanged(const QColor & color); public slots: void changeName(QString newName); private: COLOR_MAKE_PACKET_TYPE m_packet_type; QColor m_currentColor; }; #endif // COLORMAKER_H ``` `main.qml`文件源码 ```qml import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 1.4 import com.xym.colorMaker 1.0 Window { width: 800 height: 600 visible: true ColorMaker { id: colorMaker; color: Qt.green; } Button { id: changeName; text: "changeName"; anchors.left: parent.left; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; onClicked: { colorMaker.changeName("new name"); // C++类槽函数:类似qml按钮信号绑定到C++类的信号槽上一样 } } Component.onCompleted: { colorMaker.color = Qt.rgba(0,180,120, 255); // C++类属性,会自动调用属性color的ColorMaker::setColor(const QColor &color)写函数完成赋值 colorMaker.setPacketType(ColorMaker.COLOR_MAKE_PACKET_1); // C++类枚举和成员函数 } Connections { target: colorMaker; onColorChanged:{ // 使用C++类信号,在ColorMaker::setColor(const QColor &color)里面会发射信号ColorMaker::colorChanged,这里会被自动触发 console.log("C++ signals colorChanged emit:",colorMaker.color); // 这里会自动调用属性color的ColorMaker::gettColor()读函数 } } } ``` `main.cpp`文件源码 ```c++ #include #include #include "colormaker.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); /* * 注册类 ColorMaker 使其在qml中使用,第一个参数是在qml导入的名字,第二个参数和第三个参数分别是主次版本号,第四个参数是在qml使用中定义对象的类名 * 在qml中使用前就可以这样 * import ColorMaker 1.0 * * ColorMaker { * id: colorMaker; * color: Qt.green; * } */ qmlRegisterType("com.xym.colorMaker", 1,0, "ColorMaker"); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); } ``` 运行情况如下: ![](media/image-20200824114410747.png) ### C++类对象调用QML 在C++中也可以访问QML中的属性、函数和信号。在C++中加载QML文件可以用QQmlComponent或QQuickView,然后就可以在C++中访问QML对象。QQuickView提供了一个显示用户界面的窗口,而QQmlComponent没有。 #### 案例 main.cpp源码 ```c++ #include #include #include //#include "colormaker.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; /* * C++ 使用QML属性 */ QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml"))); QObject* object = component.create(); qDebug() << "width value is" << object->property("width").toInt(); object->setProperty("width", 500);//设置window的宽 qDebug() << "width value is" << object->property("width").toInt(); qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt(); QQmlProperty::write(object, "height", 300);//设置window的高 qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt(); /* * C++使用QML中信号与函数 */ //调用QML中函数 QVariant returnedValue; QVariant message = "Hello from C++"; QMetaObject::invokeMethod(object, "qmlFunction", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, message)); qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml QObject *pButton = object->findChild("quitButton"); if(pButton){ QObject::connect(pButton,SIGNAL(clicked()),object,SLOT(test())); } // engine.rootContext()->setContextObject("makeColor",&makecolor); 设置上下文的方式访问c++类 return app.exec(); } ``` mian.qml源代码 ```qml import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 1.4 Window { width: 800 height: 600 visible: true objectName: "rootRect"; 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"; } function test() { console.log("test ok!"); } //定义函数 function qmlFunction(parameter) { console.log("qml function parameter is", parameter) return "function from qml" } } ``` ## QML动画实现方法 在QML中,实现动画最常用的是State(状态)&Transactions(过渡) 与 Animating Properyty Changes(属性改变动画)这两种方法。 ### State & Transactions(状态与过渡) QML可以在组件中声明各种State(状态),在状态中可以包含该组件所需修改的属性值,当我们想改变组件的属性值时,只需该组件的State即可。Transaction(过渡)用来设置状态改变时的动画,常与State配合使用。创建一个Transaction对象,然后将其添加到组件的transcation属性中。使用方法如下: 以下代码展示的是使用State&Transactions方法实现一个页面切换的动画效果。 ```qml Window { visible: true width: 400; height: 300 title: "Animation" Rectangle { anchors.fill: parent; color: "green" } Rectangle { id: pageA color: "red" width: parent.width; height: parent.height // 创建2个状态"show","hide"分别表示显示界面和隐藏界面 states: [ // 将PageA的属性y赋值为0,opacity赋值为1以实现显示的效果 State { name: "show"; PropertyChanges { target: pageA; y: 0; opacity: 1 } }, // 将PageA的属性y赋值为-height,opaticy赋值为0以实现窗口向上移动并消失的效果 State { name: "hide"; PropertyChanges { target: pageA; y: -height; opacity: 0 } } ] state: "show" transitions: Transition { PropertyAnimation { properties: "y,opacity"; duration: 1000; easing.type: Easing.InOutBack } } } } MouseArea { anchors.fill: parent onClicked: { if(pageA.state == "show") pageA.state = "hide" else pageA.state = "show" } } } ``` 当pageA的状态由`show`改变为`hide`时,`PropertyChanges{target:pageA;y:-height;opacity:0}`执行,将属性y改为-height,opacity改为0。 当属性改变时,`PropertyAnimation{properties:"y,opacity";duration:1000;easing.type:Easing.InOutBack}`被执行,产生动画效果。 ### Animating Property Changes(属性改变动画) 在属性上使用Behaviors(行为)关键字,指定在该属性改变时使用动画。修改以上代码使用Behaviors实现相同的效果。 ```qml Window { visible: true width: 400 height: 300 title: qsTr("Animation") Rectangle { id: pageB color: "green" anchors.fill: parent } Rectangle { id: pageA color: "red" width: parent.width height: parent.height // 给y属性改变安装动画 Behavior on y { PropertyAnimation { duration: 1000; easing.type: Easing.InOutBack } } // 给opacity属性改变安装动画 Behavior on opacity { PropertyAnimation{ duration: 1000 } } } MouseArea { anchors.fill: parent onClicked: { if (pageA.y == 0) { pageA.y = -pageA.height pageA.opacity = 0 } else { pageA.y = 0 pageA.opacity = 1 } } } ``` 有些情况下还可以通过enabled属性来停用Behavior。注意这里的PropertyAnimation的from和to属性是不需要定义的,因为这些值已经提供了,分别是Rectangle的当前值和onClicked处理器中设置的新值。 ### 其他动画使用方法 #### 动画作为属性值的来源 一个动画被应用为属性值的源(property value source),要使用“动画on属性”语法。 ```qml Window { visible: true width: 400 height: 300 title: qsTr("Animation") Rectangle { id: pageB color: "green" anchors.fill: parent } Rectangle { width: 100; height: 100 color: "red" //启动后开始动画 PropertyAnimation on x {to: 50; duration: 1000; loops: Animation.Infinite} PropertyAnimation on y {to: 50; duration: 1000; loops: Animation.Infinite} } } ``` #### 在信号处理器中创建一个动画,并在接收到信号时触发 ```qml Window { visible: true width: 400 height: 300 title: qsTr("Animation") Rectangle { id: pageB color: "green" anchors.fill: parent } Rectangle { id: rect width: 100; height: 100 color: "red" MouseArea { anchors.fill: parent onClicked: PropertyAnimation { target: rect properties: "x,y" to: 50 duration: 1000 } } } } ``` 因为动画没有绑定到一个特定的对象或者属性,所以必须指定target和property(或者targets和properties)属性的值。而且还需要使用to属性来指定新的x和y的值。 #### 独立动画 动画也可以像一个普通的QML对象一样进行创建,而不需要绑定到任何特定的对象和属性。 ```qml Window { visible: true width: 400 height: 300 title: qsTr("Animation") Rectangle { id: pageB color: "green" anchors.fill: parent } Rectangle { id: rect width: 100; height: 100 color: "red" PropertyAnimation { id: animation duration: 1000 } MouseArea { anchors.fill: parent onClicked: { animation.target = rect animation.properties = "x,y" animation.to = 50 animation.start() } } } } ``` 一个独立的动画对象默认是没有运行的,必须使用running属性或者start()和stop()函数来明确地运行它。因为动画没有绑定到一个特殊得对象或属性上,所以必须定义target和property(或者targets和properties)属性的值。也需要用to属性来指定新的x和y值。对于动画在不是一对一对象属性进行动画而且动画需要明确开始和停止的情况下是非常有用的。 ### 动画元素 所有的动画都继承自Animation元素,尽管无法直接创建Animation对象,不过它为动画元素提供了必要的属性和函数。它允许使用running属性和start()和stop()函数来控制动画的开始和停止,也可以通过loops属性定义动画的循环次数。 PropertyAnimation是用来为属性提供动画的最基本的动画元素,可以用来为real、int、color、rect、point、size和vector3d等属性设置动画,被NumberAnimation、colorAnimation、RotationAnimation和Vector3dAnimation等元素继承。NumberAnimation对real和int属性提供了更高效的实现;Vector3dAnimation对vector3d属性提供了更高效的支持;而ColorAnimation和RotationAnimation分别对color和rotation属性变化动画提供了特定的属性支持。 - ColorAnimation允许颜色值设置from和to属性。 ```qml Rectangle { id: rect width: 100; height: 100 color: "green" // 启动运行,由绿色变为红色 ColorAnimation on color { from: "green"; to: "red"; duration: 1000 } } ``` - RotationAnimation允许设定旋转的方向。 ```qml Rectangle { id: rect width: 100; height: 100 color: "red" anchors.centerIn: parent // 启动运行,顺时针旋转90° RotationAnimation on rotation { to: 90; duration: 1000; direction: RotationAnimation.Clockwise } } ``` - SmoothedAnimation: 它是一个专门的NumberAnimation,当目标值改变时在动画中为其提供了一个平滑的变化; - SpringAnimation: 提供了一个像弹簧一样的动画,可以设置mass、damping和epsilon等属性 - ParentAnimation: 用来在改变父项目时产生动画(对应ParentChange元素) - AchorAnimation: 用来在改变锚时产生动画(对应AnchorChanges元素) - 对于任何基于PropertyAnimation的动画都可以通过设置easing属性来控制在属性值动画中使用的缓和曲线。它们可以影响这些属性值的动画效果,提供一些如反弹、加速和减速等视觉效果。OutBounce来创建一个动画到达目标值时的反弹效果。 ### 组合动画 多个动画可以组合成一个单一的动画,这可以使用ParallelAnimation或者SequentialAnimation动画组元素中的一个实现。在ParallelAnimation中的动画会同时进行,而在SequentialAnimation中的动画会一个个地运行。想要运行多个动画,可以在一个动画组中定义。以下代码分别表示串行动画和并行动画。 ```qml Window { visible: true width: 400 height: 300 title: qsTr("Animation") Rectangle { id: pageB color: "green" anchors.fill: parent } Rectangle { id: rect width: 100 height: 100 color: "red" // 串行动画 SequentialAnimation { id: animation // NumberAnimation { target: rect properties: "x,y" to: 50 duration: 1000 } ColorAnimation { target: rect properties: "color" to: "blue" duration: 1000 } } MouseArea { anchors.fill: parent onClicked: animation.start() } } } ``` ### 其他画元素 QML还为动画提供了其他一些有用的元素 - PauseAnimation: 在动画中间进行暂停 - ScriptAnimation: 允许在动画中执行JavaScript,也可以和StateChangeScript一起使用来重用已经存在的脚本