如何让QGraphicsView的ScrollHandDrag支持鼠标中键?
让QGraphicsView的ScrollHandDrag支持鼠标中键的解决方案
你的问题出在递归调用mousePressEvent,导致左键事件被无限拦截,完全无法正常使用。下面提供两种基于Qt现代API的可行实现方式,无需老旧hack:
方法一:修改事件参数后交给父类处理
这种方式直接复用QGraphicsView自带的ScrollHandDrag逻辑,仅在事件传递前把中键事件伪装成左键事件,保留原事件的其他属性:
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QApplication from PyQt5.QtGui import QPainter, QMouseEvent from PyQt5.QtCore import Qt, QEvent import sys class InfiniteCanvas(QGraphicsView): def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) self.setScene(QGraphicsScene(self)) self.setRenderHint(QPainter.Antialiasing) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) screen = QApplication.primaryScreen() screen_size = screen.size() width = screen_size.width() * 0.7 height = screen_size.height() * 0.7 self.setGeometry(self.x(), self.y(), width, height) self.setStyleSheet("background-color: #2a2a2a;") def mousePressEvent(self, event): if event.button() == Qt.MidButton: # 构造新事件,将按钮替换为左键,确保父类识别拖动触发 modified_event = QMouseEvent( QEvent.MouseButtonPress, event.pos(), Qt.LeftButton, Qt.LeftButton, event.modifiers() ) super().mousePressEvent(modified_event) else: super().mousePressEvent(event) def mouseReleaseEvent(self, event): if event.button() == Qt.MidButton: # 模拟左键释放,结束拖动并恢复光标 modified_event = QMouseEvent( QEvent.MouseButtonRelease, event.pos(), Qt.LeftButton, Qt.NoButton, event.modifiers() ) super().mouseReleaseEvent(modified_event) else: super().mouseReleaseEvent(event) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() def dragMoveEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() if __name__ == "__main__": app = QApplication(sys.argv) view = InfiniteCanvas() view.show() sys.exit(app.exec())
关键说明
- 调用父类的
super().mousePressEvent()而非自身递归,避免事件循环拦截 - 修改
buttons()参数为Qt.LeftButton,确保内置ScrollHandDrag逻辑能触发 - 同步处理
mouseReleaseEvent,保证拖动结束后状态正常恢复
方法二:手动实现中键拖动逻辑(不依赖ScrollHandDrag)
如果不需要依赖内置DragMode,可完全自定义拖动逻辑,灵活性更高:
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QApplication from PyQt5.QtGui import QPainter from PyQt5.QtCore import Qt import sys class InfiniteCanvas(QGraphicsView): def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) self.setScene(QGraphicsScene(self)) self.setRenderHint(QPainter.Antialiasing) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setDragMode(QGraphicsView.DragMode.NoDrag) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) screen = QApplication.primaryScreen() screen_size = screen.size() width = screen_size.width() * 0.7 height = screen_size.height() * 0.7 self.setGeometry(self.x(), self.y(), width, height) self.setStyleSheet("background-color: #2a2a2a;") self._drag_start_pos = None def mousePressEvent(self, event): if event.button() == Qt.MidButton: self._drag_start_pos = event.pos() self.viewport().setCursor(Qt.ClosedHandCursor) else: super().mousePressEvent(event) def mouseMoveEvent(self, event): if self._drag_start_pos is not None and event.buttons() & Qt.MidButton: # 计算位移并滚动视图 delta = event.pos() - self._drag_start_pos self._drag_start_pos = event.pos() self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - delta.x()) self.verticalScrollBar().setValue(self.verticalScrollBar().value() - delta.y()) else: super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): if event.button() == Qt.MidButton: self._drag_start_pos = None self.viewport().setCursor(Qt.ArrowCursor) else: super().mouseReleaseEvent(event) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() def dragMoveEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() if __name__ == "__main__": app = QApplication(sys.argv) view = InfiniteCanvas() view.show() sys.exit(app.exec())
关键说明
- 记录中键按下时的初始位置
_drag_start_pos - 鼠标移动时计算位移,通过滚动条实现视图拖动
- 释放中键时重置状态,恢复光标样式
两种方法均不会影响左键的正常使用:方法一复用内置逻辑,代码更简洁;方法二完全自定义,适合需要扩展拖动行为的场景。
内容的提问来源于stack exchange,提问作者user21736604
相关产品推荐
相关产品推荐

