繁体   English   中英

如何在 QGraphicsview 内继续绘制透明线而不重叠?

[英]How can I draw a transparent line continues inside the QGraphicsview without overlap?

我试图以两种方式绘制一条由线组成的路径:第一种,路径由直线组成,它们的末端是共同的。 使用这种方法的问题是线条在其末端重叠,从而导致不良影响,如下图所示:

在此处输入图像描述

这是代码:

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_())

我使用的第二种方法是创建一条由点组成的连续路径。 此路径继承 class:QGraphicsPathItem。 但是,当我“更新”我的“场景矩形”时,当它的一端位于我的边界 =“场景矩形”之外时,这条路径就会消失。 有什么办法可以防止它消失吗? 我正在采取的这种方法的第二个问题是,我需要保存构成我的路径的这些点......对于少量的点,这不是问题,但随着我的路径充满点,它将有memory 管理存在问题。 图片:

在此处输入图像描述

代码在这里:

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))

我正在托盘模拟 GPS,其中我正在托盘绘制的路径是汽车的位移。 但我不知道这两种方法中哪一种更好,以及是否还有另一种方法。

这是一个最小的可重现示例:

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_())

使用多个线段显然不是正确的选择,因为线被认为是单独的元素,如果没有折叠的边缘就不可能绘制它们。

QPainterPath 方法最重要的问题是,当您使用 QGraphicsPathItem 时,您根本没有使用它,因为您覆盖了它的paint()方法。
手动绘制路径使其完全无用,您应该改用它的setPath()

你的实现也是错误的,因为它没有考虑路径的rest,并且使用增量翻译。 正如在对您的另一个问题的评论中已经建议的那样,增量转换通常不是一个好主意,因为它们通常具有误导性并且通常会导致意外行为(就像在这种情况下一样)。

解决方法是正确使用QGraphicsPathItem:


class Track(QtWidgets.QGraphicsPathItem):
    def __init__(self, parent=None, offset: float = 50):
        super(Track, self).__init__(parent)  # initiate the parent class
        self.__points = [QtCore.QPointF(0, 0)]
        self.setPen(pen)

    def getPoints(self) -> list:
        return self.__points

    def append(self, point: QtCore.QPointF):
        self.__points.append(point)
        path = self.path() path.lineTo(point) self.setPath(path)

    # no paint method override!

然后,为了保证item始终可见,可以使用现有的ensureVisible(item)centerOn(item)方法; 请注意,我还删除了第一个setSceneRect()调用,这是因为将其保留为默认值可确保视图的sceneRect始终更新为场景的sceneRect ,除非明确指定,否则默认为项目的边界矩形:

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        # ...
        # remove the following!
        # self.view.setSceneRect(self.view.mapToScene(self.view.viewport().rect()).boundingRect())
        # set the antialiasing for the whole view
        self.view.setRenderHints(QtGui.QPainter.HighQualityAntialiasing)

    def getTrack(self):
        self.producer.setPoint()
        self.track.append(self.producer.getPoint())
        self.view.centerOn(self.track)
        # no setSceneRect() even here

最后,你真的不应该太在意 memory 的使用。 QPoints 的 memory 占用空间非常小,如果您担心系统无法支持,那么您就偏离了轨道:您只需要大约 2个点才能获得一兆字节的 memory。

注意与问题无关:我会避免不必要的过度使用类型提示。 Python 将始终保持为动态类型语言,虽然使用它们并不是坏习惯,但在任何地方过多地使用它们只会让人分心,尤其是在处理库/框架内部使用的重写方法时; 例如, paint()中的类型提示是完全不需要的,因为您可以确定所有 arguments 将始终使用正确的类型调用

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM