В PySide (а также в PySide2, PyQt4 и PyQt5) невозможно наследовать от QGraphicsItem и QObject (в особых случаях допускается только двойное наследование)
Таким образом, возможное решение состоит в том, чтобы использовать композицию, то есть иметь объект QObject в качестве атрибута, и это имеет сигнал:
import sys
import uuid
from PySide import QtGui, QtCore
class Signaller(QtCore.QObject):
onXMove = QtCore.Signal()
class VerticalLineSegment(QtGui.QGraphicsLineItem):
def __init__(self, _id, x, y0, y1, parent=None):
super(VerticalLineSegment, self).__init__(x, y0, x, y1, parent)
self._id = _id
self.signaller = Signaller()
self.setFlag(QtGui.QGraphicsLineItem.ItemIsMovable)
self.setFlag(QtGui.QGraphicsLineItem.ItemSendsGeometryChanges)
self.setCursor(QtCore.Qt.SizeAllCursor)
def itemChange(self, change, value):
if change is QtGui.QGraphicsItem.ItemPositionChange:
self.signaller.onXMove.emit()
value.setY(0) # Restrict movements along horizontal direction
return value
return QtGui.QGraphicsLineItem.itemChange(self, change, value)
def shape(self):
path = super(VerticalLineSegment, self).shape()
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(5)
return stroker.createStroke(path)
def boundingRect(self):
return self.shape().boundingRect()
def updateX(self, _id):
print("slot", _id)
class CustomScene(QtGui.QGraphicsScene):
def __init__(self, parent=None):
super(CustomScene, self).__init__(parent)
self.signalMapper = QtCore.QSignalMapper(self)
def addItem(self, item):
if hasattr(item, "_id"):
item.signaller.onXMove.connect(self.signalMapper.map)
self.signalMapper.setMapping(item.signaller, item._id)
self.signalMapper.mapped[str].connect(item.updateX)
super(CustomScene, self).addItem(item)
class Editor(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Editor, self).__init__(parent)
scene = CustomScene()
line0 = VerticalLineSegment(str(uuid.uuid4()), 10.0, 210.0, 300.0)
line1 = VerticalLineSegment(str(uuid.uuid4()), 10.0, 110.0, 200.0)
line2 = VerticalLineSegment(str(uuid.uuid4()), 10.0, 10.0, 100.0)
scene.addItem(line0)
scene.addItem(line1)
scene.addItem(line2)
view = QtGui.QGraphicsView()
view.setScene(scene)
self.setGeometry(250, 250, 600, 600)
self.setCentralWidget(view)
self.show()
Или используйте QGraphicsObject:
import sys
from PySide import QtCore, QtGui
class VerticalLineSegment(QtGui.QGraphicsObject):
onXMove = QtCore.Signal()
def __init__(self, x, y0, y1, parent=None):
super(VerticalLineSegment, self).__init__(parent)
self._line = QtCore.QLineF(x, y0, x, y1)
self.setFlag(QtGui.QGraphicsLineItem.ItemIsMovable)
self.setFlag(QtGui.QGraphicsLineItem.ItemSendsGeometryChanges)
self.setCursor(QtCore.Qt.SizeAllCursor)
def paint(self, painter, option, widget=None):
painter.drawLine(self._line)
def shape(self):
path = QtGui.QPainterPath()
path.moveTo(self._line.p1())
path.lineTo(self._line.p2())
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(5)
return stroker.createStroke(path)
def boundingRect(self):
return self.shape().boundingRect()
def itemChange(self, change, value):
if change is QtGui.QGraphicsItem.ItemPositionChange:
self.onXMove.emit()
value.setY(0) # Restrict movements along horizontal direction
return value
return QtGui.QGraphicsLineItem.itemChange(self, change, value)
def updateX(self , obj):
print("slot", obj)
class CustomScene(QtGui.QGraphicsScene):
def __init__(self, parent=None):
super(CustomScene, self).__init__(parent)
self.signalMapper = QtCore.QSignalMapper(self)
def addItem(self, item):
if isinstance(item, QtCore.QObject):
item.onXMove.connect(self.signalMapper.map)
self.signalMapper.setMapping(item, item)
self.signalMapper.mapped[QtCore.QObject].connect(item.updateX)
super(CustomScene, self).addItem(item)
class Editor(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Editor, self).__init__(parent)
scene = CustomScene()
line0 = VerticalLineSegment(10.0, 210.0, 300.0)
line1 = VerticalLineSegment(10.0, 110.0, 200.0)
line2 = VerticalLineSegment(10.0, 10.0, 100.0)
scene.addItem(line0)
scene.addItem(line1)
scene.addItem(line2)
view = QtGui.QGraphicsView()
view.setScene(scene)
self.setGeometry(250, 250, 600, 600)
self.setCentralWidget(view)
self.show()