Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Октябрь 24, 2013, 11:11



Название: Организация скриптов в приложении
Отправлено: Igors от Октябрь 24, 2013, 11:11
Добрый день

Есть 2 вида скриптов. Первый применяется при действии пользователя внутри приложения, напр (псевдокод)
Цитировать
Object2.position = vec3(Object1.position.x, Object1.position.y, Object1.position.z + 100);
Теперь если пользователь интерактивно двигает Object1, то Object2 движется вместе с ним.

Скрипт второго типа как бы просто команда, вызывается пользователем напр из меню и отрабатывет 1 раз, псевдокод
Цитировать
Object1 = CreateObject("Sphere");
Object1.radius = 10;
Object1.move(100, 100, 100);
Пользователь должен иметь возможность создавать, редактировать и удалять скрипты из приложения.

Как бы Вы задумали такое UI (в принципе, функционал)? Какие должны быть бубочки, где и что они должны делать?

Спасибо


Название: Re: Организация скриптов в приложении
Отправлено: sergek от Октябрь 24, 2013, 20:52
Для себя я выбрал такой вариант (см. картинку). Скрипты (Python) хранятся в БД, выбираем в списке (Хранилище сценариев), отображается в окне редактора. Можно создать, редактировать скрипт. В списке скрипты организованы в виде древовидного списка аналогично файловой системе.

Текущий скрипт можно исполнить. Результат - созданный объект (список объектов), который добавляется и отображается в таблице (вкладки ЭПД, ЭСИД), доступен для различных действий (просмотр, печать, редактирование, сохранение в файл и т.д.).

Ваш первый тип скриптов отличается от этого варианта тем, что скрипт можно в процессе исполнения программы динамически извлекать из БД по его id и исполнять (например, когда тащите объект). Легко реализуемо.

Поддержка скриптов - на PythonQt, взаимодействие программы и скриптов - через шлюз, основанный на врапперах классов объектов, используемых и в программе и в скриптах. В документации по PythonQt этого нет, но все просто, достаточно посмотреть пару примеров.
Шлюз обеспечивает функциональность, необходимую для работы с объектами.

Если нужно, могу предоставить ссылку на полный проект в исходниках, где есть и готовый виндовый исполняемый файл.


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 25, 2013, 18:27
Проект не нужен, но все равно спасибо. Да, у Вас все норм (может тулбар здоровый как лапоть, ну то дело вкуса).  Мне держать в базе - не очень с руки. Приложение помещает свои плагины в определенные фолдера - ну и файлы скриптов по аналогиии, почему нет? "Локальные" скрипты проще сохранить в файле проекта. Дерево тоже как-то не лепится к моему случаю, т.к. в обозримом будущем не видно ни одного хоть какого-то "ветвления".

Понимаю что в Вашем случае это "то что надо", просто оно механически не переносится  :) Спасибо


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 28, 2013, 13:43
Поддержка скриптов - на PythonQt, взаимодействие программы и скриптов - через шлюз, основанный на врапперах классов объектов, используемых и в программе и в скриптах. В документации по PythonQt этого нет, но все просто, достаточно посмотреть пару примеров.
Подключил PythonQt и уже прорвался к "hello world" (т.е. могу выполнять простые скрипты из приложения).

Вопрос начинающего (пытона раньше никогда не видел). Вот есть такой скрипт
Цитировать
Object1.position.x = 0  # Object1 - объект основного приложния
Понятно что я могу сделать wrap для Object1, но как это сделать "на лету"? То есть экземпляров (таких как Object1) у меня могут быть тысячи, а в скрипте используется всего 1 - ну не должен же я врапить все тысячи перед каждым выполнением скрипта?

Спасибо  

Edit:
Или я должен действовать примерно так
Цитировать
prj.GetObject("Object 1").position.x = 0
А объект prj отврапить (текущий проект приложения всегда 1)


Название: Re: Организация скриптов в приложении
Отправлено: sergek от Октябрь 29, 2013, 09:30
Цитировать
Object1.position.x = 0  # Object1 - объект основного приложния
Понятно что я могу сделать wrap для Object1, но как это сделать "на лету"? То есть экземпляров (таких как Object1) у меня могут быть тысячи, а в скрипте используется всего 1 - ну не должен же я врапить все тысячи перед каждым выполнением скрипта?
1) Обертка (wrapper) делается не для объекта, а для класса. Т.е. делаете враппер для своего класса, регистрируете в PythonQt, и можете создавать в скриптах сколько угодно объектов этого класса, работать с ними. Есть требование к классам, которые можно регистрировать в PythonQt - эти классы должны наследоваться от QObject.
2) Как правило, требуется возможность передачи данных из программы в Питон (имеется в виду встроенный в программу интерпретатор) и обратно. Такая возможность существует. Как лучше ее организовать, зависит от задачи.
Я не все возможности PythonQt знаю, только те, которые мне потребовались для реализации моего проекта. Поэтому лучше вам сформулировать, что нужно, а я попробую помочь.
3) Посмотрите доку http://pythonqt.sourceforge.net/index.html (http://pythonqt.sourceforge.net/index.html), в частности, раздел "Developer", примеры. Но документация не включает некоторые возможности, например, в ней не описан способ эмуляции свойств (obj.property = v). Можно почитать конференцию Флориана: http://sourceforge.net/p/pythonqt/discussion/631392 (http://sourceforge.net/p/pythonqt/discussion/631392).


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 10:08
Но документация не включает некоторые возможности, например, в ней не описан способ эмуляции свойств (obj.property = v). Можно почитать конференцию Флориана: http://sourceforge.net/p/pythonqt/discussion/631392 (http://sourceforge.net/p/pythonqt/discussion/631392).
Именно на этом я сейчас застрял :) Если нетрудно, укажите топик конференции (пока не нашел). Спасибо

Обертка (wrapper) делается не для объекта, а для класса.
То ясно, просто сейчас в приложении есть какой-то самопальный скриптинг который позволяет обращаться "Object1" при условии что Object1 есть в приложении. В пытоне так нельзя, переменная должна быть объявлена присваиванием (если не так - поправьте)

Большинство объектов у меня статично, их данные известны и неизменны. Но не все - есть плагины которые создают свои данные динамически. Напр в UI плагина пользователь выбрал "сфера" - появился параметр "радиус". Потом поменял на "куб" - уже др. параметры. Я могу это оформить методами (код скрипта)
Цитировать
obj.setParam("radius", 10);
Но пользователю это не очень удобно, он хочет так
Цитировать
obj.radius = 10;
Как бы это порешать?


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 10:28
Именно на этом я сейчас застрял :) Если нетрудно, укажите топик конференции (пока не нашел). Спасибо
http://sourceforge.net/p/pythonqt/discussion/631392/thread/278533e7/

В пытоне так нельзя, переменная должна быть объявлена присваиванием (если не так - поправьте)
Нужно регистрировать все необходимые объекты в контексте питона, тогда к ним можно будет обращаться из скрипта.
Есть 100500 плюсовых объектов, все нужно зарегистрировать.


Название: Re: Организация скриптов в приложении
Отправлено: sergek от Октябрь 29, 2013, 10:34
Но документация не включает некоторые возможности, например, в ней не описан способ эмуляции свойств (obj.property = v). Можно почитать конференцию Флориана: http://sourceforge.net/p/pythonqt/discussion/631392 (http://sourceforge.net/p/pythonqt/discussion/631392).
Именно на этом я сейчас застрял :) Если нетрудно, укажите топик конференции (пока не нашел). Спасибо
http://sourceforge.net/p/pythonqt/discussion/631392/thread/278533e7/ (http://sourceforge.net/p/pythonqt/discussion/631392/thread/278533e7/)

Но пользователю это не очень удобно, он хочет так
Цитировать
obj.radius = 10;
Как бы это порешать?

Простейший пример:

Исходный класс:
Код:
class CPartInfo
{
public:
    // Methods & Properties
    QString PartNo;
    QString PartQuantity;
    QString PartAggregateID;
};
Обертка для него:
Код:
class CPartInfoWrapper : public QObject {
    Q_OBJECT

public slots:
    // конструктор и деструктор
    CPartInfo* new_CPartInfo() { return new CPartInfo(); }
    void delete_CPartInfo(CPartInfo* o) { delete o; }

    // эмуляторы свойств
    QString py_get_PartNo(CPartInfo* o) { return o->PartNo; }
    void    py_set_PartNo(CPartInfo* o, QString value) { o->PartNo = value; }
    QString py_get_PartQuantity(CPartInfo* o) { return o->PartQuantity; }
    void    py_set_PartQuantity(CPartInfo* o, QString value) { o->PartQuantity = value; }
    QString py_get_PartAggregateID(CPartInfo* o) { return o->PartAggregateID; }
    void    py_set_PartAggregateID(CPartInfo* o, QString value) { o->PartAggregateID = value; }
};
Регистрация обертки в PythonQt:
Код:
    // инициализация библиотеки PythonQt
    PythonQt::init(PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut);
    // экземпляр Питона
    context = PythonQt::self()->getMainModule();

    // регистрация прикладных классов
    PythonQt::self()->registerCPPClass("CPartInfo", "","QtCore", PythonQtCreateObject<CPartInfoWrapper>);
    ...
    // импорт всех зарегистрированных классов
    context.evalScript("from PythonQt.QtCore import *");
Если членами классов являются объекты других классов, нужны обертки для них, регистрация, а свойства делаются через указатели:
Код:
    CDepartmentalInfo* py_get_DepartmentalInfo(CED101* o) { return &o->DepartmentalInfo; }
    void    py_set_DepartmentalInfo(CED101* o, CDepartmentalInfo value) { o->DepartmentalInfo = value; }
    CEDRefID* py_get_InitialED(CED101* o) { return &o->InitialED; }
    void    py_set_InitialED(CED101* o, CEDRefID value) { o->InitialED = value; }
В скрипте можно так:
Код:
>part=CPartInfo()
>part.PartNo = '123'
>print part.PartNo
Да, прошу не пинать, что переменные именуются с прописных букв - это отрыжка исходной постановки задачи, основанной на альбоме форматов...  :(


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 11:19
http://sourceforge.net/p/pythonqt/discussion/631392/thread/278533e7/
http://sourceforge.net/p/pythonqt/discussion/631392/thread/278533e7/ (http://sourceforge.net/p/pythonqt/discussion/631392/thread/278533e7/)
Работает, спасибо! Оказывается есть "волшебные слова" py_get_ и py_set_  :)

Есть 100500 плюсовых объектов, все нужно зарегистрировать.
Надо - так надо, но как мне сделать это динамически, на этапе выполнения? Пусть юзверь написал в скрипте
Цитировать
obj = prj.GetObject("Object1")
Вероятно, сейчас будет обращение к атрибутам Object1, и мне известны все их имена и типы данных. Но я не могу их знать до выполнения. Напр в любой момент юзверь может подключить файл плагина который мне был неизвестен но который объявит свои пропердии. Как бы вот тут разрулить?

Спасибо


Название: Re: Организация скриптов в приложении
Отправлено: sergek от Октябрь 29, 2013, 11:36
Надо - так надо, но как мне сделать это динамически, на этапе выполнения? Пусть юзверь написал в скрипте
Цитировать
obj = prj.GetObject("Object1")
Вероятно, сейчас будет обращение к атрибутам Object1, и мне известны все их имена и типы данных. Но я не могу их знать до выполнения. Напр в любой момент юзверь может подключить файл плагина который мне был неизвестен но который объявит свои пропердии. Как бы вот тут разрулить?
Легко. Можно регистрировать не только классы, но и объекты. В этом случае в скриптах эти объекты сразу доступны для использования, а в программе - результат их использования. Примерно так:
Код:
    // объект
    CScriptGateway gate;
    QString gatewayObjName = "gate";

    // регистрация в Питоне класса
    PythonQt::self()->registerClass(&CScriptGateway::staticMetaObject, "QtCore", PythonQtCreateObject<CScriptGateway>);
    // регистрация объекта
    context.addObject(gatewayObjName,&gate);
В скрипте сразу обращаемся к объекту (без использования конструктора):
Код:
>gate.prop1 = val;
>print gate.prop1
А в программе gate.prop1 примет значение val. Наверное ;)
Только для случая регистрации объектов необходимо, чтобы класс этого объекта (CScriptGateway) должен быть порожден от QObject.


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 13:04
Легко. Можно регистрировать ...
Да, работает! И для "цепочек" тоже

Код
C++ (Qt)
CPyProject * prj = new CPyProject();
 
QObject * cls = new QObject();
cls->setProperty("prop2", QVariant(1.0));
 
QVariant var = qVariantFromValue(cls);
prj->setProperty("prop1", var);
 
PythonQt::self()->registerClass(&CPyProject::staticMetaObject, "", PythonQtCreateObject<CPyProject>);
 
theModules[0].addObject("prj", prj);
 
После этого я могу писать в скрипте
Код:
print prj.ptop1.prop2
Круто (червона рута). А могу ли я удалить cls (или использовать локальный) - или этот экземпляр должен существовать?

Спасибо


Название: Re: Организация скриптов в приложении
Отправлено: sergek от Октябрь 29, 2013, 13:12
А могу ли я удалить cls (или использовать локальный) - или этот экземпляр должен существовать?
Пока используете его в скрипте, должен, разумеется. Поэтому объект должен быть в пределах видимости модуля, гда используется Питоновский скрипт.
А вот потом - хз. Если есть addObject, то, скорее всего, должен быть что-нибудь вроде removeObject...


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 16:09
А могу ли я удалить cls (или использовать локальный) - или этот экземпляр должен существовать?
Конечно, для работы с ним из скрипта он должен существовать. Но можно указать питону, что бы он сам уничтожил этот объект, когда последняя ссылка на него исчезнет. Или наоборот, не уничтожил объект, который в скрипте уже не используется, но необходим в хост-программе.


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 17:10
Также я могу комбинировать поля и методы, напр
Код:
print prj.prop1.prop2
print prj.prop1.value()
Однако хотелось бы создавать prop2 динамически, в тот момент когда произошло (скриптовое) обращение к prop1 (возможно кешируя). А иначе я вынужден заряжать и отслеживать все пропердии на все случаи жизни (хотя скрипт может обратится всего к 1-2). Но как отследить "вызов prop1"?

Спасибо


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 17:31
Однако хотелось бы создавать prop2 динамически, в тот момент когда произошло (скриптовое) обращение к prop1 (возможно кешируя).
Ну так у вас вызовется метод объекта вашего врапера, при обращении к prop1. В нем и создавайте динамически объект.


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 17:59
Ну так у вас вызовется метод объекта вашего врапера, при обращении к prop1. В нем и создавайте динамически объект.
Это если пропердия заряжена с помощью py_get_ и py_set_. Но это не очень выгодно, поэтому я просто наследуюсь от QObject и делаю setProperty. Все норм, пытон ее видит, кстати можно и из самого пытона
Код:
prj.setProperty("myProp", 1)
print prj.myProp   # печатает 1
Но так где взять враппер? 


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 18:14
Вы используете объект QObject только для хранения свойств? :)
Вы можете описать свой класс-наследник QObject и задать все необходимые propery с геттерами/сеттерами, которые и будут использоваться из питона.
См.: http://doc.crossplatform.ru/qt/4.6.x/properties.html


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 19:34
Вы используете объект QObject только для хранения свойств? :)
Вы можете описать свой класс-наследник QObject и задать все необходимые propery с геттерами/сеттерами, которые и будут использоваться из питона.
См.: http://doc.crossplatform.ru/qt/4.6.x/properties.html
Могу, но не хочу т.к. мне придется делать "пытон-копии" для многочисленных и обширных классов. Мне же хочется "опубликовать" в пытоне только проперди объектов которые известны в общем виде


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 19:44
Мне же хочется "опубликовать" в пытоне только проперди объектов которые известны в общем виде
И в чем противоречие, пожалуйста хотите общие, хотите частные. :)
Я вам предлагаю описать функции геттер с помощью Qt системы свойст, которая и будет создавать нужный объект "на лету". От вас кроме этого ничего не понадобиться, просто добавить строку Q_PROPERTY. Никаких многочисленных классов описывать не придется.
Из питона это будет как чтение свойства, а в плюсовом контексте будет вызываться метод.


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 20:17
Внутри приложения примерно так

Код
C++ (Qt)
class SomeAppClass {
... // конечно тут есть члены, методы, но они не для юзера
 
ChannelList mChannelList;    // а вот это для него
};
 
// использование
Channel * ch = mChannelList.GetChannelById(ID_COLOR);  // найти канал по Id
if (!ch) return;
string name = ch->GetName();   // имя канала, напр "Color"
ARGB val = ch->GetValue(frameTime);   // значение
Channel * child = ch->GetChild(0);    // возможен child
и.т.д.
 
Поэтому городить сотни геттеров/сеттеров нет смысла


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 20:22
Вопрос один: как это все соотносится с вопросом о динамическом создании объекта при чтении свойства?
:)


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 29, 2013, 20:39
Вопрос один: как это все соотносится с вопросом о динамическом создании объекта при чтении свойства?
:)
Пользователь может написать напр такой скрипт
Код:
obj.Color.red = 255
Но может и не трогать Color или вообще заняться obj2 оставив obj без внимания. Поэтому заряжать в пытон все-все проперди статически (любая может использоваться) совершенно глупо


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 29, 2013, 20:55
Пользователь может написать напр такой скрипт
Код:
obj.Color.red = 255
Но может и не трогать Color или вообще заняться obj2 оставив obj без внимания. Поэтому заряжать в пытон все-все проперди статически (любая может использоваться) совершенно глупо
Что значит глупо? Это обязательное условие. Все действия для вашего объекта должны быть полностью за декларированы в контексте скрипта. Абсолютно все.
То что за вас большую часть работы делает Qt + PythonQt, вовсе не означает что эта работа не делается.

Будет пользователь сейчас что-то вызывать или он это сделает завтра не важно, все должно быть описано перед использованием в скрипте. Поэтому, если пользователь сейчас (или через 10 лет) захочет написать obj.Color.red = 255, то сеттер для такого присвоения должен быть. Его вы можете написать сами, а можете оставить на совести Qt с PythonQt.

Теперь по поводу примера:
Код
C++ (Qt)
Channel * ch = mChannelList.GetChannelById(ID_COLOR);  // найти канал по Id
if (!ch) return;
string name = ch->GetName();   // имя канала, напр "Color"
ARGB val = ch->GetValue(frameTime);   // значение
Channel * child = ch->GetChild(0);    // возможен child
 

Если объект Channel наследник QObject, то нам достаточно добавить описание необходимых свойст и они автоматом станут доступны из питона:
Код
C++ (Qt)
class Channel : public QObject
{
   Q_OBJECT
   Q_PROPERTY( QString name READ GetName WRITE SetName )
   Q_PROPERTY( QColor value READ GetValue WRITE SetValue )
   ...
 
Больше никаких телодвижений не понадобиться, все остальное сделает Qt с PythonQt.

А если Channel не наследник, то вам все равно придется все расписывать руками, каждый сеттер и каждый геттер, не зависимо от того, будет это PythonQt или boost::python.



Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 30, 2013, 11:15
Будет пользователь сейчас что-то вызывать или он это сделает завтра не важно, все должно быть описано перед использованием в скрипте. Поэтому, если пользователь сейчас (или через 10 лет) захочет написать obj.Color.red = 255, то сеттер для такого присвоения должен быть.
Нет, можно делать геттеры/сеттеры динамически, только когда они юзаются в пытоне. Вот фрагмент работающего примера
Код
C++ (Qt)
void MyQtQObjectWrappedCB( QObject * object )
{
CPyObject * py = dynamic_cast <CPyObject *> (object);
if (!py || py->mWrapped) return;
py->mWrapped = true;    // есть анодное!
  py->setProperty("dynaProp", QVariant(1.0));
 
printf("wrapped %p\n", py);
}
...
PythonQt::self()->setQObjectWrappedCallback(MyQtQObjectWrappedCB);
 

Если объект Channel наследник QObject, то нам достаточно добавить описание необходимых свойст и они автоматом станут доступны из питона:
Конечно будут, но такой "механический" перенос ничего не дает, пользователю нужно не "внутреннее устройство", а упрощенный синтаксис. Перелопатить все классы чтобы его добавить - глупо, нужно использовать механизм dynamic properties, для него все есть


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 30, 2013, 11:23
Нет, можно делать геттеры/сеттеры динамически, только когда они юзаются в пытоне. Вот фрагмент работающего примера
Где вы здесь видите динамические геттеры/сеттеры? Здесь вы создаете новое свойство, а геттеры/сеттеры для него сделает Qt, точнее ее система свойств. О чем я и писал выше.

Пример, по крайней мере, странный. Что это дает не понятно. Объект уже существует, у него или есть какие-то свойства или их нет. Почему они должный появляться или исчезать не понятно. Вот есть точка, у нее есть два свойства - координаты x и y. Они есть всегда.


Название: Re: Организация скриптов в приложении
Отправлено: Igors от Октябрь 30, 2013, 11:41
Пример, по крайней мере, странный. Что это дает не понятно. Объект уже существует, у него или есть какие-то свойства или их нет. Почему они должный появляться или исчезать не понятно. Вот есть точка, у нее есть два свойства - координаты x и y. Они есть всегда.
Не всегда
Большинство объектов у меня статично, их данные известны и неизменны. Но не все - есть плагины которые создают свои данные динамически. Напр в UI плагина пользователь выбрал "сфера" - появился параметр "радиус". Потом поменял на "куб" - уже др. параметры.
Да и не в этом даже дело. Просто расписать "все что (может быть) нужно пытону" - уже работа на неделю. Отловить все места где вместо одних параметров могут быть другие - еще неделя.

Хорошо, а как насчет пресловутой "архитектуры"? Как это должно выглядеть с точки зрения приложения. где хранить данные доступные из скриптов?

1) Прямо в самих классах и пытон их меняет. Не верится, как-то стремно
2) Приклеить к классам что-то типа CPyObject *
3) Мапа <объект *, CPyObject *>
4) Еще ходы?

Спасибо
 


Название: Re: Организация скриптов в приложении
Отправлено: Old от Октябрь 30, 2013, 12:37
Не всегда
Всегда.

Хорошо, а как насчет пресловутой "архитектуры"? Как это должно выглядеть с точки зрения приложения. где хранить данные доступные из скриптов?
Не знаю с какого боку тут архитектура, но все это дело со скриптами как раз делается для того, что бы пользователь с минимальными усилиями мог работать с внутренними структурами программы, поэтому первый вариант самый оптимальный по соотношению кодирование/результат. Если пользователю сложно напрямую работать с объектами Host-программы, то можно сделать промежуточный слой, но тут придется весь этот простой слой кодировать (будь то новые классы или упрощенные функции).