我正在尝试使用QScriptEngine创建一个形状像Qbs(使用不推荐使用的QJSEngine)的标准JS库,因此制作Qt软件的人可以在其插件JS环境中添加诸如文件操作之类的内容。

您可以看到回购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关联。因此,您只能执行以下替代方法之一:

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

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

    我假设您的抛出构造函数始终具有一个参数。 QJSValue具有(不赞成使用的)方法engine(),您可以使用该方法。您可以将Q_INVOKABLE方法中的任何参数替换为QJSValue,而不是使用QString和friends。
    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中编写一个附加的生成器类和一个构造函数包装器。评估包装并将该函数注册为您的类的构造函数。该包装函数会将所有所需的参数传递给factory方法。像这样:
    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/

    10-09 05:25