summaryrefslogtreecommitdiff
path: root/src/main/python/shapes/line.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/python/shapes/line.py')
-rw-r--r--src/main/python/shapes/line.py304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/main/python/shapes/line.py b/src/main/python/shapes/line.py
new file mode 100644
index 0000000..310b496
--- /dev/null
+++ b/src/main/python/shapes/line.py
@@ -0,0 +1,304 @@
+from PyQt5.QtGui import QPen, QPainterPath, QBrush, QPainterPathStroker, QPainter, QCursor
+from PyQt5.QtWidgets import QGraphicsItem, QGraphicsPathItem
+from PyQt5.QtCore import Qt, QPointF, QRectF
+
+class Grabber(QGraphicsPathItem):
+ """
+ Extends QGraphicsPathItem to create grabber for line for moving a particular segment
+ """
+ circle = QPainterPath()
+ circle.addEllipse(QRectF(-5, -5, 10, 10))
+
+ def __init__(self, annotation_line, index, direction):
+ super(Grabber, self).__init__()
+ self.m_index = index
+ self.m_annotation_item = annotation_line
+ self._direction = direction
+ self.setPath(Grabber.circle)
+ # set graphical settings for this item
+ self.setFlag(QGraphicsItem.ItemIsSelectable, True)
+ self.setFlag(QGraphicsItem.ItemIsMovable, True)
+ self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
+ self.setAcceptHoverEvents(True)
+
+ def itemChange(self, change, value):
+ """ move position of grabber after resize"""
+ if change == QGraphicsItem.ItemPositionChange and self.isEnabled():
+ p = QPointF(self.pos())
+ if self._direction == Qt.Horizontal:
+ p.setX(value.x())
+ elif self._direction == Qt.Vertical:
+ p.setY(value.y())
+ movement = p - self.pos()
+ self.m_annotation_item.movePoints(self.m_index, movement)
+ return p
+ return super(Grabber, self).itemChange(change, value)
+
+ def paint(self, painter, option, widget):
+ """paints the path of grabber only if it is selected
+ """
+ if self.isSelected():
+ # show line of grabber
+ self.m_annotation_item.setSelected(True)
+ painter.setBrush(QBrush(Qt.cyan))
+ color = Qt.black if self.isSelected() else Qt.white
+ width = 2 if self.isSelected() else -1
+ painter.setPen(QPen(color, width, Qt.SolidLine))
+ painter.drawPath(self.path())
+
+ # To paint path of shape
+ # color = Qt.red if self.isSelected() else Qt.black
+ # painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))
+ # painter.drawPath(self.shape())
+
+ def shape(self):
+ """Overrides shape method and set shape to segment on which grabber is located"""
+ index = self.m_index
+ startPoint = QPointF(self.m_annotation_item.path().elementAt(index))
+ endPoint = QPointF(self.m_annotation_item.path().elementAt(index + 1))
+ startPoint = self.mapFromParent(startPoint)
+ endPoint = self.mapFromParent(endPoint)
+ path = QPainterPath(startPoint)
+ path.lineTo(endPoint)
+ # generate outlines for path
+ stroke = QPainterPathStroker()
+ stroke.setWidth(8)
+ return stroke.createStroke(path)
+
+ def boundingRect(self):
+ return self.shape().boundingRect()
+
+ def mousePressEvent(self, event):
+ print('grabber clicked', self)
+ super(Grabber, self).mousePressEvent(event)
+
+ def hoverEnterEvent(self, event):
+ """
+ Changes cursor to horizontal movement or vertical movement
+ depending on the direction of the grabber on mouse enter
+ """
+ if self._direction == Qt.Horizontal:
+ self.setCursor(QCursor(Qt.SplitHCursor))
+ else:
+ self.setCursor(QCursor(Qt.SplitVCursor))
+ super(Grabber, self).hoverEnterEvent(event)
+
+ def hoverLeaveEvent(self, event):
+ """
+ reverts cursor to default on mouse leave
+ """
+ self.setCursor(QCursor(Qt.ArrowCursor))
+ super(Grabber, self).hoverLeaveEvent(event)
+
+
+class Line(QGraphicsPathItem):
+ """
+ Extends QGraphicsPathItem to draw zig-zag line consisting of multiple points
+ """
+ penStyle = Qt.SolidLine
+
+ def __init__(self, startPoint, endPoint, **args):
+ QGraphicsItem.__init__(self, **args)
+ self.startPoint = startPoint
+ self.endPoint = endPoint
+ #stores all points of line
+ self.points = []
+ self.points.extend([startPoint, endPoint])
+ self.startGripItem = None
+ self.endGripItem = None
+ self._selected = False
+ self.m_grabbers = []
+ # stores current pen style of line
+ self.penStyle = Line.penStyle
+ # set graphical settings for this item
+ self.setFlag(QGraphicsItem.ItemIsSelectable, True)
+ self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
+ self.setAcceptHoverEvents(True)
+ # initiates path
+ self.createPath()
+
+ def createPath(self):
+ """
+ creates initial path and stores it's points
+ :return:
+ """
+ offset = 30
+ x0, y0 = self.startPoint.x(), self.startPoint.y()
+ x1, y1 = self.endPoint.x(), self.endPoint.y()
+ self.points = [self.startPoint, QPointF((x0 + x1) / 2, y0), QPointF((x0 + x1) / 2, y1), self.endPoint]
+ if self.startGripItem and self.startGripItem.m_location in ["left", "right"]:
+ if self.endGripItem and self.endGripItem.m_location in ["top", "bottom"]:
+ if self.endGripItem.m_location == "top": offset = -offset
+ self.points = [self.startPoint, QPointF((x0 + x1) / 2, y0), QPointF((x0 + x1) / 2, y1 + offset),
+ QPointF(self.endPoint.x(), y1 + offset), self.endPoint]
+
+ if self.startGripItem and self.startGripItem.m_location in ["top", "bottom"]:
+ self.points = [self.startPoint, QPointF(x0, (y0 + y1) / 2), QPointF(x1, (y0 + y1) / 2), self.endPoint]
+ if self.endGripItem and self.endGripItem.m_location in ["left", "right"]:
+ self.points = [self.startPoint, QPointF(x0, (y0 + y1) / 2), QPointF(x1 - offset, (y0 + y1) / 2),
+ QPointF(x1 - offset, self.endPoint.y()), self.endPoint]
+ # draw line
+ path = QPainterPath(self.startPoint)
+ for i in range(1, len(self.points)):
+ path.lineTo(self.points[i])
+ self.setPath(path)
+ if self.endGripItem:
+ self.addGrabber()
+
+ def updatePath(self):
+ """ update path when svg item moves
+ """
+ path = QPainterPath(self.startPoint)
+ self.updatePoints()
+
+ for i in range(1, len(self.points) - 1):
+ path.lineTo(self.points[i])
+ path.lineTo(self.endPoint)
+ self.setPath(path)
+
+ def updatePoints(self):
+ """
+ updates points of line when grabber is moved
+ :return:
+ """
+ if self.startGripItem.m_location in ["left", "right"]:
+ point = self.points[1]
+ self.points[1] = QPointF(point.x(), self.startPoint.y())
+ if self.endGripItem.m_location in ["left", "right"]:
+ point = self.points[len(self.points) - 2]
+ self.points[len(self.points) - 2] = QPointF(point.x(), self.endPoint.y())
+ else:
+ point = self.points[len(self.points) - 2]
+ self.points[len(self.points) - 2] = QPointF(self.endPoint.x(), point.y())
+
+ else:
+ point = self.points[1]
+ self.points[1] = QPointF(self.startPoint.x(), point.y())
+ if self.endGripItem.m_location in ["left", "right"]:
+ point = self.points[len(self.points) - 2]
+ self.points[len(self.points) - 2] = QPointF(point.x(), self.endPoint.y())
+ else:
+ point = self.points[len(self.points) - 2]
+ self.points[len(self.points) - 2] = QPointF(self.endPoint.x(), point.y())
+
+ def shape(self):
+ """generates outline for path
+ """
+ qp = QPainterPathStroker()
+ qp.setWidth(8)
+ path = qp.createStroke(self.path())
+ return path
+
+ def paint(self, painter, option, widget):
+ color = Qt.red if self.isSelected() else Qt.black
+ painter.setPen(QPen(color, 2, self.penStyle))
+ painter.drawPath(self.path())
+
+ # To paint path of shape
+ # painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))
+ # painter.drawPath(self.shape())
+ if self.isSelected():
+ self.showGripItem()
+ self._selected = True
+ elif self._selected:
+ self.hideGripItem()
+ self._selected = False
+
+ def movePoints(self, index, movement):
+ """move points of line
+ """
+ for i in [index, index + 1]:
+ point = self.points[i]
+ point += movement
+ self.points[i] = point
+ self.updatePath()
+ self.updateGrabber([index])
+
+ def addGrabber(self):
+ """adds grabber when line is moved
+ """
+ if self.startGripItem.m_location in ["left", "right"]:
+ direction = [Qt.Horizontal, Qt.Vertical]
+ else:
+ direction = [Qt.Vertical, Qt.Horizontal]
+ for i in range(1, len(self.points) - 2):
+ item = Grabber(self, i, direction[i - 1])
+ item.setParentItem(self)
+ item.setPos(self.pos())
+ self.scene().addItem(item)
+ self.m_grabbers.append(item)
+
+ def updateGrabber(self, index_no_updates=None):
+ """updates all grabber of line when it is moved
+ """
+ index_no_updates = index_no_updates or []
+ for grabber in self.m_grabbers:
+ if grabber.m_index in index_no_updates: continue
+ index = grabber.m_index
+ startPoint = self.points[index]
+ endPoint = self.points[index + 1]
+ pos = (startPoint + endPoint) / 2
+ grabber.setEnabled(False)
+ grabber.setPos(pos)
+ grabber.setEnabled(True)
+
+ def itemChange(self, change, value):
+ if change == QGraphicsItem.ItemSceneHasChanged and self.scene():
+ # self.addGrabber()
+ # self.updateGrabber()
+ return
+ return super(Line, self).itemChange(change, value)
+
+ def updateLine(self, startPoint=None, endPoint=None):
+
+ """This function is used to update connecting line when it add on
+ canvas and when it's grip item moves
+ :return:
+ """
+ self.prepareGeometryChange()
+ if startPoint:
+ self.startPoint = startPoint
+ if endPoint:
+ self.endPoint = endPoint
+ self.createPath()
+ self.updateGrabber()
+ return
+
+ if self.startGripItem and self.endGripItem:
+ item = self.startGripItem
+ self.startPoint = item.parentItem().mapToScene(item.pos())
+ item = self.endGripItem
+ self.endPoint = item.parentItem().mapToScene(item.pos())
+ self.updatePath()
+ self.updateGrabber()
+
+ def removeFromCanvas(self):
+ """This function is used to remove connecting line from canvas
+ :return:
+ """
+ if self.scene():
+ self.scene().removeItem(self)
+
+ def showGripItem(self):
+ """hides grip items which contains line
+ """
+ if self.startGripItem: self.startGripItem.show()
+ if self.endGripItem: self.endGripItem.show()
+ # for grabber in self.m_grabber:
+ # grabber.setSelected(True)
+
+ def hideGripItem(self):
+ """hides grip items which contains line
+ """
+ if self.startGripItem: self.startGripItem.hide()
+ if self.endGripItem: self.endGripItem.hide()
+
+ def setStartGripItem(self, item):
+ self.startGripItem = item
+
+ def setEndGripItem(self, item):
+ self.endGripItem = item
+
+ def setPenStyle(self, style):
+ """change current pen style for line"""
+ self.penStyle = style