Drawing straight line between two points using QPainterPath

Tags: , ,



I have a scene where I would like to draw a line between two points(mouse press should be the start point and mouse release as the endpoint) using the QPainterpath.

Here is a demonstration of how I want it to be.

enter image description here

Here is what’s happening with my current code.

enter image description here

Below is the code I have tried

import sys
from PyQt5 import QtWidgets, QtCore, QtGui

class Scene(QtWidgets.QGraphicsScene):

    def __init__(self, *args, **kwargs):
        super(Scene, self).__init__(*args, **kwargs)
        self.point = QtCore.QPointF(0.0, 0.0)

        self.path = QtGui.QPainterPath()

        self.start_point = None
        self.end_point = None

        self.start = False

    def mousePressEvent(self, event):

        self.start_point = event.scenePos()
        self.start = True
        self.path = QtGui.QPainterPath(self.start_point)
        # self.addLine(self.line.setP1(self.start_point))

        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):

        if self.start:
            self.path.lineTo(event.scenePos())
            # self.path.moveTo(event.scenePos())
            self.addPath(self.path, QtGui.QPen(QtCore.Qt.red))

        super(Scene, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):

        if self.start:
            print(self.path)
            self.path.lineTo(event.scenePos())
            # self.path.moveTo(event.scenePos())
            self.addPath(self.path, QtGui.QPen(QtCore.Qt.red))
            self.start = False

        super(Scene, self).mouseReleaseEvent(event)

def main():
    app = QtWidgets.QApplication(sys.argv)

    view = QtWidgets.QGraphicsView()
    view.setRenderHint(QtGui.QPainter.Antialiasing)

    view.setMouseTracking(True)
    scene = Scene()

    view.setScene(scene)
    view.show()
    
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Answer

Every time lineTo is used then a new line is created where the starting point is the last point added and the end point is the one that is passed to the function, so you see the curves since they are the union of those lines. The solution is to have 2 variables that store the start point and the end point, and be updated when necessary, then use that information to update the QGraphicsPathItem, not the QPainterPath. The same concept can be applied for QGraphicsLineItem with QLineF.

class Scene(QtWidgets.QGraphicsScene):
    def __init__(self, *args, **kwargs):
        super(Scene, self).__init__(*args, **kwargs)

        self.path_item = self.addPath(QtGui.QPainterPath())

        self.start_point = QtCore.QPointF()
        self.end_point = QtCore.QPointF()

    def mousePressEvent(self, event):
        self.start_point = event.scenePos()
        self.end_point = self.start_point
        self.update_path()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() & QtCore.Qt.LeftButton:
            self.end_point = event.scenePos()
            self.update_path()
        super(Scene, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self.end_point = event.scenePos()
        self.update_path()
        super(Scene, self).mouseReleaseEvent(event)

    def update_path(self):
        if not self.start_point.isNull() and not self.end_point.isNull():
            path = QtGui.QPainterPath()
            path.moveTo(self.start_point)
            path.lineTo(self.end_point)
            self.path_item.setPath(path)


Source: stackoverflow