|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2007 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a graphics item subclass for an arrow. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import math |
|
13 |
|
14 from PyQt5.QtCore import QPointF, QRectF, QSizeF, QLineF, Qt |
|
15 from PyQt5.QtGui import QPen, QPolygonF |
|
16 from PyQt5.QtWidgets import QAbstractGraphicsShapeItem, QGraphicsItem, QStyle |
|
17 |
|
18 NormalArrow = 1 |
|
19 WideArrow = 2 |
|
20 |
|
21 ArrowheadAngleFactor = 0.26179938779914941 |
|
22 # 0.5 * math.atan(math.sqrt(3.0) / 3.0) |
|
23 |
|
24 |
|
25 class E5ArrowItem(QAbstractGraphicsShapeItem): |
|
26 """ |
|
27 Class implementing an arrow graphics item subclass. |
|
28 """ |
|
29 def __init__(self, origin=None, end=None, |
|
30 filled=False, arrowType=NormalArrow, parent=None): |
|
31 """ |
|
32 Constructor |
|
33 |
|
34 @param origin origin of the arrow (QPointF) |
|
35 @param end end point of the arrow (QPointF) |
|
36 @param filled flag indicating a filled arrow head (boolean) |
|
37 @param arrowType arrow type (NormalArrow, WideArrow) |
|
38 @keyparam parent reference to the parent object (QGraphicsItem) |
|
39 """ |
|
40 super(E5ArrowItem, self).__init__(parent) |
|
41 |
|
42 self._origin = QPointF() if origin is None else QPointF(origin) |
|
43 self._end = QPointF() if end is None else QPointF(end) |
|
44 self._filled = filled |
|
45 self._type = arrowType |
|
46 |
|
47 self._halfLength = 13.0 |
|
48 |
|
49 self.setFlag(QGraphicsItem.ItemIsMovable, True) |
|
50 self.setFlag(QGraphicsItem.ItemIsSelectable, True) |
|
51 |
|
52 def setPoints(self, xa, ya, xb, yb): |
|
53 """ |
|
54 Public method to set the start and end points of the line. |
|
55 |
|
56 <b>Note:</b> This method does not redraw the item. |
|
57 |
|
58 @param xa x-coordinate of the start point (float) |
|
59 @param ya y-coordinate of the start point (float) |
|
60 @param xb x-coordinate of the end point (float) |
|
61 @param yb y-coordinate of the end point (float) |
|
62 """ |
|
63 self._origin = QPointF(xa, ya) |
|
64 self._end = QPointF(xb, yb) |
|
65 |
|
66 def setStartPoint(self, x, y): |
|
67 """ |
|
68 Public method to set the start point. |
|
69 |
|
70 <b>Note:</b> This method does not redraw the item. |
|
71 |
|
72 @param x x-coordinate of the start point (float) |
|
73 @param y y-coordinate of the start point (float) |
|
74 """ |
|
75 self._origin = QPointF(x, y) |
|
76 |
|
77 def setEndPoint(self, x, y): |
|
78 """ |
|
79 Public method to set the end point. |
|
80 |
|
81 <b>Note:</b> This method does not redraw the item. |
|
82 |
|
83 @param x x-coordinate of the end point (float) |
|
84 @param y y-coordinate of the end point (float) |
|
85 """ |
|
86 self._end = QPointF(x, y) |
|
87 |
|
88 def boundingRect(self): |
|
89 """ |
|
90 Public method to return the bounding rectangle. |
|
91 |
|
92 @return bounding rectangle (QRectF) |
|
93 """ |
|
94 extra = self._halfLength / 2.0 |
|
95 return QRectF(self._origin, QSizeF(self._end.x() - self._origin.x(), |
|
96 self._end.y() - self._origin.y()))\ |
|
97 .normalized()\ |
|
98 .adjusted(-extra, -extra, extra, extra) |
|
99 |
|
100 def paint(self, painter, option, widget=None): |
|
101 """ |
|
102 Public method to paint the item in local coordinates. |
|
103 |
|
104 @param painter reference to the painter object (QPainter) |
|
105 @param option style options (QStyleOptionGraphicsItem) |
|
106 @param widget optional reference to the widget painted on (QWidget) |
|
107 """ |
|
108 if (option.state & QStyle.State_Selected) == \ |
|
109 QStyle.State(QStyle.State_Selected): |
|
110 width = 2 |
|
111 else: |
|
112 width = 1 |
|
113 |
|
114 # draw the line first |
|
115 line = QLineF(self._origin, self._end) |
|
116 painter.setPen( |
|
117 QPen(Qt.black, width, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin)) |
|
118 painter.drawLine(line) |
|
119 |
|
120 # draw the arrow head |
|
121 arrowAngle = self._type * ArrowheadAngleFactor |
|
122 slope = math.atan2(line.dy(), line.dx()) |
|
123 |
|
124 # Calculate left arrow point |
|
125 arrowSlope = slope + arrowAngle |
|
126 a1 = QPointF(self._end.x() - self._halfLength * math.cos(arrowSlope), |
|
127 self._end.y() - self._halfLength * math.sin(arrowSlope)) |
|
128 |
|
129 # Calculate right arrow point |
|
130 arrowSlope = slope - arrowAngle |
|
131 a2 = QPointF(self._end.x() - self._halfLength * math.cos(arrowSlope), |
|
132 self._end.y() - self._halfLength * math.sin(arrowSlope)) |
|
133 |
|
134 if self._filled: |
|
135 painter.setBrush(Qt.black) |
|
136 else: |
|
137 painter.setBrush(Qt.white) |
|
138 polygon = QPolygonF() |
|
139 polygon.append(line.p2()) |
|
140 polygon.append(a1) |
|
141 polygon.append(a2) |
|
142 painter.drawPolygon(polygon) |