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

PyQt5如何实现QGraphicsEllipseItem拖拽时绕另一图元平滑移动不重叠

问题根源

你当前的碰撞修正逻辑是直接将移动圆的x/y坐标固定到静止圆±最小距离的位置,所以最终只会落在四个对角位置,自然会出现吸附跳变的问题。要实现平滑绕行,需要基于两个圆心的相对角度做极坐标换算,保证移动圆始终和静止圆保持刚好不重叠的距离,且方向跟随你的拖拽偏移。

修复后完整代码

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import math


class Circleitem(QGraphicsEllipseItem):

    def __init__(self, size, brush):
        super().__init__()
        radius = size / -2
        self.setRect(radius, radius, size, size)
        self.setBrush(brush)
        self.setFlag(self.ItemIsMovable)
        self.setFlag(self.ItemIsSelectable)

    def paint(self, painter, option, a):
        option.state = QStyle.State_None
        return super(Circleitem, self).paint(painter,option)
    
    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        self.scene().views()[0].parent().movearound()



class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.gscene = QGraphicsScene(0, 0, 1000, 1000)
        gview = QGraphicsView(self.gscene)
        self.setCentralWidget(gview)
        self.circle1 = Circleitem (123, brush=QColor(255,255,0))
        self.circle2 =Circleitem(80, brush=QColor(0,255,0))
        self.gscene.addItem(self.circle1)
        self.gscene.addItem(self.circle2)
        self.circle1.setPos(500, 500)
        self.circle2.setPos(300, 300)
        self.show()

    def movearound(self):
        if not self.gscene.selectedItems():
            return
        if self.gscene.selectedItems()[0] == self.circle1:
            moveditem = self.circle1
            stillitem = self.circle2
        else:
            moveditem = self.circle2
            stillitem = self.circle1
        
        # 计算两个圆心的相对坐标、实际距离、最小不碰撞距离
        dx = moveditem.x() - stillitem.x()
        dy = moveditem.y() - stillitem.y()
        current_dist = math.hypot(dx, dy)
        min_dist = moveditem.rect().width()/2 + stillitem.rect().width()/2

        # 距离小于最小值时触发修正
        if current_dist < min_dist:
            # 处理两圆完全重合的除零情况
            if current_dist == 0:
                angle = 0
            else:
                # 计算当前相对角度
                angle = math.atan2(dy, dx)
            # 按最小距离和当前角度计算新的位置
            new_x = stillitem.x() + min_dist * math.cos(angle)
            new_y = stillitem.y() + min_dist * math.sin(angle)
            moveditem.setPos(new_x, new_y)
        


app = QApplication([])
win = MainWindow()
app.exec()

关键修改说明

  • 删掉了原逻辑中无意义的距离缩放系数,最小不碰撞距离直接取两个圆的半径之和
  • 用math.hypot计算两个圆心的实际欧氏距离,替代原逻辑的x/y分别判断
  • 基于相对角度做极坐标换算,修正后的位置始终在静止圆的最小距离圆周上,跟随拖拽方向平滑移动,不会出现跳变
  • 增加了无选中项的判断,避免空索引报错

内容的提问来源于stack exchange,提问作者drivereye

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:15:05