Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Ноябрь 07, 2013, 19:00



Название: Синхронизировать property
Отправлено: Igors от Ноябрь 07, 2013, 19:00
Добрый день

Занимаюсь прикруткой скриптов, и все вроде удачно "легло", но вылазит противная мелочевка (не вписывающаяся в общую схему  :'(). Мои объекты мапируются в пытон переменные используя проперди, напр
Код:
py> obj.Geometry.Position.x = 100
Объект obj имеет property Geometry которая порождена от QObject и имеет property Position (типа QVector3D). Все работает. Однако пользователь может переключить управление объектом, в результате вместо одной проперди Position будут 3 другие, в пытоне выглядит так
Код:
py>obj.Geometry.Yaw = 100      # так называется x ;-)
py>obj.Geometry.Pitch = 0        # y
py>obj.Geometry.Roll = 0          # z
Тоже работает. Но юзверю неинтересно все время набирать "Geometry" и редактировать скрипты при смене режима. Он хочет писать так
Код:
py> obj.Position.x = 100
Ну добавил property Position (на том же уровне что и Geometry), но что делать в такой ситуации
Код:
py> obj.Position.x = 100
py> test = obj.Geometry.Yaw
То есть 2 проперди должны шарить одни данные. При этом пропердь Yaw создается только когда происходит обращение к проперди Geometry

Спасибо


Название: Re: Синхронизировать property
Отправлено: sergek от Ноябрь 12, 2013, 10:15
Но юзверю неинтересно все время набирать "Geometry" и редактировать скрипты при смене режима. Он хочет писать так
Код:
py> obj.Position.x = 100
Ваши пользователи желают странного :)
Но можно сделать с использованием врапперов:
Код:
// прикладные классы
class Position
{
public:
    int x;
    int y;
    int z;
};

class Geometry
{
public:
    Position position;
};

class Queer
{
public:
    Geometry geometry;
};
Код:
// врапперы прикладных классов
class PositionWrapper : public QObject {
    Q_OBJECT

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

    // эмуляторы свойств
    int py_get_x(Position* o) { return o->x; }
    void    py_set_x(Position* o, int value) { o->x = value; }
    int py_get_y(Position* o) { return o->y; }
    void    py_set_y(Position* o, int value) { o->y = value; }
    int py_get_z(Position* o) { return o->z; }
    void    py_set_z(Position* o, int value) { o->z = value; }
};

class GeometryWrapper : public QObject {
    Q_OBJECT

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

    // эмуляторы свойств
    int py_get_yaw(Geometry* o) { return o->position.x; }
    void    py_set_yaw(Geometry* o, int value) { o->position.x = value; }
    int py_get_pitch(Geometry* o) { return o->position.y; }
    void    py_set_pitch(Geometry* o, int value) { o->position.y = value; }
    int py_get_roll(Geometry* o) { return o->position.z; }
    void    py_set_roll(Geometry* o, int value) { o->position.z = value; }

    Position* py_get_position(Geometry* o) { return &o->position; }
    void    py_set_position(Geometry* o, Position value) { o->position = value; }
};

class QueerWrapper : public QObject {
    Q_OBJECT

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

    // эмуляторы свойств
    Geometry* py_get_geometry(Queer* o) { return &o->geometry; }
    void    py_set_geometry(Queer* o, Geometry value) { o->geometry = value; }

    Position* py_get_position(Queer* o) { return &o->geometry.position; }
    void    py_set_position(Queer* o, Position value) { o->geometry.position = value; }
};
Код:
    // регистрация прикладных классов
    PythonQt::self()->registerCPPClass("Position", "","QtCore", PythonQtCreateObject<PositionWrapper>);
    PythonQt::self()->registerCPPClass("Geometry", "","QtCore", PythonQtCreateObject<GeometryWrapper>);
    PythonQt::self()->registerCPPClass("Queer", "","QtCore", PythonQtCreateObject<QueerWrapper>);
Теперь можно и так, и так:
Код:
py>obj = Queer()
py>obj.geometry.position.x = 100
py>print obj.geometry.yaw
100

py>obj.geometry.pitch = 200
py>print obj.geometry.position.y
200


Название: Re: Синхронизировать property
Отправлено: Igors от Ноябрь 13, 2013, 11:38
Код:
class GeometryWrapper : public QObject {
    Q_OBJECT

public slots:
...
    void    py_set_yaw(Geometry* o, int value) { o->position.x = value; }
А что если данный экземпляр Geomеtry не имеет атрибута yaw? (а др экземпляр имеет). Надо выбрасывать exception, но как выскочить на данные приложения?

Та же проблема приходит с др стороны.
Код:
class PositionWrapper : public QObject {
    Q_OBJECT

public slots:
    void    py_set_x(Position* o, int value) { o->x = value; }
Да, если просто мапить переменные - все прекрасно, после выполнения скрипта они заполнены данными. Но мне нужно по присвоению атрибута немедленно произвести действия. Пример: скрипт изменил позицию объекта "Сфера" который в приложении parent других объектов - значит все они должны быть тоже передвинуты. Если какие-то из них тоже участвуют в скрипте - их позиции должны быть изменены и там. Напрашивается так
Код:
void py_set_x(Position* o, int value) 
{
  o->x = value;
  o->p->DoModifyX();   // p - указатель на данные приложения
}
Но ведь экземпляр Position может быть просто пытон-переменной, которая к приложению не имеет отношения, напр
Код:
py> test = Position()
И что тогда  ???


Название: Re: Синхронизировать property
Отправлено: sergek от Ноябрь 13, 2013, 18:19
Напрашивается так
Код:
void py_set_x(Position* o, int value) 
{
  o->x = value;
  o->p->DoModifyX();   // p - указатель на данные приложения
}
Но ведь экземпляр Position может быть просто пытон-переменной, которая к приложению не имеет отношения, напр
Код:
py> test = Position()
И что тогда  ???
Как вариант, "p" сделать статическим членом Position, тогда все экземпляры Position будут иметь к нему доступ. Можно вообще сделать отдельный класс, собрать в нем статические данные и сделать его ответственным за обмен между скриптами и приложением. Будет у вас некий шлюз. Могу даже подарить название класса - ScriptGateway ;)


Название: Re: Синхронизировать property
Отправлено: Igors от Ноябрь 14, 2013, 01:22
Как вариант, "p" сделать статическим членом Position, тогда все экземпляры Position будут иметь к нему доступ. Можно вообще сделать отдельный класс, собрать в нем статические данные и сделать его ответственным за обмен между скриптами и приложением. Будет у вас некий шлюз. Могу даже подарить название класса - ScriptGateway ;)
Так а что мне даст статический член?
Код:
py> test = Position()    # это просто пытон-переменная, 
py> obj1.Geometry.Position = test   # а здесь я должен реагировать на изменение
py> obj1.Geometry.Yaw = 10       # а здесь проверить есть ли Yaw для obj1       


Название: Re: Синхронизировать property
Отправлено: sergek от Ноябрь 14, 2013, 09:24
Так а что мне даст статический член?
Здесь:
Код:
py> test = Position() 
py> obj1.Geometry.Position = test
ничего.
А здесь:
Код:
void py_set_x(Position* o, int value) 
{
  o->x = value;
  o->p->DoModifyX();   // p - указатель на данные приложения
}
позволит выполнить DoModifyX().
Ну, а по поводу
Цитировать
А что если данный экземпляр Geomеtry не имеет атрибута yaw? (а др экземпляр имеет).
я и в первый раз ничего не понял...
 


Название: Re: Синхронизировать property
Отправлено: Igors от Ноябрь 14, 2013, 15:22
позволит выполнить DoModifyX().
Если p - статический, то DoModifyX() будет выполняться всегда, то что он должен делать  ???

я и в первый раз ничего не понял...
Код:
py> obj.Geometry.Position.x = 1   # Ок если obj имеет флаг "implicit" иначе должна быть ошибка
py> obj.Geometry.yaw = 1       # Ок если obj имеет флаг "explicit" иначе должна быть ошибка
py> obj.Position.x = 1         # универсальный вариант, сам разбирается с флагом
Все 3 варианта должны поддерживаться. Все 3 присаивают одно и то же поле пытон-переменной, но в приложении соответствуют разным данным. Флаг implicit/explicit устанавливается юзером в приложении для каждого объекта индивидуально и не меняется во время выполнения скрипта

А так все здорово и пытон очарователен, вот только эти противные "мелочи"  :'(


Название: Re: Синхронизировать property
Отправлено: sergek от Ноябрь 15, 2013, 10:38
Если p - статический, то DoModifyX() будет выполняться всегда, то что он должен делать  ???
хз. это ваша функция.
Цитировать
А так все здорово и пытон очарователен...
На этой ноте и закончим.


Название: Re: Синхронизировать property
Отправлено: Igors от Ноябрь 15, 2013, 13:16
Если p - статический, то DoModifyX() будет выполняться всегда, то что он должен делать  ???
хз. это ваша функция.
Я имел ввиду static один на всех, выскочить на модифицируемый объект приложения не удается

На этой ноте и закончим.
Да, видимо слишком специфично. Как-то сделал, не блеск, но работает. Спасибо за помощь


Название: Re: Синхронизировать property
Отправлено: sergek от Ноябрь 16, 2013, 10:37
Вот еще, может, пригодится - шаблон для изготовления врапперов, я использую его по принципу "copy/paste/replace":
Код:
// шаблон декоратора. Наследует либо QObject, либо враппер базового класса
#include <QObject>
#include "$ClassType$.h"
#include "wrap_ed_objects.h"
//---------------------------------------------------------------------------

class $ClassType$Wrapper : public $BaseType$Wrapper {
    Q_OBJECT

public slots:
    // конструктор и деструктор
    $ClassType$* new_$ClassType$() { return new $ClassType$(); }
    void delete_$ClassType$($ClassType$* o) { delete o; }

    // эмуляторы свойств
    QString py_get_$Property$($ClassType$* o) { return o->$Property$; }
    void    py_set_$Property$($ClassType$* o, QString value) { o->$Property$ = value; }

    $PropertyType$* py_get_$Property$($ClassType$* o) { return &o->$Property$; }
    void    py_set_$Property$($ClassType$* o, $PropertyType$ value) { o->$Property$ = value; }
};
//---------------------------------------------------------------------------


Название: Re: Синхронизировать property
Отправлено: Igors от Ноябрь 16, 2013, 11:47
Вот еще, может, пригодится - шаблон для изготовления врапперов,
А как "запускаете сенокосилку"?


Название: Re: Синхронизировать property
Отправлено: sergek от Ноябрь 16, 2013, 16:48
Да простят меня... Ну так:
1. Нужен враппер для класса Geometry.
2. Добавляю в проект класс C++ с именем GeometryWrapper.
3. Вместо объявления класса копирую туда указанный шаблон, в .cpp удаляю конструктор (там остается только #include "geometrywrapper.h"). Можно вообще cpp удалить, если методы враппера тривиальные.
4. Подсчитываю, сколько нужно эмулировать простых свойств ($Property$), сколько свойств с типом классов ($PropertyType$). В шаблоне повторяю пары get/set столько, сколько насчитал.
5. Делаю контекстную замену $ClassType$ на Geometry.
6. Если Geometry наследует класс, для которого есть враппер, заменяю $BaseType$ на него, иначе -на QObject.
7. Для каждого свойства заменяю $Property$ в паре get/set на имя этого свойства, например на posx, posy, posz.
8. Если есть свойства с типом классов, заменяю $PropertyType$ на нужный класс, например, Position.
Далее нужно зарегистрировать класс, для которого создан враппер.
Последнее - у меня все простые свойства имеют тип QString. Если у вас не так, значит, делаем еще замену типов.
Достоинство этого "метода" - тупость, а, значит, минимум ошибок. Можно и автоматизировать (кстати, в составе PythonQt есть генератор - не разбирался, может, и оно. Разберетесь - сообщите), но ради полсотни классов, для которых мне нужно было написать врапперы, было лень. пересчитал - оказалось, 70. нифига себе...


Название: Re: Синхронизировать property
Отправлено: Igors от Ноябрь 16, 2013, 19:21
но ради полсотни классов, для которых мне нужно было написать врапперы, было лень. пересчитал - оказалось, 70. нифига себе...
У меня 3 (делал все на пропердях), поэтому автоматизация не так актуальна :)   

Да простят меня...
Понял, не буду больше надоедать :) Спасибо