Отправляем объект из внешней библиотеки в скрипт
В Qt, как известно, есть замечательная вещь как QScript, позволяющая использовать ECMA скрипт.
В данный скриптовый движок возможно запихивать как отдельные функции, так и целые объекты.
А можно ли запихать в движок объект из внешней библиотеки (.dll, .so etc)?
Оказывается, что можно.
Делается это так:
1. Для начала напишем библиотеку.
Создаём обычный консольный проект. Назовём его plugin.
В .pro файле меняем строку TEMPLATE = app на TEMPLATE = lib
Удаляем файл main.cpp, он нам не нужен.
Добавляем файлы plugin.h, plugin.cpp, plugin_interface.h
plugin_interface.h - это интерфейс нашей библиотеки, который мы в дальнейшем будем использовать в основном приложении.
plugin.h:
#include <QObject>
#include <QtPlugin>
#include "plugin_interface.h"
//Класс будет просто давать возможность задавать значение и возвращать его потом
class ScriptObject : public QObject, ScriptInterface
{
Q_OBJECT
//Указываем интерфейс
Q_INTERFACES(ScriptInterface)
public:
explicit ScriptObject(QObject *parent = 0);
public slots:
void set(quint16 aNewValue) { value = aNewValue; }
quint16 get() { return value; }
QObject* getObject() { return this; }
private:
quint16 value;
};
plugin.cpp:
#include "scriptplugin.h"
//Конструктор обязательно надо описать в cpp файле
ScriptObject::ScriptObject(QObject *parent) :
QObject(parent),
value(0)
{}
//Указываем имя экспортируемого объекта
Q_EXPORT_PLUGIN2(scriptobject, ScriptObject);
plugin_interface.h:
//Все методы класса должны быть виртуальными, т.е. чистое описание интерфейса, который мы потом реализуем в потомке.
class ScriptInterface
{
public:
virtual ~ScriptInterface() {}
virtual quint16 get() = 0;
virtual void set(quint16) = 0;
virtual QObject* getObject() = 0;
};
//Объявляем класс как интерфейсный
Q_DECLARE_INTERFACE(ScriptInterface,
"Script.Plugin/0.1");
Всё, библиотека готова. После компиляции (под Win) получаем два файла plugin.dll и libplugin.a
Создаём еще один проект.
Закидываем в него заголовочный файл с интерфейсом.
Я создал GUI проект с основным классом Widget.
На форму набросал два TextEdit и кнопку.
widget.h:
#include <QWidget>
#include <QScriptEngine>
#include <QDir>
#include <QFileInfoList>
#include <QPluginLoader>
#include <QLibrary>
#include "plugininterface.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
void run(); //Слот в котором будет выполняться скрипт
private:
Ui::Widget *ui;
QPluginLoader *pluginLoader; //Загрузчик библиотеки
};
Реализация.
Предполагается, что библиотека лежит в папке plugins рядом с исполняемым файлом.
Для загрузки библиотеки надо:
QDir pluginsDir(QString("%1/plugins").arg(qApp->applicationDirPath()));
QFileInfoList pluginsFiles = pluginsDir.entryInfoList(QDir::Files|QDir::NoDotAndDotDot,
QDir::Name);
for(int i = 0; i < pluginsFiles.size(); i++)
{
if(QLibrary::isLibrary(pluginsFiles.value(i).absoluteFilePath())) //Проверяем, библиотека ли это
{
pluginLoader = new QPluginLoader(pluginsFiles.value(i).absoluteFilePath()); //Нашли? Загружаем.
break;
}
}
Библиотека загружена (ну или не загружена, если файл не найден)
Далее, получаем объект и загружаем его в скриптовый движок.
if(pluginLoader)
{
QObject *plugin = pluginLoader->instance(); //Получаем корневой объект из библиотеки
if (plugin)
{
ScriptInterface *sc = qobject_cast<ScriptInterface*>(plugin); //Преобразуем корневой объект в наш
QScriptEngine engine;
QScriptValue scObj = engine.newQObject(sc->getObject(), QScriptEngine::ScriptOwnership); //Засовываем наш объект в движок
engine.globalObject().setProperty("ScObj", scObj); //Даём ему имя и регистрируем
QScriptValue result = engine.evaluate(ui->scriptEdit->toPlainText()); //Выполняем (текст берем из первого TextEdit)
//Результат отправляем во второй TextEdit
if(result.isError())
{
ui->logEdit->append(QString("Script error").append(QString::fromLatin1("%1: %2")
.arg(result.property("lineNumber").toInt32())
.arg(result.toString())));
}
else
{
ui->logEdit->append(result.toString());
}
engine.collectGarbage();
}
}
Вот и всё.
Теперь в скрипте можно написать:
ScObj.set(9); ScObj.get();
И он вернёт нам 9.
Нет обратных ссылок на эту запись.
Leave a comment