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 交互的四种方式

相关推荐
肉肉不吃 肉4 小时前
Vue Router 路由模式
前端·javascript·vue.js
北寻北爱4 小时前
Vue-Router
前端·javascript·vue.js
肉肉不吃 肉4 小时前
什么是闭包
前端·javascript
天天向上10244 小时前
vue 页面内实现el-table和div自动滚动
前端·javascript·vue.js
谁动了我的代码?4 小时前
VNC中使用QT的GDB调试,触发断点时与界面窗口交互导致整个VNC冻结
开发语言·qt·svn
lxh01134 小时前
函数防抖题解
前端·javascript·算法
颜酱4 小时前
环检测与拓扑排序:BFS/DFS双实现
javascript·后端·算法
bluceli5 小时前
JavaScript WeakMap与WeakSet:内存优化的秘密武器
前端·javascript
颜酱5 小时前
理解并查集Union-Find:从原理到练习
javascript·后端·算法
前端小菜鸟也有人起5 小时前
Vue2父子组件通信方法总结
javascript·vue.js·ecmascript