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

拦截QGraphicsSceneMouseEvent修改位置报错,如何实现控件垂直移动?

问题:实现QGraphicsRectItem按住Control键垂直拖动

我通过设置QGraphicsItem.ItemIsMovable属性实现了QGraphicsRectItem的鼠标拖动功能,希望按住Control键时,该控件仅垂直移动(保持鼠标位置x值固定)。为简化问题,暂未实现Control键判断逻辑。

我的思路是继承QGraphicsScene类,重写mouseMoveEvent方法,修改event.pos().x为固定值后调用父类方法,代码如下:

import sys
from PyQt5.QtWidgets import *

class QGraphicsScene(QGraphicsScene):
    def mouseMoveEvent(self, event):
        newEvent = event
       #newEvent.setPos(  100.0, event.pos().y() ) #### HERE, I need help ! !
        super().mouseMoveEvent(newEvent)

class MainWindow(QWidget):
    def __init__(self ):
        super(QWidget, self).__init__()

        self.scene = QGraphicsScene()
        self.scene.setSceneRect( 0.0, 0.0, 640.0, 480.0 )

        view= QGraphicsView()
        view.setScene(self.scene)
        layout = QVBoxLayout()
        layout.addWidget( view )
        self.setLayout( layout )

        self.myRect= QGraphicsRectItem( 0,  0, 100, 100)
        self.scene.addItem( self.myRect )

        self.myRect.setFlag( QGraphicsItem.ItemIsMovable,      True  )

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

但取消注释第7行代码:

newEvent.setPos(  100.0, event.pos().y() )

时出现错误:

AttributeError: 'QGraphicsSceneMouseEvent' object has no attribute 'setPos'

错误原因

QGraphicsSceneMouseEvent类没有提供setPos方法,而且Qt中的事件对象通常是只读的,不能直接修改其属性,必须创建新的事件实例来传递修改后的参数。


解决方案

方案1:重写QGraphicsRectItem的鼠标事件(推荐)

直接在自定义的矩形Item中处理拖动逻辑,更符合职责单一原则,代码如下:

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt

class RestrictedRectItem(QGraphicsRectItem):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self._drag_start_x = 0  # 记录拖动开始时鼠标相对Item的x坐标

    def mousePressEvent(self, event):
        # 按下鼠标时记录初始位置
        self._drag_start_x = event.pos().x()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.modifiers() & Qt.ControlModifier:
            # 按住Control键时,固定x坐标,只允许垂直移动
            new_pos = event.pos()
            new_pos.setX(self._drag_start_x)
            # 创建新的事件对象,传递修改后的位置
            modified_event = QGraphicsSceneMouseEvent(event.type())
            modified_event.setPos(new_pos)
            modified_event.setScenePos(event.scenePos().x(), event.scenePos().y())
            modified_event.setScreenPos(event.screenPos())
            modified_event.setModifiers(event.modifiers())
            modified_event.setButton(event.button())
            modified_event.setButtons(event.buttons())
            super().mouseMoveEvent(modified_event)
        else:
            # 正常拖动逻辑
            super().mouseMoveEvent(event)

class MainWindow(QWidget):
    def __init__(self ):
        super().__init__()

        self.scene = QGraphicsScene()
        self.scene.setSceneRect(0.0, 0.0, 640.0, 480.0)

        view = QGraphicsView()
        view.setScene(self.scene)
        layout = QVBoxLayout()
        layout.addWidget(view)
        self.setLayout(layout)

        self.myRect = RestrictedRectItem(0, 0, 100, 100)
        self.scene.addItem(self.myRect)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

方案2:在自定义Scene中处理事件

如果需要在Scene层面统一处理多个Item的拖动限制,可以创建新的事件对象传递修改后的坐标:

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt

class CustomScene(QGraphicsScene):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._drag_start_x = 0
        self._current_drag_item = None

    def mousePressEvent(self, event):
        # 记录拖动开始时的场景x坐标和当前拖动的Item
        self._current_drag_item = self.itemAt(event.scenePos(), QTransform())
        if self._current_drag_item:
            self._drag_start_x = event.scenePos().x()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.modifiers() & Qt.ControlModifier and self._current_drag_item:
            # 创建新的事件对象,固定x坐标为拖动初始值
            modified_event = QGraphicsSceneMouseEvent(event.type())
            modified_event.setScenePos(self._drag_start_x, event.scenePos().y())
            modified_event.setScreenPos(event.screenPos())
            modified_event.setModifiers(event.modifiers())
            modified_event.setButton(event.button())
            modified_event.setButtons(event.buttons())
            super().mouseMoveEvent(modified_event)
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        # 释放鼠标后清空当前拖动Item
        self._current_drag_item = None
        super().mouseReleaseEvent(event)

class MainWindow(QWidget):
    def __init__(self ):
        super().__init__()

        self.scene = CustomScene()
        self.scene.setSceneRect(0.0, 0.0, 640.0, 480.0)

        view = QGraphicsView()
        view.setScene(self.scene)
        layout = QVBoxLayout()
        layout.addWidget(view)
        self.setLayout(layout)

        self.myRect = QGraphicsRectItem(0, 0, 100, 100)
        self.scene.addItem(self.myRect)
        self.myRect.setFlag(QGraphicsItem.ItemIsMovable, True)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:08:08