PyQt5中QGraphicsItem多继承问题:图形项显示异常求助
问题:多继承
QGraphicsRectItem和QGraphicsEllipseItem仅显示第一个父类图形 我正在开发一个可切换为矩形或圆形的图形项(二者功能、属性一致),尝试通过同时继承QGraphicsRectItem和QGraphicsEllipseItem实现单一类:
class Pad(QGraphicsRectItem, QGraphicsEllipseItem): def __init__(self, pos, isCircle=False, parent=None): w, h = 200, 100 if isCircle: QGraphicsEllipseItem.__init__(self, -w/2, -h/2, w, h) else: QGraphicsRectItem.__init__(self, -w/2, -h/2, w, h)
但实际运行后仅显示第一个父类(QGraphicsRectItem)的图形,完整代码如下:
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * class Pad(QGraphicsRectItem, QGraphicsEllipseItem): def __init__(self, pos, isCircle=False, parent=None): w, h = 200, 100 if isCircle: QGraphicsEllipseItem.__init__(self, -w/2, -h/2, w, h) else: QGraphicsRectItem.__init__(self, -w/2, -h/2, w, h) self.setPos(pos) self.parent = parent self.color = QColor(255, 0, 0) self.setPen(QPen(self.color, Qt.MiterJoin, 1)) self.setBrush(QBrush(self.color)) def main(): import sys app = QtWidgets.QApplication(sys.argv) scene = QGraphicsScene() rect = Pad(QPointF(0, 0), False, scene) circle = Pad(QPointF(300, 300), True, scene) scene.addItem(rect) scene.addItem(circle) view = QtWidgets.QGraphicsView(scene) view.setRenderHints(QtGui.QPainter.Antialiasing) view.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
原因分析
PyQt图形项的绘制逻辑由paint()方法决定,多继承时Python会按照**方法解析顺序(MRO)**查找该方法。你的Pad类继承顺序是QGraphicsRectItem在前,QGraphicsEllipseItem在后,因此始终优先调用QGraphicsRectItem的paint()实现,无论初始化时选择哪个父类,最终都会绘制矩形。
另外,QGraphicsRectItem和QGraphicsEllipseItem都继承自QAbstractGraphicsShapeItem,二者各自维护形状数据,但多继承场景下,初始化其中一个父类不会覆盖另一个的绘制逻辑,反而因MRO优先级导致第一个父类的绘制逻辑始终生效。
解决方案
方案一:继承QGraphicsItem手动实现绘制
直接继承最基础的QGraphicsItem,自行处理矩形和圆形的绘制、边界计算等逻辑,灵活性最高:
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * class Pad(QGraphicsItem): def __init__(self, pos, isCircle=False, parent=None): super().__init__(parent) self.w, self.h = 200, 100 self.isCircle = isCircle self.setPos(pos) self.color = QColor(255, 0, 0) self.pen = QPen(self.color, Qt.MiterJoin, 1) self.brush = QBrush(self.color) def boundingRect(self): # 返回图形边界矩形,用于场景裁剪和交互判断 return QRectF(-self.w/2, -self.h/2, self.w, self.h) def paint(self, painter, option, widget=None): painter.setPen(self.pen) painter.setBrush(self.brush) if self.isCircle: painter.drawEllipse(self.boundingRect()) else: painter.drawRect(self.boundingRect()) # 可选:重写shape()方法,提升交互(如点击)的精准度 def shape(self): path = QPainterPath() if self.isCircle: path.addEllipse(self.boundingRect()) else: path.addRect(self.boundingRect()) return path def main(): import sys app = QtWidgets.QApplication(sys.argv) scene = QGraphicsScene() rect = Pad(QPointF(0, 0), False, scene) circle = Pad(QPointF(300, 300), True, scene) scene.addItem(rect) scene.addItem(circle) view = QtWidgets.QGraphicsView(scene) view.setRenderHints(QtGui.QPainter.Antialiasing) view.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
方案二:使用组合而非继承
在类内部维护QGraphicsRectItem或QGraphicsEllipseItem实例,将操作委托给内部实例,可复用已有子类的功能:
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * class Pad(QGraphicsItem): def __init__(self, pos, isCircle=False, parent=None): super().__init__(parent) self.w, self.h = 200, 100 # 创建内部图形项实例 if isCircle: self.inner_item = QGraphicsEllipseItem(-self.w/2, -self.h/2, self.w, self.h, self) else: self.inner_item = QGraphicsRectItem(-self.w/2, -self.h/2, self.w, self.h, self) self.setPos(pos) self.color = QColor(255, 0, 0) self.inner_item.setPen(QPen(self.color, Qt.MiterJoin, 1)) self.inner_item.setBrush(QBrush(self.color)) def boundingRect(self): return self.inner_item.boundingRect() def paint(self, painter, option, widget=None): # 委托内部项执行绘制 self.inner_item.paint(painter, option, widget) # 支持动态切换形状 def setIsCircle(self, isCircle): # 移除旧的内部项 self.inner_item.setParentItem(None) # 创建新的内部项 if isCircle: self.inner_item = QGraphicsEllipseItem(-self.w/2, -self.h/2, self.w, self.h, self) else: self.inner_item = QGraphicsRectItem(-self.w/2, -self.h/2, self.w, self.h, self) # 恢复样式 self.inner_item.setPen(QPen(self.color, Qt.MiterJoin, 1)) self.inner_item.setBrush(QBrush(self.color)) self.update() def main(): import sys app = QtWidgets.QApplication(sys.argv) scene = QGraphicsScene() rect = Pad(QPointF(0, 0), False, scene) circle = Pad(QPointF(300, 300), True, scene) # 测试动态切换形状:circle.setIsCircle(False) scene.addItem(rect) scene.addItem(circle) view = QtWidgets.QGraphicsView(scene) view.setRenderHints(QtGui.QPainter.Antialiasing) view.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
内容的提问来源于stack exchange,提问作者MinhTNguyen
相关产品推荐
相关产品推荐

