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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Как грамотно организовать полиморфизм поведения QGraphicsScene  (Прочитано 11051 раз)
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« : Август 11, 2011, 15:46 »

Есть сцена, на которой куча объектов (светофоры, пути, индикаторы и прочее).
В программе есть несколько режимов работы.
Один из режимов - режим редактирования, в котором на сцену можно кидать новые элементы, удалять их, перемещать, копировать/вставлять и т.п.
Следующие режимы - режимы исполнения программы, на сцене элементы уже фиксированы.
Мне нужно для каждого из режимов переопределить поведение сцены на:
-нажатие,отпускание,перемещение мыши
-нажатие клавы
-скроллинг колесиком.
Сначала я делал так: в некой переменной хранил текущий тип режима работы, и в зависимости от его значения по switch вызывал функции обработки событий для данного режима работы. Но у такого подхода недостаток, что при добавлении нового режима приходится лезть внуть сцены и править функции-обработчики мышы/клавы что крайне неудобно.
Решил сделать так, объявить сигналы нажатия,движения,отпускания мыши, emit их в виртуальных функциях-обработчиках сцены, а уже в кажом режиме подключать свои слоты (нечто подобного реализовано в Delphi через события объекта).
Все работает, но при движении мыши проц грузится нешуточно,я так понимаю изза частого emit. Я уже ставил и Qt::DirectConnection - не помогает.
Подскажите, как можно грамотно организовать полиморфизм этих обработчиков для сцены. В будущем планирую все режимы вывести в отдельные плагины.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #1 : Август 11, 2011, 15:52 »

как было раньше:
Код:
void      QEditorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QExGraphicsScene::mouseReleaseEvent(event);

    switch(work_state)
    {
    case    wsEditing:
   {
   //обработка в режиме редактора
   }
   break;
    case    wsRunningSHN:
        {
            //обработка в режиме исполнения
        }
        break;
    //и так далее для всех режимов
    }
}
как сделал сейчас
Код:
class QExGraphicsScene : public QGraphicsScene
{
    Q_OBJECT

protected:

    void    mousePressEvent(QGraphicsSceneMouseEvent *event);
    void    mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    void    mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void    contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
...
};

void    QExGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    emit sigMousePressEvent(event);
    QGraphicsScene::mousePressEvent(event);
}

void    QExGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseReleaseEvent(event);
    emit sigMouseReleaseEvent(event);
}

void    QExGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseMoveEvent(event);
    emit    sigMouseMoveEvent(event);
}

а в каждом режиме работы (считай плагине)
Код:
void    CArmEditor::createLogic()
{
connect(fproject->fieldView()->scene(),SIGNAL(sigContextMenu(QGraphicsSceneContextMenuEvent*)),this,SLOT(fieldContextMenu(QGraphicsSceneContextMenuEvent*)),Qt::DirectConnection);
    connect(fproject->fieldView()->scene(),SIGNAL(sigMousePressEvent(QGraphicsSceneMouseEvent*)),this,SLOT(sceneMousePressed(QGraphicsSceneMouseEvent*)),Qt::DirectConnection);
    connect(fproject->fieldView()->scene(),SIGNAL(sigMouseMoveEvent(QGraphicsSceneMouseEvent*)),this,SLOT(sceneMouseMoved(QGraphicsSceneMouseEvent*)),Qt::DirectConnection);
    connect(fproject->fieldView()->scene(),SIGNAL(sigMouseReleaseEvent(QGraphicsSceneMouseEvent*)),this,SLOT(sceneMouseReleased(QGraphicsSceneMouseEvent*)),Qt::DirectConnection);

Но последний подход к сожалению дико тормозит. Можно ли в Qt как то грамотно реализовать мою задачу?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #2 : Август 11, 2011, 15:53 »

Ибо с перым подходом я уже влажу в такие дебри, и теряется универсальность,расширяемость программы (о плагинах и речи идти не может, а надо)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Август 11, 2011, 16:08 »

Ситуация может быть хуже, напр. в режиме редактирования объекты можно выбирать и двигать, а в режиме просмотра - только выбирать. Я задавал подобный вопрос неск месяцев назад. Насколько помню, ответы были

- использовать QStateMachine (без указания деталей)
- навесить фильтры (в зависимости от режима). Ну как-то помогает, но не более того

Склоняюсь к мысли что это проблема "архитектурно-концептуальная", т.е.. не решается каким-то конкретным классом/тулзом.
Записан
brankovic
Гость
« Ответ #4 : Август 11, 2011, 18:06 »

Код:
как сделал сейчас

class QExGraphicsScene : public QGraphicsScene
{
    Q_OBJECT

protected:

    void    mousePressEvent(QGraphicsSceneMouseEvent *event);
    void    mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    void    mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void    contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
...
};

void    QExGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    emit sigMousePressEvent(event);
    QGraphicsScene::mousePressEvent(event);
}
...

можно вынести полиморфизм в отдельный класс, например так:

Код
C++ (Qt)
struct ActorBase
{
  virtual void mousePressEvent (QExGraphicScene *scene, QGraphicsSceneMouseEvent *event) = 0;
  virtual void mouseReleaseEvent (QExGraphicScene *scene, QGraphicsSceneMouseEvent *event) = 0;
  ...
};
 
struct ActorEdit : public ActorBase { ... };
struct ActorView : public ActorBase { ... };
 
struct QExGraphicsScene : public QGraphicScene
{
  ActorBase *m_actor;
 
  void setEditMode () { delete m_actor; m_actor = new ActorEdit (); }
  ...
 
  void mousePressEvent (QGraphicsSceneMouseEvent *event) {m_actor->mousePressEvent (this, event);}
  ...
};
 

тогда Actor будет работать с QExGraphicsScene как с внешним классом. Пусть некая лишняя писанина, но зато код edit и view режима будет лежать отдельно в ActorEdit и ActorView.
Записан
_OLEGator_
Гость
« Ответ #5 : Август 11, 2011, 18:17 »

Я вижу это так:

1) Реализовать для сцены абстрактный класс обработки событий, в который будут передаваться события и устанавливаться сцена, возможно еще какие данные.
    Для каждого режима реализовать свои классы, унаследованные от абстрактного.
    Устанавливать в сцену экземпляр нужного класса. (brankovic предложил выше)
или
2) Реализовать один менеджер, в который вынести режимы и обработку событий.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Август 11, 2011, 19:18 »

тогда Actor будет работать с QExGraphicsScene как с внешним классом. Пусть некая лишняя писанина, но зато код edit и view режима будет лежать отдельно в ActorEdit и ActorView.
Этот книжный приемчик мало помогает когда проект разрастается. Два и более обработчиков начинают иметь все более и более общего что трудно (или невозможно) вынести в методы их базового класса.   
Записан
brankovic
Гость
« Ответ #7 : Август 11, 2011, 20:14 »

Этот книжный приемчик мало помогает когда проект разрастается. Два и более обработчиков начинают иметь все более и более общего что трудно (или невозможно) вынести в методы их базового класса.   

Ммм.. это Вы про проблемы C++ в целом или данного конкретного решения? В любом случае я всегда за лучшие альтернативы. У Вас какие предложения, напомните?
Записан
iroln
Гость
« Ответ #8 : Август 11, 2011, 20:49 »

Ситуация может быть хуже, напр. в режиме редактирования объекты можно выбирать и двигать, а в режиме просмотра - только выбирать. Я задавал подобный вопрос неск месяцев назад. Насколько помню, ответы были

- использовать QStateMachine (без указания деталей)
- навесить фильтры (в зависимости от режима). Ну как-то помогает, но не более того

Склоняюсь к мысли что это проблема "архитектурно-концептуальная", т.е.. не решается каким-то конкретным классом/тулзом.
Вот у меня ситуация один в один. В режиме редактирования объекты можно модифицировать, в режиме просмотра их можно выбирать и подсвечивать. И тоже столкнулся с проблемой фильтрации событий мышки и клавиатуры. Сейчас пока сделал как автор темы, задал переменную режима работы, но мне это тоже не нравится. Если здесь предложат красивое верное решение, буду рад его использовать, а пока буду тоже думать. Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Август 12, 2011, 08:12 »

Ммм.. это Вы про проблемы C++ в целом или данного конкретного решения?
Данного конкретного. Где фильтроваться? На уровне application - так оно отдает события окнам. На уровне окна - так их могут быть десятки, любой немодальный диалог. Как фильтроваться? Напр что должен делать ActionView::KeyDown? Заблокировать весь ввод - не годится, что-то вводить можно и в режиме просмотра. Заблокировать некоторые клавиши? Так это зависит от фокуса. Перебирать на фильтре "какой контрол сейчас в фокусе" выглядит совсем глупо, уж лучше сделать у окна метод вроде SetViewMode() где задизаблить что надо (тоже впрочем не фонтан)

Ситуация с MouseDown гораздо хуже, потому что часто куда проще драгаться именно в MouseDown (не бегая с флажками в MouseMove). В MouseDown удобно зарядить ряд scoped и крутить драг с вторичным циклом событий. Но при этом разборка view/edit проникает на самый нижний уровень  Плачущий

В любом случае я всегда за лучшие альтернативы. У Вас какие предложения, напомните?
Разве я от Вас стал бы скрывать если б знал?  Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Август 12, 2011, 14:34 »

Вот примерчик:

- есть окно, а в нем банальный листбокс. Выбранные айтемы могут быть перемещены (внутри листбокса) драгом, удалены с помощью клавиши Del или если пользователь открыл контекстуальное меню и выбрал там delete. По нажатию спец-клавиши (напр Enter) появляется inline-editor и имя айтема можно поменять

Понятно что в режиме view удаление и изменение имени должны быть запрещены, но как это сделать "на уровне окна"? (mouse/key-события) Запретим драг? Но он может иметь смысл и для view. Запретим вызов контекстуального меню - тоже не годится, там должно быть только задизаблено delete. Запретим inline-edit? А откуда мы знаем какая клавиша его включит? Напр для др. листбокса редактирование начинается по dbl-click.

Др. словами мы навесили обработчик который пытается управлять тем чего он не знает. А вот когда мы "спустимся" на уровень листбокса - все прекрасно, у него-то все на руках и ему отлично известно что должно быть вкл/выкл в зависимости от режима. Но так мы ничего не выигрываем по сравнению с тупеньким if/switch.

Ситуация легко проецируется на QGraphicsScene и многие др случаи. Но вероятно я "ударился в концептуальность" (бывает), и это никому не интересно  Улыбающийся

Edit: вспомнился старый анекдот по поводу view/edit
Цитировать
(Большой грабитель) Всем оставаться на своих местах! Сейчас будем грабить и насиловать!
(Маленький грабитель) Только грабить!
(Женщина в переднем ряду) А ты, маленький, помолчи...
« Последнее редактирование: Август 12, 2011, 15:36 от Igors » Записан
iroln
Гость
« Ответ #11 : Август 12, 2011, 15:11 »

Ситуация легко проецируется на QGraphicsScene и многие др случаи. Но вероятно я "ударился в концептуальность" (бывает), и это никому не интересно  Улыбающийся

Да вообще-то как раз интересно. Потому что часто не очень понятно как сделать так, чтобы было концептуально правильно, гибко, расширяемо, и т.д. Возможно из-за нехватки опыта, возможно из-за недостаточных навыков проектирования, я часто застреваю на таких вот штуках. Улыбающийся
Записан
_OLEGator_
Гость
« Ответ #12 : Август 12, 2011, 18:43 »

Я, честно говоря, не понимаю о чем спор.
Был предложен вариант решения, когда все эти switch сгруппировать и вынести в отдельный класс. Реализовать несколько классов под различное поведение, а потом прогонять все события через него и менять их под нужные режимы.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #13 : Август 15, 2011, 11:28 »

ну а как это сделать так, что бы все классы наследники сцены и режимы работы вынести в отдельные плагины, так как предвидится 3 и более режимов работы программы, на каждый режим хочется свой плагин. Пока все в одной куче.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #14 : Август 15, 2011, 12:22 »

решил пойти путем создания делегатов, но увы результат тот же - жуткие тормоза при событиях мыши...
Код:
#ifndef QEXGRAPHICSSCENE_H
#define QEXGRAPHICSSCENE_H

#include    "config.h"
#include    <QGraphicsScene>
#include    <QGraphicsItem>


class QExGraphicsScene : public QGraphicsScene
{
    Q_OBJECT

protected:

    void    mousePressEvent(QGraphicsSceneMouseEvent *event);
    void    mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    void    mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void    contextMenuEvent(QGraphicsSceneContextMenuEvent *event);

public:

    explicit    QExGraphicsScene(QObject *parent = 0);

    void        detachDelegates();


//делегаты
    void        (*__mousePressEvent)(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event);
    void        (*__mouseReleaseEvent)(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event);
    void        (*__mouseMoveEvent)(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event);
    void        (*__contextMenuEvent)(QExGraphicsScene * scene, QGraphicsSceneContextMenuEvent *event);


signals:
};

#endif // QEXGRAPHICSSCENE_H

Код:
#include <QGraphicsSceneMouseEvent>
#include "qexgraphicsscene.h"

QExGraphicsScene::QExGraphicsScene(QObject *parent) :
    QGraphicsScene(parent)
{
    detachDelegates();
}

void    QExGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mousePressEvent(event);
    if(__mousePressEvent) __mousePressEvent(this,event);
}

void    QExGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseReleaseEvent(event);
    if(__mouseReleaseEvent) __mouseReleaseEvent(this,event);
}

void    QExGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseMoveEvent(event);
    if(__mouseMoveEvent) __mouseMoveEvent(this,event);
}


void    QExGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    if(__contextMenuEvent) __contextMenuEvent(this,event);
}

void    QExGraphicsScene::detachDelegates()
{
    __mousePressEvent = NULL;
    __mouseReleaseEvent = NULL;
    __mouseMoveEvent = NULL;
    __contextMenuEvent = NULL;
}

вот класс-логики редактора

Код:
...
    CArmEditor(QObject * parent = 0);
    ~CArmEditor();

    static  void    fieldMousePressed(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event);
    static  void    fieldMouseMoved(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event);
    static  void    fieldMouseReleased(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event);
    static  void    fieldContextMenu(QExGraphicsScene * scene, QGraphicsSceneContextMenuEvent *event);

вот привязка в режиме редактирования

Код:
void    CArmEditor::createLogic()
{

    fproject->fieldView()->scene()->__mousePressEvent = &CArmEditor::fieldMousePressed;
    fproject->fieldView()->scene()->__mouseMoveEvent = &CArmEditor::fieldMouseMoved;
    fproject->fieldView()->scene()->__mouseReleaseEvent = &CArmEditor::fieldMouseReleased;
    fproject->fieldView()->scene()->__contextMenuEvent = &CArmEditor::fieldContextMenu;

    /*
старый вариант со слотами    connect(fproject->fieldView()->scene(),SIGNAL(sigContextMenu(QGraphicsSceneContextMenuEvent*)),this,SLOT(fieldContextMenu(QGraphicsSceneContextMenuEvent*)),Qt::DirectConnection);
    connect(fproject->fieldView()->scene(),SIGNAL(sigMousePressEvent(QGraphicsSceneMouseEvent*)),this,SLOT(sceneMousePressed(QGraphicsSceneMouseEvent*)),Qt::DirectConnection);
    connect(fproject->fieldView()->scene(),SIGNAL(sigMouseMoveEvent(QGraphicsSceneMouseEvent*)),this,SLOT(sceneMouseMoved(QGraphicsSceneMouseEvent*)),Qt::DirectConnection);
    connect(fproject->fieldView()->scene(),SIGNAL(sigMouseReleaseEvent(QGraphicsSceneMouseEvent*)),this,SLOT(sceneMouseReleased(QGraphicsSceneMouseEvent*)),Qt::DirectConnection);
    connect(fproject->modulView()->scene(),SIGNAL(sigContextMenu(QGraphicsSceneContextMenuEvent*)),this,SLOT(modulesContextMenu(QGraphicsSceneContextMenuEvent*)),Qt::DirectConnection);
    */
...
}

вот обработчики
Код:
void    CArmEditor::fieldMousePressed(QExGraphicsScene * scene, QGraphicsSceneMouseEvent *event)
{
    QEditorScene * fscene = (QEditorScene*)scene;

    switch(fscene->sceneAction)
    {
    case    saAddingItem:
    {
        if(event->button()==Qt::LeftButton)
        switch(fscene->addingObject)
        {
        case    otLight:
            fscene->createLight(event->scenePos());
            break;

        case    otSimpleIndicator:
            fscene->createIndicator(event->scenePos());
            break;
...
}
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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