Issue
I want to make my QGraphicsEllipseItem
emit signal on mouseMoveEvent()
. I found similar question but about C++ Qt, but when try to use that solution and do multiple inheritance thing in 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)
I get error:
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'
But I didn't pass int
to QObject
constructor...
How can I add signal to QGraphicsEllipseItem
?
Solution
In general, PyQt doesn't allow multiple inheritance of Qt classes, so there are only two alternatives.
QObject "proxy" as instance attribute
Create a subclass of QObject that acts as a signal "proxy", then add an instance of the subclass as an instance attribute of the graphics item:
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
# ...
This is the most basic solution, it has the benefit of allowing direct access to all the methods of the graphics items, most importantly those of the QGraphicsEllipseItem (setRect
, setPen
, etc.).
The drawback is that the signal is not actually emitted by the graphics item, so in case you need to use self.sender()
(which should be avoided anyway, if possible), you cannot directly know the item that sent the signal.
A possible workaround would be to create a property or attribute on the KnobSignalProxy instance as a reference to the graphics item:
self._proxy.item = self
# ...
def someFunction(self, pos):
item = self.sender().item
Note that, in order to receive mouse move events, you must implement 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.
QGraphicsObject with a child item
In this case the graphics item added is a QGraphicsObject (which is a QGraphicsItem that also inherits from QObject), and the ellipse item is actually its child. Since QGraphicsObject is a base class as QGraphicsItem, it requires that at least boundingRect
and paint
are implemented.
Obviously you're not interested in the painting (the QGraphicsEllipseItem will do it on its own), but the returned bounding rect has to use that of the child.
This approach has the benefit of using a more correct pattern from the OOP perspective, but doesn't directly expose the child item's method. In any case, you can just create a reference to those functions.
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
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.