3

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

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

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

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

4

4 回答 4

9

相比之下QScriptEngine,如果自定义类继承自宏,则可以添加自定义类QObjectQ_SCRIPT_DECLARE_QMETAOBJECTQJSEngine不直接提供此功能。

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

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

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()

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

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

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

于 2016-06-01T18:11:21.420 回答
3

在 Qt 5.8 中,QJSEngine 添加了一个新特性:newQMetaObject

&MyQObjectDerivedClass::staticMetaObject您只需使用上述函数将静态元对象添加到 JSEngine 即可。

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

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

属性系统(setter/getter)和插槽一样工作。

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

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

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

我现在可以编写news该类型的对象的 JS 并使用 setter/getter 等。此代码实际上存在于 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;
};
于 2020-05-07T14:09:56.590 回答
1

自 Qt5.5QScriptEngine起已弃用,因此仅QJsEngine应在将来使用。 https://wiki.qt.io/New_Features_in_Qt_5.5#Deprecated_Functionality

于 2019-08-23T21:32:54.650 回答
-1

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

这是一个很好的起点: 链接到示例

QScriptEngine 与 QJsEngine 非常相似,因此对您来说应该不是什么大问题。

希望这可以帮助 :)

于 2013-11-01T05:15:56.757 回答