Qt Quick/QML学习笔记

(基于qt 5.3.1)

一、Qt Quick入门

1.启动Qt Quick App的两种方式
(1)窗口标题、图标等由qml控制
main.cpp用QQmlApplicationEngine:

//main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])//主函数必须带参
{
    QGuiApplication app(argc, argv);//app的参数和main的参数对应

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); 
    //  "qrc:///"表示该qt工程的根目录
    return app.exec();
}

main.qml用Window:

//main.qml
import QtQuick 2.2
import QtQuick.Window 2.1

Window {
    visible: true
    width: 360
    height: 360

    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

(2)窗口标题、图标由c++代码控制
main.cpp用QQuickView:

#include <QGuiApplication>
#include <QQuickView> //头文件

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView viewer;
    viewer.setResizeMode(QQuickView::SizeRootObjectToView);
    viewer.setSource(QUrl("qrc:///main.qml"));
    viewer.show();

    return app.exec();
}

main.qml用Item(Rectangle等):

import QtQuick 2.2
import QtQuick.Window 2.1

Rectangle {
    visible: true
    width: 360
    height: 360

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

注意:此处不能调用Qt.quit()。

2.Window对象
(1)visibility:控制窗口最小化、最大化、全屏等。
直接在Window里面加入:

    visibility: Window.Minimized 
    visibility: Window.Maximized
    visibility: Window.FullScreen

(2)color:"blue","#FFFFFF"
opacity:设置窗口透明度,取值范围0~1
title:标题

3.Rectangle:
color: Qt.rgba(r,g,b,a) ,rgba在0~1之间,rgb表示每种颜色所占的比例,a表示颜色的深浅。
gradient:渐变填充,优先级高于color。每一个GradientStop为一个确定颜色的位置,在两个确定颜色之间渐变填充。

Rectangle {
    width: 320
    height: 480
    gradient: Gradient{
        GradientStop{
            position:0.0
            color:"black"
        }
        GradientStop{
            position:0.33
            color:"blue"
        }
        GradientStop{
            position:1.0
            color:"white"
        }
    }
}

rotation:旋转角度,可以用rotation:90实现横向渐变。

4.Item
Item是Rectangle的父类。
x,y:位置。
z:图层的优先级。z越大,越在其他图层上面。
clip:在父类设定,默认为false,当设置为true时自动调整子类的大小使之不会跑到父类外面去。

5.anchors
利用图元之间的相对位置布局。
anchors.left:parent.left表示当前图元的最左侧和它父类的最左侧重合。
anchors.leftMargin:20表示当前图元的最左侧向右移动20像素。
anchors.fill:parent表示当前图元填满它的父类。
anchors.centerIn:parent表示当前图元居中放置于它的父类中。

Rectangle {
    width: 300
    height: 200
    Rectangle{
        id:rect1
        anchors.left:parent.left
        anchors.leftMargin: 20
        anchors.top:parent.top
        anchors.topMargin: 20
        width:120
        height:120
        color:"red"
    }
    Rectangle{
        anchors.left: rect1.right
        anchors.leftMargin: 20
        anchors.top: rect1.top
        width:120
        height:120
        color:"blue"
    }
}
Rectangle {
    width: 300
    height: 200
    Rectangle{
        color:"blue"
        anchors.fill:parent
        border.width: 6
        border.color: "black"
        Rectangle{
            anchors.centerIn: parent
            width:120
            height:120
            radius:8
            border.width: 2
            border.color: "white"
            antialiasing: true //抗锯齿
            color:"red"
        }
    }
}

6.Keys
响应键盘。
focus:焦点,要让某个元素响应按键,必须把它的focus设成true。
Keys.enabled:键盘使能
Keys.onPressed:检测到某个键被按下时,就将event.key设成Qt.Keys_x,后者是宏,为那个键对应的ASCII码。

按某个数字键,就显示某个数字的程序:(按Esc键退出)
注意:默认为中文输入法,要手动切换。

import QtQuick 2.2
import QtQuick.Window 2.1

Window{
    visible: true
    height: 360
    width: 480

    Rectangle{
        width: 480
        height: 360
        color:"#c0c0c0"
        focus:true
        Keys.enabled: true
        Keys.onEscapePressed: Qt.quit()
        Keys.onBackPressed: Qt.quit()
        Keys.onPressed: {
            switch(event.key){
            case Qt.Key_0:
            case Qt.Key_1:
            case Qt.Key_2:
            case Qt.Key_3:
            case Qt.Key_4:
            case Qt.Key_5:
            case Qt.Key_6:
            case Qt.Key_7:
            case Qt.Key_8:
            case Qt.Key_9:
                event.accepted=true
                keyView.text=event.key-Qt.Key_0;
                break;
            }
        }
        Text{ //可以写在后面
            id:keyView
            font.bold: true
            font.pixelSize: 24
            text: qsTr("text")
            anchors.centerIn: parent
        }
    }
}

7.Text
显示文本。
wrapMode:换行方式,WordWrap在两个单词之间换行,WrapAnywhere可以在单词内部换行。
font:字体设置,font.bold:true表示加粗,font.pixelSize:24表示一个字占24像素,font.underline:true表示下划线。
text:文本内容。
style:文字风格,有Outline,Raised,Sunken等。
stylecolor:描边的颜色。
font.family: "楷体" 设置字体
8.Button
需要引入import QtQuick.Controls 1.2
用onClicked表示按下按钮后的操作。
这个操作相当于c++代码。

        Button{
            text:"Quit"
            anchors.centerIn: parent
            onClicked:{
                Qt.quit();
            }
        }

checkable:设置Button是否可选。
iconSource:用URL的方式指定图标图片。
focus:如果设置为true,则空格表示按下此按钮。
style:设置按钮的风格。

style:ButtonStyle 有background,control和lable三个属性,可以用于设置按钮的背景,并随操作而改变。
implicitWidth: 必须要加implicit。
implicitHeight:
control.pressed:是否按下按钮
control.hovered:鼠标是否悬停在按钮上

      style: ButtonStyle{
                background: Rectangle{
                    implicitWidth: 70
                    implicitHeight: 25
                    border.width: control.pressed?2:1
                    border.color: 
(control.hovered||control.pressed)?"green":"#888888"
                }
            }

9.Image
显示照片。
sources:照片的来源,可以是本地照片也可以是网络照片,本地照片前要加"file:///"。

Window{
    visible: true
    height: 360
    width: 480
    Image{
        anchors.fill: parent
        source: "file:///C:/Users/doujihu/Desktop/其他/photos/照片.jpg"
    }
}

默认同步显示图片,要开启异步设置:asynchronous: true

10.busyIndicator
等待的圆圈标记。
running:true 显示等待标记
style:BusyIndicatorStyle{} 用于设置等待图片。

11.FileDialog
需要引入import QtQuick.Dialogs 1.1
文件对话框,用于保存\打开文件。
id.open():弹出文件对话框
title:文件对话框的标题
nameFilters:["Image Files(*.jpg *.png *.gif)"]
文件过滤器,只有这些后缀名的文件能被选中。
onAccepted:{}
选中后执行的操作
id.fileUrl:文件地址,以file:///开头
string类的slice方法:s.slice(a,b)截取从a到b的部分,可以省略a或b。

    FileDialog{
        id:fileDialog
        title:"please choose"
        nameFilters: ["Image Files(*.jpg *.png *.gif)"]
        onAccepted:{
            imageViewer.source=fileDialog.fileUrl;
            var imageFile=new String(fileDialog.fileUrl);
            imagePath.text=imageFile.slice(8);
        }
    }

二、ECMAScript

弱类型:类型都叫var,一个变量可以赋值不同的数据类型。
语句后可以不写分号,以换行作为结尾标记。
原始类型:Undefined,Null,Boolean,Number,String,Object
typeof 运算符:返回类型名
Undefined类型只有一个underfined值,表示未初始化的变量,没有明确返回值的函数,也返回undefined。
Null类型只有一个值null。
Boolean相当于bool。
Number支持十六进制,八进制,科学记数法等。最大值为Number.MAX_VALUE,最小值为Number.MIN_VALUE。如果超过最大值,会返回Infinity,可以用isFinit()判断是否有穷。NaN:表示强制类型转换的对象不是纯数字,只能用isNaN()判断。
String字符串类型,没有char。String类的变量是只读的,不能通过S[0]='a'来修改。

Number.toString(base=10) 把数字以base为基转化为字符串。
parseInt(String)和parseFloat(String) 从字符串的最左边开始,到第一个非数字字符为止,提取数字。
Boolean() 对于非空对象,返回true,否则返回false。
Number() 对整个值转换,如果整个值不是有效数字,返回NaN。

Object类:所有类的基类。
类成员可以动态添加:

Window {
    height:480
    width:640
    Component.onCompleted: {
        var person= new Object();
        person.name="zhang san"; //person原来没有name成员
        person.year=20;
        console.log(person.name);
        console.log(person.year);
    }
}

也可以添加方法:

    Component.onCompleted: {
        var person= new Object();
        person.name="zhang san";
        person.year=20;
        person.show = function show(){ \\用等号声明函数
            console.log("name:",this.name,"\nyear:",this.year);
            \\this的使用和c++一样
        }
        person.show();
    }

对象的属性和方法可以用类似于map的下标访问。

        console.log(person["name"]);
        person["show"](); //相当于person.show();

使用for...in...可以遍历一个对象的属性。

      for(var i in person){
            console.log(i,":",person[i]);
        }

对象的字面量表示法:可以直接用大括号加冒号实例化类。

Window {
    height:480
    width:640
    Component.onCompleted: {
        var person = {
            "name": "zhangsan",
            "year": 20, //注意:两个成员之间必须用逗号隔开
            "show": function show(){
                console.log(this.name,":",this.year);
            }
        }
        person.show();
    }
}

String类:
str.indexOf("string",first=0):从first开始查找"string",返回string在str中的第一个字符的下标,如果搜不到,输出-1。
str.search("string") :总是从头开始查找"string",支持正则表达式,返回下标。
str.match("string"):从头开始匹配"string",支持正则表达式,返回存放所有子串的数组。
str.slice(a,b):提取[a,b)的子串。若为负数,则表示倒数几个字符。(和python类似)。
str.substr(a,b):从a开始,提取长度为b的子串。
str.toLowerCase():转化成小写。
str.replace("str1","str2"):把str中的第一个str1换成str2,如果想把str中的str1都换成str2,要用正则表达式/str1/g。
str.split(str1):以str1为分隔符,将str划分,返回数组(不含str1)。
str.arg(i):把%d换成i。console.log("%1+%2=%3".arg(3).arg(4).arg(7));

正则表达式:
/"String"/i等价于RegExp("String","i"),"String"叫模式,"i"叫修饰符。
修饰符:
"i":忽略大小写。
"g":查找所有符合条件的匹配,而不是在找到第一个匹配后停止。
"m":跳过换行符匹配。
元字符:
".":匹配除换行符外的任意字符。
"\w":匹配字母,数字,下划线或汉字。
"\s":匹配任意空白字符。
"\S":匹配任意非空白字符。
"\d":匹配数字字符。
"\D":匹配非数字字符。
"\b":匹配单词的开始或结束。
"^":匹配字符串的开始。
"$":匹配字符串的结束。
量词:
"*":重复零次,一次或多次。
"+":重复一次或多次。
"?":重复零次或一次。
"{n}":重复n次。
"{n,}":重复n次或多次。
"{n,m}":重复n到m次。

console.log(str.match(/^We.*/mg));

表示匹配所有以We开头的行。
字符集:[xyz]匹配x,y,z,[0-9]匹配0到9。
转义字符:*,.,\。
正则表达式可用于String类的search(),match(),replace(),split()方法。

Array类:可变数组,类型可以不同。
var a=[1,2,3,4,5];
a.push(6,7,8,9):向数组末尾插入一个或多个元素,返回数组长度。
a.pop():删除并返回最后一个元素。
用越界下标也可以扩充数组,直接a[6]=7即可,此时a[5]=undefined。
a.shift():删除并返回a[0]。
a.unshift(6,7,8,9):从a[0]开始塞进去若干元素。
a.sort(cmp):对数组排序。
数组的下标可以为负数,负数下标不会受到任何方法的影响。如a[-1]=6。
a.join(str):以str为分隔符,将a转化为字符串。
a.concat(b):返回数组a连接数组b形成的新数组。注意:不能写成a+b。
a.slice(start,end):返回[start,end)的新数组。
a.splice(index,num):删除a从index开始的num个元素,并返回它们组成的数组。

Math类:
Math.random() 返回0-1之间的随机数。

Date类:
Date()返回现在时间构成的字符串。
Date.now()返回1970.1.1至今的毫秒数,可以用来做计时器。

和c++不一样的是,对象使用完不用delete,直接array=null即可。
函数:
function 函数名(变量名1,...){}
变量名前面不用写类型,任何一个函数都有返回值,返回值可以为任何类型。
运算符:
===:全等于,值和类型都一样。为了防止null==0。
">>>":带零扩展的右移,移动后最左侧补0,负数右移后变成正数。
void():强制转化为undefined。
str instanceof String 判断str是否是String类型,如果是,返回true,可以用于一般的Object(typeof不行)。

console:
console.time(tag):启动计时器,tag是个字符串。
console.time(tag):停止tag对应的计时器并输出时间。

Global,Math,JSON不需要实例化就可以使用,被称作内置对象。

QML基本类型:
url:资源地址,可以是本地地址,相对地址和网络位置。
encodeURI("url"):把url中无效的空格自动替换成/
list:储存对象的数组,如item的children属性,children[i]表示它的第i个孩子。
enumeration:枚举类型

Qt.openUrlExternally("url"):打开url,可以是本地文件,也可以用浏览器打开网址。

三、Qt Quick 信号处理

信号处理器:on<Signal>:{代码块}
附加信号处理器:<别的类型>.on<Signal>:{代码块}
如在Rectangle里面用Keys.onEscapePressed:{},表示按下Esc键的情况。
Component信号:
Component.onCompleted:组件创建完成时执行的操作。
Component.onDestruction:组件销毁时执行的操作。

Connections:将多个对象和一个信号连接,或者在对象的作用域外使用它的信号。

        Connections{
            target:发出信号的对象名
            on<Signal>:{
                    代码块;
            }
        }

(当然也可以在Button里面直接写)
信号分为两类:输入操作(鼠标,键盘等)产生的信号,属性改变产生的信号(on<Property>Changed)。

自定义信号:相当于一个返回值为Signal的函数

Signal 信号名(属性名1 参数1,属性名2 参数2...);

当需要发出这个信号时,调用此函数。
在Connection里面用on信号名对这个信号进行处理,这里可以使用Signal函数的参数。
Loader:加载组件。
Loader.sourceComponet表示它使用哪个已有的组件来加载新的组件,这个已有的组件叫item。
onLoaded是加载完成的信号。

Window {
    height:640
    width:480
    visible:true

    Rectangle{
        anchors.fill: parent
        Text{
            id:coloredText
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top:parent.top
            anchors.topMargin: 4
            text:"Hello World!"
            font.pixelSize: 32
        }
        Component{
            id: colorComponent
            Rectangle{
                id:colorPicker
                width:50
                height:30
                signal colorPicked(color clr);
                MouseArea{
                    anchors.fill: parent
                    onPressed: colorPicker.colorPicked(colorPicker.color)
                }
            }
        }
        Loader{
            id:redLoder
            anchors.left: parent.left
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            sourceComponent: colorComponent
            onLoaded:{
                item.color="red";
            }
        }
        Loader{
            id:blueLoder
            anchors.left: redLoder.right
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            sourceComponent: colorComponent
            onLoaded:{
                item.color="blue";
            }
        }
        Connections{
            target: redLoder.item
            onColorPicked:{
                coloredText.color=clr;
            }
        }
        Connections{
            target: blueLoder.item
            onColorPicked:{
                coloredText.color=clr;
            }
        }
    }
}

signal.connect(func1,func2,...):把signal对象连接到另外的信号或方法上,当信号发射时,连接到信号上的其他信号或方法就会被调用。用于一个信号连接多个方法。

    Rectangle{
        id: forwarder
        anchors.fill: parent
        signal send()
        onSend: console.log("Send!");

        MouseArea{
            id:mousearea
            anchors.fill: parent
            onClicked: console.log("Mouse!");
        }

        Component.onCompleted: {
            mousearea.clicked.connect(send);
            //当clicked被触发时,调用send方法,即send也被触发。
        }
    }

鼠标:
在一个item内部用MouseArea对象。
enable:true表示开始处理鼠标事件。
acceptedButtons设定接受哪些按键,中间用或隔开:Qt.LeftButton|Qt.RightButton|Qt.MidButton
hoverEnabled:true表示开始处理鼠标悬停事件。
鼠标信号:
clicked:单击,在内部用mouse.button读取哪个键,mouse.x和mouse.y为鼠标坐标。
doubleClicked:双击。
pressed:按住鼠标。
pressAndHold:长按鼠标。
released:松开鼠标。
entered:鼠标进入事件区域。
exited:鼠标离开事件区域。
mouse.accepted=true:鼠标事件终止,不再往下传递。

键盘:
必须先把focus设成true。
Keys.enabled:true表示开始处理按键。
Keys.onPressed:一般按键信号,有一个叫event的参数。
Keys.onXXXPressed:特殊按键信号,如空格,回车等。
Keys.forwardTo:[a,b,c] 表示传递给列表内的对象,如果某个对象accept了某个按键,那么列表中排在它后面的对象就不会收到该按键事件。
event.accepted:处理完成标记。
event.key:按下的键,用于和Qt.Key_x比较。
Keys.priority:设置Keys附加属性的优先级。Keys.BeforeItem在Item之前处理按键(默认),Keys.AfterItem在Item之后处理按键。比如Item自带按键处理,如果在Item之后处理按键,按键会被Item吞掉,如CheckBox。

Window {
    height:640
    width:480
    visible:true

    Rectangle{
        id:rect1
        anchors.fill: parent
        color:"gray"

        focus:true
        Keys.enabled: true
        Keys.onEscapePressed: Qt.quit();
        Keys.forwardTo: [moveText,likeQt]
        Keys.priority: Keys.BeforeItem
        Text{
            id:moveText
            x:20
            y:20
            width:200
            height:30
            text:"Moving Text"
            color:"blue"
            font.bold: true
            font.pixelSize: 24
            Keys.enabled: true
            Keys.onSpacePressed: {
                rect1.color="black";
                event.accepted=true;
            }
            Keys.onPressed: {
                switch(event.key){
                case Qt.Key_Left:
                    if(x>=5)
                    x-=10;
                    break;
                case Qt.Key_Right:
                    if(x<=330)
                    x+=10;
                    break;
                case Qt.Key_Down:
                    if(y<=600)
                    y+=10;
                    break;
                case Qt.Key_Up:
                    if(y>=5)
                    y-=10;
                    break;
                default:
                    return; 
            //必须return,否则switch外面的accepted会把信号吃掉,checkbox收不到空格信号
                }
                event.accepted=true;
            }
        }

        CheckBox{
            id:likeQt;
            text:"Like Qt Quick"
            anchors.left: parent.left
            anchors.leftMargin: 10
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 10
            z:1
        }
    }
}

计时器:Timer
running:true 计时器开始工作
onTriggered:每经过一个计时周期产生一个信号。
interval:计时周期,默认为1000ms。
repeat:true 可以复位
triggeredOnStart:true 启动后立刻触发一次。
start() stop() restart()

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
       anchors.fill: parent
       color:"gray"
       QtObject{
           id:attrs
           property int counter;  //设置临时变量,不能用var。
           Component.onCompleted: {
               attrs.counter = 10;
           }
       }
       Text{
           id: countShow
           anchors.centerIn: parent;
           color: "blue"
           font.pixelSize: 40
       }
       Timer{
           id: countDown
           interval: 1000;
           repeat :true
           triggeredOnStart: true
           onTriggered: {
               countShow.text=attrs.counter;
               attrs.counter--;
               if(attrs.counter<0){
                   countDown.restart();
                   attrs.counter=10;
               }
           }
       }
       Button{
           id:startButton
           anchors.top: countShow.bottom
           anchors.topMargin: 20
           anchors.horizontalCenter: countShow.horizontalCenter;
           text:"Start"
           onClicked:{
               attrs.counter=10;
               countDown.start();
           }
       }
    }
}

四、组件与动态对象

1.Component
既可以定义在单独的.qml文件中,也可以在其他item里面定义。
Component只能包含一个顶层Item,而且除了这个Item外只能有id。它自己不可见,必须通过Loader实例化后才可见。定义在单独文件中的Component,文件名就是组件名,组件名的首字母必须大写。
KeyNavigation.left/right/tab:XXX 把focus传递给XXX。
一个完整的实例:
ColorPicker.qml:

//ColorPicker.qml
//增加了用回车和空格选定的功能
import QtQuick 2.2

Rectangle {
    id: colorPicker
    width: 50
    height: 30
    signal colorPicked(color clr);

    function configureBorder(){ //当focus改变时,用来改变边框粗细和颜色的函数
        colorPicker.border.width=colorPicker.focus?2:0
        colorPicker.border.color=colorPicker.focus?"yellow":"black"
    }

    MouseArea{
        anchors.fill: parent
        onClicked:{
            colorPicker.colorPicked(colorPicker.color);
            mouse.accepted=true;
            colorPicker.focus=true;
        }
    }
    Keys.onReturnPressed: {
        colorPicker.colorPicked(colorPicker.color);
        event.accepted=true;
    }
    Keys.onSpacePressed: {
        colorPicker.colorPicked(colorPicker.color);
        event.accepted=true;
    }
    onFocusChanged: configureBorder();
    Component.onCompleted: configureBorder();
}

main.qml:

//main.qml
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
        anchors.fill: parent
        Text{
            id:coloredText
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top:parent.top
            anchors.topMargin: 4
            text:"Hello World!"
            font.pixelSize: 32
        }
        function setTextColor(clr){ //染色函数
            coloredText.color=clr;
        }

        ColorPicker{
            id:redColor
            color:"red"
            focus:true
            anchors.left: parent.left
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4

            KeyNavigation.right: blueColor //按右方向键把focus传递给blueColor
            KeyNavigation.tab: blueColor
        }

        ColorPicker{
            id:blueColor
            color:"blue"
            anchors.left: redColor.right
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4

            KeyNavigation.left: redColor
            KeyNavigation.right: pinkColor
            KeyNavigation.tab: pinkColor
        }
        ColorPicker{
            id:pinkColor
            color:"pink"
            anchors.left: blueColor.right
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4

            KeyNavigation.left: blueColor
            KeyNavigation.tab: redColor
        }
        Component.onCompleted: {
            redColor.colorPicked.connect(setTextColor);
            blueColor.colorPicked.connect(setTextColor); //这里是函数名,不是func()
            pinkColor.colorPicked.connect(setTextColor);
        }
    }
}

2.Loader
source:加载qml文档。
sourceComponent:加载Component对象。
item:指向它加载组件对应的顶层对象,如它加载的组件是个Rectangle,则用item可以访问那个Rectangle的属性和信号。
Loader.item可以作为Connections的target来处理信号。
Loader的尺寸和位置默认与Component一样,可以单独在Loader内修改。
注意Loader的focus和它对应的item的focus不一样,在Loader里可以直接用item.focus=focus来传递,但是在item中要单独设一个property Item loader,用它的focus来保存它对应Loader的focus。
用Loader实现与上例相同的功能,但是逻辑更复杂:

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
        anchors.fill: parent
        Text{
            id:coloredText
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top:parent.top
            anchors.topMargin: 4
            text:"Hello World!"
            font.pixelSize: 32
        }
        Component{
            id: colorComponent
            Rectangle{
                id:colorPicker
                width:50
                height:30
                signal colorPicked(color clr);
                property Item loader;
                border.width: focus?2:0
                border.color: focus?"yellow":"black"
                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        colorPicker.colorPicked(colorPicker.color);
                        loader.focus=true;
                    }
                }
                Keys.onReturnPressed: {
                    colorPicker.colorPicked(colorPicker.color);
                    event.accepted=true;
                }
                Keys.onSpacePressed: {
                    colorPicker.colorPicked(colorPicker.color);
                    event.accepted=true;
                }
            }
        }
        Loader{
            id:redLoder
            width:80
            height:60
            focus:true
            anchors.left: parent.left
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            sourceComponent: colorComponent
            KeyNavigation.right: blueLoder
            onLoaded:{
                item.color="red";
                item.focus=true;
                item.loader=redLoder
            }
            onFocusChanged: item.focus=focus;
        }
        Loader{
            id:blueLoder
            anchors.left: redLoder.right
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            sourceComponent: colorComponent
            KeyNavigation.left: redLoder
            onLoaded:{
                item.color="blue";
                item.loader=blueLoder;
            }
            onFocusChanged: item.focus=focus;
        }
        Connections{
            target: redLoder.item
            onColorPicked:{
                coloredText.color=clr;
            }
        }
        Connections{
            target: blueLoder.item
            onColorPicked:{
                coloredText.color=clr;
            }
        }
    }
}

3.动态创建和销毁组件
在Rectangle(rootItem)加一个标记位:property bool colorPickerShow: false;
把sourceComponent改成undefined,这个组件就隐藏了。
相当于一个状态机:当click时,根据现态是true还是false决定如何转移。

        Button{
            id:ctrlButton
            text:"Show"
            anchors.left: parent.left
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4

            onClicked:{
                if(rootItem.colorPickerShow){
                    redLoader.sourceComponent=undefined;
                    blueLoader.sourceComponent=undefined;
                    rootItem.colorPickerShow=false;
                    ctrlButton.text="Show";
                }
                else{
                    redLoader.sourceComponent=colorComponent;
                    blueLoader.sourceComponent=colorComponent;
                    redLoader.focus=true;
                    rootItem.colorPickerShow=true;
                    ctrlButton.text="Hide";
                    ctrlButton.anchors.leftMargin=400;
                }
            }
        }

4.用ECMAScript动态创建对象
Qt.creatComponent("XXX.qml"):创建一个XXX.qml组件并返回该组件。
Component.creatObject(parent,{"属性1":XXX,"属性2":YYY,...}):以parent为父类,以大括号内的内容为属性初始值,创建一个该组件的实例并返回该实例。
rootItem.component.status==Component.Ready 判断组件是否创建成功。

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
        id: rootItem
        anchors.fill: parent
        property int count: 0;
        property Component component: null;

        Text{
            id:coloredText;
            text: "Hello World!"
            anchors.centerIn: parent
            font.pixelSize: 24
        }

        function changeTextColor(clr){
            coloredText.color=clr;
        }

        function createColorPicker(clr){
            if(rootItem.component==null){
                rootItem.component=
                        Qt.createComponent("ColorPicker.qml");
            }
            var colorPicker;
            if(rootItem.component.status==Component.Ready){
                colorPicker=rootItem.component.createObject(rootItem,
                {"color":clr,"x":rootItem.count*55,"y":10});
                colorPicker.colorPicked.connect(rootItem.changeTextColor);
//clr这个参数从createColorPicker传递到changeTextColor
            }
            rootItem.count++;
        }
        Button{
            id: add
            text:"add"
            anchors.left: parent.left
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            onClicked:{
                //下面这一行书上写错了
                rootItem.createColorPicker(Qt.rgba(Math.random(),
                                 Math.random(),Math.random(),1));
            }
        }
    }
}

5.销毁动态创建的对象
对于Loader创建的对象,直接把source设为空串或者把sourceComponent设置成undefined即可自动销毁。
对于Qt方法创建的对象,调用其destroy(time=0ms)方法即可销毁,time为销毁延迟的时间。
利用数组实现组件的删除:

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
        id: rootItem
        anchors.fill: parent
        property int count: 0;
        property Component component: null;
        property var array: new Array();

        Text{
            id:coloredText;
            text: "Hello World!"
            anchors.centerIn: parent
            font.pixelSize: 24
        }

        function changeTextColor(clr){
            coloredText.color=clr;
        }

        function createColorPicker(clr){
            if(rootItem.component==null){
                rootItem.component=
                        Qt.createComponent("ColorPicker.qml");
            }
            var colorPicker;
            if(rootItem.component.status==Component.Ready){
                colorPicker=rootItem.component.createObject(rootItem,
                {"color":clr,"x":rootItem.count*55,"y":10});
                colorPicker.colorPicked.connect(rootItem.changeTextColor);
                rootItem.array.push(colorPicker);
            }
            rootItem.count++;
        }
        Button{
            id: add
            text:"add"
            anchors.left: parent.left
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            onClicked:{
                rootItem.createColorPicker(Qt.rgba(Math.random(),
                                 Math.random(),Math.random(),1));
            }
        }
        Button{
            id: del
            text:"del"
            anchors.left: add.right
            anchors.leftMargin: 4
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 4
            onClicked:{
                rootItem.array[rootItem.array.length-1].destroy();
                rootItem.array.pop();
                rootItem.count--;
            }
        }
    }
}

五、元素布局

布局定位器:只改变父类的大小,不改变孩子们的尺寸。
布局管理器:根据界面尺寸变化自动调整孩子们的尺寸。
1.Row和Column
用锚布局设置行的位置,用spacing属性设定Item的间隔,用layoutDirection属性指定布局方向,默认为从左到右,设定为Qt.RightToLeft时从右向左。
Column一样。
2.Grid和Flow
网格,由rows和columns设置行列数,默认为x行4列;rowSpacing和columnSpacing指定行列间距。
flow属性为放置方法,默认为从左到右,放满一行再放下一行;可以改成Grid.TopToBottom。
Flow:按照自身的宽和高是否超出边界而自动折行。
3.GridLayout
高级的Grid。
要引入import Qtquick.Layouts 1.1
Layout.fillWidth: true 填充一行的所有剩余空间,要提前确定Layout的宽度。
RowLayout、ColumnLayout同理。

六、常用元素

1.TextInput
输入一行文本。要设置focus:true才能有效。
maximumLength:设置编辑框允许输入字符的最大长度。
echoMode:回显属性,默认为TextInput.Normal,可以设置为TextInput.Password:显示黑圆圈(用Passwordcharacter设置),TextInput.NoEcho:不回显。
inputMask:限制输入字符的格式,如000.000.000.000可以作为ip地址。
IntValidator、RegExpValidator分别为整数和正则表达式验证器,用于限制输入字符的格式。
selectByMouse:true 可以用鼠标选择编辑框内的文字。
当按下回车键或编辑框失去焦点时,会发出accepted信号,可以用onAccepted来处理。
2.TextField
高级版TextInput。
textColor:设置文本颜色,注意不是color。
控件TextFieldStyle的background属性可以设定TextField的背景图案。

       TextField{
           id:input
           style: TextFieldStyle{
               textColor:"black"
               background: Rectangle{
                   radius:2
                   implicitWidth: 100
                   implicitHeight: 24
                   border.color: "yellow"
                   border.width: 1
               }
           }
       }

3.TextEdit
多行文本编辑框。
warpMode:TextEdit.WordWrap 在单词边界处折行。

4.TextArea
可以用TextAeraStyle的background属性设定背景色,但是不能设置背景图案,只能用TextEdit底下放个z序较小的Rectangle对象来实现。
selectionColor:选中字符边框的颜色。
selectedTextColor:选中字符的颜色。
TextArea支持鼠标和键盘的滚动。

5.RadioButton
单选。
需要引入import QtQuick.Controls 1.2
ExclusiveGroup:互斥分组,分组内的元素只能选一个。通过按钮类的exclusiveGroup属性声明是哪个分组。
XXX.current:表示XXX当前被选中的元素。
text:显示文本
checked:显示RadioButton是否被选中
hovered:指示鼠标是否悬停在按钮上
pressed:按钮按下时为true。
activeFocusOnPress:按钮获得焦点时为true。

RadioButtonStyle组件:定制按钮样式。
background:定制选项的背景。
indicator:定制选中的指示图标。
label:定制单选按钮的文本。
control:使用style的RadioButton对象,如control.text为对应按钮旁边的文字。
spacing:图标和文本之间的间隔。

当选中某个选项时,发送clicked信号,可以用onClicked处理。
单项选择示例:

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
       anchors.fill: parent
       color:"white"

       Rectangle{
           id:resultHolder
           color:"#a0a0a0"
           width:200
           height:60
           anchors.centerIn: parent
           visible: false
           z:2
           opacity: 0.8
           border.width: 2
           border.color: "#808080"
           Text{
               id:result
               anchors.centerIn: parent
               font.pointSize: 20
               color:"#0500ff"
               font.bold: true
           }
       }

       ExclusiveGroup{
           id:mos;
       }

       Component{
           id:radioStyle
           RadioButtonStyle{
               background:Rectangle{
                   opacity: 0.8
                   color:"#a0a0a0"
                   border.width: 2
                   border.color: control.activeFocus?"yellow": "#808080"
               }
               indicator:Rectangle{
                   visible:false
               }
               label: Text{
                   font.pixelSize: 24
                   color: control.activeFocus? "blue":"black"
                   text: control.text
               }
               MouseArea{
                   anchors.fill: parent
                   enabled: true
                   onDoubleClicked:{
                       console.log(666);
                       result.text=choose1.text;
                       resultHolder.visible=true;
                   }
               }
           }
       }
       Text{
           id:notation
           text:"请选择喜欢的文字游戏:"
           color:"black"
           font.pixelSize: 32
           anchors.top:parent.top
           anchors.topMargin:45
           anchors.left: parent.left
           anchors.leftMargin: 45
       }
       RadioButton{
           id:choose1
           text:"ever17"
           exclusiveGroup: mos
           anchors.top:notation.bottom
           anchors.topMargin: 4
           anchors.left: notation.left
           anchors.leftMargin: 20
           focus:true
           activeFocusOnPress: true
           style:radioStyle

           property bool click : false  //实现双击确定
           onCheckedChanged: {
               if(!this.checked) this.click=0;
           }
           onClicked: {
               if(this.click){
                   result.text=this.text;
                   resultHolder.visible=true;
                   this.click=false;
               }
               else this.click=true;
           }

           Keys.enabled: true
           Keys.onReturnPressed: {
               result.text=this.text;
               resultHolder.visible=true;
           }
           Keys.onEscapePressed: {
               resultHolder.visible=false;
           }
           KeyNavigation.up: choose4
           KeyNavigation.down: choose2
       }
       RadioButton{
           id:choose2
           text:"素晴日"
           exclusiveGroup: mos
           anchors.top:choose1.bottom
           anchors.topMargin: 4
           anchors.left: choose1.left
           activeFocusOnPress: true
           style:radioStyle


           property bool click : false  //实现双击确定
           onCheckedChanged: {
               if(!this.checked) this.click=0;
           }
           onClicked: {
               if(this.click){
                   result.text=this.text;
                   resultHolder.visible=true;
                   this.click=false;
               }
               else this.click=true;
           }

           Keys.enabled: true
           Keys.onReturnPressed: {
               result.text=this.text;
               resultHolder.visible=true;
           }
           Keys.onEscapePressed: {
               resultHolder.visible=false;
           }
           KeyNavigation.up: choose1
           KeyNavigation.down: choose3
       }
       RadioButton{
           id:choose3
           text:"Clannad"
           exclusiveGroup: mos
           anchors.top:choose2.bottom
           anchors.topMargin: 4
           anchors.left: choose2.left
           activeFocusOnPress: true
           style:radioStyle

           property bool click : false  //实现双击确定
           onCheckedChanged: {
               if(!this.checked) this.click=0;
           }
           onClicked: {
               if(this.click){
                   result.text=this.text;
                   resultHolder.visible=true;
                   this.click=false;
               }
               else this.click=true;
           }

           Keys.enabled: true
           Keys.onReturnPressed: {
               result.text=this.text;
               resultHolder.visible=true;
           }
           Keys.onEscapePressed: {
               resultHolder.visible=false;
           }
           KeyNavigation.up: choose2
           KeyNavigation.down: choose4
       }
       RadioButton{
           id:choose4
           text:"白色相簿2"
           exclusiveGroup: mos
           anchors.top:choose3.bottom
           anchors.topMargin: 4
           anchors.left: choose3.left
           activeFocusOnPress: true
           style:radioStyle

           property bool click : false  //实现双击确定
           onCheckedChanged: {
               if(!this.checked) this.click=0;
           }
           onClicked: {
               if(this.click){
                   result.text=this.text;
                   resultHolder.visible=true;
                   this.click=false;
               }
               else this.click=true;
           }

           Keys.enabled: true
           Keys.onReturnPressed: {
               result.text=this.text;
               resultHolder.visible=true;
           }
           Keys.onEscapePressed: {
               resultHolder.visible=false;
           }
           KeyNavigation.up: choose3
           KeyNavigation.down: choose1
        }
    }
}

6.CheckBox
多选。
用CheckBoxStyle设置样式。

7.GroupBox
多选,外面加个边框。
title:标题
checkable:true 标题可选
flat:true 去掉边框

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
       anchors.fill: parent
       color:"white"

       Rectangle{
           id:resultHolder
           color:"#a0a0a0"
           width:200
           height:100
           anchors.centerIn: parent
           visible: false
           z:2
           opacity: 0.8
           border.width: 2
           border.color: "#808080"
           radius: 8
           Text{
               id:result
               anchors.fill: parent
               anchors.margins:5
               font.pointSize: 16
               color:"blue"
               font.bold: true
               wrapMode: Text.WordWrap
           }
       }
       Component{
           id:checkStyle
           CheckBoxStyle{
               indicator: Rectangle{
                   implicitHeight: 14
                   implicitWidth: 14
                   border.color: control.hovered ? "darkblue" : "gray"
                   border.width:1
                   Canvas{  //画布,详见下一章
                       anchors.fill: parent
                       anchors.margins: 3
                       visible:control.checked
                       onPaint:{
                           var ctx=getContext("2d");
                           ctx.save();
                           ctx.strokeStyle="#C00020"
                           ctx.lineWidth=2;
                           ctx.beginPath();
                           ctx.moveTo(0,0);
                           ctx.lineTo(width,height);
                           ctx.moveTo(0,height);
                           ctx.lineTo(width,0);
                           ctx.stroke();
                           ctx.restore();
                       }
                   }
               }
               label: Text{
                   color:control.checked?"blue":"black"
                   text:control.text
               }
           }
       }
       GroupBox{
         id:groupbox
         checkable: true
         title:"请选择你喜欢的文字游戏(们):"
         anchors.top:parent.top
         anchors.topMargin:8
         anchors.left: parent.left
         anchors.leftMargin: 20
         width:280
         height:160
         Column{
           id:games
           anchors.horizontalCenter: parent.horizontalCenter
           anchors.top: parent.top
           anchors.topMargin: 5
           spacing: 8
           CheckBox{
               text:"潜伏之赤途"
               style:checkStyle
               onClicked: resultHolder.visible = false;
           }

           CheckBox{
               text:"中心少女"
               style:checkStyle
               onClicked: resultHolder.visible = false;
           }

           CheckBox{
               text:"刻痕"
               style:checkStyle
               onClicked: resultHolder.visible = false;
           }
           CheckBox{
               id:checkbox4
               text:"恒水中学连环虐杀"
               style:checkStyle
               onClicked: resultHolder.visible = false;
           }
        }
           Button{
               id:confirm
               anchors.bottom: parent.bottom
               anchors.horizontalCenter: parent.horizontalCenter
               text:"确定"
               onClicked: {
                   var str=new Array();
                   var index=0;
                   var count=games.children.length;
                   for(var i=0;i<count;i++){
                       if(games.children[i].checked){
                           str[index++]=games.children[i].text;
                       }
                   }
                   if(index>0){
                       result.text=str.join();
                       resultHolder.visible=true;
                   }
               }
           }
       }
    }
}

8.ComboBox
下拉菜单。
用model:[字符串数组] 表示下拉选择的内容。
currentText表示当前选中的内容,currentIndex表示其数组序号。

9.ProgressBar
进度条。
minimumValue:最小值
maximumValue:最大值
value:当前值
orientation:Qt.Horizontal水平方向,Qt.Vertical垂直方向。
给进度条加百分比:不能只重写ProgressBarStyle里面的panel,否则进度条无法显示!必须重新载入background和progress。

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
       anchors.fill: parent
       color:"white"
       ProgressBar{
           id:bar
           minimumValue: 0
           maximumValue: 100
           value: 0
           width: 150
           height: 20
           z:1
           anchors.centerIn: parent
           style: ProgressBarStyle{
               id:progressbarstyle
               background: Rectangle{
                   border.width: 1
                   border.color: control.hovered? "green":"gray"
                   color:"lightgray"
               }
               progress: Rectangle{
                   color:"#208020"
               }
               panel: Item{
                   implicitWidth: 200
                   implicitHeight: 20

                   Loader{
                       anchors.fill: parent
                       sourceComponent: background
                   }
                   Loader{
                       id:progressLoader
                       anchors.top:parent.top
                       anchors.left: parent.left
                       anchors.bottom: parent.bottom
                       anchors.margins: 3
                       z:1
                       width:currentProgress*(parent.width-6)
                       sourceComponent:progressbarstyle.progress
                   }

                   Text{
                       color:"blue"
                       text:currentProgress*100+"%"
                       z:2
                       anchors.centerIn: parent
                   }
               }
           }

           Timer{
               interval:1000
               repeat: true
               running: true
               onTriggered: {
                   if(parent.value<99.9){
                       parent.value+=10;
                   }
                   else{
                       restart();
                       parent.value=0;
                   }
               }
           }
       }
    }
}

10.TabView
标签页。
count:标签页的总数。
currentIndex:标签页的序号,从0开始。
tabVisible:默认在顶部,Qt.BottomEdge在底部。
addTab(title,component):增添一个以title为标题,以component控件为内容的标签页。

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    height:640
    width:480
    visible:true

    Rectangle{
       anchors.fill: parent
       color:"white"
       Component{
         id:tabContent
         Rectangle{
            implicitWidth: 100
            implicitHeight: 100
            anchors.fill: parent
            color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
         }
       }
       TabView{
           id:tabView
           anchors.top: parent.top
           anchors.margins: 8
           anchors.left: parent.left
           anchors.right: parent.right
           anchors.bottom: parent.bottom
           tabPosition: Qt.BottomEdge
           Component.onCompleted: {
               for(var i=0;i<5;i++){
                   tabView.addTab("tab-"+i,tabContent);
               }
           }
       }
    }
}

11.Slider
滑块。
minimumValue:设置最小值,默认为0。
maximumValue:设置最大值,默认为1。
value:当前值,默认为0
onValueChanged:当前值改变时,处理信号。
stepSize:步长。

       Column{
           width:200
           spacing:16
           Text{
               id:sliderStat
               color:"blue"
               text:"current-0.1"
           }
           Slider{
               width:200
               height:30
               stepSize:0.01
               value:0.1
               onValueChanged: {
                   sliderStat.text="current-"+value;
               }
           }
       }
    }

12.Flickable
可拖拽组件。

Window {
    height:200
    width:200
    visible:true
    visibility:Window.Maximized
    Flickable {
        anchors.fill: parent
        contentWidth: image.width; contentHeight: image.height
        Image { id: image; source: "file:///C:/Users/doujihu/Pictures/water.jpg" }
    }
}

七、Canvas

画布。

    Canvas{
        anchors.fill: parent
        onPaint:{
            var ctx=getContext("2d");
        }
    }

lineWidth:画笔粗细
strokeStyle:画笔颜色
fillStyle:填充颜色
beginPath():开始绘制路径。
moveTo(x,y):将路径点从纸上拿起来,移动到(x,y)。
lineTo(x,y):从当前点到(x,y)连一条直线路径,但是不涂色。
closePath():将路径终点与起点相连,绘制一条封闭路径。
stroke():将当前路径用strokeStyle涂色。
fill():若当前路径闭合,则用fillStyle填充颜色。
rect(x1,x2,y1,y2):绘制矩形路径。
arc(x,y,r,start,end,anticlockwise=1):以(x,y)为圆心,r为半径,从start到end画弧路径,anticlockwise表示是否逆时针。
createLinearGradient(x1,y1,x2,y2):返回一个从(x1,y1)到(x2,y2)的线性渐变对象gradient。用gradient.addColorStop(0~1,颜色)来设置渐变色的边界点。

Window {
    width:400
    height:240
    visible:true
    Canvas{
        anchors.fill: parent
        onPaint:{
            var ctx=getContext("2d");
            ctx.lineWidth=2;
            ctx.strokeStyle="red";
            var gradient=ctx.createLinearGradient(60,50,180,130);
            gradient.addColorStop(0.0,Qt.rgba(1,0,0,1));
            gradient.addColorStop(1.0,Qt.rgba(0,0,0,1));
            ctx.fillStyle=gradient;
            ctx.beginPath();
            ctx.arc(100,100,50,0,2*Math.PI,1);
            ctx.fill();
            ctx.stroke();
        }
    }
}

绘制文字:
text("string",x,y):建立文字路径。
fillText(),strokeText():填充文字和描边。
设置字体:ctx.font="42px fantasy" 42像素大小,fantasy字体。

绘制图片:
drawImage(image,x,y):在(x,y)处绘制image(可以是Image元素或url)。
当图片加载完成时,才能绘制图片:(放在onPaint下面)

Component.onCompleted: loadImage(source);
onImageLoaded: requestPaint();

可以利用这个来实现快速切换图片。
注意要清空画布,否则原来的图片不会消失。

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    width:1024
    height:768
    visibility: Window.Maximized
    visible:true
    Canvas{
        id:root
        anchors.fill: parent
        property string source: "file:///C:/Users/doujihu/Desktop/其他/素材/1.jpg"
        onPaint:{
            var ctx=getContext("2d");
            ctx.save();
            ctx.clearRect(0,0,root.width,root.height);
            ctx.beginPath();
            ctx.translate(400,0);
            ctx.rotate(Math.PI/4);
            ctx.drawImage(source,0,0);
            ctx.restore();
        }
        Component.onCompleted: loadImage(source);
        onSourceChanged: loadImage(source);
        onImageLoaded: requestPaint();
        MouseArea{
            anchors.fill: parent
            onClicked:{
                root.source="file:///C:/Users/doujihu/Desktop/其他/素材/2.png"
            }
        }
    }
}

图形变换:
变换前要ctx.save(),变换后要ctx.restore()。
translate(x,y):将画布中心从(0,0)平移到(x,y)。
rotate(rad):顺时针旋转rad弧度。
scale(x,y):横向缩放x倍,纵向缩放y倍。
clip():裁剪已有路径的内部。
先绘制路径,再调用clip(),最后再调用drawImage()。

八、动画

1.PropertyAnimation
通过改变对象的property实现动画。
有3种方法:(1)单独声明一个PropertyAnimation对象,在信号处理时调用animation.start()。(2)on<Signal>:PropertyAnimation+大括号,表示收到信号时开始运行动画。(3)在目标对象内部用Animation on <property>+大括号:将PropertyAnimation与一个属性绑定,然后设置running:触发属性的条件。
targets:目标对象 可以是[obj1,obj2,...]的数组,也可以是一个对象。
properties:"width,height" 对象的属性,多个属性用逗号隔开
to:多个属性共用的同一个目标值。
duration:持续时间(ms)。

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    width:360
    height:240
    visible: true
    Rectangle{
        id:rootItem
        anchors.fill: parent
        color:"#EEEEEE"

        Rectangle{
            id:rect
            width:50
            height:150
            anchors.centerIn: parent
            color:"blue"

            MouseArea{
                id:mousearea
                anchors.fill: parent
                onClicked:{
                    animation.start();
                }
            }

            PropertyAnimation on width{
                id:animation
                to:150
                duration:1000
                running:false
            }
        }
    }
}

animation.running:true/animation.start() 开始运行动画
onStarted和onStopped为开始和结束信号,用一个var保存当前的状态,可以实现动画反复运行。

Window {
    width:360
    height:240
    visible: true
    Rectangle{
        id:rootItem
        anchors.fill: parent
        color:"#EEEEEE"

        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();
                    }
                }
            }
        }
    }
}

easing.type:动画速度变化模型。默认为Easing.Linear。
NumberAnimation,RotationAnimation,ColorAnimation为专用的动画处理器。

2.PathAnimation
让目标对象沿着一个既定的路径运动。
anchorPoint:"x,y" 对象初始位置
orientationEntryDuration:将对象调整到初始方向所用的时间
orientationExitDuration:将对象调整到设定的终止方向(endRotation)所用的时间
orientation:控制目标对象沿路径运动时的旋转策略。默认值为PathAnimation.Fixed保持物体方位不旋转。TopFirst表示目标顶部贴合路径。
path:Path对象。

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    width:400
    height:240
    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;
                    }
                }
            }
        }
    }
}

3.ParallelAnimation
并行播放动画。
loops:循环方式。数字表示循环几次,Animation.Infinite表示无限循环。
animation.pause()表示暂停,此时animation.paused置1,running置0。可以通过anim.resume()恢复。

               onClicked: {
                   if(anim.paused) anim.resume();
                   else if(anim.running) anim.pause();
                   else anim.start();
               }

4.SequentialAnimation
顺序播放动画。
5.State
把几个属性绑成一个状态。
states: [State1,State2,...]:状态列表。
State{name:"string",when:表达式,changes:[PropertyChanges,AnchorsChanges等]}
when:当表达式为true时,触发状态变化。也可以用XXX.state="name"。
explicit:true:只变化一次,false将changes绑定。
6.StateChangeScript和ScriptAction
当状态改变\调用start()时,执行script:后面的代码。
name:"name"表示script的名字,可以被ScriptAction对象引用。
这样虽然逻辑上更直接,但是没法插入动画。
利用状态转移修改属性:

Window {
    width:360
    height:240
    visible: true

    Rectangle{
        id:rootItem
        color:"#EEEEEE"
        anchors.fill: parent

        Rectangle{
            id:rect
            color:"blue"
            width:200
            height:200
            anchors.centerIn: parent

            MouseArea{
                id:mouseArea
                anchors.fill: parent
                onClicked:{
                    rect.state="resetWidth"
                }
            }

            states:[
                State{
                   name:"resetWidth"
                   StateChangeScript{
                       script:{
                           rect.color="red";
                           rect.width=rootItem.width;
                       }
                   }
                }
            ]
        }
    }
}

等效于:

Window {
    width:360
    height:240
    visible: true

    Rectangle{
        id:rootItem
        color:"#EEEEEE"
        anchors.fill: parent

        Rectangle{
            id:rect
            color:"blue"
            width:200
            height:200
            anchors.centerIn: parent

            MouseArea{
                id:mouseArea
                anchors.fill: parent
                onClicked:{
                    action1.start();
                }
            }
            ScriptAction{
                id:action1
                script:{
                    rect.color="red";
                    rect.width=rootItem.width;
                }
             }
          }
      }
 }

7.Transition
两个状态之间的过渡动画。
Item的transitions属性是个列表,保存Item定义的所有Transition。
scale:缩放系数。
与state对应的动画不用写target和from to。
对于并行动画,可以直接放在大括号内,对于顺序动画,必须加SequentitalAnimation并置reversible为true。

Window {
    width:360
    height:240
    visible: true

    Rectangle{
        id:rootItem
        color:"#EEEEEE"
        anchors.fill: parent
        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
                }
            }
            transitions: Transition {
                NumberAnimation{
                    property: "scale"
                    easing.type: Easing.InOutQuad
                    duration:1000
                }
                ColorAnimation { duration: 600 }
            }
        }
    }
}

8.Behavior
给property绑定动画,当property改变时自动播放动画。
一个Behavior内只能有一个顶层动画。

Window {
    width:360
    height:240
    visible: true

    Rectangle{
        id:rootItem
        color:"#EEEEEE"
        anchors.fill: parent
        Rectangle{
            id:rect
            color:"yellow"
            width:100
            height:100
            anchors.centerIn: parent
            MouseArea{
                id:mouseArea
                anchors.fill: parent
                onClicked:{
                    text1.opacity=0;
                    text1.text="bye!";
                }
            }
            Text{
                id:text1
                color:"blue"
                anchors.centerIn: parent
                font.bold: true
                font.pixelSize: 24
                text:"hello world!"
                Behavior on text{
                    PropertyAnimation{
                        duration:500
                    }
                }

                Behavior on opacity{
                    SequentialAnimation{
                    NumberAnimation{
                        id:anim1
                        from:1
                        to:0
                        duration:500
                    }
                    NumberAnimation{
                        id:anim2
                        from:0
                        to:1
                        duration:500
                    }
                  }
                }
            }
        }
    }
}

一个完整的对话框实现:

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    width:360
    height:240
    visible: true
    visibility: Window.Maximized
    Rectangle{
        id:rootItem
        color:"#EEEEEE"
        anchors.fill: parent

        Rectangle{
            id:rect
            color:Qt.rgba(0,0,1,0.2);
            width:parent.width
            height:parent.height/4
            anchors.bottom: parent.bottom
            property string str: "一个人的一生应该是这样度过的:当他回首往事的时候,他不会因为虚度年华而悔恨,也不会因为碌碌无为而羞耻;这样,在临死的时候,他就能够说:“我的整个生命和全部精力,都已经献给世界上最壮丽的事业——为人类的解放而斗争。”"
            property int index:0
            MouseArea{
                id:mouseArea
                anchors.fill: parent
                onClicked:{
                    if(rect.index<rect.str.length-1){
                        anim.start();
                    }
                }
                onDoubleClicked: {
                    var temp=rect.index;
                    rect.index=rect.str.length-1;
                    text1.text=rect.str;
                    anim.stop();
                }
            }
            Text{
                id:text1
                anchors.top:parent.top
                anchors.margins: 20
                anchors.left: parent.left
                height:parent.height-20
                width:parent.width-20
                opacity: 1
                color:"black"
                font.pixelSize: 24
                font.bold: true
                font.family: "楷体"

                wrapMode:Text.WrapAnywhere
                PropertyAnimation on text{
                    id:anim
                    to:text1.text+rect.str[rect.index]
                    duration:100
                    running:false
                    onStopped: {
                        if(rect.index<rect.str.length-1){
                            rect.index++;
                            anim.start();
                        }
                    }
                }
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容