QJSEngine - 暴露类和抛出错误
Posted
技术标签:
【中文标题】QJSEngine - 暴露类和抛出错误【英文标题】:QJSEngine - exposing classes and throwing errors 【发布时间】:2020-04-27 00:59:17 【问题描述】:我正在尝试创建一个标准 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 定义的方法?
【问题讨论】:
也许不可能。这个错误似乎可能是相关的:bugreports.qt.io/browse/QTBUG-39041 【参考方案1】:正在构建的对象与QJSEngine
没有任何关联。所以你只能做以下选择之一:
-
如果您可以确保在整个应用程序中只有一个
QJSEngine
实例,请将引擎实例存储在静态变量中。
如果可以确保每个线程只有一个引擎,请将引擎实例存储在线程局部变量 (QThreadStorage
) 中。
在评估您的 JS 代码之前,在当前线程中设置当前活动引擎。这可能是最简单但最可靠的解决方案。
从QJSValue
参数中检索引擎。
为构造函数实现一个 JS 包装器
解决方案 4。:通过 QJSValue
参数隐式传递引擎。
我假设你的抛出构造函数总是有一个参数。 QJSValue
有一个(已弃用)方法 engine(),然后您可以使用它。您可以将Q_INVOKABLE
方法中的任何参数替换为QJSValue
,而不是使用QString
和朋友。
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()。
【讨论】:
这太棒了。我遇到了另一个与 js 和 c++ 之间的细微差别有关的问题,#5 应该可以全部解决。感谢大家的帮助!以上是关于QJSEngine - 暴露类和抛出错误的主要内容,如果未能解决你的问题,请参考以下文章