如何在PyQt6中实现QGraphicsItem元素之间的防碰撞功能
以下是可直接运行的完整实现代码,支持单轴碰撞阻断,同时兼容QGraphicsEllipseItem和QGraphicsRectItem:
from PyQt6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsEllipseItem, QGraphicsItem from PyQt6 import QtCore import typing class CollisionAvoidItem(QGraphicsEllipseItem): def __init__(self, geo): super().__init__(geo) self.setFlag(self.GraphicsItemFlag.ItemIsMovable) self.setFlag(self.GraphicsItemFlag.ItemSendsGeometryChanges) def itemChange(self, change: 'QGraphicsItem.GraphicsItemChange', value: typing.Any) -> typing.Any: if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange: new_pos = value.toPointF() old_pos = self.pos() final_pos = QtCore.QPointF(new_pos) # 测试X方向移动是否碰撞 self.setPos(final_pos.x(), old_pos.y()) if self.collidingItems(QtCore.Qt.ItemSelectionMode.IntersectsShape): final_pos.setX(old_pos.x()) # 测试Y方向移动是否碰撞 self.setPos(final_pos.x(), final_pos.y()) if self.collidingItems(QtCore.Qt.ItemSelectionMode.IntersectsShape): final_pos.setY(old_pos.y()) # 还原测试前的位置,最终位置由返回值生效 self.setPos(old_pos) return final_pos return super().itemChange(change, value) if __name__ == "__main__": app = QApplication([]) view = QGraphicsView() s = QGraphicsScene() s.addItem(CollisionAvoidItem(QtCore.QRectF(50, 50, 50, 50))) s.addItem(CollisionAvoidItem(QtCore.QRectF(200, 200, 50, 50))) # 可直接添加矩形元素测试,只需修改继承父类为QGraphicsRectItem即可 # class RectItem(QGraphicsRectItem, CollisionAvoidItem): pass # s.addItem(RectItem(QtCore.QRectF(100, 100, 60, 40))) view.setScene(s) view.resize(600, 500) view.show() app.exec()
核心逻辑说明
- 收到位置变更请求时,拆分X、Y两个轴分别做碰撞测试
- 先仅修改X坐标测试是否碰撞,碰撞则保留原X坐标,否则使用新X坐标
- 再基于处理后的X坐标,仅修改Y坐标测试是否碰撞,碰撞则保留原Y坐标,否则使用新Y坐标
- 测试过程中临时修改的位置会回滚,最终通过返回值提交最终位置,避免影响Qt原生的事件流
- 逻辑完全兼容所有QGraphicsItem子类,替换父类即可适配矩形、多边形等其他图形元素
内容的提问来源于stack exchange,提问作者Ron lisnyansky
相关产品推荐
相关产品推荐

