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

PyQt6拖动矩形时mouseMoveEvent异常跳动问题求助

PyQt6 GanttItem拖动时自发跳动问题修复

我在PyQt6中实现了基于QGraphicsItem的GanttItem组件,添加鼠标拖动功能后,拖动矩形时出现自发左右跳动的异常情况。控制台打印的event.pos().x()值随机跳变(如332.0与-212.0交替)。

示例代码

from PyQt6.QtWidgets import QGraphicsItem, QGraphicsScene, QGraphicsView, QApplication
from PyQt6.QtGui import QBrush, QPainter, QMouseEvent
from PyQt6.QtCore import QRectF, Qt

class GanttItem(QGraphicsItem):
    def __init__(self, start, duration, name, color, parent=None):
        super().__init__(parent)
        self.start = start
        self.duration = duration
        self.name = name
        self.color = color
        self.dragging = False

    def boundingRect(self):
        return QRectF(0, 0, self.duration * 30, 50)

    def paint(self, painter, option, widget):
        painter.setBrush(QBrush(self.color))
        painter.drawRect(self.boundingRect())
        painter.drawText(self.boundingRect(), Qt.AlignmentFlag.AlignCenter, self.name)

    def mousePressEvent(self, event: QMouseEvent):
        self.dragging = True
        self.mouse_press_pos = event.pos()

    def mouseReleaseEvent(self, event: QMouseEvent):
        self.dragging = False

    def mouseMoveEvent(self, event: QMouseEvent):
        if self.dragging:
            event_x = event.pos().x()
            print(event_x)
            self.setPos(event_x, self.pos().y())

演示效果

拖动跳动演示

控制台输出示例

332.0
-212.0
331.0
-213.0
90.0
-210.0

问题原因

核心问题是坐标系混淆:

  • event.pos()返回的是鼠标相对于当前GanttItem自身坐标系的位置,不是场景全局坐标。
  • 当调用self.setPos(event_x, ...)修改Item位置后,下一次mouseMoveEvent触发时,event.pos()的参考系已经变成Item新的位置,导致位置计算完全错乱,出现来回跳动。

修复方案

修改鼠标事件处理逻辑,记录按下时的关键坐标,基于场景坐标系计算偏移:

修正后的代码

class GanttItem(QGraphicsItem):
    def __init__(self, start, duration, name, color, parent=None):
        super().__init__(parent)
        self.start = start
        self.duration = duration
        self.name = name
        self.color = color
        self.dragging = False

    def boundingRect(self):
        return QRectF(0, 0, self.duration * 30, 50)

    def paint(self, painter, option, widget):
        painter.setBrush(QBrush(self.color))
        painter.drawRect(self.boundingRect())
        painter.drawText(self.boundingRect(), Qt.AlignmentFlag.AlignCenter, self.name)

    def mousePressEvent(self, event: QMouseEvent):
        self.dragging = True
        # 记录鼠标在Item自身坐标系内的按下偏移量
        self.mouse_item_offset = event.pos()

    def mouseReleaseEvent(self, event: QMouseEvent):
        self.dragging = False

    def mouseMoveEvent(self, event: QMouseEvent):
        if self.dragging:
            # 用鼠标场景位置减去偏移量,得到Item目标位置
            target_pos = event.scenePos() - self.mouse_item_offset
            # 仅更新X轴位置,保持Y轴不变
            self.setPos(target_pos.x(), self.pos().y())

逻辑说明

  1. mousePressEvent中:self.mouse_item_offset记录鼠标按下时相对于Item左上角的偏移,避免拖动时Item直接跳转到鼠标位置。
  2. mouseMoveEvent中:event.scenePos()获取鼠标在场景全局坐标系的位置,减去偏移量后得到Item的目标位置,彻底避免坐标系混淆导致的跳动问题。

内容的提问来源于stack exchange,提问作者Lukas Fürst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 22:08:16