Отправляем объект из внешней библиотеки в скрипт
В 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