13

与 Qt 中的其他原语一样,QGraphicsItems 可以处理鼠标事件等。甜的!现在说我需要将一个 QGraphicsItem 上的事件传播到同一场景中的其他一些 QGraphicsItem。我可以想到两种方法可以解决这个问题:


(A) 天真的方法 - 发信号

概念:将兄弟 QGraphicsItems 与信号连接在一起。QGraphicsItem 上的事件处理程序调用 emit()s 来唤起其他 QGraphicItems 上的协调响应。这遵循在整个 Qt 框架中建立的一般设计模式。

实现:由于我不完全掌握的原因,QGraphicsItems 不能发出()信号。有人建议,也继承自 QGraphicsObject 的派生类可能能够解决此问题。不过,在我看来,在 QGraphicsItems 上排除 emit() 可能是 Qt 开发人员有意的设计决定,因此,多重继承可能不是正确的解决方案。

(B) 容器级事件处理

概念: QGraphicsItems 总是存在于 QGraphicsScene 类型的容器的上下文中。(A) 中在 QGraphicsItem 级别处理的事件由继承自 QGraphicsScene 的对象处理。该对象还实现了在同级 QGraphicsItems 之间协调响应的逻辑。

实现: QGraphicsScene 绝对有能力处理否则会进入 QGraphicsItems 的事件。QGraphicsScene 还提供了 itemsAt() 方法,用于确定其中的哪些事物受到位置事件的影响,例如鼠标点击。尽管如此,在容器类中为容器之间的协调动作建立相当多的逻辑感觉就像是无法正确封装。不好的做法?也许吧,但这似乎是至少在一个官方示例中的做法。


问题

  1. 什么是正确的解决方案?如果不是 A 或 B,那是不是我没有想到的其他东西?
  2. 为什么 Qt 开发人员允许 QGraphicsItems 接收事件但不发送信号?这似乎是整个框架中使用的设计模式的一个主要例外。
  3. 这个问题的一个扩展是 QGraphicsItems 和高阶容器类之间的通信,比如主应用程序。这意味着如何解决?
4

2 回答 2

9

Signaling 不是 QGraphicItem 的一部分,因为它们不是从 QObjects 继承的。这是出于性能原因的设计决定,以允许非常大和快速的场景。如果您决定确实需要信号的特殊情况,则创建 QGraphicsWidget 来填补这一空白。它确实继承自 QObject 并允许您混合使用 QWidget 和 QGraphicsItem 功能。尽管如果您的场景规模适中,建议您避免这种情况。

可能与您的情况相关的另一个选项是使用 sceneEventFilter 方法。您可以设置一项以接收另一项的事件并决定是否应该传播它们: http
: //www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qgraphicsitem.html#sceneEventFilter 可以设置一项作为多个对象的过滤器。它可以识别每个单独的项目和事件来响应。

通常,尽管您应该利用场景来协调其对象。这已经是用于事件的模式(场景协调将所有事件传递给项目)。

此外,您的选项 A 似乎是不可能的,因为 QGraphicsItem 甚至没有发出方法。您需要在其中组成一个 QObject 实例作为成员并使用它来发出信号。类似的东西myItem.qobject.emit()。否则,您将不得不从 QGraphicsObject 继承您自己的完全自定义的

更新 1:解决您的主要评论更新

您的具体情况是一个带有“热角”的矩形。我会看到这是一个自定义的 QGraphicsItem。您可能会将 QGraphicsRectItem 子类化,然后将其中的子热角项组合为子项 ( setParentItem())。现在您的矩形项目知道它的孩子并且可以直接对他们采取行动。您可以将矩形项目设置为孩子的场景事件过滤器并直接处理他们的事件。无需返回现场。让所有这些逻辑都存在于课堂上。

更新 2:解决您添加的问题 #3

将通信传播到 QWidget 的场景之外有几种我能想到的方法:

  1. 在这种情况下,您可以考虑是否要将 QGraphicsObject 子类用作根项,然后将其余对象组合为子项(矩形,然后将热角作为矩形的子项)。这将允许对象发出信号。为清楚起见,它们可能仍会连接到场景,然后场景的高阶容器将连接到场景。您必须根据场景的复杂性以及 QGraphicsObject 是否对它有任何性能影响来逐个选择这种方法。如果您将拥有大量此类实例,您可能应该避免这种情况。
  2. 您可以为您的 rect 类定义一个回调,场景可以为其设置。要么是:graphicsRect.resizedCallback作为属性,要么是 setter graphicsRect.setResizedCallback(cbk)。在您的 rect 类中,您只需在适当的时候调用它。如果它设置了回调,它可以用来直接调用场景中的某些东西。rect 类仍然不知道该逻辑。它只是调用一个回调。

这些只是一些建议。我敢肯定还有其他方法。

于 2012-05-14T21:14:58.427 回答
1

我建议 B,除非您的 QGraphicsItems 相对较少。我相信 QGraphicsItems 不是 QObjects,因为有一定数量的与 QObjects 相关的开销。QGraphicsView 框架被设计成允许在一个场景中快速插入和删除许多(例如,数千个)QGraphicsItem,因此更轻量级的方法是首选。

我会看看 QGraphicsItems 中的育儿概念。QGraphicsItems 可以有父母和孩子,这有几个类似于 QObjects 之间的父母关系的效果。例如,如果你移动一个父 QGraphicsItem,它的子项也会随之移动,如果你删除一个父项,它的子项也会被删除。您可以使用 访问 QGraphicsItem 的父项QGraphicsItem::parentItem(),使用 访问子项QGraphicsItem::childItems()。因此,您可以像这样轻松访问同级项目:

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

请注意,mySiblings包括this.

于 2012-05-14T21:05:39.817 回答