Qt的QGraphicsItem中的事件和信号:这应该如何工作?

2024-09-29 21:36:41 发布

您现在位置:Python中文网/ 问答频道 /正文

与Qt中的其他原语一样,QGraphicsItems可以处理鼠标事件等。太好了!现在假设我需要将一个QGraphicsItem上的事件传播到同一场景中的其他QGraphicsItem。我可以想到两种方法:


(A) 天真的方法-信号

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

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

(B) 容器级事件处理

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

实现:qgraphicscene绝对有能力处理事件,否则这些事件将进入QGraphicsItems。qgraphicscene还提供itemsAt()方法,用于确定其中哪些内容受位置事件(如鼠标单击)的影响。尽管如此,在容器类中为容器之间的协调操作构建相当多的逻辑感觉像是未能正确封装。坏习惯?也许吧,但这似乎是至少在one official example中完成的方式。


问题

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

Tags: 方法框架概念信号开发人员设计模式事件鼠标
2条回答

信令不是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())。现在,矩形项知道了它的子项,并且可以直接作用于它们。可以将矩形项设置为子级的sceneEventFilter并直接处理其事件。不用再回到现场了。让所有这些逻辑活在课堂上。

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

将通信传播到QWidget的场景之外,我可以想到以下几种方法:

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

这些只是一些建议。我肯定还有别的办法。

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

我会在QGraphicsItems中寻找育儿的概念。QGraphicsItems可以有父母和孩子,这与QObjects中的育儿方式有一些相似的效果。例如,如果移动父级QGraphicsItem,其子级将随之移动,如果删除父级,其子级将被删除。您可以使用QGraphicsItem::parentItem()访问QGraphicsItem的父级,使用QGraphicsItem::childItems()访问子级。因此,您可以很容易地访问类似这样的同级项:

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

注意mySiblings包括this

相关问题 更多 >

    热门问题