如何为包含子路径的QGraphicsPolygonItem设置正确的Shape?
问题描述
尝试绘制一个带有若干连接点的多边形,但在设置其Shape时,路径中出现了CircleItem的偏移阴影效果,这并非预期结果。
原代码
from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import pyqtSignal as Signal from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtNetwork import * class CircleItem(QGraphicsPathItem): def __init__(self, parent=None): super().__init__(parent=parent) # self.setFlags(QGraphicsItem.ItemIsMovable|QGraphicsItem.ItemIsSelectable) self.setBrush(Qt.red) path = QPainterPath() path.addEllipse(-20, -20, 40, 40) self.setPath(path) class PolyItem(QGraphicsPolygonItem): def __init__(self): super().__init__() poly = QPolygonF([ QPointF(200, 0), QPointF(120, 150) ]) self.circles = [] for pt in poly: c = CircleItem(self) c.setPos(pt) self.circles.append(c) self.setPolygon(poly) def shape(self) -> QPainterPath: path = QPainterPath() path.addPolygon(self.polygon()) for c in self.circles: path.addPath(c.path()) return path def boundingRect(self) -> QRectF: return self.shape().boundingRect() def paint(self, painter: QPainter, option: 'QStyleOptionGraphicsItem', widget) -> None: painter.drawRect(self.boundingRect()) painter.setPen(Qt.blue) painter.drawPath(self.shape()) return super().paint(painter, option, widget) class View(QGraphicsView): def __init__(self): super().__init__() pl = PolyItem() self.setScene(QGraphicsScene()) self.scene().addItem(pl) if __name__ == '__main__': app = QApplication([]) win = View() win.show() app.exec()
运行结果

解决方案
问题根源有两点:
shape方法中直接添加CircleItem的路径时,未将子项的局部路径转换为父项坐标系下的路径,导致路径位置偏移,出现错位阴影。paint方法额外绘制了shape路径,和子项CircleItem的自动绘制产生重叠。
修正后的代码
from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import pyqtSignal as Signal from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtNetwork import * class CircleItem(QGraphicsPathItem): def __init__(self, parent=None): super().__init__(parent=parent) # self.setFlags(QGraphicsItem.ItemIsMovable|QGraphicsItem.ItemIsSelectable) self.setBrush(Qt.red) path = QPainterPath() path.addEllipse(-20, -20, 40, 40) self.setPath(path) class PolyItem(QGraphicsPolygonItem): def __init__(self): super().__init__() poly = QPolygonF([ QPointF(200, 0), QPointF(120, 150) ]) self.circles = [] for pt in poly: c = CircleItem(self) c.setPos(pt) self.circles.append(c) self.setPolygon(poly) def shape(self) -> QPainterPath: path = QPainterPath() path.addPolygon(self.polygon()) for c in self.circles: # 将CircleItem的局部路径转换为父项坐标系下的路径 path.addPath(c.path().translated(c.pos())) return path def boundingRect(self) -> QRectF: return self.shape().boundingRect() def paint(self, painter: QPainter, option: 'QStyleOptionGraphicsItem', widget) -> None: painter.drawRect(self.boundingRect()) # 移除重复的shape绘制,子项会自动完成自身渲染 # painter.setPen(Qt.blue) # painter.drawPath(self.shape()) return super().paint(painter, option, widget) class View(QGraphicsView): def __init__(self): super().__init__() pl = PolyItem() self.setScene(QGraphicsScene()) self.scene().addItem(pl) if __name__ == '__main__': app = QApplication([]) win = View() win.show() app.exec()
说明
- 在
shape方法中,通过c.path().translated(c.pos())将CircleItem的局部路径平移到它在PolyItem中的实际位置,确保碰撞检测路径的准确性。 - 注释掉
paint方法中绘制shape的代码,依赖子项的自动绘制避免重复渲染,消除错位阴影。
内容的提问来源于stack exchange,提问作者jett chen
相关产品推荐
相关产品推荐

