Как и обещал, привожу пример загрузки gui при помощи Qt Script.
Для начала нам нужно расширить базовые возможности Qt Script, и добавить ряд глобальных объектов, для более гибкого проектирования.
Я написал небольшое расширение(ScriptEngine) для стандартного QScriptEngine, которое имеет рад особенностей:
1. Поддерживает систему плагинов.
Это очень важно, т.к. это позволяет нам расширять функционал движка "на ходу". Т.е. если нам чегото не хватает, то мы это пишем и подгружаем(хоть в динамике).
На данный момент мой движок поддерживает 2 типа плагинов:
- статические;
- контекстные;
Их различие в том, что статический плагин может содержать в себе только набор некоторых функций, а контекстный, может еще содержать в себе некоторые классы, которые он(плагин) может зарегистрировать в контексте движка, и соответственно эти классы станут доступны непосредственно из скрипта.
2. Мультиконтекстность. Это значит что каждый скрипт, может выполнятся в своем независимом контексте.
Ниже декларация движка:
class ScriptEngine : public QObject {
Q_OBJECT
Q_PROPERTY( bool autoDrop READ autoDrop WRITE setAutoDrop )
Q_PROPERTY( bool internalErrorOutput READ internalErrorOutput WRITE setInternalErrorOutput )
protected:
QScriptEngine * se;
bool engineautodrop, showerror;
static QList<QObject*> plugins;
static QHash<QString,ScriptEngine*> crengines;
static ScriptEngine * firstEngine;
public:
ScriptEngine( QObject * parent=0 );
~ScriptEngine();
bool autoDrop() { return engineautodrop; }
bool internalErrorOutput() { return showerror; }
Q_INVOKABLE static ScriptEngine * getFirstEngine( QObject * parent=0 );
Q_INVOKABLE static ScriptEngine * newEngine( QString name );
Q_INVOKABLE static ScriptEngine * engine( QString name );
Q_INVOKABLE static bool dropEngine( QString name );
Q_INVOKABLE static bool killEngine( QString name );
Q_INVOKABLE static void dropAll();
Q_INVOKABLE static void killAll();
Q_INVOKABLE static QStringList engineNames();
Q_INVOKABLE static bool addPlugin( QString filename );
Q_INVOKABLE static void loadAllPluginFromDir( QString path );
protected:
static bool addPlugin( QObject * plugin );
QStringList configure( bool v=false );
private:
void setObjectName( QString name ) { QObject::setObjectName( name ); }
public slots:
void setAutoDrop( bool e ) { engineautodrop = e; }
void setInternalErrorOutput( bool e ) { showerror = e; }
void dropEngine();
void executeEngine( QString str, QStringList args=QStringList(), bool v=false );
signals:
void error( int line, QString text, QString enginename );
};
Не буду углубляться в детали, скажу лиш то, что для постановки скрипта на выполнение требуется вызвать функцию executeEngine, которая принимает ряд аргументов:
str - если имя файла, то выполняется файл, если нет, то интерпретируется как скрипт;
args - аргументы, первый(если есть) - имя функции в скрипте, которую необходимо запустить, остальные параметры передаются как аргументы для данной функции;
v - для отладки, нам не интересен.
Допустим движок удовлетворяет нас своими возможностями, теперь нам необходим загрузщик ui - файлов. А так как в нашем движке есть интерфейс плагинов, загрузчик ui - файлов будет выполнен именно в виде плагина для движка.
Интерфейс плагина:
class StaticModuleInterface {
public:
virtual ~StaticModuleInterface() {}
virtual QString moduleName() = 0; // возвращает имя модуля, по которому можно будет обращатся к нему из скрипта
};
Q_DECLARE_INTERFACE( StaticModuleInterface, "RES.ScriptEngine.StaticModuleInterface/1.0" )
Выше я упоминал о 2 типах плагинов, но тут только 1, статический, контекстный нам пока и не нужен))
загрузчик ui - файлов:
class UiLoaderPlugin :
public QObject,
public StaticModuleInterface
{
Q_OBJECT
Q_INTERFACES(StaticModuleInterface)
Q_PROPERTY( QString workingDirectory READ workingDirectory )
Q_PROPERTY( QStringList pluginPaths READ pluginPaths )
Q_PROPERTY( QStringList availableWidgets READ availableWidgets )
private:
QUiLoader * uiloader;
public:
UiLoaderPlugin( QObject * parent=0 );
~UiLoaderPlugin();
QString moduleName(); // вернет uiloader, подробно смотри выше StaticModuleInterface
QStringList pluginPaths();
QString workingDirectory();
QStringList availableWidgets();
Q_INVOKABLE QWidget * load( QString filename, QWidget * parent=0 );
Q_INVOKABLE QAction * createAction( QObject * parent = 0, QString name = QString() );
Q_INVOKABLE QActionGroup * createActionGroup( QObject * parent = 0, QString name = QString() );
Q_INVOKABLE QLayout * createLayout( QString className, QObject * parent = 0, QString name = QString() );
Q_INVOKABLE QWidget * createWidget( QString className, QWidget * parent = 0, QString name = QString() );
public slots:
void addPluginPath( QString path );
void clearPluginPaths();
void setWorkingDirectory( QString path );
void appExec();
void appQuit();
};
В принципе если вы в ассистенте посмотрите класс QUiLoader, то особых отличий от вышеприведенного плагина не увидите, плагин выполняет роль транслятора функциональности класса QUiLoader, для движка.
+ 2 функции, которые будут полезны при работе с gui appExec - QApplication::exec() и appQuit - QApplication::quit().
Ну а далее все просто до безобразия:
1. Засовываем загрузщик ui - файлов в шаред либрари.
2. Пишем программку которая будет выполнять роль интерпретатора.
#include "ScriptEngine.h"
#include <QApplication>
#include <QStringList>
int main( int a, char ** b ) {
QApplication app( a, b );
QStringList args( app.arguments() );
args.takeAt( 0 );
if ( args.count() == 0 )
return 0;
ScriptEngine e;
e.loadAllPluginFromDir( app.applicationDirPath() );
if ( args.count() > 0 ) {
QString filename = args.takeAt( 0 );
e.executeEngine( filename, args, v );
}
return 0;
}
Тут еще стоит обратить внимание на функцию loadAllPluginFromDir. Она пытается загрузить все библиотеки из указанного каталога, если загружаемая библиотека - есть плагин, то загружает его, если нет то игнорирует.
3. Нарисовать форму в дизайнере, и написать скрипт который будет ее подгружать.
Собственно привожу уже заезженный пример загрузки окна с кнопкой(кнопка называется - myButton):
function myButtonClick() {
print( "Button pressed!" );
}
var win = uiloader.load( "myform.ui" );
if ( win == null ) {
print( "can't load ui-file" );
uiloader.appQuit();
}
var button = win.findChild( "myButton" );
if ( button == null ) {
print( "bad ui-file" );
uiloader.appQuit();
}
button.clicked.connect( myButtonClick() );
win.show();
uiloader.appExec();
Соответственно в функции myButtonClick может быть все что угодно. Также никто не мешает коннектить сигнал clicked кнопки к другим объектам, в том числе и плагинам/модулям движка.
Приклеел исходники того что я тут втирал))