拖拽QGraphicsView内Item出现重影,需保留drawForeground该如何解决
问题原因
重影问题由两个配置冲突导致:
- 配置的
QGraphicsView.BoundingRectViewportUpdate视口更新模式,只会在拖拽元素时重绘元素移动涉及的局部区域,不会刷新整个视口 drawForeground方法基于传入的局部更新矩形rect绘制边框,每次局部更新只会绘制当前区域对应的边框片段,之前绘制的旧边框没有被清除,层层叠加形成重影。
解决方法
方案1:修改视口更新模式(最简方案,仅需修改1行代码)
直接将视口更新模式改为全视口更新,每次触发重绘时都会刷新整个视图区域,自动清除旧的前景绘制内容:
把View初始化代码中的这行:
self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
替换为:
self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
该方案足够应对绝大多数常规业务场景,不会产生明显的性能损失。
方案2:保留局部更新性能优化,调整drawForeground逻辑
如果你确实需要保留局部更新的性能优势,可以根据边框的业务定位修改绘制逻辑:
- 如果边框需要固定在视图窗口边缘(不跟随场景拖拽、缩放移动),可以改为基于视图坐标绘制,避免和场景局部更新区域绑定:
def drawForeground(self, painter: QPainter, rect: QRectF): painter.save() # 重置坐标变换,使用视图视口的像素坐标 painter.resetTransform() pen = QPen(QBrush(Qt.blue), 2, Qt.DotLine) painter.setPen(pen) # 直接基于视口大小绘制边框 viewport_rect = self.viewport().rect() painter.drawRect(viewport_rect.adjusted(10, 10, -10, -10)) painter.restore()
- 如果边框需要绘制在场景的固定位置,只需要在元素移动时主动调用
self.scene().invalidate()触发前景的全局刷新即可。
内容的提问来源于stack exchange,提问作者jett chen
相关产品推荐
相关产品推荐

