如何向 QGraphicsEllipseItem 添加信号?

How can I add signal to QGraphicsEllipseItem?

我想让我的 QGraphicsEllipseItemmouseMoveEvent() 上发射信号。我发现 similar question 但关于 C++ Qt,但是当尝试使用该解决方案并在 Python 中进行多重继承时:

class Knob(QObject, QGraphicsEllipseItem):
    changePos = pyqtSignal(QPointF)

    def __init__(self, x, y, d, maxRadius):
        super(QObject, self).__init__()
        super(QGraphicsEllipseItem, self).__init__(0, 0, d, d)
        self.outPos = QPointF()
        ...

    def mouseMoveEvent(self, event):
        self.changePos.emit(self.outPos)

我收到错误:

File "/home/user/.../gui.py", line 11, in __init__
    super(QGraphicsEllipseItem, self).__init__(0, 0, d, d)
TypeError: QObject(parent: QObject = None): argument 1 has unexpected type 'int'

但我没有将 int 传递给 QObject 构造函数... 如何向 QGraphicsEllipseItem 添加信号?

一般来说,PyQt不允许多重继承Qtclasses,所以只有两个选择。

QObject“代理”作为实例属性

创建QObject的子class作为信号“代理”,然后添加子class的实例作为图形项的实例属性:

class KnobSignalProxy(QObject): changePos = pyqtSignal(QPointF)

class Knob(QGraphicsEllipseItem):
    def __init__(self, x, y, d, maxRadius):
        super(QGraphicsEllipseItem, self).__init__(0, 0, d, d)
        self._proxy = KnobSignalProxy()
        self.changePos = self._proxy.changePos
        # ...

这是最基本的解决方案,它的好处是允许直接访问图形项的所有方法,最重要的是 QGraphicsEllipseItem 的方法(setRectsetPen 等。 ).
缺点是信号实际上不是由图形项目发出的,所以如果你需要使用 self.sender()(无论如何应该避免,如果可能的话),你不能直接知道 item 发送了信号。

一种可能的解决方法是在 KnobSignalProxy 实例上创建一个 属性 或属性作为对图形项的引用:

        self._proxy.item = self

# ...

    def someFunction(self, pos):
        item = self.sender().item

请注意,为了接收鼠标移动事件,您必须实施mousePressEvent():

The mouse press event decides which item should become the mouse grabber (see QGraphicsScene::mouseGrabberItem()). If you do not reimplement this function, the press event will propagate to any topmost item beneath this item, and no other mouse events will be delivered to this item.

带有 child 项的 QGraphicsObject

本例中添加的图形项是一个QGraphicsObject(也是继承自QObject的QGraphicsItem),椭圆项其实就是它的child。由于 QGraphicsObject 是作为 QGraphicsItem 的基础 class,它要求至少实现 boundingRectpaint
显然你对绘画不感兴趣(QGraphicsEllipseItem 会自己完成),但返回的边界矩形必须使用 child.

的边界矩形

这种方法的好处是从 OOP 的角度使用更正确的模式,但不会直接公开 child 项的方法。在任何情况下,您都可以创建对这些函数的引用。

class Knob(QGraphicsObject):
    changePos = pyqtSignal(QPointF)

    def __init__(self, x, y, d, maxRadius):
        super().__init__()
        self.ellipseItem = QGraphicsEllipseItem(0, 0, d, d, self)
        self.outPos = QPointF()
        self.rect = self.ellipseItem.rect
        self.setRect = self.ellipseItem.setRect
        self.setPen = self.ellipseItem.setPen
        # ...

    def boundingRect(self):
        return self.childrenBoundingRect()

    def mousePressEvent(self, event):
        pass

    def mouseMoveEvent(self, event):
        self.changePos.emit(self.outPos)

    def paint(self, qp, opt, widget=None):
        pass