I have two subclassed QGraphicsRectItems that are supposed to be connected with a line that adjusts based on the position of the textboxes.
In the diagramscene example of the Qt docus the itemChanged
method of a subclassed QGraphicsPolygonItem
calls a updatePosition
method of the connected arrow which calls setLine
to update the arrow's position. In my case I cannot call setLine
as I am subclassing QGraphicsItem
instead of QGraphicsLineItem
.
How should I implement updatePosition
method in the Arrow
class below to update the position of my QGraphicsItem? The following is a runnable example that shows what happens currently when the textboxes are clicked and moved.
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Arrow(QGraphicsItem):
def __init__(self, startItem, endItem, parent=None, scene=None):
super().__init__(parent, scene)
self.startItem = startItem
self.endItem = endItem
def boundingRect(self):
p1 = self.startItem.pos() + self.startItem.rect().center()
p3 = self.endItem.pos() + self.endItem.rect().center()
bounds = p3 - p1
size = QSizeF(abs(bounds.x()), abs(bounds.y()))
return QRectF(p1, size)
def paint(self, painter, option, widget=None):
p1 = self.startItem.pos() + self.startItem.rect().center()
p3 = self.endItem.pos() + self.endItem.rect().center()
pen = QPen()
pen.setWidth(1)
painter.setRenderHint(QPainter.Antialiasing)
if self.isSelected():
pen.setStyle(Qt.DashLine)
else:
pen.setStyle(Qt.SolidLine)
pen.setColor(Qt.black)
painter.setPen(pen)
painter.drawLine(QLineF(p1, p3))
painter.setBrush(Qt.NoBrush)
def updatePosition(self):
#Not sure what to do here...
class TextBox(QGraphicsRectItem):
def __init__(self, text, position, rect=QRectF(0, 0, 200, 100),
parent=None, scene=None):
super().__init__(rect, parent, scene)
self.setFlags(QGraphicsItem.ItemIsFocusable |
QGraphicsItem.ItemIsMovable |
QGraphicsItem.ItemIsSelectable)
self.text = QGraphicsTextItem(text, self)
self.setPos(position)
self.arrows = []
def paint(self, painter, option, widget=None):
painter.setPen(Qt.black)
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(Qt.white)
painter.drawRect(self.rect())
def addArrow(self, arrow):
self.arrows.append(arrow)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange:
for arrow in self.arrows:
arrow.updatePosition()
return value
if __name__ == "__main__":
app = QApplication(sys.argv)
view = QGraphicsView()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 500, 1000)
view.setScene(scene)
textbox1 = TextBox("item 1", QPointF(50, 50), scene=scene)
textbox1.setZValue(1)
textbox2 = TextBox("item 2", QPointF(100, 500), scene=scene)
textbox2.setZValue(1)
arrow = Arrow(textbox1, textbox2, scene=scene)
arrow.setZValue(0)
textbox1.addArrow(arrow)
textbox2.addArrow(arrow)
view.show()
sys.exit(app.exec_())
The position of the item doesn't actually matter - it can remain at 0,0 - providing the bounding box is correct (which it will be according to your Arrow::boundingBox implementation). Hence, I think if you simply trigger a bounding box change, and a redraw in updatePosition, everything will work as you want.
Of course, if you care about the position of the arrow being at the head or tail of the line, you can move it in updatePosition, and adjust the bounding box / paint coordinates accordingly - but that's entirely up to you if that makes sense or not.