События и сигналы в Qt QGraphicsItem: как это * предполагается * работать?

Как и другие примитивы в Qt, QGraphicsItems могут обрабатывать события мыши и т.п. Милая! Теперь скажите, что мне нужно, чтобы событие на одном QGraphicsItem распространялось на некоторые другие объекты QGraphicsItems в одной и той же сцене. Я могу думать о двух способах, которые можно было бы подойти к этому:


(A) Наивный подход - сигнализация

Концепция: Соединяйте sigling QGraphicsItems вместе с сигналами. Обработчики событий на QGraphicsItem вызывают emit() s, которые вызывают согласованные ответы на другие QGraphicItems. Это следует за общей схемой проектирования, установленной в рамках Qt.

Реализация: По причинам, которые я не полностью понимаю, QGraphicsItems не могут генерировать() сигналы. Было высказано предположение, что производные классы, которые также наследуются от QGraphicsObject, могут обойти это. Мне кажется, что исключение emit() в QGraphicsItems было, вероятно, преднамеренным дизайнерским решением со стороны разработчиков Qt, и поэтому множественное наследование, вероятно, не является правильным решением.

(B) Обработка событий на уровне контейнера

Концепция: QGraphicsItems всегда существуют в контексте контейнера типа QGraphicsScene. События, выполняемые в (A) на уровне QGraphicsItem, обрабатываются объектом, наследующим от QGraphicsScene. Этот объект также реализует логику для координации ответов между sigling QGraphicsItems.

Реализация: QGraphicsScene определенно имеет возможность обрабатывать события, которые в противном случае могли бы перейти к QGraphicsItems. QGraphicsScene также предоставляет метод itemsAt() для определения того, какие из вещей в нем затронуты позиционными событиями, такими как щелчки мыши. Тем не менее, создание значительной логики в классе контейнера для согласованных действий между хранителями чувств похоже на неспособность инкапсуляции должным образом. Плохая практика? Возможно, но это похоже на то, как это делается, по крайней мере, один официальный пример.


Вопросы

  • Какое правильное решение здесь? Если не A или B, то это что-то еще, о чем я не думал?
  • Почему разработчики Qt позволяют QGraphicsItems получать события, но не отправлять сигналы? Это похоже на основное исключение из шаблона проектирования, используемого во всей структуре.
  • Расширением этой проблемы является связь между QGraphicsItems и классами контейнеров более высокого порядка, такими как основное приложение. Как это должно быть адресовано?

Ответ 1

Сигнализация не является частью QGraphicItem, поскольку они не наследуются от QObjects. Это было конструктивное решение по соображениям производительности, чтобы обеспечить очень большие и быстрые сцены. Если вы решите, что вам действительно нужны специальные случаи для сигналов, QGraphicsWidget был создан, чтобы заполнить этот пробел. Он наследует QObject и позволяет вам комбинировать функции QWidget и QGraphicsItem. Хотя рекомендуется избегать этого, если ваши сцены даже умеренно значимы.

Другим вариантом, который может иметь отношение к вашей ситуации, является использование метода sceneEventFilter. Вы можете установить один элемент для получения событий для другого и решить, должны ли они распространяться или нет: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qgraphicsitem.html#sceneEventFilter
Один элемент может быть установлен как фильтр для нескольких объектов. И он может идентифицировать каждый отдельный элемент и событие для ответа.

В общем, хотя вы должны использовать сцену для координации по ее объектам. Это уже шаблон, используемый для событий (сцена, координирующая доставку всех событий в элементы).

Кроме того, кажется, что ваш вариант A невозможен, поскольку QGraphicsItem даже не имеет метода emit. Вам нужно будет создать экземпляр QObject внутри него как члена и использовать его для испускания сигналов. Что-то вдоль линий myItem.qobject.emit(). В противном случае вам нужно наследовать свой собственный полностью настраиваемый из QGraphicsObject

Обновление 1: адресация вашего основного комментария.

Ваша конкретная ситуация представляет собой прямоугольник с "горячими углами". Я бы увидел, что это пользовательский QGraphicsItem. Вероятно, вы должны подклассифицировать QGraphicsRectItem, а затем составить дочерние элементы горячего угла внутри дочерних элементов (setParentItem()). Теперь ваш элемент прямоугольника знает о его дочерних элементах и ​​может действовать прямо на них. Вы можете установить для элемента rectangle элемент sceneEventFilter для детей и обрабатывать их события напрямую. Не нужно возвращаться на сцену. Пусть вся эта логика живет в классе.

Обновление 2: адресация вашего добавленного вопроса # 3

Распространение коммуникаций выше сцены на QWidget имеет пару подходов, о которых я могу думать:

  • Это ситуация, когда вы можете рассмотреть, хотите ли вы использовать подкласс QGraphicsObject в качестве корневого элемента, а затем составить остальные объекты в виде дочерних элементов (прямые, а затем горячие углы как дочерние элементы rect). Это позволит объекту излучать сигналы. Для ясности они, вероятно, все еще будут связаны со сценой, а затем контейнер более высокого порядка сцены будет подключен к сцене. Вам придется выбирать этот подход в каждом конкретном случае, в зависимости от сложности вашей сцены и того, влияет ли на QGraphicsObject воздействие на нее. Вероятно, вам следует избегать этого, если у вас будет большое количество этих экземпляров.
  • Вы можете определить обратный вызов для вашего класса rect, для которого может быть установлена ​​сцена. Либо что-то вроде: graphicsRect.resizedCallback как атрибут, или setter graphicsRect.setResizedCallback(cbk). В вашем классе rect вы просто назовете это, когда это необходимо. Если установленный обратный вызов, он может использоваться для вызова чего-либо на вашей сцене напрямую. Прямой класс до сих пор не знает этой логики. Он просто вызывает обратный вызов.

Это лишь некоторые предложения. Я уверен, что есть и другие способы.

Ответ 2

Я предлагаю B, если у вас относительно мало QGraphicsItems. Я считаю, что QGraphicsItems не являются QObjects, потому что есть определенное количество накладных расходов, связанных с QObjects. Структура QGraphicsView была разработана для обеспечения быстрой вставки и удаления многих (например, тысяч) QGraphicsItems в сцену, поэтому предпочтительным был подход с более легким весом.

Я бы посмотрел на концепцию воспитания в QGraphicsItems. У QGraphicsItems могут быть родители и дети, и это имеет несколько эффектов, подобных родительскому положению среди QObjects. Например, если вы переместите родительский QGraphicsItem, его дети будут перемещаться вместе с ним, а если вы удалите родителя, его дети будут удалены. Вы можете получить доступ к родительскому элементу QGraphicsItem с помощью QGraphicsItem::parentItem() и детей, использующих QGraphicsItem::childItems(). Таким образом, вы можете легко получить доступ к следующим элементам:

QList<QGraphicsItem *> mySiblings = this->parentItem()->childItems();

Обратите внимание, что mySiblings включает this.