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

适用场景:逻辑简单,仅在当前组件内部使用的短代码。
在 QML 根元素内直接定义 function,就像在 HTML 中写 JS 一样。
关键代码 (来自 JSInlineFunc.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 声明这是一个共享库 。
// Utility.js
.pragma library
function formatDate(date) {
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate()
}
然后在 JSImportFile.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 ):
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.qml 和 script.mjs):
Step 1: 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 端 (处理与回传)
// 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 文件中。
工程下载
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/u011186532/article/details/156126031




