Qt QML 入门 — 使用C++定义QML类型


注册C++类

注册可实例化的类型

如果一个C++类继承自QObject,如果需要在QML中使用创建对象,则需要注册为可实例化的QML类型。

使用qmlRegisterType()注册可实例化的QML类型,具体查看qmlRegisterType()的文档说明。

//Message.cpp
class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
    // ...
};

//main.cpp
#include 
...
qmlRegisterType("com.mycompany.messaging", 1, 0, "Message");
...

//aQmlFile.qml
import com.mycompany.messaging 1.0

Message {
    author: "Amelie"
    creationDate: new Date()
}

注册不实例化的QML类型

1. qmlRegisterType()不带参数 这种方式无法使用引用注册的类型,所以无法在QML中创建对象。

2. qmlRegisterInterface() 这种方式用于注册C++中的虚基类。

3. qmlRegisterUncreatableType()

4. qmlRegisterSingletonType() 这种方法可以注册一个能够在QML中使用的单例类型。

附带属性

在QML语法中有一个附带属性的概念。

这里使用C++自定义QML类型的时候,也可以定义附带属性。

核心的亮点就是

static *qmlAttachedProperties(QObject *object);

QML_DECLARE_TYPEINFO() 中声明 QML_HAS_ATTACHED_PROPERTIES标志

例如:

//Message.cpp
class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
    // ...
};


//MessageBoardAttachedType.cpp
class MessageBoardAttachedType : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool expired READ expired WRITE expired NOTIFY expiredChanged)
public:
    MessageBoardAttachedType(QObject *parent);
    bool expired() const;
    void setExpired(bool expired);
signals:
    void published();
    void expiredChanged();
};

//MessageBoard.cpp
class MessageBoard : public QObject
{
    Q_OBJECT
public:
    static MessageBoard *qmlAttachedProperties(QObject *object)
    {
        return new MessageBoardAttachedType(object);
    }
};
QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES)

//在QML中的使用
Message {
    author: "Amelie"
    creationDate: new Date()

    MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00")
    MessageBoard.onPublished: console.log("Message by", author, "has been
published!")
}

//main.cpp
...
Message *msg = someMessageInstance();
MessageBoardAttachedType *attached =
        qobject_cast(qmlAttachedPropertiesObject(msg));
qDebug() << "Value of MessageBoard.expired:" << attached->expired();
...

MessageBoard这个类中首先实现了static *qmlAttachedProperties(QObject *object),然后又用QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES)声明MessageBoard为附带属性。


水平有限,还请不吝指正!

文章属原创,转载请注明转自>>[Thuai’s blog][http://www.thuai.com]

文章链接>>http://www.thuai.com/archives/154

发表在 Qt、Qt QML | 发表回复

Qt QML — QML 和 JavaScript 整合

  • 在QML中使用JavaScript
    • pragam library
    • 导入JavaScript
    • 在JavaScript文件中引用其他JavaScript文件
  • 使用JavaScript动态创建QML object
  • JavaScript的运行环境

在QML中使用JavaScript

pragam library

C/C++中关键字#pragam,就是指定编译器执行一些特定动作的指令。这里也可以在JavaScript中使用这个关键字,告诉编译器生成一个shared library。

因为在QML component 中如果使用了JavaScript文件,则会每个component都会有 独立 JavaScript实例copy,但是如果我们不想每个Component都有独立的JavaScript实例呢?这怎么办呢?

这时候就需要在JavaScript文件中使用.pragam library告诉编译器,这个JavaScript文件是可以共享的。

导入JavaScript

同样也是使用import来导入JavaScript文件到QML文件中使用。

例如:

import "./myJs" as MyJs

这里我使用相对路径来指定js文件,也可以使用绝对路径来指定js文件。为什么这样写呢?其实这就是告诉编译器,需要使用到的qml文件和js文件的目录位置方便引用解析。如此,你可以将QML文件和js文件按照层级关系和不同的作用,分类放置不用放在和main.qml文件同级目录,从而有个清晰的项目文件结构。

在JavaScript文件中引用其他JavaScript文件

QtQuick1.x中是不支持,在js文件中引用另外的非无状态js文件的。

QtQuick2.0版本则支持在一个非无状态的js文件中引用另外一个无状态的js文件或者QML模块。

使用.pragam library定义无状态的JavaScript。

使用.imports则可以导入其他js文件和QML模块。

.import TypeNamespace MajorVersion.MinorVersion as Qualifier

使用Qt.include包含其他非无状态的js文件。文档中有这样一个例子,可以很明了的看出如何使用Qt.include

//main.qml
import QtQuick 2.0
import "script.js" as MyScript

Item {
    width: 100; height: 100

    MouseArea {
        anchors.fill: parent
        onClicked: {
            MyScript.showCalculations(10)
            console.log("Call factorial() from QML:",
                MyScript.factorial(10))
        }
    }
}

// script.js
Qt.include("factorial.js")

function showCalculations(value) {
    console.log("Call factorial() from script.js:",
        factorial(value));
}

// factorial.js
function factorial(a) {
    a = parseInt(a);
    if (a <= 0)
        return 1;
    else
        return a * factorial(a - 1);
}

script.js中使用Qt.include来包含factorial.js,而在main.qml文件中只是使用了一个qualifier MyScript就访问了两个js文中定义的function。

使用JavaScript动态创建QML object

动态创建QML object 有两种方式:

  1. 使用Qt.createComponent()

  2. 使用Qt.createQmlObject()

如果你自定义了一个QML组件,则使用Qt.createComponent()动态创建对象会方便一些。方便重用自定义component。

可以参看我的这篇Qt QML– 动态创建QML objects和销毁

JavaScript的运行环境

QML引擎提供的JavaScript运行环境和浏览器提供的JavaScript运行环境是不一样的。QML的运行环境实现了”ECMAScript Language Specification”的标准,支持ECMAScript标准的内建类型和函数,如Object,Array,Math等。关于详细的ECMAScript标准有哪些内建类型,可以参看ECMA-262 第5版的文档。


水平有限,还请不吝指正,谢谢!

文章属原创,转载请注明转自>>Thuai’s blog

文章链接>>http://www.thuai.com/archives/118

发表在 Qt、Qt QML | 标签有 QML、Qt | 发表回复

Qt QML — 调试QML程序

  • 使用Console调试
    • Log
    • Assert
    • Timer
    • Trace
    • Count
    • Profile
    • Exception
  • 包含模块的调试

使用Console调试

Log

console.log 打印日志信息

console.debug 打印调试信息

console.info 打印普通信息

console.warn 打印警告信息

console.error 打印错误信息

Assert

就像C++就的assert,判断表达式是否成立。QML中的console.assert在表达式不成立时并不会终止程序,而是会打印出错误的代码位置。

console.assert 断言

Timer

console.time 和 console.timeEnd 用来查看代码运行所花费的时间。

Trace

在JavaScript的函数中加入console.trace()就可以跟踪代码的调用过程,但是只能够跟踪最后的10次调用。

Count

console.count 会打印出代码调用的次数

Profile

JavaScript 函数的性能分析, console.profile 开始,console.profileEnd 结束。

Exception

console.exception 打印出错误信息和堆栈调用信息。


包含模块的调试 (Debugging module imports)

添加环境变量QML_IMPORT_TRACE=1,可以查看import的module是否正确。然后在终端中使用qmlscene命令运行指定的qml文件,终端就会输出import信息。


水平有限,还请不吝指正,谢谢!

原创文章,转载请注明转自>>Thuai’s blog

文章链接>>http://www.thuai.com/archives/123

发表在 Qt、Qt QML | 标签有 QML、Qt | 发表回复

Ubuntu12.10 中QtCreator无法显示示例的解决方法

os版本: Ubuntu12.10_x64 QtCreator版本: 2.6.1 Qt版本:5.0.1

现象:

在打开了QtCreator之后,在欢迎页中示例,无法显示出来,一个也没有。

进入Qt5.0.1的安装目录***\/Qt5.0.1\/5.0.1\/gcc_64\/查看,发现examples目录的权限为700,所属用户为root,所在组为root,而我使用的非root用户登陆的系统,当然在QtCreator由于权限问题,无法访问examples目录了。

解决方法:

更改权限为744即可。

只需要更改权限就可以解决问题了,QtCreator重新启动,在欢迎页中就可以显示所有示例了。

因为我的Qt5.0.1是安装在/opt目录下的,所以运行示例的时候,会提示复制到可写位置。选择一个可写的目录,然后复制打开即可。


水平有限,请不吝指正,谢谢!

文章属原创,转载请注明转自>>Thuai’s blog

文章链接>>http://www.thuai.com/archives/125

发表在 Qt、QtCreator | 标签有 Qt、QtCreator | 发表回复

Qt Quick 2 — QML类型(QML Types)

  • 可视类型
  • 可视的实用功能项
  • 可视项的生成器
  • 可视项的变换
  • 获取用户输入
  • 文本输入的实用工具项
  • 用户输入事件
  • 位置
  • 状态
  • 转变和动画
  • 与类型相关的动画
  • 底层动画
  • 路径动画
  • 数据模型
  • 视图
  • 数据存储
  • 图形效果
  • 实用方便的类型
  • 画布

注意: 末尾写了一个2的,表示QtQuick2中才出现的。对于部分新出现的一些类型,我自己也不怎么熟悉,所以暂时不写中文,免得误人子弟就不好了。以后我会再次更新这篇文章。

可视类型 (visual types)

  • Item — QML 基本的试图类型,其他可视类型都是从Item继承来的
  • Rectangle — 矩形区域
  • Image — 图片
  • BorderImage — 边框背景
  • AnimatedImage — 播放一张GIF图片
  • AnimatedSprite — 播放一系列帧动画 2
  • SpriteSequence — 播放一系列帧动画中的部分帧 2
  • Text — 显示文本
  • Window — 显示一个顶层窗口 2

可视的实用功能项 (Visual Item Utility)

  • Accessible — 提供Component的获取性 2
  • Gradient — 渐变
  • GradientStop — 渐变阈值
  • SystemPalette — 系统调色板
  • Screen — 获取设备的屏幕宽高横向参数 2
  • Sprite — 显示特定的Sprite动画 2
  • FontLoader — 字体加载器

可视项的生成器 (Visual Item Generation)

  • Repeater — 能够根据model生成多个可视化的项
  • Loader — QML component动态加载器

可视项的变换 (Visual Item Transformations)

  • Transform — 变形 2
  • Scale — 缩放
  • Rotation — 旋转
  • Translate — 平移

获取用户输入 (User Input)

  • MouseArea — 鼠标区域
  • Keys — 按键
  • KeyNavigation — 导航键 2
  • FocusScope — 焦点区域
  • Flickable — 橡皮筋区域
  • PinchArea — 捏拽区域
  • MultiPointTouchArea — 多点触控区域 2
  • Drag –拖动区域 2
  • DropArea — 掉落区域 2
  • TextInput — 文本输入区域
  • TextEdit — 文本编辑区域

文本输入的实用工具项 (Text Input Utility)

  • IntValidator — 整数校验器
  • DoubleValidator — 双精度浮点校验器
  • RegExpValidator — 正则表达式校验器

用户输入事件 (User input events)

  • TouchPoint — 触摸点击事件 2
  • PinchEvent — 捏拽事件
  • WheelEvent — 鼠标滚轮事件 2
  • MouseEvent — 鼠标点击事件
  • KeyEvent — 按键事件
  • DragEvent — 拖动事件 2

位置 (Positioning)

  • Positioner — Item在scene中的位置信息(附带属性) 2
  • Column — 子对象竖直方向排列
  • Row — 子对象水平方向排列
  • Grid — 子对象按照网格形式排列
  • Flow — 流动
  • LayoutMirroring — 布局镜像(附带属性) 2

状态 (States)

  • State — 状态
  • PropertyChanges — 属性变更
  • StateGroup — 状态组
  • StateChangeScript — 状态变更脚本
  • ParentChange — 父对象变更
  • AnchorChanges — 锚点变更

转变和动画 (Transitions and Animations)

  • Transition — 转变动画
  • ViewTransition — 视图转变 2
  • SequentialAnimation — 串行动画序列
  • ParallelAnimation — 并行动画序列
  • Behavior — 特定属性变化时的行为
  • PropertyAction — 在动画序列中执行属性变更动作
  • SmoothedAnimation — 属性值平滑变化动画
  • SpringAnimation — 弹力动画
  • ScriptAction — 在动画序列中执行脚本(主要用于动画中执行脚本)

与类型相关的动画 (Type-specific Animations)

  • PropertyAnimation — 属性动画
  • NumberAniamtion — 数值动画
  • Vector3dAnimation — Vector3d属性的动画
  • ColorAnimation — color属性的动画
  • RotationAnimation — rotation属性的动画
  • ParentAnimation — parent属性动画
  • AnchorAnimation — anchor属性动画
  • PathAnimation — path动画 2

底层动画 (Lower-level Animation Types)

  • PathInterpolator — path修改器 2
  • AnimationController — 动画控制器 2

路径动画 (Animation paths)

  • Path — 路径
  • PathLine — 路径为直线
  • PathQuad — 路径为二次方程式贝尔曲线
  • PathCubic — 路径为三次方程式贝尔曲线
  • PathArc — 路径为弧线 2
  • PathCurve — 路径为曲线 2
  • PathSvg — SVG 路径 2
  • PathAttribute — 在path中设置属性
  • PathPercent — 修改path中item的间距

数据模型 (Model and Model Data)

  • ListModel
  • ListElement
  • VisualItemModel
  • VisualDataModel
  • VisualDataGroup
  • XmlListModel
  • XmlRole

视图 (Views)

  • ListView
  • GridView
  • PathView
  • Pack age

数据存储 (Data Storage)

  • QtQuick.LocalStorage 2 — 本地存储模块 2

图形效果 (Graphical Effects)

  • Flipable
  • ShaderEffect 2
  • ShaderEffectSource
  • GridMesh 2
  • QtQuick.Particles 2 2

实用方便的类型 (Convenience Types)

  • Connections
  • Binding — 绑定器
  • Timer — 定时器
  • WorkScript

画布 (Canvas)

  • Canvas 2
  • Context2D 2
  • CanvasGradient 2
  • CanvasPixelArray 2
  • CanvasImageData 2
  • TextMetrics 2

水平有限,还请不吝指正,谢谢!


原创文章,转载请注明 >> Thuai’s blog

文章链接 >>http://www.thuai.com/archives/122

发表在 Qt、Qt QML、Qt Quick | 标签有 QML | 发表回复

Qt QML– 动态创建QML objects和销毁

  • 动态创建Qml对象

    • Qt.createComponent
    • Qt.createQmlObject
  • 维护动态创建的Qml对象

  • 动态销毁对象

动态创建Qml对象

使用JavaScript动态创建Qml对象有两种方式,一是使用Qt.createComponent,二是使用Qt.createQmlObject

如果你已经有一个qml文件定义了一个component,则使用Qt.createComponent()这种方式会比较好。如果本身对象就是在Qml运行期生成的对象且简单,则使用Qt.createQmlObject(),你肯定不想在一条语句中写上百来行代码定义一个component对吧!

Qt.createComponent

先看代码

//Sprite.qml
import QtQuick 2.0

Rectangle { width: 80; height: 50; color: "red" }

//main.qml
import QtQuick 2.0
import "componentCreation.js" as MyScript

Rectangle {
    id: appWindow
    width: 300; height: 300

    Component.onCompleted: MyScript.createSpriteObjects();
}

//componentCreation.js
var component;
var sprite;

function createSpriteObjects() {
    component = Qt.createComponent("Sprite.qml");
    if (component.status == Component.Ready)
        finishCreation();
    else
        component.statusChanged.connect(finishCreation);
}

function finishCreation() {
    if (component.status == Component.Ready) {
        sprite = component.createObject(appWindow, {"x": 100, "y": 100});
        if (sprite == null) {
            // Error Handling
            console.log("Error creating object");
        }
    } else if (component.status == Component.Error) {
        // Error Handling
        console.log("Error loading component:", component.errorString());
    }
}

从上面的代码看出,先自定义了一个qml文件Sprite.qml,这个qml文件中有个宽80、高50、颜色为红色的Rectangle组件。在main.qml首先是导入了componentCreation.js这个JavaScript脚本,并去了别名MyScript。rootObject则是一个宽300、高300、id为appWindow的Rectangle组件。当这个Rectanle类型的组件加载完成的时候就会调用componentCreation.js中的createSpriteObjects()方法动态创建对象。

核心就是createSpriteObjects()这个方法了。

可以看出使用Qt.createComponet()这种方法动态创建qml对象,需要两个步骤:1.使用Qt.createComponent(url,parent, mode). url即为自定义好的需要创建qml对象的qml文件。parent和mode通常可以不写。具体可以查看文档中Qt.createComponent()的详细说明。因为Qt.createCompoent()成功则会返回一个Component类型的对象,所以需要第二步。2.使用createObject()创建实例,createObject()方法是Component类型的一个方法。createObject()返回的才是Script.qml的实例。

在上面的这段代码中,还可以看到,component.statusChanged这个signal连接了一个createScriptObjects.js中的另外一个方法,通过component.status和Component组件中的不同枚举值对比,进行错误的判断和处理。

总结: Qt.createComponent()这个function是 Qt 这个Qml全局类型中的一个方法,这个方法返回一个Component类型的对象。需要得到自定义qml组件的实例,还需要使用Component.createObject()方法。如果需要使用非阻塞方式创建qml object则可以使用incubateObject()方法。

Qt.createQmlObject()

如果需要在qml运行期创建一个qml对象且这个对象定义并不复杂时,则使用Qt.createQmlObject()这种方法比较好。

import QtQuick 2.0

Rectangle {
    id: rect
    width: 360
    height: 360
    Text {
        anchors.centerIn: parent
        text: "Hello World"
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            createObject();
        }
    }

    function createObject()
    {
        //第一种写法
        /*
        var object = Qt.createQmlObject('import QtQuick 2.0;' +
                                        'Rectangle {' +
                                        'width:30; ' +
                                        'height:30;' +
                                        'colo: "red"}', rect, "error");
        */

        //第二种写法
        //var newObject = Qt.createQmlObject('import QtQuick 2.0; ' + 'Rectangle { width: 20; height: 20; colo: "red"}', rect, "dynamicSnippet1");

        //第三种写法
        var newObject = Qt.createQmlObject('import QtQuick 2.0; \n Rectangle {width: 20; height: 20; colo: "red"}', rect, "error3");
   }
}

从上面的代码可以看出Qt.createQmlObject()有三个参数,第一个为定义qml对象的字符串。第二个为创建的qml对象的父对象。第三个则为出错时候的文件路径提示。并且提示会出现行数和列数,可以分别试验三种写法看错误提示有何不同。

维护动态创建的Qml对象

动态创建Qml对象,你必须知道它所存在的context的生命周期,必须保证context的生命周期比动态创建的Qml对象生命周期要长,因为context销毁了,其中绑定的动态创建的Qml对象也会失效。动态创建的Qml对象的context取决于它创建的方式。

  • Qt.createComponet(),使用这种方法,则context就是调用这个Qt.createComponent方法的QQmlContext。QQmlContext跟Qt4.8.x中的QDecalartiveContext()其实差不多。

  • Qt.createQmlObject(),使用这种方法,则context就是这个方法的的parent参数。

  • 如果使用Component{}定义一个对象,然后在这个Component对象中调用createObject()或者incubateObject()方法,这时动态创建出的对象的context就是这个Component的context。

动态销毁对象

使用Object.destroy()方法删除对象。

在多数情况下,你可以使用改变可视对象的opacity值为0或者将它移出屏幕就可以了,而不是delete它。如果你有很多动态创建的对象,delete那些不用的对象,则是值得做的事情。

记住,不要去删除不是你手动创建的对象,例如Repeater和Loader创建的对象。

例如下面这段代码中SelfDestroyingRect就不能使用destroy来销毁它。

Item {
    SelfDestroyingRect {
        // ...
    }
}

水平有限,还请不吝指正,谢谢!

文章属原创,转载请注明转自>>Thuai’s blog

文章链接>>http://www.thuai.com/archives/119

发表在 Qt、Qt QML | 发表回复

Qt QML—QML signal与signal handler系统

QML 的signal 和 signal handler机制的存在,是为了应用能够和UI组件之间相互交互。signal就是button clicked、mouse area pressed这些event,signal handler则就是这些事件的响应。

当一个signal emitted,相应的signal handler就会被调用,在signal handler中执行一些scripts或是其他操作,已完成event的响应。

signal和signal handler的定义

signal

signal [([ [, ...]])]

signal handler

on

可以参考另一篇文章 Qt QML入门– QML 语法 (2)

Property Change Signal Handler

当Property的值改变了,就会自动emitted一个signal,这种property changed signal,有property Signal handler与之对应。你只需要实现signal handler即可。

onChanged

使用Connections Type

很多人都知道Qt一个很好的东西,就是signalslot机制。你可以很方便的connect的signal和slot。QML中的Connections,它能够让你接收指定object的任意signal,从而能够在signal声明的那个Object之外接收到声明的signal,从而实现自己想要logic。

import QtQuick 2.0

Rectangle {
    id: rect
    width: 100; height: 100

    MouseArea {
        id: mouseArea
        anchors.fill: parent
    }

    Connections {
        target: mouseArea
        onClicked: {
            rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
        }
    }
}

上面的代码中,Connections中的target绑定的是ouseArea,从而在rect中能够接收到mouseArea的signal,如clicked、pressed、released等等signal。

附带的signal handler

附带的signal handler是附带组件的signal与之对应的signal handler,并不是使用附带组件本身Object的signal的signal handler。

可以参看另一篇文章 Qt QML入门– QML 语法 (2)

signal与signal的连接,signal与method的连接

signal 都有一个connect()方法,可以连接method或者signal。

可以看下面两段代码

signal connect signal

Rectangle {
    id: forwarder
    width: 100; height: 100

    signal send()
    onSend: console.log("Send clicked")

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

    Component.onCompleted: {
        mousearea.clicked.connect(send)
    }
}

注意:mousearea.clicked.connect(send),这里send是signal但是却没有加或括号。emitted signal的时候则不论是否signal声明的时候有无括号,都须要加上括号。

signal connect method

Rectangle {
    id: relay

    signal messageReceived(string person, string notice)

    Component.onCompleted: {
        relay.messageReceived.connect(sendToPost)
        relay.messageReceived.connect(sendToTelegraph)
        relay.messageReceived.connect(sendToEmail)
    }

    function sendToPost(person, notice) {
        console.log("Sending to post: " + person + ", " + notice)
    }
    function sendToTelegraph(person, notice) {
        console.log("Sending to telegraph: " + person + ", " + notice)
    }
    function sendToEmail(person, notice) {
        console.log("Sending to email: " + person + ", " + notice)
    }
}

注意:这里使用connect连接是的method,同样没有加上括号。

既然能够connect,那有没有disconnect呢?当然有。

有时候,你不使用disconnect,你某些动态create的对象都无法distroy。

Rectangle {
    id: relay
    //...

    function removeTelegraphSignal() {
        relay.messageReceived.disconnect(sendToTelegraph)
    }
}

disconnect跟connect就只是一个单词差别而已,没有其他特别不同的地方。


水平有限,还请不吝指正,谢谢!


原创文章,转载请注明 >> Thuai’s blog

文章链接 >>http://www.thuai.com/archives/116

发表在 Qt、Qt QML | 发表回复

Qt QML—QML 属性的绑定(Property Binding)

QML 属性绑定 (Property Binding)

属性的绑定能够更好的使用QML的特性-QML object动态行为变化的自动响应。这是QML一个很重要的特性。

注意:绑定的一个很重要的标志就是“:”–冒号

当QML object 的属性既可以分配一个static value,也可以绑定一个JavaScript表达式,也可以使用JavaScript的自带的Date Math这些对象。因为QML uses a standards compliant JavaScript engine。

Rectangle {
    id: parentRect
    width: 200; height: 200

    Rectangle {
        id: childRect
        width: 100; height: parent.height
        color: "blue"
    }
}

上面的代码中,childRect的height绑定到了parent的height,当parentRect的height改变的时候,QML engine会重新计算childRect.height

import QtQuick 2.0

Rectangle {
    width: 100
    height: width * 2
    focus: true
    Keys.onSpacePressed: {
        height = width * 3
    }
}

在看上面的这段代码,Rectangle的height首先绑定了width2,然后在Key.onSpacePressed这个附带的signal handler中,height 被赋值width3,注意是赋值,不是绑定。

所以之前的bind被这个赋值语句移除了,也就是说以后Rectangle的width变化了,height不会自动变成width的2倍。

如果是想要重新binding而不是赋值,则需要使用Qt.binding()。

this的使用

使用JavaScript绑定时,QML允许使用Javascript中this关键字。

例如下面的代码:

Item {
    width: 500
    height: 500

    Rectangle {
        id: rect
        width: 100
        color: "yellow"
    }

    Component.onCompleted: {
        rect.height = Qt.binding(function() { return this.width * 2 })
        console.log("rect.height = " + rect.height) // prints 200, not 1000
    }
}

rect.heigth属性的绑定使用了JavaScript的语句,所以可以使用this,这里的this表示的是Item而不是rect


水平有限,还请不吝指正,谢谢!


原创文章,转载请注明 >> Thuai’s blog

文章链接 >>http://www.thuai.com/archives/115

发表在 Qt、Qt QML | 发表回复

Qt QML入门– QML 语法 (2)

  • QML 对象的属性
    • id
    • Property Attributes
      • 为Property Attributes赋值
      • 属性组
      • 属性别名
      • Property Modifier Objects
    • 信号
      • Property Change Signal Handlers
    • 方法
    • 附带的属性和附带的信号处理函数

QML 对象的属性

一个QML对象所具有的属性有以下几种:

  • id -id 标识
  • property attributes –属性(其中包括继承自Item的attributes,自定义的attributes)
  • signal attributes –信号
  • signal handler attributes –信号处理
  • method attributes –函数
  • attached propeties and attached signal handler attributes –附带的属性 和 附带的signal handler

id

id 用来标识QML对象,id不能够以大写字母开头,同样method也不能够以大写字母开头。请记住这点,不然就会有“xxx cannot begin with an upper case letter”这样的error。 有了id你就能够通过id引用id为xxx的对象了。所以尽量的给你的每个QML object都写上id吧! QML object 一旦实例化,id值就无法改变。


Property Attributes

自定义属性的语法格式:

[default] property

例如:

property int myAge 25;
default property string myName thuai

//MyCustomRectangle.qml
property MyCustomRectangle myCusRect
property color myFavoriteColor: "blue"

default关键字表示,该QML object有个默认属性,你使用这个属性的时候不需要显式的声明。

例如:

// MyLabel.qml
import QtQuick 2.0

Text {
    default property var someText

    text: "Hello, " + someText.text
}

上面的MyLabel类型中中有个default属性someText,所以在MyLable使用的时候,

MyLabel {
    Text { text: "world!" }
}

//和这段代码是等效的
MyLable {
    someText: Text { text: "world!" }
}

这就是default property

单行语句的分号是可写不用写的。但是一行写多条语句,语句则必须要用分号分隔!

propertyType 可以是QML的基本类型,一种QML对象类型,也可以是QML可以是C++通过Q_PROPERTY宏注册的类,还可以是JavaScript中的var,var它可以表示任何类型的数据,还可以是自定义的QML类型,如MyCustomRectangle

为Property Attributes赋值

  • 一是初始化的时候赋值

    import QtQuick 2.0 Item{ x: 10 y: 10 width: 100 height: 100 }

  • 一是使用的时候赋值。

    import QtQuick 2.0 Rectangle { id: rect Component.OnCompleted: { rect.color = “blue” } }

属性组(Grouped Properties)

将相同的属性写成一句代码 例如:

Text {
    //dot notation
    font.pixelSize: 12
    font.b: true
}

Text {
    //group notation
    font { pixelSize: 12; b: true }
}

font.pixelSize 和 font.blod用括号括起来写成了一句,注意 pixelSize和 b之间有个 ;

属性别名(Property Aliases)

格式: [default] property alias :

// Button.qml
import QtQuick 2.0

Rectangle {
    property alias buttonText: textItem.text

    width: 100; height: 30; color: "yellow"

    Text { id: textItem }
}

Button中的属性buttonText是textItem.text属性的别名

Button { buttonText: "Click Me" }

所以对Button的buttonText属性赋值就相当于给Text的text属性赋值

注意: Property Aliases 必须在组件所有初始化工作都完成之后,才能够使用,否则会出错。

import QtQuick 2.0

Rectangle {
    width: 360
    height: 360

    property alias aText: te

    //error before Rectangle initialized completed
    //aText: "init text"

    Component.onCompleted: {
        aText.text = "init text onCompleted"
    }

    Text {
        id: te
        anchors.centerIn: parent
    }
}

如果你在component初始化完成,对alias properties赋初始值,QtCreator会报“Cannot assign to non-existent property “aText”错误!

这里又看到了一个有意思的东西。Component.onCompleted,这是一个QtQuick所有的Object都有的一个附带信号处理函数。组件初始化创建完成就会触发这个处理函数。后面在关于signal的文章中有更详细的讲解。

Property Modifier Objects

 on  {
    // attributes of the object instance
}

这里Property Modifier Objects我没有用中文,因为我也不知道该译成什么。具体后面的例子中大家可以看到Property Modifier Objects的应用。如下面代码中的NumberAnimation

import QtQuick 2.0

Rectangle {
    width: 360
    height: 360

    Rectangle {
        id: moveRect
        width: 50
        height: 50
        radius: 5
        color: "red"

        NumberAnimation on x {
            from: 0
            to: 100
            duration: 200
        }
    }
}

信号(Signal Attributes)

信号属性定义语法:

signal [([ [, ...]])]

[]表示可选

例如:

//不带参数和括号
signal mySignal

//带空括号
signal mySignal2()

//signal 信号名(参数类型 参数名, 参数a类型 参数a)
signal mySignal3(string name, var any)

对应的signal handler的语法格式则是:

on

如上例中的mySignal,对应的signal handler 就写成 onMySignal

注意: mySignal的第一个字母S成了大写的了。因为这里onMySignal,on作为开头了。所以按照QML的语法,mySignal中的m就要写成M, signal 还可以以下划线_开头, 比如_mySignal4,对应的signal handler则写成on_MySignal4,下划线的数目也可以不同。再比如__mySignal4和_mySignal4**就是两个不同的signal(前者一个下划线,后者两个下划线)

signal mySignal3的 signal handler:

onMySignal3: {
     console.debug("i'm a signal handler")
     console.debug("name"en);
}

QML有内建的属性signal,当属性的value变化的时候,就会emitted signal. 这个就是文档中所说的Property Changed Signal

import QtQuick 2.0

Item {
    width: 100; height: 100

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log("Click!")
        }
    }
}

MouseArea有clicked signal, 当鼠标点击MouseArea 的时候,就会emit clicked signal。 signal handler onClicked 就会触发,console 就会打印出“Click!“

Signal Handler是一种特殊的method属性。当你在QML中文件中声明一个singal的时候,QML会自动帮你关联一个signal handler,这个signal handler默认是没有实现的。所以你只需要实现这个signal handler就可以了,然后在emitted一个signal的时候,与之关联的signal handler就会自动的被QML引擎调用。

例如文档中的一个例子:

// SquareButton.qml
Rectangle {
    id: root

    signal activated(real xPosition, real yPosition)
    signal deactivated //注意我没有括号哦!

    width: 100; height: 100

    MouseArea {
        anchors.fill: parent
        onPressed: root.activated(mouse.x, mouse.y) //emit activated signal and parameter
        onRelased: root.deactivated() //emit deactivated signal 注意我有括号哦!
    }
}

// myapplication.qml
SquareButton {
    //implement onActivated signal
    onActivated: console.log("Activated at " + xPosition + "," + yPosition) 
    //implement onDeactivated signal
    onDeactivated: console.log("Deactivated!")
}

在SquareButton.qml中的MouseArea,pressed、released都emitted了一个signal。 由此可以看出QML中emitted一个signal,你只需要调用它就行了。

注意:在QML中声明无参signal你可以不带括号,但是emitted它的时候,就必须要带括号了。否则,它不会报错,但是它也不会emitted signal

而在Qt C++代码中你要想emittd一个signal,你就必须使用emit <定义的信号>,才能够emit一个signal。

在myapplication.qml中你使用SquareButton这个自定义的Component时,只需实现下onActivated、onDeactivated这两个signal handler就行,因为QML已经帮你declare并connected了。

signal不仅有signal handler,它还可以与后面讲到的method连接(connect),Qt 一个很重要的机制就是信号和槽机制,其实在QML中也有这个,只是叫法不一样而已。QML中所有的method都是slot。

Property Change Signal Handlers

语法格式:

onChanged

signal 有signal handler,property呢? property有property changed signal handler(属性变化信号处理方法,呵呵有点拗口,翻译水平不行,不纠结在这种意会的层次,能够理解就行),既然也是signal hanlder那就不需要你去declare它并关联到信号了。你也只需要使用它就行了。

例文档中的:

import QtQuick 2.0

TextInput {
    text: "Change this!"
    onTextChanged: console.log("Text has changed to:", text)
}

方法(Method Attributes)

QML定义一个method:

function ([[, ...]]){ }

QML中的method定义不像signal的定义,需要声明参数(parameter)的类型(type),QML中method的参数类型是var,也只能是var,不论它是在QML中定义的method还是C++中定义的指定参数的method然后注册到QML system中使用的。

对JavaScript熟悉的朋友,应该知道var这个关键字。(此处可以问歌哥或者度娘,建议歌哥,因为度娘都是copy的相似答案)

这是QML定义method的语法格式,C++中的method,可以用Q_INVOKABLE和 slots注册到QML系统中,这样QML中就可以访问你C++中写的method方法了。具体我会在后面关于C++与QML交互的文章中详细表述。

这里先暂不写JavaScript中的method如何在QML object中访问。先理解QML object定义的method如何正确使用,再拓展其他方法定义的method如何使用。

import QtQuick 2.0

Item {
    width: 200; height: 200

    MouseArea {
        id: msArea
        anchors.fill: parent
        onClicked: label.moveTo(mouse.x, mouse.y)
    }

    Text {
        id: label
        function moveTo(newX, newY) {
            label.x = newX;
            label.y = newY;
        }
        text: "Move me!"
    }
}

上面的代码中,id为label的Text中有个method moveTo(newX, newY) 更改label的x、y值,从而移动label,当点击Item中的任意一点,Item中MouseArea msArea自动emitted clicked signal, signal handler onClicked 自动调用label.moveTo()。

从这个例子,你应该能够理解了如何定义一个QML method以及如何调用一个QML method。所以我建议我同事都最好每个component都加上id,不要图少敲几个键,而让代码的可读性降低。

附带的属性和附带的信号处理函数 (Attached Properties and Attached Signal Handlers)

定义的语法格式:

.

.on

import QtQuick 2.0

ListView {
    width: 240; height: 320
    model: 3
    delegate: Rectangle {
        width: 100; height: 30
        color: ListView.isCurrentItem ? "red" : "yellow"
    }
}

在上面的代码中,isCurrentItem就是ListView的一个附带属性。ListView还有很多附带的属性,详见QtCreator中的帮助。

Component.onCompleted则是一个附带的Signal handler。



点击打开原文链接

你可能感兴趣的:(QML,Qt)