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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:02:49