I'm trying to draw a path made up of lines in two ways: the first, the path is made of straight lines, which have their extremities in common. The problem with using this methodology is that the lines overlap in their extremities causing an undesirable effect as can be seen in the following figure:
And here is the code:
from PyQt5 import QtWidgets, QtCore, QtGui
import typing
import random
class Track(QtWidgets.QGraphicsPathItem):
def __init__(self, parent=None, offset: float = 50):
super(Track, self).__init__(parent) # initiate the parent class
self.__points: list = [QtCore.QPointF(0, 0)]
self.__pen: QtGui.QPen = QtGui.QPen()
self.setPen()
def getPoints(self) -> list:
return self.__points
def append(self, point: QtCore.QPointF):
self.__points.append(point)
def getPen(self) -> QtGui.QPen:
return self.__pen
def setPen(self, pen: QtGui.QPen = None, width: int = 10, color: QtGui.QColor = QtGui.QColor(0, 24, 128, 100),
cap: QtCore.Qt.PenCapStyle = QtCore.Qt.SquareCap, line_style: QtCore.Qt.PenStyle = QtCore.Qt.SolidLine,
join: QtCore.Qt.PenJoinStyle = QtCore.Qt.RoundJoin) -> None:
"""
Set the pen that will be used to paint the implement.
:param pen : set the pen or its arguments
:param width: the pen width.
:param color: the pen color.
:param cap: the cap style: rounded, flatted and squared.
:param line_style: dashed, solid ...
:param join: miter , rounded ...
:return: None
"""
if pen == None:
self.__pen.setWidth(width) # set the pen width
self.__pen.setColor(color) # define your color from QtCore, it is safer to use the statement:
self.__pen.setCapStyle(cap) # set the cap style of the line
self.__pen.setStyle(line_style) # set the line style for instance: solid, dash... whatever
self.__pen.setJoinStyle(join) # set how the lines will be connected.
else:
self.__pen = pen
def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionGraphicsItem,
widget: typing.Optional[QtWidgets.QWidget] = ...) -> None:
painter.setPen(self.getPen())
painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
try:
path = QtGui.QPainterPath()
path.moveTo(self.getPoints()[0])
for point in self.getPoints():
path.lineTo(point)
painter.drawPath(path)
except IndexError:
self.append(QtCore.QPointF(0, 0))
class Producer(QtCore.QObject):
def __init__(self, parent=None):
super(Producer, self).__init__(parent)
self.__last_point = QtCore.QPointF(10, 0)
self.__point = QtCore.QPointF(10, 0)
self.upper = 10
self.bottom = 0
def setPoint(self) -> None:
self.setLastpoint(self.getPoint())
x = random.randint(self.bottom, self.upper)
y = random.randint(self.bottom, self.upper)
self.upper += 50 # increases the range of probability at the upper limit
self.__point = QtCore.QPointF(x, y) # produce a new random point
def getPoint(self) -> QtCore.QPointF:
return self.__point
def setLastpoint(self, point: QtCore.QPointF):
self.__last_point = point
def getLastPoint(self) -> QtCore.QPointF:
return self.__last_point
class Window2(QtWidgets.QMainWindow):
def __init__(self):
super(Window2, self).__init__()
central_widget = QtWidgets.QWidget()
self.__pen = QtGui.QPen()
self.setMinimumHeight(500)
self.setMinimumWidth(500)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setSceneRect(self.view.mapToScene(self.view.viewport().rect()).boundingRect())
self.btn = QtWidgets.QPushButton('Get Track')
self.btn.clicked.connect(self.getTrack)
self.producer = Producer()
hbox = QtWidgets.QHBoxLayout(central_widget)
hbox.addWidget(self.view)
hbox.addWidget(self.btn)
self.setCentralWidget(central_widget)
self.setPen()
def getPen(self) -> QtGui.QPen:
return self.__pen
def getTrack(self):
print('run')
self.producer.setPoint()
line = QtCore.QLineF(self.producer.getPoint(), self.producer.getLastPoint())
self.scene.addLine(line, pen = self.getPen())
dx = self.producer.getPoint().x() - self.producer.getLastPoint().x()
dy = self.producer.getPoint().y() - self.producer.getLastPoint().y()
print(dx, dy)
self.view.setSceneRect(self.view.sceneRect().translated(dx, dy))
def setPen(self, pen: QtGui.QPen = None, width: int = 10, color: QtGui.QColor = QtGui.QColor(0, 24, 128, 100),
cap: QtCore.Qt.PenCapStyle = QtCore.Qt.SquareCap, line_style: QtCore.Qt.PenStyle = QtCore.Qt.SolidLine,
join: QtCore.Qt.PenJoinStyle = QtCore.Qt.RoundJoin) -> None:
"""
Set the pen that will be used to paint the implement.
:param pen : set the pen or its arguments
:param width: the pen width.
:param color: the pen color.
:param cap: the cap style: rounded, flatted and squared.
:param line_style: dashed, solid ...
:param join: miter , rounded ...
:return: None
"""
if pen == None:
self.__pen.setWidth(width) # set the pen width
self.__pen.setColor(color) # define your color from QtCore, it is safer to use the statement:
self.__pen.setCapStyle(cap) # set the cap style of the line
self.__pen.setStyle(line_style) # set the line style for instance: solid, dash... whatever
self.__pen.setJoinStyle(join) # set how the lines will be connected.
else:
self.__pen = pen
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
# w = Window()
w = Window2()
w.show()
sys.exit(app.exec_())
The second way I am using is creating a path that is continuous and made up of points. This path inherits the class: QGraphicsPathItem. However, when I 'update' my 'scene Rectangle' this path disappears when it has one of its ends outside my border = 'scene Rectangle'. Was there any way to prevent it from disappearing? A second concern with this approach that I am taking is the fact that I need to save these points that make up my path ... for a small amount of points this is not a problem but as my path gets full of points it will have problems with memory management. image:
And the code is here:
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
central_widget = QtWidgets.QWidget()
self.setMinimumHeight(500)
self.setMinimumWidth(500)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setSceneRect(self.view.mapToScene(self.view.viewport().rect()).boundingRect())
self.btn = QtWidgets.QPushButton('Get Track')
self.btn.clicked.connect(self.getTrack)
self.producer = Producer()
hbox = QtWidgets.QHBoxLayout(central_widget)
hbox.addWidget(self.view)
hbox.addWidget(self.btn)
self.track = Track()
self.scene.addItem(self.track)
self.setCentralWidget(central_widget)
def getTrack(self):
self.producer.setPoint()
self.track.append(self.producer.getPoint())
dx = self.producer.getPoint().x() - self.producer.getLastPoint().x()
dy = self.producer.getPoint().y() - self.producer.getLastPoint().y()
print(dx, dy)
self.view.setSceneRect(self.view.sceneRect().translated(dx, dy))
I am traying to simulate a GPS, where the path that I'm traying to paint is the car's displacement. But I don't know what of this two are the better approach and if there is another one.
here a minimal reproducible example:
from PyQt5 import QtWidgets, QtCore, QtGui
import typing
import random
pen = QtGui.QPen()
pen.setColor(QtGui.QColor(0, 24, 128, 100))
pen.setWidth(10)
pen.setStyle(QtCore.Qt.SolidLine)
pen.setCapStyle(QtCore.Qt.SquareCap)
class Track(QtWidgets.QGraphicsPathItem):
def __init__(self, parent=None, offset: float = 50):
super(Track, self).__init__(parent) # initiate the parent class
self.__points: list = [QtCore.QPointF(0, 0)]
def getPoints(self) -> list:
return self.__points
def append(self, point: QtCore.QPointF):
self.__points.append(point)
def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionGraphicsItem,
widget: typing.Optional[QtWidgets.QWidget] = ...) -> None:
painter.setPen(pen)
painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
path = QtGui.QPainterPath()
path.moveTo(self.getPoints()[0])
for point in self.getPoints():
path.lineTo(point)
painter.drawPath(path)
class Producer(QtCore.QObject):
def __init__(self, parent=None):
super(Producer, self).__init__(parent)
self.__last_point = QtCore.QPointF(10, 0)
self.__point = QtCore.QPointF(10, 0)
self.upper = 10
def setPoint(self) -> None:
self.setLastpoint(self.getPoint())
x = random.randint(0, self.upper)
y = random.randint(0, self.upper)
self.upper += 50 # increases the range of probability at the upper limit
self.__point = QtCore.QPointF(x, y) # produce a new random point
def getPoint(self) -> QtCore.QPointF:
return self.__point
def setLastpoint(self, point: QtCore.QPointF):
self.__last_point = point
def getLastPoint(self) -> QtCore.QPointF:
return self.__last_point
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
central_widget = QtWidgets.QWidget()
self.setMinimumHeight(500)
self.setMinimumWidth(500)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setSceneRect(self.view.mapToScene(self.view.viewport().rect()).boundingRect())
self.btn = QtWidgets.QPushButton('Get Track')
self.btn.clicked.connect(self.getTrack)
self.producer = Producer()
hbox = QtWidgets.QHBoxLayout(central_widget)
hbox.addWidget(self.view)
hbox.addWidget(self.btn)
self.track = Track()
self.scene.addItem(self.track)
self.setCentralWidget(central_widget)
def getTrack(self):
self.producer.setPoint()
self.track.append(self.producer.getPoint())
dx = self.producer.getPoint().x() - self.producer.getLastPoint().x()
dy = self.producer.getPoint().y() - self.producer.getLastPoint().y()
self.view.setSceneRect(self.view.sceneRect().translated(dx, dy))
class Window2(QtWidgets.QMainWindow):
def __init__(self):
super(Window2, self).__init__()
central_widget = QtWidgets.QWidget()
self.setMinimumHeight(500)
self.setMinimumWidth(500)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setSceneRect(self.view.mapToScene(self.view.viewport().rect()).boundingRect())
self.btn = QtWidgets.QPushButton('Get Track')
self.btn.clicked.connect(self.getTrack)
self.producer = Producer()
hbox = QtWidgets.QHBoxLayout(central_widget)
hbox.addWidget(self.view)
hbox.addWidget(self.btn)
self.setCentralWidget(central_widget)
def getTrack(self):
self.producer.setPoint()
self.scene.addLine(QtCore.QLineF(self.producer.getPoint(), self.producer.getLastPoint()), pen=pen)
dx = self.producer.getPoint().x() - self.producer.getLastPoint().x()
dy = self.producer.getPoint().y() - self.producer.getLastPoint().y()
self.view.setSceneRect(self.view.sceneRect().translated(dx, dy))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
# w = Window()
w = Window2()
w.show()
sys.exit(app.exec_())