Russian Qt Forum
Ноябрь 23, 2024, 07:20 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Как передавать событие QEvent через несколько объектов?  (Прочитано 8441 раз)
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« : Ноябрь 21, 2012, 18:56 »

Привет всем. Возникла такая задачка:

Есть объект Controller, он может генерить некоторое событие.
Есть много объектов CustomAction, которые должны принимать это событие.
Есть обект Proxy, через которые Controller связан с CustomAction посредством агрегации. Т.е. Controller и CustomActionы хранят в себе указатель на Proxy.
Вот что представляет собой мое событие
Код:
class RAILCORESHARED_EXPORT CAbstractEvent : public QEvent
{
    QString         fsenderTag;        //кто отправляет (записывает отправитель свой tag)
    QStringList     fdestinationTag;   //кто должен получить (tag приемника, можеть быть больше одного)
    QVariantHash    fvarData;          //где хранятся параметры

public:

    static const int customType = 1003;
    CAbstractEvent(QEvent::Type InitType);

    const   QString         &   senderTag()     const;
    const   QStringList     &   destiantionTag()const;
    const   QVariantHash    &   varData()       const;

    void    setSenderTag(const QString & Value);
    void    setDestinationTag(const QStringList & Value);
    void    setVarData(const QVariantHash & Value);
};

Что нужно: при создании события указать в destination текстовую строку(или строки)-описатель, по которому можно найти нужный CSceneAction.

Как сейчас сделано:
Код:
bool CMVCEventProxy::event(QEvent *e)
{
    qDebug("type %d",e->type());
    if(e->type() == static_cast<QEvent::Type>(CAbstractEvent::customType))
    {
        CAbstractEvent  *   abstractEvent = static_cast<CAbstractEvent*>(e);
        {
            CAbstractSceneAction    *   targetSceneAction;
            CGenericRailController  *   targetRailController;

            foreach(const QString & currentDestinationTag, abstractEvent->destiantionTag())
            {
                //сначала смотрим по action в качестве адресата
                if((targetSceneAction = factionsHash.value(currentDestinationTag,NULL)))
                {
                    QApplication::postEvent(targetSceneAction,abstractEvent); //отправляем событие дальше на сцену
                    continue;
                }

                if((targetRailController = fcontrollersHash.value(currentDestinationTag,NULL)))
                {
                    QApplication::postEvent(targetRailController,abstractEvent); //отправляем контроллеру
                    continue;
                }
            }
        }
        return  false;
    }
    else
        return  QObject::event(e);
}

Т.е. я ловлю событие, и перенапрвляю его уже в нужный CSceneAction.
Как я и догадывался, такая фишка не прокатывает - сегментация.
Подскажите, как правильно такое сделать?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #1 : Ноябрь 21, 2012, 18:58 »

Как генерится событие в контроллере
Код:
 ...
if(manevrWasChanged && !lightObject->manevrDestination().isEmpty())
            {
                CAbstractEvent  *   setupManevrRouteEvent = new CAbstractEvent(static_cast<QEvent::Type>(CAbstractEvent::customType));
                setupManevrRouteEvent->setSenderTag(tag());
                destinations<<"com.action.dsp.manevr_route"; //это кто должен словить это соыбтие
                destinations<<"com.action.dsp.manevr_route_auto";
                QVariantHash    params;
                params["begin_light"] = lightObject->objectId();
                params["end_light"] = lightObject->manevrDestination();
                setupManevrRouteEvent->setVarData(params);
                setupManevrRouteEvent->setDestinationTag(destinations);
                qDebug("postEvent");
                QApplication::postEvent(eventProxy(),setupManevrRouteEvent);
            }
            else ...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Ноябрь 21, 2012, 19:33 »

Ну шансы на "конкретный" ответ здесь невелики - слишком много специфики проекта, которую никто кроме Вас не знает. Поэтому "взагалi"

- вызывающе выглядят static_cast как впрочем и выборки из хеша

- не смущает ли Вас что событийный механизм используется для целей далеких от UI? Конечно это не запрещено, но по-взрослому надо строить граф зависимостей, проверять его на зацикливание, а потом процессировать.

 
Записан
Bepec
Гость
« Ответ #3 : Ноябрь 21, 2012, 19:58 »

Я бы сказал проще - у вас сложный довольно проект для понимания.

Вы сыпете кодом, в котором всё понятно только (ну и тем, кто работает с вами) разработчикам сего чуда Улыбающийся

Локализуйте проблему. Сделайте лёгкий пример без лишней мутотени с заглушками на ожидаемом результате. Выложите сие творение. (именно в такой последовательности)

Я вот лично не поручусь, что сегментация идёт в Qt, а не в ваших чудодействиях.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Ноябрь 21, 2012, 20:19 »

Локализуйте проблему. Сделайте лёгкий пример без лишней мутотени с заглушками на ожидаемом результате. Выложите сие творение. (именно в такой последовательности)
Fregloin, не поймали ли Вы себя на мысли что это нереально? Впрочем как и для многих моих проектов Улыбающийся Не все вещи должны быть просты и очевидны, это нормально. Но все же когда нельзя изложить примитивно - это плохо, тревожный "симптом". Мне кажется Вы увлеклись "логикой интерпретирования", ну максимальная гибкость и все такое. Как человек в свое время работавший на AutoLisp соглашусь - это красиво. Но в то же время мой личный опыт показал - только неск процентов из заложенных возможностей будут использованы.А вот отсутствие жесткой типизации колет довольно больно  Улыбающийся
Записан
_OLEGator_
Гость
« Ответ #5 : Ноябрь 21, 2012, 20:50 »

Помоему, если я не ошибаюсь, надо создавать новое событие, а точнее для каждого объекта свое событие. Так действует стандартный механизм Qt - он удаляет объект QEvent.
Записан
Bepec
Гость
« Ответ #6 : Ноябрь 21, 2012, 21:14 »

to Igors - поправьте своё сообщение и смените объект претензии на меня Улыбающийся

Нет, не поймал себя на мысли. Насколько я вижу, тут используются самописные классы в размере 2 штук, которые хзчто делают и являются чёрными ящиками.

Проблема же в передаче сообщения дальше. Убираем чёрные ящики, убираем разделение, получаем простейший пример.

Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #7 : Ноябрь 22, 2012, 12:28 »

Перефразирую первый пост по другому:

Есть несколько классов объектов, которые должны посылать друг другу сообщения.
Объекты этих классов напрмямую не связаны между собой. При том, при смене режима работы, одни объекты могут выгружаться, другие загружаться посредством dll.
У каждого из этих объектов есть свой tag - просто строка определенного формата. По сути что нужно: "подписать" по тегам эти объекты в некоторый общий диспетчер своих событий.
В определенный момент времени (смена состояния например) один из объектов говорит, что нужно по такому то предполагаемому тегу отправить сообщение. Сообщение шлётся в диспетчер.
Диспетчер смотрит, если у него объект с таким тегом, и если есть, перенаправялет это событие адресату (адресат и отправитель могут быть как одного класса, так и разных, не связанных между собой). Надеюсь доходчиво написал.

Что то подобное есть в iOS, когда можно слать нотификации объектам с определенным тегом.
Записан
Bepec
Гость
« Ответ #8 : Ноябрь 22, 2012, 13:05 »

Вы описали простейший диспетчер. Который в качестве идентификаторы использует tag.

Помоему гораздо проще писать функции с процедурой обработки своих евентов (1 функция с обработкой), чем издеваться над QEvent.  Или у вас есть какие то аргументы за и против?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #9 : Ноябрь 22, 2012, 13:49 »

Я уже понял что QEvent здесь не особо применим, поэтому решил написать свой диспетчер, чем и занялся.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #10 : Ноябрь 22, 2012, 14:12 »

Вот что набросал:

Интерфейс моего кастомного события

Код:
#include    <QString>

class   CAbstractEvent;
class   CMVCEventProxy;

class CAbstractEventInterface
{
    CMVCEventProxy      *   feventProxy;

protected:

    virtual void    attachEventProxy();                             //обработчик привяки диспетчера событий
    virtual void    detachEventProxy();                             //обработчик отвязки
    virtual void    receiveMVCEvent(CAbstractEvent * event);        //обработчки приёма события
    void    sendEvent(CAbstractEvent * event);                      //отправка события
    virtual    void    acceptEvent(CAbstractEvent * event);         //принять событие

    friend  class   CMVCEventProxy;

public:

    CAbstractEventInterface();
    virtual const   QString    &    tag()   const = 0;  //тег объекта (может выступать адресом применика, и адресом отправителя)

    void    setEventProxy(CMVCEventProxy    *   EventProxyObject);  //установка проки
    const   CMVCEventProxy  *   eventProxyObject()  const;          //получение прокси
};
реализация
Код:
#include "cabstracteventinterface.h"
#include "cabstractevent.h"
#include "cmvceventproxy.h"

CAbstractEventInterface::CAbstractEventInterface()
{
    feventProxy = NULL;
}


void CAbstractEventInterface::setEventProxy(CMVCEventProxy *EventProxyObject)
{
    if(feventProxy!=EventProxyObject)
    {
        if(feventProxy)
            detachEventProxy();
        feventProxy = EventProxyObject;
        if(feventProxy)
            attachEventProxy();
    }
}

const CMVCEventProxy *CAbstractEventInterface::eventProxyObject() const
{
    return  feventProxy;
}

void CAbstractEventInterface::sendEvent(CAbstractEvent *event)
{
    if(feventProxy)
    {
        feventProxy->sendEvent(event);
    }
}

void CAbstractEventInterface::acceptEvent(CAbstractEvent *event)
{
    Q_UNUSED(event)
}

void CAbstractEventInterface::attachEventProxy()
{
}

void CAbstractEventInterface::detachEventProxy()
{
}

void CAbstractEventInterface::receiveMVCEvent(CAbstractEvent *event)
{
    Q_UNUSED(event)
}

вот сам прокси
Код:
#include <QObject>
#include <QHash>
#include "cabstracteventinterface.h"

class   CAbstractEvent;
class   CAbstractArmLogic;

class CMVCEventProxy : public QObject
{
Q_OBJECT

    QHash<QString, CAbstractEventInterface*>    fsubscribedObjects; //подписчики на события

protected:

public:

    explicit CMVCEventProxy(QObject *parent = 0);
    ~CMVCEventProxy();

    bool            sendEvent(CAbstractEvent * event);  //отправка события

    void    subscribeObjectForEventReceiving(const QString & address, CAbstractEventInterface    *   InterfaceObject);  //подписывает объект на приём событий
    void    unsubscribeObjectFromEventReceiving(const QString & address);   //отписывает объект по адерсу
    void    unsubscribeObjectFromEventReceiving(CAbstractEventInterface    *   InterfaceObject);    //отписывает объект по интерфейсу

};

Код:
#include "cmvceventproxy.h"
#include "cabstractevent.h"

CMVCEventProxy::CMVCEventProxy(QObject *parent) :
    QObject(parent)
{

}

CMVCEventProxy::~CMVCEventProxy()
{

}

bool CMVCEventProxy::sendEvent(CAbstractEvent *event)
{
    if(!event || event->destiantionTag().isEmpty()) return  false;

    qDebug("CMVCEventProxy::sendEvent");

    CAbstractEventInterface *   interface;
    foreach(const QString & destination, event->destiantionTag())
    {
        if((interface = fsubscribedObjects.value(destination,NULL)))
        {
            interface->acceptEvent(event);
        }
    }
    delete  event;
    return  true;
}

void CMVCEventProxy::subscribeObjectForEventReceiving(const QString &address, CAbstractEventInterface *InterfaceObject)
{
    if(address.isEmpty() || !InterfaceObject)   return;

    qDebug("subscribeObjectForEventReceiving %s",qPrintable(address));

    CAbstractEventInterface *   oldInterface = fsubscribedObjects.value(address,NULL);
    if(oldInterface)
        oldInterface->setEventProxy(NULL);

    InterfaceObject->setEventProxy(this);
    fsubscribedObjects[address] = InterfaceObject;
}

void CMVCEventProxy::unsubscribeObjectFromEventReceiving(const QString &address)
{
    qDebug("unsubscribeObjectFromEventReceiving %s",qPrintable(address));

    CAbstractEventInterface *   oldInterface = fsubscribedObjects.value(address,NULL);
    if(oldInterface)
        oldInterface->setEventProxy(NULL);
    fsubscribedObjects.remove(address);
}

void CMVCEventProxy::unsubscribeObjectFromEventReceiving(CAbstractEventInterface *InterfaceObject)
{
    for(QHash<QString, CAbstractEventInterface*>::iterator it = fsubscribedObjects.begin(); it!= fsubscribedObjects.end(); it++)
    {
        if(it.value() == InterfaceObject)
        {
            unsubscribeObjectFromEventReceiving(it.key());
            break;
        }
    }
}

вот как шлю событие
Код:
if(manevrWasChanged && !lightObject->manevrDestination().isEmpty())
            {
                CAbstractEvent  *   setupManevrRouteEvent = new CAbstractEvent("com.event.route.setup.manevr");
                setupManevrRouteEvent->setSenderTag(tag());
                destinations<<"com.action.dsp.manevr_route";
                destinations<<"com.action.dsp.manevr_route_auto";
                QVariantHash    params;
                params["begin_light"] = lightObject->objectId();
                params["end_light"] = lightObject->manevrDestination();
                setupManevrRouteEvent->setVarData(params);
                setupManevrRouteEvent->setDestinationTag(destinations);
                sendEvent(setupManevrRouteEvent);
            }

и принимаю в нужном месте
Код:
void CSetupRouteAction::acceptEvent(CAbstractEvent *event)
{
//на самом деле тут данные из соыбтия обрабатываются, но для простоты просто подвтерждаем что сделано
    doneAction();
}

Это набросал по быстрому, но думаю что есть что усовершенствовать. Моежт будут какие то советы?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #11 : Ноябрь 22, 2012, 14:16 »

Такой механизм работает, но во первых, только в одном потоке, и есть смущения по поводу удаления event.
Может его обернуть в QSharedPointer? что бы был подсчёт ссылок, ну и сделать передачу событий асинхронной (пока получается блокирование на acceptEvent)...
Записан
Bepec
Гость
« Ответ #12 : Ноябрь 22, 2012, 14:24 »

Я бы на вашем месте не городил кучу кода и прочего. (я специфики конечно вашей незнаю)

Сделал бы структурку одну штуку. Сделал бы диспетчер с функциями (подписаться, отписаться, переслать событие).

Эм... ну и всё в принципе.

PS вы выкладываете много кода. Это конечно хорошо. Но вы его обрезаете, названия файлов не даёте, комментариев по коду не видно... Лучше не выкладывайте) Редкий форумчанин будет разбираться в каше Улыбающийся

Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #13 : Ноябрь 23, 2012, 10:42 »

Да пожалуй Вы правы. Писалось на эмоциях ).
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.124 секунд. Запросов: 23.