【问题标题】:Problem in shape method of QGraphicsPathItemQGraphicsPathItem的shape方法中的问题
【发布时间】:2023-03-05 00:05:01
【问题描述】:

在下图中,我将场景中的QGraphicsPathItem 设置为红色部分,并将其形状覆盖为蓝色部分。我想要当拖动和移动红色空间时,该项目被线性延长或缩短,当拖动蓝色空间​​时,必须移动整个项目。 这是我尝试过的......

import sys

from PyQt5.QtCore import QRectF, Qt, QPointF
from PyQt5.QtGui import QPainterPath, QPen, QPainterPathStroker, QPainter
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsPathItem, QGraphicsItem

class Item(QGraphicsPathItem):
    circle = QPainterPath()
    circle.addEllipse(QRectF(-5, -5, 10, 10))

    def __init__(self):
        super(Item, self).__init__()
        self.setPath(Item.circle)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)

    def paint(self, painter, option, widget):
        color = Qt.red if self.isSelected() else Qt.black
        painter.setPen(QPen(color, 2, Qt.SolidLine))
        painter.drawPath(self.path())

        # To paint path of shape
        painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))
        painter.drawPath(self.shape())

    def shape(self):
        startPoint = self.mapFromScene(self.pos())
        endPoint = self.mapFromScene(QPointF(10, 10))
        path = QPainterPath(startPoint)
        path.lineTo(endPoint)
        stroke = QPainterPathStroker()
        stroke.setWidth(10)
        return stroke.createStroke(path)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = QMainWindow()
    window.show()
    scene = QGraphicsScene()
    scene.setSceneRect(0, 0, 200, 200)
    view = QGraphicsView()
    view.setScene(scene)
    window.setCentralWidget(view)
    scene.addItem(Item())
    sys.exit(app.exec_())

我的输出是受干扰的路径

【问题讨论】:

  • 我不懂你,解释清楚你的要求
  • 我有一个 qgraphicspath 项目,在图像中显示为红色圆圈,可以移动。当我在红色圆圈和场景上的固定点之间单击鼠标时,我希望 item.isSelected() 方法返回 true
  • 好吧,为什么要重写 shape 方法?
  • 应该改变蓝色空间的大小还是应该始终保持相同的大小?
  • 正如我所说的,我已经理解你想要什么,而且我知道它为什么会失败,所以你不再说同样的话,否则 cmets 会扩展太多。最好花时间改进您的帖子,清楚地解释您想要什么。

标签: python pyqt pyqt5 qgraphicsitem qgraphicspathitem


【解决方案1】:

在同一个项目中处理调整大小和拉伸的任务很复杂,所以为了避免这种情况,我使用了两个项目:一个手柄和一个管道。因此,每个人都管理自己的任务并更新其他元素的位置:

import sys

from PyQt5 import QtCore, QtGui, QtWidgets


class HandleItem(QtWidgets.QGraphicsPathItem):
    def __init__(self, parent=None):
        super().__init__(parent)
        path = QtGui.QPainterPath()
        path.addEllipse(QtCore.QRectF(-5, -5, 10, 10))
        self.setPath(path)

        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)

        self._pipe_item = None

    @property
    def pipe_item(self):
        return self._pipe_item

    @pipe_item.setter
    def pipe_item(self, item):
        self._pipe_item = item

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
            ip = self.pipe_item.mapFromScene(value)
            self.pipe_item.end_pos = ip
        elif change == QtWidgets.QGraphicsItem.ItemSelectedChange:
            color = QtCore.Qt.red if value else QtCore.Qt.black
            self.setPen(QtGui.QPen(color, 2, QtCore.Qt.SolidLine))
        return super().itemChange(change, value)


class PipeItem(QtWidgets.QGraphicsPathItem):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)

        self._end_pos = QtCore.QPointF()

        self._handle = HandleItem()
        self.handle.pipe_item = self

        self.end_pos = QtCore.QPointF(10, 10)
        self.handle.setPos(self.end_pos)

        self.setPen(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine))

    @property
    def handle(self):
        return self._handle

    @property
    def end_pos(self):
        return self._end_pos

    @end_pos.setter
    def end_pos(self, p):
        path = QtGui.QPainterPath()
        path.lineTo(p)
        stroke = QtGui.QPainterPathStroker()
        stroke.setWidth(10)
        self.setPath(stroke.createStroke(path))
        self._end_pos = p

    def paint(self, painter, option, widget):
        option.state &= ~QtWidgets.QStyle.State_Selected
        super().paint(painter, option, widget)

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemSceneHasChanged:
            if self.scene():
                self.scene().addItem(self.handle)
        elif change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
            p = self.mapToScene(self.end_pos)
            self.handle.setPos(p)
        return super().itemChange(change, value)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    scene = QtWidgets.QGraphicsScene(sceneRect=QtCore.QRectF(0, 0, 200, 200))
    item = PipeItem()
    scene.addItem(item)
    view = QtWidgets.QGraphicsView(scene)
    window = QtWidgets.QMainWindow()
    window.setCentralWidget(view)
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

更新:

如果你想要实现你想要的逻辑,那么它会更复杂。错误的原因是paint() 方法使用boundingRect() 来设置绘制区域,但是在您的情况下它没有考虑到它的变化,可能的解决方案如下:

class Item(QGraphicsPathItem):
    circle = QPainterPath()
    circle.addEllipse(QRectF(-5, -5, 10, 10))

    # ...

    def boundingRect(self):
        return self.shape().boundingRect()

【讨论】:

  • 好的,我同意你的看法,但我想将它作为一个单独的项目来实现,就像我们覆盖形状方法以进行碰撞检测
  • 我不是在拉伸物品,只是在拉伸它的形状
  • 在我的代码中,如果我将形状方法中的路径设为常量,那么它将起作用。您可以尝试通过将路径设置为常量来稍微更改我的代码。我在问为什么当这条路径不恒定时它不起作用。例如设置 startPoint = QPointF(0,-10) 和 endPoint=QPointF(0,10) 。
  • 更新部分是我想要的。它有效并加强了我的概念。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-19
  • 1970-01-01
相关资源
最近更新 更多