QML与JavaScript 交互的四种方式

目录

引言

在Qt6中,QML引擎深度集成了JavaScript。本文将用四个示例,展示QML与Javascript的四种交互方式:内联调用、外部文件、信号处理、工作线程(WorkerScript)。


交互方式说明

方式一:内联调用

演示效果

适用场景:逻辑简单,仅在当前组件内部使用的短代码。

在 QML 根元素内直接定义 function,就像在 HTML 中写 JS 一样。

关键代码 (来自 JSInlineFunc.qml ):

qml 复制代码
Rectangle {
    // 1. 定义一个内联 JS 函数
    function calculateArea(w, h) {
        return w * h
    }

    Text {
        // 2. 属性绑定中直接调用:当 width 或 height 变化时自动重算
        text: "计算面积: " + calculateArea(this.width, this.height) 
    }

    Button {
        // 3. 在信号处理器(如 onClicked)中调用
        onClicked: {
            var result = calculateArea(100, 100)
            textResult.text = "Area: " + result
        }
    }
}

关键点

  • 作用域:这些函数可以访问该 QML 文件内的所有 id 和属性。
  • 绑定特性:在属性绑定(如 text: ...)中使用时,若参数发生变化,函数会自动重新执行 。

方式二:外部文件

演示效果

适用场景:通用的工具函数(如日期格式化、字符串处理),需要在多个 QML 文件中共享。

关键代码

首先在 Utility.js 中定义函数,并使用 .pragma library 声明这是一个共享库 。

javascript 复制代码
// Utility.js
.pragma library 

function formatDate(date) {
    return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate()
}

然后在 JSImportFile.qml 中导入并使用 :

qml 复制代码
import "Utility.js" as Util // 定义别名为 Util

Text {
    // 使用 别名.函数名 进行调用
    text: "日期: " + Util.formatDate(new Date()) 
}

关键点

  • 必须使用 as 关键字定义别名(如Util),避免命名冲突。同时注意Util首字母要大写
  • 加上 .pragma library 后,该 JS 文件在整个应用程序中只有一份实例(单例),适合存放无状态的工具函数。如果不加这行,每个导入它的 QML 组件都会生成一个新的 JS 上下文副本。

方式三:信号处理

演示效果

适用场景:将复杂的逻辑封装在 JS 函数中,通过 QML 的信号(Signal)来触发,保持代码整洁。

关键代码 (来自 JSSignal.qml ):

qml 复制代码
Text {
    id: myText
    property int counter: 0

    // 1. 定义处理逻辑
    function handleButtonClick() {
        myText.counter++
    }

    Connections {
        target: myButton // 目标按钮
        // 2. 将按钮的 onClicked 信号连接到 JS 函数
        function onClicked() { myText.handleButtonClick() } 
    }
}

关键点

  • 这体现了 Qt 的 信号与槽 (Signal & Slot) 机制。JS 函数在 QML 中可以直接作为槽函数使用。
  • 这种方式解耦了 UI 元素(Button)和业务逻辑(Text 的计数逻辑)。

方式四:工作线程 (WorkerScript)

演示效果

适用场景:耗时操作(如图像处理、大规模计算)。如果在主线程做这些,界面会"卡死"。

原理 :使用 WorkerScript 将 JS 代码放到后台线程运行。注意:后台线程不能访问 QML 的 UI 元素(DOM),只能通过消息传递(Message Passing)交互。

关键代码 (来自 WorkerScriptDemo.qmlscript.mjs):

Step 1: QML 端 (发送与接收)

qml 复制代码
// 工作线程脚本
WorkerScript {
    id: myWorker
    source: "script.mjs"

    // 接收工作线程返回的消息
    onMessage: function (msgObj) {
        isProcessing = false;

        if (msgObj.error) {
            resultText = "错误: " + msgObj.error;
        } else {
            resultText = "计算结果: " + msgObj.result +
                    "\n耗时: " + msgObj.time + " ms" +
                    "\n任务ID: " + msgObj.taskId +
                    "\n原始消息: " + JSON.stringify(msgObj.originalMessage)
        }
    }
}

//发送按钮
Button {
    id: btnCalc
    text: "计算斐波那契数列"
    font.pointSize: 12
    enabled: !isProcessing && myWorker.ready
    onClicked: {
        isProcessing = true;
        resultText = "计算斐波那契数列 ...";
        myWorker.sendMessage({ 'taskId': 1001, 'n': textField.text, 'timestamp': Date.now() });
    }
}

Step 2: JS 端 (处理与回传)

javascript 复制代码
// script.mjs
// 计算斐波那契数列(递归,耗时操作)
function fibonacci(n) {
    function fib(n) {
        if (n <= 1) return n;
        return fib(n - 1) + fib(n - 2);
    }
    return fib(n);
}

// 监听主线程发送的消息
WorkerScript.onMessage = function(message) {   
    try {
        var startTime = Date.now();
        var result;
        
        // 计算斐波那契数列
        result = fibonacci(message.n || 30);        
        var endTime = Date.now();
        var processingTime = endTime - startTime;
        
        // 将结果发送回主线程
        WorkerScript.sendMessage({
            result: result,
            time: processingTime,
            taskId: message.taskId,
            originalMessage: message
        });
        
    } catch (error) {
        // 错误处理
        WorkerScript.sendMessage({
            error: error.toString(),
            originalMessage: message
        });
    }
}

关键点

  • 异步特性 :调用 sendMessage 后,主线程立即继续执行,不会等待。
  • 数据隔离 :Worker 内部无法访问 id: mainWindow 等 UI 对象,必须通过 message 对象传递所需的所有数据。

总结

方式 关键字 适用场景 备注
内联调用 function 简单的 UI 逻辑 访问方便,但不易复用
外部文件 import ... as 工具类库、常量定义 推荐使用 .pragma library
信号连接 Connections 响应事件 实现 UI 与逻辑解耦
工作线程 WorkerScript 耗时计算、网络请求 唯一不阻塞 UI 的方式

初学时从内联函数开始,随着代码量增加,逐渐将逻辑抽离到外部 JS 文件中。

工程下载

下载链接:QML与JavaScript 交互的四种方式

相关推荐
不懒不懒1 小时前
【Python办公自动化进阶指南:系统交互与网页操作实战】
开发语言·python·交互
加农炮手Jinx1 小时前
Flutter for OpenHarmony 实战:疯狂头像 App(三)— 复合动画与交互反馈 — 让 UI 跃动起来
flutter·ui·交互·harmonyos·鸿蒙
会周易的程序员1 小时前
cNetgate插件架构设计详解 动态库 脚本二开lua, python, javascript
javascript·c++·python·物联网·lua·iot
王码码20351 小时前
lutter for OpenHarmony 实战之基础组件:第六十二篇 SystemChannels — 探秘 Flutter 与系统交互的捷径
flutter·microsoft·交互·harmonyos
xyty33202 小时前
QImageReader 的全局静态锁原理
c++·windows·qt
NEXT065 小时前
普通函数与箭头函数的区别
前端·javascript·面试
TechFind5 小时前
如何为 AI Agent 写出完美的 SOUL.md 人格文件(2026指南)
javascript
薛一半5 小时前
React三大属性之refs
前端·javascript·react.js
Lao乾妈官方认证唯一女友:D6 小时前
Ethers.js使用方法
javascript·web3