拦截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
相关产品推荐
相关产品推荐

