c++ - QJSEngine - 暴露类并抛出错误

标签 c++ qt qt5 qjsengine

我正在尝试创建一个标准的 JS 库,它的形状主要像 Qbs(它使用已弃用的 QScriptEngine)和 QJSEngine,因此制作 Qt 软件的人可以添加东西比如对他们的插件 JS 环境的文件操作。

你可以看到 repo here

我已经将基本类暴露给 JS 引擎,如下所示:

QJSEngine jsEngine;
jsEngine.installExtensions(QJSEngine::AllExtensions);

jsEngine.globalObject().setProperty("BinaryFile", jsEngine.newQMetaObject(&Qbs4QJS::BinaryFile::staticMetaObject));

但我似乎无法弄清楚如何在函数内部获取对 QJSEngine 的引用,因此我可以抛出错误:

Q_INVOKABLE BinaryFile(const QString &filePath, QIODevice::OpenModeFlag mode = QIODevice::ReadOnly) {
    m_file = new QFile(filePath);
    if (!m_file->open(mode)) {
        // how do I get jsEngine, here
        jsEngine->throwError(m_file->errorString());
    }
}

如果我能以某种方式从函数内部派生调用引擎,我会很高兴,这样类就可以暴露给多个单独的引擎实例,例如。

我看到了QScriptable它是 engine() 方法,但不知道如何使用它。

我加了

Depends { name: "Qt.script" }

在我的 qbs 文件中,和

#include <QtScript>

但它仍然没有抛出错误(只是默默地失败):

#include <QObject>
#include <QString>
#include <QFile>
#include <QIODevice>
#include <QFileInfo>
#include <QtScript>

namespace Qbs4QJS {

class BinaryFile :  public QObject, protected QScriptable
{
    Q_OBJECT

public:
    Q_ENUM(QIODevice::OpenModeFlag)

    Q_INVOKABLE BinaryFile(const QString &filePath, QIODevice::OpenModeFlag mode = QIODevice::ReadOnly) {
        m_file = new QFile(filePath);
        // should check for false and throw error with jsEngine->throwError(m_file->errorString());
        if (!m_file->open(mode)){
            context()->throwError(m_file->errorString());
        }
    }

private:
    QFile *m_file = nullptr;
};

} // end namespace Qbs4QJS

我也可能对此感到困惑,但它似乎正在使用 QScriptEngine,我正试图摆脱它。

完成添加 QJSEngine 可以使用的类的任务的最佳方法是什么,它具有 cpp 定义的方法,可以在调用引擎中抛出错误?

最佳答案

正在构建的对象还没有与QJSEngine有任何关联。所以你只能做以下选择之一:

  1. 如果您可以确保在整个应用程序中只有一个 QJSEngine 实例,则将引擎实例存储在静态变量中。
  2. 如果可以确保每个线程只有一个引擎,则将引擎实例存储在线程局部变量 (QThreadStorage) 中。
  3. 在评估您的 JS 代码之前,在当前线程中设置当前事件的引擎。这可能是最简单但最可靠的解决方案。
  4. QJSValue 参数中检索引擎。
  5. 为构造函数实现一个 JS 包装器

解决方案 4:通过 QJSValue 参数隐式传递引擎。

我假设你的抛出构造函数总是有一个参数。 QJSValue 有一个(已弃用)方法 engine()然后你可以使用。您可以将 Q_INVOKABLE 方法中的任何参数替换为 QJSValue 而不是使用 QString 和 friend 。

class TextFileJsExtension : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE TextFileJsExtension(const QJSValue &filename);
};

TextFileJsExtension::TextFileJsExtension(const QJSValue &filename)
{
    QJSEngine *engine = filename.engine();
    if (engine)
        engine->throwError(QLatin1String("blabla"));
}

我想它被弃用是有原因的,所以您可以询问 QML 团队,为什么以及您可以使用什么替代方案。

解决方案 5 为构造函数实现一个 JS 包装器

这建立在解决方案 4 的基础上,甚至适用于无参数构造函数。而不是像这样直接注册你的帮助类:

    engine->globalObject().setProperty("TextFile", engine->newQMetaObject(&TextFile::staticMetaObject));

您可以在 JS 中编写一个额外的生成器类和一个构造函数包装器。评估包装器并将此函数注册为您的类的构造函数。这个包装函数会将所有需要的参数传递给工厂方法。像这样:

engine->evaluate("function TextFile(path) { return TextFileCreator.createObject(path);

TextFileCreator 是一个帮助程序类,您可以将其注册为单例。 createObject() 方法将最终创建 TextFile 对象并将引擎作为参数传递:

QJSValue TextFileCreator::createObject(const QString &path)
{
    QJSEngine *engine = qmlEngine(this);
    return engine->createQObject(new TextFile(engine, filePath));
}

这使您可以在 TextFile 构造函数中访问 QJSEngine,并且可以调用 throwError()

关于c++ - QJSEngine - 暴露类并抛出错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61450160/

相关文章:

递归方法中的c++凸包

c++ - 在 64 位机器上构建 32 位 Qt 应用程序

java - 最容易学习和使用 Java 的图形用户界面工具包

c++ - 更改对话框的父级禁用拖放

c++ - QTreeView 与 QFileSystemModel : How can I remove all columns except "Name"?

c++ - 通过函数传递模板参数时出错

c++ - 关于CRITICAL_SECTION的使用问题

c++ - QWidget(一个 qlineedit)自动调整内容

qt - qml 文本字段中的换行符,文本属性不起作用

c++ - MinGW 链接路径