QJSEngine(JavaScript引擎)

一、描述

QJSEngine 类提供了一个运行 JavaScript 代码的环境。 

1.1、运行脚本

使用 evaluate() 来运行脚本代码。evaluate() 返回一个 QJSValue 保存运行结果。QJSValue 类提供了将结果转换为各种 C++ 类型的函数。

    QJSEngine myEngine;
    QJSValue three = myEngine.evaluate("1 + 2");
    qDebug()<<three.toInt();//3

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

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

QString中的原始字符串字面值常量

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

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

对于更大的功能块,一般将代码和数据封装到模块中。模块是包含脚本代码、变量等的文件。一个模块可以引用其他模块的功能。这允许以安全的方式从较小的连接构建块构建脚本化应用程序。

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

//script.mjs
export function sum(left, right)
{
    return left + right
}

可以使用 importModule() 加载该模块:

    QJSEngine myEngine;
    QJSValue module = myEngine.importModule(":/script.mjs");
    QJSValue sumFunction = module.property("sum");
    QJSValueList args;
    args << 1 << 2;
    QJSValue result = sumFunction.call(args);
    qDebug()<<result.toInt();//3

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

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

模块不必是文件。 它们可以是用 registerModule() 注册的值:

//script.mjs
import version from "version";
export function getVersion()
{
    return version;
}
    QJSEngine myEngine;
    QJSValue version(610);
    myEngine.registerModule("version", version);
    QJSValue module = myEngine.importModule(":/script.mjs");
    QJSValue getVersion = module.property("getVersion");
    QJSValue result = getVersion.call();
    qDebug()<<result.toInt();//610

1.2、引擎配置

globalObject() 函数返回与脚本引擎关联的全局对象。 全局对象的属性可以从任何脚本代码访问(即它们是全局变量)。 可通过向全局对象添加属性来配置脚本引擎:

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

1.3、脚本异常

evaluate() 可以抛出脚本异常(例如语法错误导致)。 如果抛出了异常,则 evaluate() 返回抛出的值(通常是Error 对象)。可使用 isError() 检查异常,使用QJSValue::toString()获取错误信息。

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

1.4、脚本对象创建

使用 newObject() 创建一个 JavaScript 对象; 这是脚本语句 new Object() 的 C++ 等效项。类似地,使用 newArray() 创建一个 JavaScript 数组对象。

1.5、QObject集成

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


    QPushButton *button = new QPushButton;
    QJSValue scriptButton = myEngine.newQObject(button);//返回一个JavaScript代理对象,代理按钮
    myEngine.globalObject().setProperty("button", scriptButton);

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

    qDebug() << scriptButton.property("checkable").toBool();
    scriptButton.property("show").call(); //按钮显示

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

class MyObject : public QObject
{
    Q_OBJECT

public:
    Q_INVOKABLE MyObject() {}
};

类的 staticMetaObject 可以像这样暴露给 JavaScript:

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

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

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

1.6、动态 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();// 0

1.7、扩展

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

二、类型成员

1、enum QJSEngine::Extension:此枚举用于指定要通过 installExtensions() 安装的扩展。

  • TranslationExtension:应安装翻译函数(例如 qsTr())。 这也会安装 Qt.uiLanguage 属性。
  • ConsoleExtension:表示应该安装控制台功能(例如console.log())。
  • GarbageCollectionExtension:表示应该安装垃圾收集功能(例如gc())。
  • AllExtensions:表示应安装所有扩展。

翻译扩展

脚本翻译函数和C++翻译函数的关系如下所示:

  • qsTr() - QObject::tr()
  • QT_TR_NOOP() - QT_TR_NOOP()
  • qsTranslate() - QCoreApplication::translate()
  • QT_TRANSLATE_NOOP() - QT_TRANSLATE_NOOP()
  • qsTrId() - qtTrId()
  • QT_TRID_NOOP() - QT_TRID_NOOP()

垃圾收集扩展

gc() 函数等效于调用 collectGarbage()。

2、enum QJSEngine::ObjectOwnership:控制当相应的 JavaScript 对象被引擎垃圾回收时 JavaScript 内存管理器是否自动销毁 QObject

  • CppOwnership:该对象归 C++ 代码所有,JavaScript 内存管理器永远不会删除它。 JavaScript destroy() 方法不能用于这些对象。
  • JavaScriptOwnership:该对象归 JavaScript 所有。 当对象作为方法调用的返回值返回给 JavaScript 内存管理器时,如果没有剩余的 JavaScript 引用并且它没有 QObject::parent(),JavaScript 内存管理器将跟踪它并删除它。

通常,应用程序不需要显式设置对象的所有权。 JavaScript 内存管理器设置默认所有权。

  • 默认情况下,由 JavaScript 内存管理器创建的对象具有 JavaScriptOwnership。 从调用 QQmlComponent::create() 或 QQmlComponent::beginCreate() 创建的根对象除外,这些根级对象的所有权被视为已转移给 C++ 调用方。
  • 默认情况下,不是由 JavaScript 内存管理器创建的对象具有 CppOwnership。 从 C++ 方法调用返回的对象除外;这仅适用于 Q_INVOKABLE 方法或槽的显式调用,但不适用于属性 getter 调用。

三、属性成员

1、uiLanguage : QString

此属性保存用于翻译用户界面字符串的语言。当 QJSEngine::TranslationExtension 安装在引擎上时,它以 Qt.uiLanguage 的形式公开进行读写。它总是在 QQmlEngine 的实例中公开。

可以自由设置该值并在绑定中使用它。建议在应用程序中安装翻译器后进行设置。 按照惯例,空字符串意味着不打算从源代码中使用的语言进行翻译。

四、成员函数

1、~QJSEngine()

在 QJSEngine 销毁期间,不会从 JS 堆中收集垃圾。如果需要释放所有内存,请在销毁 QJSEngine 之前手动调用 collectGarbage

2、QJSValue catchError()

如果异常当前正在挂起,则捕获它并将其作为 QJSValue 返回。否则返回 undefined 作为 QJSValue。调用此方法后 hasError() 返回 false。

 3、void collectGarbage()

运行垃圾收集器。将尝试通过定位和处理脚本环境中不再可访问的对象来回收内存。

通常不需要调用这个函数,但是,可以调用此函数来明确请求应尽快执行垃圾收集。

4、QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1, QStringList *exceptionStackTrace = nullptr)

执行JavaScript代码。代码将在全局对象的上下文中进行评估。

若发生异常则返回值将是抛出的异常(通常是一个 Error 对象)。

lineNumber 用于指定程序的起始行号;引擎报告的与执行代码有关的行号信息将基于此参数。例如,如果程序由两行代码组成,并且第二行的语句导致脚本异常,则异常行号将为 lineNumber 加 1。当未指定起始行号时,行号将从 1 开始。

fileName 用于错误报告。例如,在错误对象中,如果此函数提供了 fileName,则可以通过“fileName”属性访问文件名。

exceptionStackTrace 用于报告是否抛出了未捕获的异常。如果将指向 QStringList 的非空指针传递给它,如果脚本抛出未处理的异常,它将设置为“堆栈帧消息”列表,否则将设置为空列表。堆栈帧消息的格式为 function name:line number:column:file name

在某些情况下,例如对于原生函数,函数名和文件名可以为空,行号和列可以为 -1。

如果抛出异常并且异常值不是 Error 实例(即 QJSValue::isError() 返回 false),仍将返回异常值。使用 exceptionStackTrace->isEmpty() 来区分该值是正常返回值还是异常返回值。

5、template <typename T> T QJSEngine::fromScriptValue(const QJSValue &value)

     template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)

QMetaType 类型值与 QJSValue 类型值互转。

6、QJSValue globalObject() 

返回此引擎的全局对象。

默认情况下,全局对象包含属于 ECMA-262 一部分的内置对象,例如数学、日期和字符串。 此外,可以设置全局对象的属性,使用户自己的扩展可用于所有脚本代码。脚本代码中的非局部变量将被创建为全局对象的属性,以及全局代码中的局部变量。

7、bool hasError() 

上次 JavaScript 执行是否导致异常或调用 throwError()。

8、QJSValue importModule(const QString &fileName)

导入位于 fileName 的模块并返回一个模块命名空间对象,该对象包含所有导出的变量、常量和函数作为属性。

如果这是第一次在引擎中导入模块,则文件将从本地文件系统或 Qt 资源系统中的指定位置加载,并作为 ECMAScript 模块进行评估。 该文件应以 UTF-8 文本编码。

同一模块的后续导入将返回先前导入的实例。 模块是单例的,会一直存在,直到引擎被销毁。

指定的文件名将在内部使用 QFileInfo::canonicalFilePath() 进行规范化。这意味着使用不同的相对路径在磁盘上多次导入同一文件只会加载该文件一次。

注意:如果在加载模块期间抛出异常,返回值将是异常对象。

9、void installExtensions(QJSEngine::Extensions extensions, const QJSValue &object = QJSValue())

安装 JavaScript 扩展以添加标准 ECMAScript 实现中不可用的功能。

扩展安装在给定的对象上,如果没有指定对象,则安装在全局对象上。

通过对枚举值进行 OR 运算,可以一次安装多个扩展:

    installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension);

10、bool isInterrupted() 

返回 JavaScript 执行当前是否被中断。

11、QJSValue newArray(uint length = 0)

创建具有给定长度的类 Array 的 JavaScript 对象。

12、QJSValue newErrorObject(QJSValue::ErrorType errorType, const QString &message = QString())

创建一个 Error 类的 JavaScript 对象,以 message 作为错误信息。

13、QJSValue newObject()

创建一个 Object 类的 JavaScript 对象。创建的对象的原型将是 Object 原型对象

14、QJSValue newQMetaObject(const QMetaObject *metaObject)

创建一个包含给定 QMetaObject 的 JavaScript 对象。 metaObject 必须比脚本引擎存活时间更长。建议仅将此方法用于静态元对象

当作为构造函数调用时,将创建该类的一个新实例。 只有 Q_INVOKABLE 公开的构造函数才能从脚本引擎中看到。

15、template <typename T> QJSValue newQMetaObject()

创建一个 JavaScript 对象,该对象包装与类 T 关联的静态 QMetaObject。

16、QJSValue newQObject(QObject *object)

使用 JavaScriptOwnership 创建一个封装给定 QObject 对象的 JavaScript 对象。

信号和槽、对象的属性和子对象可用作创建的 QJSValue 的属性。

如果 object 是空指针,则此函数返回空值。

如果已为对象的类(或其超类)注册了默认原型,则新脚本对象的原型将被设置为该默认原型。

如果给定的对象在引擎控制之外被删除,任何通过 JavaScript 包装对象(通过脚本代码或 C++)访问已删除 QObject 成员的尝试都将导致脚本异常。

17、QJSValue newSymbol(const QString &name)

创建一个带有值为 name 的 Symbol 类的 JavaScript 对象。

创建的对象的原型将是 Symbol 原型对象。

18、【静态】QJSEngine::ObjectOwnership objectOwnership(QObject *object)

返回对象的所有权。

19、bool registerModule(const QString &moduleName, const QJSValue &value)

注册一个 QJSValue 作为一个模块。返回注册结果。

调用此函数后,所有导入 moduleName 的模块都会导入 value 的值,而不是从文件系统中加载 moduleName。

这允许导入文件系统上不存在的模块。

20、void setInterrupted(bool interrupted)

中断或重新启用 JavaScript 执行。

如果interrupted 为 true,则此引擎执行的任何JavaScript 都会立即中止并返回一个错误对象,直到再次调用此函数并为 interrupted 设置 false 值。

这个函数是线程安全的。 可以从不同的线程调用它以中断。

21、【静态】void setObjectOwnership(QObject *object, QJSEngine::ObjectOwnership ownership)

设置对象的所有权。

具有 JavaScriptOwnership 的对象只要它仍然有父对象,即使没有对它的引用也不会被垃圾回收。

22、void throwError(const QString &message)

使用message抛出运行时错误(异常)。

此方法相当于 JavaScript 中 throw 语句。它使 C++ 代码能够向 QJSEngine 报告运行时错误。

当从 C++ 返回时,引擎将中断正常的执行流程使用包含 message 的错误对象调用下一个预注册的异常处理程序。错误对象将指向 JavaScript 调用者堆栈中最顶层上下文的位置;具体来说,它将具有属性 lineNumber、fileName 和 stack。 这些属性在脚本异常中进行了描述。

在以下示例中,FileAccess.cpp 中的 C++ 方法在 qmlFile.qml 中调用 readFileAsText() 的位置引发错误:

// qmlFile.qml
function someFunction()
{
    var text = FileAccess.readFileAsText("/path/to/file.txt");
}
// FileAccess.cpp
// 假设 FileAccess 是一个 QObject 派生的类注册为单例类型并提供可调用的方法readFileAsText()

QJSValue FileAccess::readFileAsText(const QString & filePath) 
{
  QFile file(filePath);

  if (!file.open(QIODevice::ReadOnly)) 
  {
    jsEngine->throwError(file.errorString());
    return QString();
  }

  ...
  return content;
}

也可以在 JavaScript 中捕获抛出的错误:

// qmlFile.qml
function someFunction() 
{
    ...
    var text;
    try 
    {
        text = FileAccess.readFileAsText("/path/to/file.txt");
    } 
    catch (error) 
    {
        console.warn("In " + error.fileName + ":" + "error.lineNumber" + ": " + error.message);
    }
}

      void throwError(QJSValue::ErrorType errorType, const QString &message = QString())

重载函数,使用给定的 errorType 和消息抛出运行时错误(异常)。

      void throwError(const QJSValue &error)

重载函数,抛出预先构造的运行时错误(异常)对象。通过这种方式,可以使用 newErrorObject() 创建错误并根据需要对其进行自定义。

五、相关非成员

1、QJSEngine *qjsEngine(const QObject *object)

返回与 object 关联的 QJSEngine(如果有)。

如果已将 QObject 暴露给 JavaScript 环境并且稍后想要重新获得访问权限,则此函数很有用。它不需要保留从 QJSEngine::newQObject() 返回的包装器。

Guess you like

Origin blog.csdn.net/kenfan1647/article/details/121353454