如何使用 QJSEngine 构建 API?

Posted

技术标签:

【中文标题】如何使用 QJSEngine 构建 API?【英文标题】:How to build an API with QJSEngine? 【发布时间】:2013-10-04 00:11:18 【问题描述】:

我从 Qt 开始,我的一个项目是使用 QJSEngine 来评估 javascript,我想为脚本提供完整的 API,包括类和全局函数。

现在我的程序只提供 ECMAScript 默认的东西(eval、encodeURI、parseInt 等),但我需要向代码公开一些自定义类,比如浏览器 API(WebSocket 类、Image 类、文档目的)。例如:

var obj = new CustomClass("", 0);
var ret = obj.customClassMethod("[...]!");
customFunction(ret);

我需要在 C++ 中定义类的行为,这无助于评估类定义并让用户代码运行。

【问题讨论】:

【参考方案1】:

QScriptEngine 相比,如果自定义类从QObject 继承,您可以使用Q_SCRIPT_DECLARE_QMETAOBJECT 宏添加自定义类,QJSEngine 不直接提供此功能。

您仍然可以使用 Qt 元对象系统为 Javascript 提供接口,但您必须在 C++ 中实例化该对象并将其添加到 Javascript 上下文中。 然后它的槽、Q_INVOKABLE 定义的方法和Q_PROPERTY 定义的属性都可以从 Javascript 运行时中访问。

现在您可以创建一个工厂,为包装为 Javascript 对象的给定 QJSEngine 创建自定义类 CustomClass 的实例:

class CustomClassFactory : public QObject

    Q_OBJECT
public:
  CustomClassFactory(QJSEngine* engine) : m_engine(engine) 
  Q_INVOKABLE QJSValue createInstance() 
      // The engine takes ownership and destroys the object if no longer required.
      return m_engine->newQObject(new CustomClass());
  
private:
    QJSEngine* m_engine;

需要构造一个工厂实例并将其添加到 Javascript 运行时的全局对象中:

QJSEngine engine;
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);

现在我们可以用 Javascript 构造对象了:

var obj = _customClassFactory.createInstance()

到目前为止,让我们另外将自定义类的构造函数注入到 Javascript 运行时:

QJSEngine engine;
// Again, the QJSEngine will take ownership of the created object.
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
engine.evaluate(
    "function CustomClass() "
    "    return _customClassFactory.createInstance()"
    "");

等等,现在您可以在 Javascript 中构造 C++ 对象,就像您自定义 Javascript 类一样:

var obj = new CustomClass()

对于提到的WebSocket API,您可以为此目的包装QtWebSocket——这正是我提出建议方法时所需要的。

请注意,为简单起见,我省略了构造函数的参数,但也可以简单地添加它们。

PS:我会添加更多官方文档的链接,但由于缺乏声誉,我不允许这样做。

【讨论】:

这很有帮助!适用于类 Color:public QObject,具有属性 r,g,b 加上 getR/G/B 和 setR/G/B,三个构造函数和方法 toHex 和 toStr。但是:一旦我使用 toString,该方法就会返回 s.th。像“颜色(0x564cfa5c3c10)”。有什么想法吗? @laune QColor 可能没有 toString() 成员。您正在寻找的可能是QColor.name()。对于十六进制字符串,您应该可以在 Javascript 中调用 color.name(0) “Color”这个名字是巧合;颜色与 QColor 无关。如果我将自定义类 Color 中的方法名称“toString”替换为“toStr”,它就可以正常工作。是否有一些规则不能覆盖 QObject 的 to 方法?【参考方案2】:

在 Qt 5.8 中,QJSEngine 添加了一个新功能:newQMetaObject

您只需添加静态元对象,例如使用上述函数将&MyQObjectDerivedClass::staticMetaObject 发送给JSEngine。

然后,您将能够在 QML 中以 Javascript 格式 new 那些对象。我发现这是一个非常巧妙的解决方案。

正如文档所说,您必须标记您的构造函数Q_INVOKABLE,否则您将无法实例化您的类的对象。

属性系统(setters/getters)和槽一样工作。

https://doc.qt.io/qt-5/qjsengine.html#newQMetaObject

这是我的测试代码 - 首先是添加元对象的 C++ 部分

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

我现在可以编写 news 那个类型的对象并使用 setter/getter 等的 JS。此代码实际上存在于 MouseArea onClicked 处理程序中以进行手动测试。

var bob = new MyClassOfObject();
print(bob.x);
bob.x = 918264;
print(bob.x);
print(bob.words);

这是类定义...

class MyClassOfObject : public QObject

    Q_OBJECT
    Q_PROPERTY(int x READ getX WRITE setX)
    Q_PROPERTY(int y READ getY WRITE setX)
    Q_PROPERTY(QStringList words READ getWords)    
public:
    Q_INVOKABLE explicit MyClassOfObject(QObject *parent = nullptr);
public slots:
    int getX() const  return x; 
    int getY() const  return y; 
    void setX(int x)  this->x = x; 
    void setY(int y)  this->y = y; 
    QStringList getWords() const;
private:
    int x = -113;
    int y = 616;
    QStringList stringList;
;

【讨论】:

QJSEngine 抱怨:“ReferenceError: Color is not defined”在你的提议之后使用了一个类 Color:public QObject 属性 r,g,b。 3 个构造函数标记为 Q_INVOKABLE。有什么我可能会错过的吗?我使用 QScriptEngine 管理这个。有什么想法吗? @laune - 我已经更新了原帖。我希望这有帮助。我会先检查“setProperty”中给出的名称。抱歉,如果您收到很多“编辑”消息。我对 SO 很陌生! 非常感谢 - 它有效!我 ifdef'd setProperty 从之前的努力(见 Max 的回答)完全消失,认为 newQMetaObject 方法会处理所有事情。实际上,如果类名作为属性名很好,它可能会,但现在我看到增强的灵活性重新命名和包含对象仍然需要额外的调用。 @laune。是的 - 如果你需要做很多,那么可以使用静态元对象的className 成员来自动推断属性名称。【参考方案3】:

从 Qt5.5 开始,QScriptEngine 已被弃用,因此以后只能使用 QJsEngine。 https://wiki.qt.io/New_Features_in_Qt_5.5#Deprecated_Functionality

【讨论】:

【参考方案4】:

如果您查找 QScriptEngine 的文档,或者通过搜索“QScriptEngine 示例”,您可以找到一些关于使自定义 C++ 类可用于 QScriptEngine 的内容。

这是一个很好的起点: link to example

QScriptEngine 与 QJsEngine 非常相似,所以对你来说应该不是什么大问题。

希望这会有所帮助:)

【讨论】:

以上是关于如何使用 QJSEngine 构建 API?的主要内容,如果未能解决你的问题,请参考以下文章

使用 QJSEngine 从 JavaScript 访问 Qt API

如何使用 QJSEngine 实例化 c++ 对象

QJSEngine 删除我的 QObject,如何在 QJSEngine::newQObject 之后更改所有权?

如何将枚举类型注册到 QJSEngine 以从脚本环境中使用?

如何从 Qt 5.12 中的 QJSEngine 获取标准输出?

如何在 QJSEngine 中注册 QList<quint64> 之类的类型?