Пятница, 19.04.2024, 23:31
Приветствую Вас Гость

Программирование

Меню сайта
Наш опрос
Оцените мой сайт
Всего ответов: 0
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Главная » 2015 » Сентябрь » 14 » QT scripting + GUI
09:30
QT scripting + GUI
Захотелось мне как то в Qt скрипте создать в диалоге дополнительные элементы управления, чуть чуть порывшись в документации, понял что все не так уж тривиально. Свои "терзания" описал здесь, может кому ни будь поможет.Все сведения почерпнул из статьи "Making Applications Scriptable".Задался целью, что бы работал такой скрипт: var myBtn = new QPushButton(mainWindow); var geometry = myBtn.geometry; print(geometry); geometry.x = 50; //geometry.y = 30; myBtn.geometry = geometry; myBtn.geometry.y = 30; myBtn.text = "My button"; 1. Ну запустить скрипт и передать ему указатель на mainWindow оказалось просто: void registerGuiTypes(QScriptEngine *engine); QScriptValue evaluateFile(QScriptEngine *engine, const QString &fileName); int main(int argc, char *argv[]) { QApplication a(argc, argv); QScriptEngine engine; MainWindow w; /**Init engine*/ //Тут кое какие манипуляции ... registerGuiTypes(&engine); //Тут самое интересное, см дальше /**Put application & mainWindow to engine*/ engine.globalObject().setProperty("application", engine.newQObject(&a)); engine.globalObject().setProperty("mainWindow", engine.newQObject(&w)); QScriptValue result = evaluateFile(&engine, "gui_test.js"); if (result.isError()) { QMessageBox::critical(0, "Evaluating error", result.toString(), QMessageBox::Yes); } w.show(); return a.exec(); } Функция запуска файла: /**Запуск скрипта из файла*/ QScriptValue evaluateFile(QScriptEngine *engine, const QString &fileName) { QFile file(fileName); if(!file.open(QIODevice::ReadOnly)) { return engine->evaluate(QString("throw \"Error open file %1\"").arg(fileName)); } return engine->evaluate(file.readAll(), fileName); } Что бы скрипт умел создавать элементы управления (var myBtn = new QPushButton(mainWindow)), сделал такой макрос: /*----------------------------------------------------------------------------*/ #define ADD_QWIDGET_WRAPER(type) \ class type ## Wrapper\ {\ public:\ static QScriptValue objConstructor(QScriptContext *context, QScriptEngine *engine)\ {\ QWidget *parent = qobject_cast<QWidget *>(context->argument(0).toQObject());\ QWidget *object = new type(parent);\ return engine->newQObject(object, QScriptEngine::ScriptOwnership);\ }\ };\ do\ {\ QScriptValue ctor = engine->newFunction(type##Wrapper::objConstructor);\ QScriptValue metaObject = engine->newQMetaObject(&type::staticMetaObject, ctor);\ engine->globalObject().setProperty(#type, metaObject);\ } while(0) /*----------------------------------------------------------------------------*/ и в тело registerGuiTypes добавил: ADD_QWIDGET_WRAPER(QPushButton); (не забываем нужные инклуды, напр. #include - об этом не пишу)Скрипт имеет доступ ко всем свойствам наследников QObject и к методам объявленным как слоты. С простыми типами пропертей, как то целые и строки проблем нет, например myBtn.text = "My button"; Вся сложность заключается в том, как работать со свойствами сложных типов, например свойство geometry с типом QRect.Для определения пользовательских типов в скрипте есть два метода:1. с помощью qScriptRegisterMetaType - регистрируется функции преобразования из скриптового объекта в объект С++ и обратно.2. с помощь регистрации прототипа setDefaultPrototype.Я попробовал оба способа, и пока не совсем понял, какой лучше.Для регистрации типа QRect я использовал определения классов со статическими методами только для удобства определения оберток для нескольких типов. Итак первый способ: Q_DECLARE_METATYPE(QRect); Q_DECLARE_METATYPE(QRect*); class QRectWrapper { //Конструктор. static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine) { QRect r; int count = context->argumentCount(); if(count x", s.x()); obj.setProperty("y", s.y()); obj.setProperty("width", s.width()); obj.setProperty("height", s.height()); obj.setProperty("toString", engine->newFunction(toString)); return obj; } //------------------------- static void fromScriptValue(const QScriptValue &obj, QRect &s) { s.setX(obj.property("x").toInt32()); s.setY(obj.property("y").toInt32()); s.setWidth(obj.property("width").toInt32()); s.setHeight(obj.property("height").toInt32()); } //------------------------- static QScriptValue toString(QScriptContext *context, QScriptEngine *engine) { QRect r; fromScriptValue(context->thisObject(), r); return QScriptValue(QString("QRect(%1,%2,%3,%4)").arg( QString::number(r.x()), QString::number(r.y()), QString::number(r.width()), QString::number(r.height()))); } //--------------------------- public: static void registerWrapper(QScriptEngine *engine) { //Регистрирую преобразование qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); //регистрирую конструктор engine->globalObject().setProperty("QRect", engine->newFunction(ctor)); }; }; /*----------------------------------------------------------------------------*/ Метод toString добавил для того, что бы в скрипте можно было сделатьprint(geometry);аналог __str__(self) в питоне.Конструктор регистрирую на всякий случай, что бы в скрипте можно было сделать new QRect()и в тело registerGuiTypes добавил: QRectWrapper::registerWrapper(engine); Второй способ: Q_DECLARE_METATYPE(QRect); Q_DECLARE_METATYPE(QRect*); class QRectPrototype { //--------------------------- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine) { QRect r; int count = context->argumentCount(); if(count QRect.prototype.toString: this object is not a QRect"); return QScriptValue(QString("QRect(%1,%2,%3,%4)").arg( QString::number(r->x()), QString::number(r->y()), QString::number(r->width()), QString::number(r->height()))); } //--------------------------- static QScriptValue x(QScriptContext *context, QScriptEngine *engine) { QRect *r = qscriptvalue_cast<QRect *>(context->thisObject()); if(r QRect.prototype.x: this object is not a QRect"); if(context->argumentCount()) r->setX(context->argument(0).toInt32()); return QScriptValue(r->x()); } //--------------------------- static QScriptValue y(QScriptContext *context, QScriptEngine *engine) { QRect *r = qscriptvalue_cast<QRect *>(context->thisObject()); if(r QRect.prototype.y: this object is not a QRect"); if(context->argumentCount()) r->setY(context->argument(0).toInt32()); return QScriptValue(r->y()); } //--------------------------- static QScriptValue width(QScriptContext *context, QScriptEngine *engine) { QRect *r = qscriptvalue_cast<QRect *>(context->thisObject()); if(r QRect.prototype.width: this object is not a QRect"); if(context->argumentCount()) r->setWidth(context->argument(0).toInt32()); return QScriptValue(r->width()); } //--------------------------- static QScriptValue height(QScriptContext *context, QScriptEngine *engine) { QRect *r = qscriptvalue_cast<QRect *>(context->thisObject()); if(r QRect.prototype.height: this object is not a QRect"); if(context->argumentCount()) r->setHeight(context->argument(0).toInt32()); return QScriptValue(r->height()); } //--------------------------- public: static void registerPrototype(QScriptEngine *engine) QScriptValue::PropertySetter); engine->setDefaultPrototype(qMetaTypeId<QRect>(), prototype); //регистрирую конструктор QScriptValue ct = engine->newFunction(ctor); ct.setProperty("prototype", prototype); engine->globalObject().setProperty("QRect", ct); }; /*----------------------------------------------------------------------------*/ и в тело registerGuiTypes добавил: QRectPrototype::registerPrototype(engine); Нужно добавить, что авторы рекомендуют свой прототип делать наследников от классов QScriptable и QObject. Пример - Default Prototypes Example.При обоих подходах не работает строка в скрипте myBtn.geometry.y = 30; но я подозреваю, что это баг самого движка: выполняется как то так: temp = propertyGet(myBtn, geometry) propertySet(temp, y, 30)
Просмотров: 429 | Добавил: admin | Рейтинг: 0.0/0
Всего комментариев: 0
avatar
Вход на сайт

Поиск
Календарь
«  Сентябрь 2015  »
ПнВтСрЧтПтСбВс
 123456
78910111213
14151617181920
21222324252627
282930
Архив записей