如何将QGraphicsItem移动至QGraphicsItemGroup指定位置?
QGraphicsItemGroup 自动将子项对齐到组中心的实现方法
问题描述
需要实现:创建QGraphicsItemGroup并设置场景坐标,当事件触发时将场景中的部分项加入组,使所有加入组的项自动移动到组中心。现有代码已将项设置为组的父项,但未看到项在组内的位置变化,询问是否有无需遍历调用setPos()的实现方式。
问题分析
直接调用setParentItem()或addToGroup()时,子项的位置会保留原有的局部坐标逻辑:子项的位置是相对于父容器(组)的局部坐标,而非自动转换为场景坐标并对齐到组中心。另外,自定义MyGroup中重写固定的boundingRect()会干扰组的自动边界计算,导致位置逻辑混乱。
解决方案
无需手动逐个调用setPos(),但需要通过坐标转换实现子项与组中心的对齐,核心步骤如下:
- 移除自定义组中固定的
boundingRect()重写(QGraphicsItemGroup默认会自动计算所有子项的联合边界) - 计算待添加项在场景坐标下的整体中心
- 将组移动到该中心位置
- 将每个项的场景坐标转换为组的局部坐标,再设置为组的子项
修改后的完整代码示例
import sys from PyQt5.QtCore import Qt, QRectF from PyQt5.QtWidgets import ( QGraphicsView, QGraphicsScene, QGraphicsEllipseItem, QGraphicsItemGroup, QApplication ) from PyQt5.QtGui import QPen, QBrush class MyDisk(QGraphicsEllipseItem): def __init__(self, top_left_x, top_left_y, radius, color): super().__init__(top_left_x, top_left_y, radius, radius) self.setBrush(color) self.setFlag(QGraphicsEllipseItem.ItemIsMovable) class MyGroup(QGraphicsItemGroup): def __init__(self): super().__init__() self.setFlag(QGraphicsItemGroup.ItemIsMovable) def paint(self, painter, option, widget=None): # 可选:绘制组的边界框,方便观察组范围 painter.setPen(QPen(Qt.gray, 2)) painter.setBrush(Qt.NoBrush) painter.drawRect(self.boundingRect()) class MyView(QGraphicsView): def __init__(self): super().__init__() self.scene = QGraphicsScene() self.setScene(self.scene) self.setWindowTitle('点击鼠标将绿色、红色圆加入组并对齐到组中心') self.setSceneRect(0, 0, 250, 250) # 创建组并添加到场景 self.group = MyGroup() self.scene.addItem(self.group) # 创建三个圆盘并添加到场景 self.d1 = MyDisk(0, 0, 20, Qt.green) self.d2 = MyDisk(150, 50, 20, Qt.red) self.d3 = MyDisk(150, 80, 20, Qt.blue) self.scene.addItem(self.d1) self.scene.addItem(self.d2) self.scene.addItem(self.d3) def mousePressEvent(self, event): # 触发事件:将d1、d2加入组并对齐到组中心 items_to_add = [self.d1, self.d2] if not items_to_add: super().mousePressEvent(event) return # 计算待添加项的场景联合边界及中心 total_bounds = QRectF() for item in items_to_add: total_bounds = total_bounds.united(item.sceneBoundingRect()) items_scene_center = total_bounds.center() # 将组移动到待添加项的场景中心 self.group.setPos(items_scene_center) # 将每个项转换为组的子项,同时转换坐标 for item in items_to_add: if item.scene() == self.scene: self.scene.removeItem(item) # 将项的场景坐标转换为组的局部坐标 local_pos = self.group.mapFromScene(item.scenePos()) item.setParentItem(self.group) item.setPos(local_pos) super().mousePressEvent(event) if __name__ == '__main__': app = QApplication(sys.argv) view = MyView() view.show() sys.exit(app.exec_())
代码说明
MyGroup移除了固定的boundingRect()重写,保留默认的自动边界计算逻辑- 鼠标点击事件中,先计算待添加项的整体场景中心,将组移动到该位置
- 通过
mapFromScene()方法将项的场景坐标转换为组的局部坐标,确保项加入组后位置与组的相对关系正确,视觉上对齐到组中心
内容的提问来源于stack exchange,提问作者Cmarv
相关产品推荐
相关产品推荐

