【问题标题】:Events and signals in Qt's QGraphicsItem: How is this *supposed* to work?Qt 的 QGraphicsItem 中的事件和信号:*应该*如何工作?
【发布时间】:2012-05-22 09:11:41
【问题描述】:

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


(A) 天真的方法 - 信号

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

实现:由于我没有完全理解的原因,QGraphicsItems 不能发出()信号。 It has been suggested 同样从 QGraphicsObject 继承的派生类可能能够解决这个问题。不过,在我看来,在 QGraphicsItems 上排除 emit() 可能是 Qt 开发人员有意的设计决定,因此,多重继承可能不是正确的解决方案。

(B) 容器级事件处理

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

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


问题

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

【问题讨论】:

  • 仅供参考:这里的用例是一个可调整大小的矩形。它将在其每个角上都有驱动点,当单击并拖动这些驱动点时,也会导致入射边缘移动。
  • 这些边不会是矩形的子级,因此父级应该知道它们吗?
  • 我想,是的,如果我使用 QGraphicsItemGroup 或类似的东西实现 (B)。
  • 不,我的意思是这种情况听起来您应该创建一个具有子项的自定义子类。因此,父母可以直接对孩子采取行动,而不是上下协调。

标签: qt qt4 pyqt4


【解决方案1】:

信号不是 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. 您可以为您的矩形类定义一个回调,场景可以为其设置。要么是:graphicsRect.resizedCallback 作为属性,要么是 setter graphicsRect.setResizedCallback(cbk)。在您的 rect 类中,您只需在适当的时候调用它。如果它设置了回调,它可以用来直接调用场景中的某些东西。 rect 类仍然不知道该逻辑。它只是调用一个回调。

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

【讨论】:

  • @BrianTheLion:是的,这就是我所说的为你的情况做的事情。它使用事件过滤器。
  • @jdl:干杯!对我的问题 #3 有任何想法吗?这是一个迟到的补充。对于我的可调整大小的矩形,每次矩形坐标发生变化时,我都需要调整持久存储中的数据结构。
【解决方案2】:

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

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

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

注意mySiblings 包括this

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-08
    • 2011-06-20
    • 1970-01-01
    相关资源
    最近更新 更多