Как рисовать стрелки (с помощью мыши) PyQt QPainter

Я знаю как рисовать линии в PyQt при помощи метода drawLine, а вот как стрелки рисовать не знаю. Как рисовать стрелки?

Знаю в Tkinter это можно делать с помощью create_line, думаю в PyQt должен быть какой-то способ сделать это.

введите сюда описание изображения

main.py

import sys
from PyQt5.Qt import *


class drawingProcess(QWidget):
    def __init__(self, parent):
        super().__init__(parent)

        self.parent = parent
   
        self.is_pressed = False
        self.drawingPath = None
        self.begin, self.destination = QPoint(), QPoint()
        self.resize(500, 500)
        self.image = QPixmap(500, 500)
        self.image.fill(Qt.white) 

    def draw(self, parent):
        painter = QPainter(parent)
        if self.drawingPath:
            if not self.begin.isNull() and not self.destination.isNull():
                painter.setPen(QPen(QColor(0, 0, 0), 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
                painter.drawLine(self.begin, self.destination)
                    
    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawPixmap(QPoint(), self.image)
        if self.is_pressed:
            self.draw(self)

    def mousePressEvent(self, event):
        self.is_pressed = True
        if event.button() == Qt.LeftButton:
            self.drawingPath = QPainterPath()
            self.drawingPath.moveTo(event.pos())
            self.begin = event.pos()
            self.destination = self.begin
            self.update()

    def mouseMoveEvent(self, event):
        if event.buttons() and Qt.LeftButton and self.drawingPath:     
            self.drawingPath.lineTo(event.pos())
            self.destination = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        self.is_pressed = False
        self.draw(self.image)
        self.update()
        

class Main(QWidget):
    def __init__(self):
        super().__init__()

        self.drawing_proc = drawingProcess(self)
        self.resize(500, 500)
        self.show()
        self.drawing_proc.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Main()
    sys.exit(app.exec_())

Ответы (2 шт):

Автор решения: Alexander Chernin

Для рисования таких сложных объектов рекомендую использовать SVG и классы типа QSvgRenderer.

Вот небольшой пример:

from PyQt5.QtSvg import QSvgRenderer
# другие импорты в том числе файл-ресурсов

class DrawArrow(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.svgRen = QSvgRenderer(":/arrow.svg") # из файла ресурсов
        self.arrowRect = QRectF() # Рамка, выделяемая мышкой

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        # инициализируем рамку-область отрисовки стрелки
        self.arrowRect.setTopLeft(event.pos())
        self.arrowRect.setBottomRight(event.pos())
        self.update()

    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        if not self.arrowRect.topLeft().isNull():
            self.arrowRect.setBottomRight(event.pos())
        self.update()

    def paintEvent(self, event)
        super().paintEvent(event)
        # Рамка валидна и файл со стрелкой загрузился
        if self.arrowRect.isValid() and self.svgRen.isValid():
            painter = QPainter(self)
            painter.setPen(Qt.DashLine)
            painter.drawRect(self.arrowRect)

            # Расчитываем и устанавливаем угол поворота изображения
            # painter.rotate(...)
            # Рисуем стрелку
            self.svgRen.render(painter, self.arrowRect)
→ Ссылка
Автор решения: S. Nick

Мне кажется, что довольно сложно для вас будет сделать такой рывок в рисовании от линии до фигуры, которую вы показали.

Предлагаю начать со стрелки, которая выглядит попроще. Ну, а затем переходить к рисованию более сложных вещей.

main.py

import sys
from math import acos, degrees, sqrt
from PyQt5.Qt import *


class DrawingProcess(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent = parent
   
        self.is_pressed = False
        self.drawingPath = None
        self.begin, self.destination = QPoint(), QPoint()
        self.resize(500, 500)
        self.image = QPixmap(500, 500)
        self.image.fill(Qt.white) 

    def draw(self, parent):
        painter = QPainter(parent)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
        
        if self.drawingPath:
            if not self.begin.isNull() and not self.destination.isNull():
                painter.setPen(
                    QPen(
                        QColor(0, 0, 0), 
                        5, 
                        Qt.SolidLine, 
                        Qt.RoundCap, 
                        Qt.RoundJoin
                    )
                )
                painter.drawLine(self.begin, self.destination)

                l = 30
                x_right = QPointF(self.destination + QPointF(15, 0)) 
                
                right_triangle = QPainterPath()
                right_triangle.lineTo(-0.5 * sqrt(3) * l, 0.5 * l)
                right_triangle.lineTo(-0.5 * sqrt(3) * l, -0.5 * l)
                right_triangle.closeSubpath()
                right_triangle.translate(x_right)
                
                painter.setBrush(QColor("blue"))
                painter.translate(self.destination)

                x1, y1 = self.begin.x(), self.begin.y()
                x2, y2 = self.destination.x(), self.destination.y()
                a = y2 - y1
                c = x2 - x1
                b = sqrt(a**2 + c**2)

                angle = 0
                if a==0 and b==c:
                    angle = 0
                elif c==0 and -a==b:
                    angle = 90
                elif a==0 and b==-c:
                    angle = 180
                elif c==0 and a==b:
                    angle = 270  
                elif a<0 and b>0:
                    angle = degrees(acos((b*b + c*c - a*a)/(2.0 * b * c)))
                else:
                    angle = 360 - degrees(acos((b*b + c*c - a*a)/(2.0 * b * c)))

                painter.rotate(-angle)
                painter.translate(-self.destination)                
                painter.drawPath(right_triangle)
                    
    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawPixmap(QPoint(), self.image)
        if self.is_pressed:
            self.draw(self)

    def mousePressEvent(self, event):
        self.is_pressed = True
        if event.button() == Qt.LeftButton:
            self.drawingPath = QPainterPath()
            self.drawingPath.moveTo(event.pos())
            self.begin = event.pos()
            self.destination = self.begin
            self.update()

    def mouseMoveEvent(self, event):
        if event.buttons() and Qt.LeftButton and self.drawingPath:     
            self.drawingPath.lineTo(event.pos())
            self.destination = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        self.is_pressed = False
        self.draw(self.image)
        self.update()
        

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.drawing_proc = DrawingProcess(self)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка