QJSEngine — раскрытие классов и выдача ошибок

Я пытаюсь создать стандартную библиотеку JS, которая в основном имеет форму Qbs (в которой используется устаревшее QScriptEngine) с QJSEngine, поэтому люди, которые создают программное обеспечение Qt, могут добавлять такие вещи, как операции с файлами, в свою среду JS для плагинов.

Репозиторий можно посмотреть здесь

У меня есть базовые классы, открытые для движка 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, которые могут вызывать ошибки в вызывающем механизме?


person konsumer    schedule 27.04.2020    source источник
comment
Может быть, это невозможно. Эта ошибка может иметь значение: bugreports.qt.io/browse/QTBUG-39041   -  person konsumer    schedule 27.04.2020


Ответы (1)


Строящийся объект пока не имеет никакой связи с QJSEngine. Таким образом, вы можете сделать только один из следующих вариантов:

  1. Сохраните экземпляр движка в статической переменной, если вы можете гарантировать, что во всем приложении есть только один экземпляр QJSEngine.
  2. Сохраните экземпляр движка в локальной переменной потока (QThreadStorage), если вы можете гарантировать, что в каждом потоке будет только один движок.
  3. Установите текущий активный движок в текущем потоке прямо перед оценкой вашего JS-кода с тех пор. Это может быть самым простым и в то же время надежным решением.
  4. Получить двигатель из параметра QJSValue.
  5. Реализовать 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));
}

Это дает вам доступ к QJSEngine в конструкторе TextFile, и вы можете вызвать throwError().

person Richard W    schedule 29.04.2020
comment
Это чертовски круто. У меня есть еще одна проблема, связанная с небольшими различиями между js и c++, и № 5 должен решить их все. Спасибо за помощь! - person konsumer; 30.04.2020