You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将QGraphicsItem移动至QGraphicsItemGroup指定位置?

QGraphicsItemGroup 自动将子项对齐到组中心的实现方法

问题描述

需要实现:创建QGraphicsItemGroup并设置场景坐标,当事件触发时将场景中的部分项加入组,使所有加入组的项自动移动到组中心。现有代码已将项设置为组的父项,但未看到项在组内的位置变化,询问是否有无需遍历调用setPos()的实现方式。

问题分析

直接调用setParentItem()或addToGroup()时,子项的位置会保留原有的局部坐标逻辑:子项的位置是相对于父容器(组)的局部坐标,而非自动转换为场景坐标并对齐到组中心。另外,自定义MyGroup中重写固定的boundingRect()会干扰组的自动边界计算,导致位置逻辑混乱。

解决方案

无需手动逐个调用setPos(),但需要通过坐标转换实现子项与组中心的对齐,核心步骤如下:

  1. 移除自定义组中固定的boundingRect()重写(QGraphicsItemGroup默认会自动计算所有子项的联合边界)
  2. 计算待添加项在场景坐标下的整体中心
  3. 将组移动到该中心位置
  4. 将每个项的场景坐标转换为组的局部坐标,再设置为组的子项

修改后的完整代码示例

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:15:26