qt 之 QJSEngine 相关

本文介绍了Qt5中QJSEngine的使用,包括脚本评估、模块导入、引擎配置、异常处理和对象创建,展示了如何从C++调用JavaScript函数以及如何安全地封装和使用模块。QJSEngine是Qt5中对旧有QtScript的更新,支持更现代的脚本编程和模块化开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如今QtScript是Qt纯粹兼容的一部分,缺乏QML集成以及它已经在一段时间内不会更新/维护,而是有利于新引擎。
所以在qt5中将使用QJSEngine来代替 QtScript

QJSEngine

使用evaluate () 来评估脚本代码。

  QJSEngine myEngine;
  QJSValue three = myEngine.evaluate("1 + 2");

evaluate () 返回一个保存评估结果的QJSValue。QJSValue类提供了将结果转换为各种 C++ 类型的函数(例如QJSValue::toString () 和QJSValue::toNumber ())。

以下代码片段显示了如何定义脚本函数,然后使用QJSValue::call () 从 C++ 调用:

 QJSValue fun = myEngine.evaluate("(function(a, b) { return a + b; })");
  QJSValueList args;
  args << 1 << 2;
  QJSValue threeAgain = fun.call(args);

从上面的片段可以看出,脚本是以字符串的形式提供给引擎的。加载脚本的一种常见方法是读取文件的内容并将其传递给evaluate ():

  QString fileName = "helloworld.qs";
  QFile scriptFile(fileName);
  if (!scriptFile.open(QIODevice::ReadOnly))
      // handle error
  QTextStream stream(&scriptFile);
  QString contents = stream.readAll();
  scriptFile.close();
  myEngine.evaluate(contents, fileName);

这里我们将文件名作为第二个参数传递给evaluate ()。这不会以任何方式影响评估;第二个参数是存储在Error对象中用于调试目的的通用字符串。

对于更大的功能,您可能希望将代码和数据封装到模块中。模块是包含脚本代码、变量等的文件,并使用导出语句来描述其与应用程序其余部分的接口。在 import 语句的帮助下,一个模块可以引用其他模块的功能。这允许以安全的方式从较小的连接构建块构建脚本应用程序。相比之下,使用evaluate () 的方法存在一个风险,即来自一次evaluate () 调用的内部变量或函数会意外污染全局对象并影响后续评估。

以下示例提供了一个可以添加数字的模块:

 export function sum(left, right)
  {
      return left + right
  }

这个模块可以用 QJSEngine::import() 加载,如果它保存在 name 下math.mjs:

  QJSvalue module = myEngine.importModule("./math.mjs");
  QJSValue sumFunction = module.property("sum");
  QJSValue result = sumFunction.call(args);

模块还可以使用 import 语句使用其他模块的功能:

  import { sum } from "./math.mjs";
  export function addTwice(left, right)
  {
      return sum(left, right) * 2;
  }
 

引擎配置

globalObject () 函数返回与脚本引擎关联的全局对象。可以从任何脚本代码访问全局对象的属性(即它们是全局变量)。通常,在评估“用户”脚本之前,您需要通过向全局对象添加一个或多个属性来配置脚本引擎:

  myEngine.globalObject().setProperty("myNumber", 123);
  ...
  QJSValue myNumberPlusOne = myEngine.evaluate("myNumber + 1");

向脚本环境添加自定义属性是提供特定于您的应用程序的脚本 API 的标准方法之一。通常这些自​​定义属性是由newQObject () 或newObject () 函数创建的对象。

脚本异常

evaluate () 可以抛出脚本异常(例如由于语法错误)。如果是,那么evaluate () 返回抛出的值(通常是一个Error对象)。使用QJSValue::isError () 来检查异常。

有关错误的详细信息,请使用QJSValue::toString () 获取错误消息,并使用QJSValue::property () 查询Error对象的属性。以下属性可用:

  • name
  • message
  • fileName
  • lineNumber
  • stack
  QJSValue result = myEngine.evaluate(...);
  if (result.isError())
      qDebug()
              << "Uncaught exception at line"
              << result.property("lineNumber").toInt()
              << ":" << result.toString();

脚本对象创建

使用newObject () 创建一个 JavaScript 对象;这是脚本语句的 C++ 等效项new Object()。您可以使用QJSValue中的对象特定功能来操作脚本对象(例如QJSValue::setProperty ())。同样,使用newArray () 创建一个 JavaScript 数组对象。

QObject 集成

使用newQObject () 包装一个QObject(或子类)指针。newQObject () 返回一个代理脚本对象;QObject的属性、子项以及信号和槽可用作代理对象的属性。不需要绑定代码,因为它是使用 Qt 元对象系统动态完成的。

  QPushButton *button = new QPushButton;
  QJSValue scriptButton = myEngine.newQObject(button);
  myEngine.globalObject().setProperty("button", scriptButton);

  myEngine.evaluate("button.checkable = true");

  qDebug() << scriptButton.property("checkable").toBool();
  scriptButton.property("show").call(); // call the show() slot

使用newQMetaObject () 包装一个QMetaObject;这为您提供了基于QObject的类的“脚本表示” 。newQMetaObject () 返回一个代理脚本对象;类的枚举值可用作代理对象的属性。

可以从脚本调用暴露给元对象系统的构造函数(使用Q_INVOKABLE )以使用 JavaScriptOwnership 创建一个新的QObject实例。例如,给定以下类定义:

 class MyObject : public QObject
  {
      Q_OBJECT

  public:
      Q_INVOKABLE MyObject() {}
  };

staticMetaObjectfor 类可以像这样暴露给 JavaScript :

  QJSValue jsMetaObject = engine.newQMetaObject(&MyObject::staticMetaObject);
  engine.globalObject().setProperty("MyObject", jsMetaObject);

然后可以在 JavaScript 中创建类的实例:

  engine.evaluate("var myObject = new MyObject()");

注意:目前只支持使用Q_OBJECT宏的类;不可能将Q_GADGET staticMetaObject类暴露给JavaScript。

动态 QObject 属性

不支持动态QObject属性。例如,以下代码将不起作用:

  QJSEngine engine;
  QObject *myQObject = new QObject();
  myQObject->setProperty("dynamicProperty", 3);
  QJSValue myScriptQObject = engine.newQObject(myQObject);
  engine.globalObject().setProperty("myObject", myScriptQObject);
  qDebug() << engine.evaluate("myObject.dynamicProperty").toInt();

QJSEngine 提供了一个兼容的 ECMAScript 实现。默认情况下,日志等熟悉的实用程序不可用,但可以通过installExtensions () 函数安装它们。

QJSValue

QJSValue支持ECMA-262标准中定义的类型:基本类型,未定义、空、布尔、数字和字符串;以及对象和数组类型。此外,还为QVariant和QObject等Qt/C++类型提供了内置支持。
对于基于对象的类型(包括Date和RegExp),使用QJSEngine中的newT()函数(例如QJSEngine::newObject())创建所需类型的QJSValue。对于基元类型,使用QJSValue构造函数重载之一。对于其他类型,例如注册的小工具类型,如QPoint,可以使用QJSEngine::toScriptValue。
名为isT()的方法(例如isBool()、isUndefined())可用于测试某个值是否属于特定类型。名为toT()的方法(例如toBool()、toString())可用于将QJSValue转换为另一种类型。还可以使用通用的qjsvalue_cast()函数。
对象值具有零个或多个属性,这些属性本身就是QJSValue。使用setProperty()设置对象的属性,并调用property()检索属性的值。

  QJSEngine myEngine;
  QJSValue myObject = myEngine.newObject();
  QJSValue myOtherObject = myEngine.newObject();
  myObject.setProperty("myChild", myOtherObject);
  myObject.setProperty("name", "John Doe");

QJSValue for 数组

要使用QJSValue创建数组,请使用QJSEngine::newArray()

  // Assumes that this class was declared in QML.
  QJSValue jsArray = engine->newArray(3);

要设置数组中的单个元素,请使用 setProperty(quint32 arrayIndex, const QJSValue &value) 重载。例如,要用整数填充上面的数组:

  for (int i = 0; i < 3; ++i) {
      jsArray.setProperty(i, QRandomGenerator::global().generate());
  }

要确定数组的长度,请访问“length”属性。要访问数组元素,请使用属性(quint32 arrayIndex)重载。下面的代码将我们在上面创建的数组读回一个列表:

  QVector<int> integers;
  const int length = jsArray.property("length").toInt();
  for (int i = 0; i < length; ++i) {
      integers.append(jsArray.property(i).toInt());
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值