QML文档中的JavaScript表达式

The QML提供的JavaScript宿主环境可以运行有效的标准JavaScript结构,如条件运算符、数组、变量设置和循环。除了标准JavaScript属性之外,QML全局对象还包括一些辅助方法,这些方法简化了UI的构建和与QML环境的交互。

QML提供的JavaScript环境比网页浏览器中的更严格。例如,在QML中,您不能向或修改JavaScript全局对象的成员。在常规JavaScript中,通过使用未声明变量的方式,可能会意外地执行此操作。在QML中,这将抛出异常,因此所有局部变量都必须显式声明。有关从QML执行JavaScript代码的限制的完整描述,请参阅JavaScript环境限制

QML文档的各种部分可以包含JavaScript代码

  1. 属性绑定的主体。这些JavaScript表达式描述了QML对象属性之间的关系。当属性的依赖项发生变化时,根据指定的关系,属性也会自动更新。
  2. 信号处理程序的主体。每当QML对象发出关联的信号时,这些JavaScript语句将自动执行。
  3. 自定义方法的定义。定义在QML对象主体内的JavaScript函数成为该对象的方法。
  4. 独立的JavaScript资源(.js)文件。实际上,这些文件与QML文档是分开的,但它们可以导入到QML文档中。在其中定义的函数和变量可以在属性绑定、信号处理程序和自定义方法中使用。

属性绑定中的JavaScript

在以下示例中,Rectangle的color属性取决于TapHandler的pressed属性。此关系使用条件表达式描述

import QtQuick 2.12

Rectangle {
    id: colorbutton
    width: 200; height: 80;

    color: inputHandler.pressed ? "steelblue" : "lightsteelblue"

    TapHandler {
        id: inputHandler
    }
}

实际上,可以使用任何JavaScript表达式(无论多么复杂)作为属性绑定定义的一部分,只要表达式的结果是可赋给属性的值。这包括副作用。然而,由于它们可能会降低代码的性能、可读性和可维护性,因此不建议使用复杂的绑定和副作用。

定义属性绑定的有两种方式:最常见的一种在先前的示例中已展示,即在属性初始化时进行。第二种(且较为罕见)的方式是通过命令式JavaScript代码,从Qt.binding()函数返回的功能中分配属性,如下所示

import QtQuick 2.12

Rectangle {
    id: colorbutton
    width: 200; height: 80;

    color: "red"

    TapHandler {
        id: inputHandler
    }

    Component.onCompleted: {
        color = Qt.binding(function() { return inputHandler.pressed ? "steelblue" : "lightsteelblue" });
    }
}

请参阅属性绑定文档以了解如何定义属性绑定,以及参阅关于属性赋值与属性绑定的区别的文档以了解绑定与值赋值的区别。

信号处理程序中的JavaScript

QML对象类型可以响应某些事件的发生并发出信号。这些信号可以通过信号处理函数进行处理,由客户端定义以实现自定义程序逻辑。

假设一个由Rectangle类型表示的按钮有一个TapHandler和一个Text标签。《TapHandler》在用户按下按钮时发出它的tapped信号。客户端可以在onTapped处理程序中使用JavaScript表达式来响应这个信号。QML引擎需要执行时才会执行这些处理程序中定义的JavaScript表达式。通常,信号处理程序绑定到JavaScript表达式,以启动其他事件或赋值属性。

import QtQuick 2.12

Rectangle {
    id: button
    width: 200; height: 80; color: "lightsteelblue"

    TapHandler {
        id: inputHandler
        onTapped: {
            // arbitrary JavaScript expression
            console.log("Tapped!")
        }
    }

    Text {
        id: label
        anchors.centerIn: parent
        text: inputHandler.pressed ? "Pressed!" : "Press here!"
    }
}

有关信号和信号处理程序的更多详细信息,请参阅以下主题

独立函数中的JavaScript

程序逻辑也可以在JavaScript函数中定义。这些函数可以定义在QML文档中(作为自定义方法)或在外部导入的JavaScript文件中。

自定义方法中的JavaScript

可以在QML文档中定义自定义方法,并可以从信号处理程序、属性绑定或其他QML对象的函数中调用它们。这样的方法通常被称为内联JavaScript函数,因为它们的实现包含在QML对象类型定义(QML文档)中,而不是在外部JavaScript文件中。

以下是一个内联自定义方法的示例:

import QtQuick 2.12

Item {
    function fibonacci(n){
        var arr = [0, 1];
        for (var i = 2; i < n + 1; i++)
            arr.push(arr[i - 2] + arr[i -1]);

        return arr;
    }
    TapHandler {
        onTapped: console.log(fibonacci(10))
    }
}

TapHandler发出tapped信号时,将运行fibonacci函数。

注意:在QML文档中内联定义的自定义方法对其他对象是可访问的,因此QML组件根对象的内联函数可以被组件外的调用者调用。如果不希望这样,可以将该函数添加到非根对象中,或者最好将其写入外部JavaScript文件。

有关在QML中使用JavaScript定义自定义方法的更多信息,请参阅QML对象属性文档。

JavaScript文件中定义的函数

复杂程序逻辑最好分离到单独的JavaScript文件中。该文件可以通过import语句导入QML,就像QML的模块一样。

例如,前面示例中的fibonacci()方法可以移动到名为fib.js的外部文件中,并按以下方式访问:

import QtQuick 2.12
import "fib.js" as MathFunctions

Item {
    TapHandler {
        onTapped: console.log(MathFunctions.fibonacci(10))
    }
}

有关将外部JavaScript文件加载到QML中的更多信息,请阅读在QML中导入JavaScript资源部分。

将信号连接到JavaScript函数

发出信号的自定义类型也为其信号提供了默认信号处理程序,具体请参见上一节中关于
JavaScript 在信号处理程序中的作用。然而,有些情况下,客户端希望在另一个自定义类型发出信号时触发一个定义在 QML 类型中的函数。此类场景可以通过信号连接来解决。

可以通过调用信号对象的 connect() 方法并将 JavaScript 函数作为参数传递来将自定义类型发出的信号连接到 JavaScript 函数。例如,以下代码将 TapHandlertapped 信号连接到 script.js 中的 jsFunction()

import QtQuick
import "script.js" as MyScript

Item {
    id: item
    width: 200; height: 200

    TapHandler {
        id: inputHandler
    }

    Component.onCompleted: {
        inputHandler.tapped.connect(MyScript.jsFunction)
    }
}
// script.js

function jsFunction() {
    console.log("Called JavaScript function!")
}

TapHandlertapped 信号发出时,会调用 jsFunction()

有关详细信息,请参阅 将信号连接到方法和信号

启动代码中的 JavaScript

有时候,需要在应用(或组件实例)启动时运行一些命令式代码。虽然直接将启动脚本作为 全局代码 包含在外部脚本文件中很有吸引力,但这可能会带来严重的限制,因为 QML 环境可能尚未完全建立。例如,某些对象可能尚未创建,或者某些 属性绑定 也可能尚未建立。有关全局脚本代码的确切限制,请参见JavaScript 环境限制

当某个 QML 对象的实例化完成后,它会发出 Component.completed 附加信号。相应地,在 Component.onCompleted 处理程序中的 JavaScript 代码会实例化对象后运行。因此,编写应用启动代码的最佳位置是在顶级对象的 Component.onCompleted 处理程序中,因为这个对象会在 QML 环境完全建立后发出 Component.completed

例如

import QtQuick 2.0

Rectangle {
    function startupFunction() {
        // ... startup code
    }

    Component.onCompleted: startupFunction();
}

QML 文件中的任何对象都可以使用这个附加属性——包括嵌套对象和嵌套 QML 组件实例。如果启动时需要执行多个 onCompleted() 处理程序,它们将按未定义的顺序顺序执行。

同样,每个 Component 都在销毁前发出一个 销毁信号()。

© 2024 Qt 公司 Ltd. 本文档中包含的贡献是各自所有者的版权。本文档提供的文档遵守由自由软件基金会发布、版本 1.3 的 GNU 自由文档许可证 的条款。Qt 和相应的标志是芬兰及其它国家的 Qt 公司的商标。所有其他标志均为各自所有者的财产。