Python PySide2.QtCore.QPointF() Examples

The following are 24 code examples of PySide2.QtCore.QPointF(). You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module PySide2.QtCore , or try the search function .
Example #1
Source File: tileGAN_client.py    From tileGAN with GNU General Public License v3.0 6 votes vote down vote up
def updateIndicatorPos(self, eventPos):
		"""
		move indicator cursor to correct position under mouse
		"""
		rect = self.getImageDims()
		gridSize = 	rect.width() / self.gridSize.width()
		latentSize = self.latentSize * gridSize
		centerOffset = latentSize // 2

		scenePos = QGraphicsView.mapToScene(self, eventPos)
		scenePos -= QPointF(centerOffset, centerOffset) #center latentIndicator around mouse

		roundPos = QPoint(int(gridSize * round(scenePos.x()/gridSize)), int(gridSize * round(scenePos.y()/gridSize)))

		roundPos.setX(int(max(-centerOffset, min(roundPos.x(), rect.width()  - ( latentSize - centerOffset )))))
		roundPos.setY(int(max(-centerOffset, min(roundPos.y(), rect.height() - ( latentSize - centerOffset )))))
		self._latentIndicator.setPos(roundPos) 
Example #2
Source File: qgraph_arrow.py    From angr-management with BSD 2-Clause "Simplified" License 6 votes vote down vote up
def _make_arrow(self, location, direction):
        if location == "start":
            coord = self.start
        else:
            coord = self.end

        if direction == "down":
            return [QPointF(coord.x() - 3, coord.y()), QPointF(coord.x() + 3, coord.y()),
                     QPointF(coord.x(), coord.y() + 6)]
        elif direction == "right":
            return [QPointF(coord.x(), coord.y() - 3), QPointF(coord.x(), coord.y() + 3),
                 QPointF(coord.x() + 6, coord.y())]
        elif direction == "left":
            return [QPointF(coord.x(), coord.y() - 3), QPointF(coord.x(), coord.y() + 3),
                    QPointF(coord.x() - 6, coord.y())]
        else:
            raise NotImplementedError("Direction %s is not supported yet." % direction) 
Example #3
Source File: Airfoil.py    From PyAero with MIT License 6 votes vote down vote up
def makeContourPolygon(self, coordinates):
        """Add airfoil points as GraphicsItem to the scene"""

        # instantiate a graphics item
        contour = gic.GraphicsCollection()
        # make it polygon type and populate its points
        points = [QtCore.QPointF(x, y) for x, y in zip(*coordinates)]
        contour.Polygon(QtGui.QPolygonF(points), self.name)
        # set its properties
        contour.pen.setColor(self.pencolor)
        contour.pen.setWidthF(self.penwidth)
        # no pen thickness change when zoomed
        contour.pen.setCosmetic(True)
        contour.brush.setColor(self.brushcolor)

        self.contourPolygon = GraphicsItem.GraphicsItem(contour) 
Example #4
Source File: qgraph_arrow.py    From angr-management with BSD 2-Clause "Simplified" License 6 votes vote down vote up
def __init__(self, edge, arrow_location="end", arrow_direction='down', parent=None):
        super().__init__(parent)

        self.edge = edge
        self.rect = None
        self._start = QPointF(*self.edge.coordinates[0])
        self.coords = [self.create_point(c) for c in self.edge.coordinates]
        self.end = self.coords[-1]
        self.start = self.coords[0]

        self.color = EDGE_COLORS.get(self.edge.sort, EDGE_COLORS[EdgeSort.DIRECT_JUMP])
        self.arrow = self._make_arrow(arrow_location, arrow_direction)
        self.style = EDGE_STYLES.get(self.edge.sort, EDGE_STYLES[EdgeSort.DIRECT_JUMP])
        self.path = self._make_path()

        self._hovered = False

        self.setAcceptHoverEvents(True) 
Example #5
Source File: lightmap.py    From GridCal with GNU General Public License v3.0 5 votes vote down vote up
def tileForCoordinate(lat, lng, zoom):
    zn = float(1 << zoom)
    tx = float(lng + 180.0) / 360.0
    ty = (1.0 - math.log(math.tan(lat * math.pi / 180.0) +
                         1.0 / math.cos(lat * math.pi / 180.0)) / math.pi) / 2.0

    return QPointF(tx * zn, ty * zn) 
Example #6
Source File: qdisasm_graph.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def _initial_position(self):
        entry_block_rect = self.entry_block.mapRectToScene(self.entry_block.boundingRect())
        viewport_height = self.viewport().rect().height()
        min_rect = self.scene().itemsBoundingRect()
        if min_rect.height() < (viewport_height // 1.5):
            return min_rect.center()
        else:
            focus_point = (entry_block_rect.center().x(), entry_block_rect.top() + (viewport_height // 4))
            return QPointF(*focus_point) 
Example #7
Source File: qdisasm_graph.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def request_relayout(self):

        node_coords, edges = self._layout_graph()

        self._edges = edges

        categorize_edges(self.disasm, edges)

        if not node_coords:
            print("Failed to get node_coords")
            return

        # layout nodes
        for block in self.blocks:
            x, y = node_coords[block.addr]
            block.setPos(x, y)

        scene = self.scene()

        # remove exiting arrows
        for arrow in self._arrows:
            scene.removeItem(arrow)
        self._arrows.clear()

        for edge in self._edges:
            arrow = QDisasmGraphArrow(edge, self.disasm_view, self.infodock)
            self._arrows.append(arrow)
            scene.addItem(arrow)
            arrow.setPos(QPointF(*edge.coordinates[0])) 
Example #8
Source File: qfeature_map.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def _paint_insn_indicators(self, **kwargs):

        scene = self.view.scene()  # type: QGraphicsScene
        for item in self._insn_indicators:
            scene.removeItem(item)
        self._insn_indicators.clear()

        for selected_insn_addr in self.disasm_view.infodock.selected_insns:
            pos = self._get_pos_from_addr(selected_insn_addr)
            if pos is None:
                continue

            pos -= 1  # this is the top-left x coordinate of our arrow body (the rectangle)

            pen = QPen(Qt.yellow)
            brush = QBrush(Qt.yellow)
            rect = QRectF(pos, 0, 2, 5)
            # rectangle
            item = scene.addRect(rect, pen, brush)
            self._insn_indicators.append(item)
            # triangle
            triangle = QPolygonF()
            triangle.append(QPointF(pos - 1, 5))
            triangle.append(QPointF(pos + 3, 5))
            triangle.append(QPointF(pos + 1, 7))
            triangle.append(QPointF(pos - 1, 5))
            item = scene.addPolygon(triangle, pen, brush)
            self._insn_indicators.append(item) 
Example #9
Source File: qgraph_arrow.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def _get_line_end(self, i: int) -> QPointF:
        pt0 = self.coords[i]
        pt1 = self.coords[i + 1] if i + 1 < len(self.coords) else self.coords[0]
        rat = self._radius / self._get_distance(pt0, pt1)
        if rat > 0.5:
            rat = 0.5
        return QPointF(rat * pt0.x() + (1.0 - rat) * pt1.x(), rat * pt0.y() + (1.0 - rat) * pt1.y()) 
Example #10
Source File: qgraph_arrow.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def _get_distance(pt0: QPointF, pt1: QPointF) -> float:
        d = (pt1.x() - pt0.x()) ** 2 + (pt1.y() - pt0.y()) ** 2
        return math.sqrt(d) 
Example #11
Source File: qgraph_arrow.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def create_point(self, stuff):
        return QPointF(*stuff) - self._start 
Example #12
Source File: qsymexec_graph.py    From angr-management with BSD 2-Clause "Simplified" License 5 votes vote down vote up
def _draw_edges(self, painter, topleft_point, bottomright_point):
        for edge in self._edges:
            edge_coords = edge.coordinates

            color = QColor(0x70, 0x70, 0x70)
            pen = QPen(color)
            pen.setWidth(1.5)
            painter.setPen(pen)

            for from_, to_ in zip(edge_coords, edge_coords[1:]):
                start_point = QPointF(*from_)
                end_point = QPointF(*to_)
                # optimization: don't draw edges that are outside of the current scope
                if (start_point.x() > bottomright_point.x() or start_point.y() > bottomright_point.y()) and \
                        (end_point.x() > bottomright_point.x() or end_point.y() > bottomright_point.y()):
                    continue
                elif (start_point.x() < topleft_point.x() or start_point.y() < topleft_point.y()) and \
                        (end_point.x() < topleft_point.x() or end_point.y() < topleft_point.y()):
                    continue
                painter.drawPolyline((start_point, end_point))

            # arrow
            # end_point = self.mapToScene(*edges[-1])
            end_point = (edge_coords[-1][0], edge_coords[-1][1])
            arrow = [QPointF(end_point[0] - 3, end_point[1]), QPointF(end_point[0] + 3, end_point[1]),
                     QPointF(end_point[0], end_point[1] + 6)]
            brush = QBrush(color)
            painter.setBrush(brush)
            painter.drawPolygon(arrow) 
Example #13
Source File: GuiSlots.py    From PyAero with MIT License 5 votes vote down vote up
def fitAirfoilInView(self):

        if len(self.parent.airfoils) == 0:
            return

        # get bounding rect in scene coordinates
        item = self.parent.airfoil.contourPolygon
        rectf = item.boundingRect()
        rf = copy.deepcopy(rectf)

        center = rf.center()
        # make 4% smaller than width of graphicsview
        w = 1.04 * rf.width()
        h = 1.04 * rf.height()
        # do not use setWidhtF and setHeightF here!!!
        rf.setWidth(w)
        rf.setHeight(h)

        # shift center of rectf
        cx = center.x()
        cy = center.y()
        rf.moveCenter(QtCore.QPointF(cx, cy))

        self.parent.view.fitInView(rf,
                                   aspectRadioMode=QtCore.Qt.KeepAspectRatio)

        # it is IMPORTANT that this is called after fitInView
        # adjust airfoil marker size to MARKERSIZE setting
        self.parent.view.adjustMarkerSize()

        # cache view to be able to keep it during resize
        self.parent.view.getSceneFromView() 
Example #14
Source File: lightmap.py    From GridCal with GNU General Public License v3.0 5 votes vote down vote up
def pan(self, delta):
        dx = QPointF(delta) / float(TDIM)
        center = tileForCoordinate(self.latitude, self.longitude, self.zoom) - dx
        self.latitude = latitudeFromTile(center.y(), self.zoom)
        self.longitude = longitudeFromTile(center.x(), self.zoom)
        self.invalidate()

    # slots 
Example #15
Source File: GraphicsItemsCollection.py    From PyAero with MIT License 5 votes vote down vote up
def Line(self, x1, y1, x2, y2):
        eps = 0.01
        p1 = QtCore.QPointF(x1-eps, y1-eps)
        p2 = QtCore.QPointF(x2+eps, y2+eps)
        self.rect = QtCore.QRectF(p1, p2)
        self.shape.addRect(self.rect)
        self.method = 'drawLine'
        self.args = [x1, y1, x2, y2] 
Example #16
Source File: tileGAN_client.py    From tileGAN with GNU General Public License v3.0 5 votes vote down vote up
def roundToGlobalGridCoords(self, pos):
		"""
		return a global coordinate for a latent grid position
		"""
		roundedGridPos = self.getGridCoords(pos)
		imageRect = self.getImageDims()
		pixelPos = QPointF(roundedGridPos.x() * imageRect.width() / self.gridSize.width(), roundedGridPos.y() * imageRect.height() / self.gridSize.height())
		return QGraphicsView.mapToGlobal(self, QGraphicsView.mapFromScene(self, pixelPos)) 
Example #17
Source File: tileGAN_client.py    From tileGAN with GNU General Public License v3.0 5 votes vote down vote up
def updateIndicatorSize(self, stroke=3, offset=2, crossSize=10):
		"""
		draw a box and crosshair under mouse cursor as rectangle of size latentSize
		"""
		multiplier = 1 #TODO optional: scale indicator with zoom level

		stroke *= multiplier
		offset *= multiplier
		crossSize *= multiplier

		halfStroke = stroke / 2
		rect = self.getImageDims()
		latentSize = self.latentSize * rect.width() / self.gridSize.width()
		halfSize = latentSize / 2
		crossSize = min(crossSize, int(halfSize - 3))

		pixmap = QPixmap(QSize(int(latentSize + stroke + offset), int(latentSize + stroke + offset)))
		#fill rectangle with transparent color
		pixmap.fill(QColor(0,0,0,0)) #transparent

		painter = QPainter(pixmap)
		r = QRectF(QPoint(), QSizeF(latentSize, latentSize))
		r.adjust(offset+halfStroke, offset+halfStroke, -halfStroke, -halfStroke)
		#draw shadow under rectangle
		pen = QPen(QColor(50, 50, 50, 100), stroke) #shadow
		painter.setPen(pen)
		painter.drawRect(r)
		if crossSize > 4:
			painter.drawLine(QPointF(offset+halfSize, offset+halfSize-crossSize), QPointF(offset+halfSize, offset+halfSize+crossSize))
			painter.drawLine(QPointF(offset+halfSize-crossSize, offset+halfSize), QPointF(offset+halfSize+crossSize, offset+halfSize))
		r.adjust(-offset, -offset, -offset, -offset)
		pen = QPen(QColor(styleColor[0], styleColor[1], styleColor[2], 200), stroke)
		painter.setPen(pen)
		painter.drawRect(r)
		if crossSize > 4:
			painter.drawLine(QPointF(halfSize, halfSize - crossSize), QPointF(halfSize, halfSize + crossSize))
			painter.drawLine(QPointF(halfSize - crossSize, halfSize), QPointF(halfSize + crossSize, halfSize))
		painter.end()

		self._latentIndicator.setPixmap(pixmap) 
Example #18
Source File: geometry.py    From hotbox_designer with BSD 3-Clause Clear License 5 votes vote down vote up
def move(self, rects, cursor):
        x = cursor.x() - self.reference_x
        y = cursor.y() - self.reference_y
        if self.snap is not None:
            x, y = snap(x, y, self.snap)
        width = self.rect.width()
        height = self.rect.height()
        self.rect.setTopLeft(QtCore.QPointF(x, y))
        self.rect.setWidth(width)
        self.rect.setHeight(height)
        self.apply_relative_transformation(rects) 
Example #19
Source File: canvas.py    From spore with MIT License 5 votes vote down vote up
def paintEvent(self, event):

        super(CircularBrush, self).paintEvent(event)

        # draw brush
        if hasattr(self, 'brush_state') and self.brush_state.draw:
            painter = QPainter()
            shapes = self.create_brush_shape()
            for shape in shapes:
                shape = [QPointF(point[0], point[1]) for point in shape]

                path = QPainterPath()
                start_pos = shape.pop(0)
                path.moveTo(start_pos)
                [path.lineTo(point) for point in shape]

                painter.setRenderHint(painter.Antialiasing)
                #  painter.setRenderHint(painter.HighQualityAnti)
                painter.begin(self)

                painter.setPen(QPen(Qt.red, 1))
                painter.drawPath(path)

            painter.end() 
Example #20
Source File: shunt_graphics.py    From GridCal with GNU General Public License v3.0 4 votes vote down vote up
def __init__(self, parent, api_obj, diagramScene):
        """

        :param parent:
        :param api_obj:
        """
        super(ShuntGraphicItem, self).__init__(parent)

        self.w = 15.0
        self.h = 30.0

        self.parent = parent

        self.api_object = api_obj

        self.diagramScene = diagramScene

        self.width = 4

        if self.api_object is not None:
            if self.api_object.active:
                self.style = ACTIVE['style']
                self.color = ACTIVE['color']
            else:
                self.style = DEACTIVATED['style']
                self.color = DEACTIVATED['color']
        else:
            self.style = OTHER['style']
            self.color = OTHER['color']

        pen = QPen(self.color, self.width, self.style)

        # Properties of the container:
        self.setFlags(self.ItemIsSelectable | self.ItemIsMovable)
        # self.setCursor(QCursor(Qt.PointingHandCursor))

        # line to tie this object with the original bus (the parent)
        self.nexus = QtWidgets.QGraphicsLineItem()
        self.nexus.setPen(QPen(self.color, self.width, self.style))
        parent.scene().addItem(self.nexus)

        self.lines = list()
        self.lines.append(QLineF(QPointF(self.w / 2, 0), QPointF(self.w / 2, self.h * 0.4)))
        self.lines.append(QLineF(QPointF(0, self.h * 0.4), QPointF(self.w, self.h * 0.4)))
        self.lines.append(QLineF(QPointF(0, self.h * 0.6), QPointF(self.w, self.h * 0.6)))
        self.lines.append(QLineF(QPointF(self.w / 2, self.h * 0.6), QPointF(self.w / 2, self.h)))
        self.lines.append(QLineF(QPointF(0, self.h * 1), QPointF(self.w, self.h * 1)))
        self.lines.append(QLineF(QPointF(self.w * 0.15, self.h * 1.1), QPointF(self.w * 0.85, self.h * 1.1)))
        self.lines.append(QLineF(QPointF(self.w * 0.3, self.h * 1.2), QPointF(self.w * 0.7, self.h * 1.2)))
        for l in self.lines:
            l1 = QLine(self)
            l1.setLine(l)
            l1.setPen(pen)
            self.addToGroup(l1)

        self.setPos(self.parent.x(), self.parent.y() + 100)
        self.update_line(self.pos()) 
Example #21
Source File: qdep_graph.py    From angr-management with BSD 2-Clause "Simplified" License 4 votes vote down vote up
def request_relayout(self):
        self._reset_scene()
        if self.graph is None:
            return

        # remove all arrows
        self._arrows.clear()
        self._arrows_by_src.clear()
        self._arrows_by_dst.clear()

        # remove all nodes
        self.blocks.clear()

        node_sizes = {}
        for node in self.graph.nodes():
            self.blocks.add(node)
            node_sizes[node] = (node.width, node.height)
        gl = TreeGraphLayouter(self.graph, node_sizes)

        self._edges = gl.edges

        min_x, max_x, min_y, max_y = 0, 0, 0, 0

        scene = self.scene()
        for node, (x, y) in gl.node_coordinates.items():
            scene.addItem(node)
            node.setPos(x, y)
            min_x = min(min_x, node.x())
            max_x = max(max_x, node.x() + node.width)
            min_y = min(min_y, node.y())
            max_y = max(max_y, node.y() + node.height)

        # draw edges
        for edge in self._edges:
            arrow = QDepGraphArrow(self._dep_view, edge, arrow_location="start", arrow_direction="left")
            self._arrows.append(arrow)
            self._arrows_by_src[edge.src].append(arrow)
            self._arrows_by_dst[edge.dst].append(arrow)
            scene.addItem(arrow)
            arrow.setPos(QPointF(*edge.coordinates[0]))

        min_x -= self.LEFT_PADDING
        max_x += self.LEFT_PADDING
        min_y -= self.TOP_PADDING
        max_y += self.TOP_PADDING

        self._reset_view() 
Example #22
Source File: Airfoil.py    From PyAero with MIT License 4 votes vote down vote up
def makeContourSpline(self):
        """Add splined and refined airfoil points as GraphicsItem to
        the scene
        """
        self.pencolor = QtGui.QColor(80, 80, 220, 255)
        self.penwidth = 3.5

        # instantiate a graphics item
        splinecontour = gic.GraphicsCollection()
        # make it polygon type and populate its points
        points = [QtCore.QPointF(x, y) for x, y in zip(*self.spline_data[0])]
        splinecontour.Polygon(QtGui.QPolygonF(points), self.name)
        # set its properties
        splinecontour.pen.setColor(self.pencolor)
        splinecontour.pen.setWidthF(self.penwidth)
        # no pen thickness change when zoomed
        splinecontour.pen.setCosmetic(True)
        splinecontour.brush.setColor(self.brushcolor)
        # add the pline polygon without filling
        splinecontour.brush.setStyle(QtCore.Qt.NoBrush)

        # remove items from iterated uses of spline/refine and trailing edge
        if hasattr(self, 'contourSpline'):
            self.mainwindow.scene.removeItem(self.contourSpline)
        self.contourSpline = GraphicsItem.GraphicsItem(splinecontour)
        self.mainwindow.scene.addItem(self.contourSpline)

        # remove items from iterated uses of spline/refine and trailing edge
        if hasattr(self, 'splineMarkersGroup'):
            self.mainwindow.scene.removeItem(self.splineMarkersGroup)
        self.makeSplineMarkers()
        self.splineMarkersGroup = self.mainwindow.scene. \
            createItemGroup(self.splineMarkers)

        self.mainwindow.airfoil.contourSpline.brush. \
            setStyle(QtCore.Qt.SolidPattern)
        color = QtGui.QColor()
        color.setNamedColor('#7c8696')
        self.contourSpline.brush.setColor(color)
        self.polygonMarkersGroup.setZValue(100)
        self.chord.setZValue(99)

        # switch off raw contour and toogle corresponding checkbox
        if self.polygonMarkersGroup.isVisible():
            self.mainwindow.centralwidget.cb2.click()

        # activate ckeck boxes for contour points and chord in viewing options
        self.mainwindow.centralwidget.cb3.setChecked(True)
        self.mainwindow.centralwidget.cb3.setEnabled(True)
        self.mainwindow.centralwidget.cb4.setChecked(True)
        self.mainwindow.centralwidget.cb4.setEnabled(True)

        self.contourPolygon.brush.setStyle(QtCore.Qt.NoBrush)
        self.contourPolygon.pen.setStyle(QtCore.Qt.NoPen)
        self.mainwindow.view.adjustMarkerSize() 
Example #23
Source File: Meshing.py    From PyAero with MIT License 4 votes vote down vote up
def drawMesh(self, airfoil):
        """Add the mesh as ItemGroup to the scene

        Args:
            airfoil (TYPE): object containing all airfoil properties and data
        """

        # toggle spline points
        self.mainwindow.centralwidget.cb3.click()

        # delete old mesh if existing
        if hasattr(airfoil, 'mesh'):
            logger.debug('MESH item type: {}'.format(type(airfoil.mesh)))
            self.mainwindow.scene.removeItem(airfoil.mesh)

        mesh = list()

        for block in self.blocks:
            for lines in [block.getULines(),
                          block.getVLines()]:
                for line in lines:

                    # instantiate a graphics item
                    contour = gic.GraphicsCollection()
                    # make it polygon type and populate its points
                    points = [QtCore.QPointF(x, y) for x, y in line]
                    contour.Polyline(QtGui.QPolygonF(points), '')
                    # set its properties
                    contour.pen.setColor(QtGui.QColor(0, 0, 0, 255))
                    contour.pen.setWidthF(0.8)
                    contour.pen.setCosmetic(True)
                    contour.brush.setStyle(QtCore.Qt.NoBrush)

                    # add contour as a GraphicsItem to the scene
                    # these are the objects which are drawn in the GraphicsView
                    meshline = GraphicsItem.GraphicsItem(contour)
                    mesh.append(meshline)

        airfoil.mesh = self.mainwindow.scene.createItemGroup(mesh)

        # activate viewing options if mesh is created and displayed
        self.mainwindow.centralwidget.cb6.setChecked(True)
        self.mainwindow.centralwidget.cb6.setEnabled(True) 
Example #24
Source File: application.py    From torba with MIT License 4 votes vote down vote up
def paint(self, painter, option, widget=None):
        if self.start_node.collidesWithItem(self.end_node):
            return

        start_node = self.start_node
        end_node = self.end_node
        color = self.arrow_color
        pen = self.pen()
        pen.setColor(self.arrow_color)
        arrow_size = 20.0
        painter.setPen(pen)
        painter.setBrush(self.arrow_color)

        end_rectangle = end_node.sceneBoundingRect()
        start_center = start_node.sceneBoundingRect().center()
        end_center = end_rectangle.center()
        center_line = QtCore.QLineF(start_center, end_center)
        end_polygon = QtGui.QPolygonF(end_rectangle)
        p1 = end_polygon.at(0)

        intersect_point = QtCore.QPointF()
        for p2 in end_polygon:
            poly_line = QtCore.QLineF(p1, p2)
            intersect_type, intersect_point = poly_line.intersect(center_line)
            if intersect_type == QtCore.QLineF.BoundedIntersection:
                break
            p1 = p2

        self.setLine(QtCore.QLineF(intersect_point, start_center))
        line = self.line()

        angle = math.acos(line.dx() / line.length())
        if line.dy() >= 0:
            angle = (math.pi * 2.0) - angle

        arrow_p1 = line.p1() + QtCore.QPointF(
            math.sin(angle + math.pi / 3.0) * arrow_size,
            math.cos(angle + math.pi / 3.0) * arrow_size
        )
        arrow_p2 = line.p1() + QtCore.QPointF(
            math.sin(angle + math.pi - math.pi / 3.0) * arrow_size,
            math.cos(angle + math.pi - math.pi / 3.0) * arrow_size
        )

        self.arrow_head.clear()
        for point in [line.p1(), arrow_p1, arrow_p2]:
            self.arrow_head.append(point)

        painter.drawLine(line)
        painter.drawPolygon(self.arrow_head)
        if self.isSelected():
            painter.setPen(QtGui.QPen(color, 1, QtCore.Qt.DashLine))
            line = QtCore.QLineF(line)
            line.translate(0, 4.0)
            painter.drawLine(line)
            line.translate(0, -8.0)
            painter.drawLine(line)