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

PySide6中QGraphicsView缩放时AnchorUnderMouse异常问题(疑似Bug)

QGraphicsView缩放锚点异常问题分析

需求目标

实现QGraphicsView缩放时,鼠标下方的场景位置始终保持在鼠标正下方。

复现代码

from PySide6.QtCore import QPoint
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QApplication
import math

class MyGraphicsView(QGraphicsView):
    def wheelEvent(self, event):
        self.setTransformationAnchor(self.ViewportAnchor.AnchorUnderMouse)
        self.setResizeAnchor(self.ViewportAnchor.AnchorUnderMouse)
        if event.angleDelta().y() > 0:
            self.scale(1.1, 1.1)
        elif event.angleDelta().y() < 0:
            self.scale(1 / 1.1, 1 / 1.1)
        self.setTransformationAnchor(self.ViewportAnchor.NoAnchor)
        self.setResizeAnchor(self.ViewportAnchor.NoAnchor)

class MyTestScene(QGraphicsScene):
    def drawBackground(self, painter, rect):
        left = int(math.floor(rect.left()))
        top = int(math.floor(rect.top()))
        right = int(math.ceil(rect.right()))
        bottom = int(math.ceil(rect.bottom()))
        first_left = left - (left % 100)
        first_top = top - (top % 100)

        for x in range(first_left, right + 1, 100):
            for y in range(first_top, bottom + 1, 100):
                painter.drawEllipse(QPoint(x, y), 2.5, 2.5)
                painter.drawText(x, y, f"{x},{y}")

if __name__ == '__main__':
    app = QApplication([])
    scene = MyTestScene()
    scene.setSceneRect(-2000, -2000, 4000, 4000)
    view = MyGraphicsView()
    view.setScene(scene)
    view.setGeometry(100, 100, 900, 600)
    view.setVisible(True)
    # view.setInteractive(False)
    app.exec()

异常现象

  • 交互模式下,首次滚轮缩放前点击视图,功能正常;
  • 交互模式下未点击视图,首次滚轮事件会触发场景跳转,将(0,0)位置移至鼠标下方,后续操作恢复正常;
  • 视图设置为非交互模式时,每次滚轮事件都会触发上述跳转。

测试环境:Windows系统,PySide6.7.2+Python3.12.4、PySide6.8.1+Python3.13.1。

成因分析

  1. 焦点与鼠标跟踪机制:QGraphicsView在未获得焦点(未点击)或非交互模式下,不会主动跟踪鼠标在场景中的实时位置。此时使用AnchorUnderMouse锚点时,无法获取正确的鼠标对应场景坐标,会 fallback 到场景原点(0,0)作为缩放锚点,导致场景跳转。
  2. 交互模式的影响:默认交互模式下,点击视图后,视图获得焦点并启动鼠标位置跟踪,AnchorUnderMouse能正确获取鼠标对应的场景点,缩放行为符合预期;而非交互模式会禁用包括鼠标位置跟踪在内的大部分交互逻辑,导致每次缩放都无法获取正确锚点。
  3. 临时锚点设置的局限性:代码中临时切换锚点的逻辑本身没有错误,但依赖Qt内部的鼠标位置跟踪状态,当跟踪状态异常时就会触发错误行为。

结论

该问题属于Qt在焦点/交互模式与锚点缩放结合时的设计缺陷(未明确文档化的行为),而非代码疏漏。Qt的AnchorUnderMouse依赖视图的鼠标跟踪状态,而该状态在视图未激活或非交互模式下无法正常工作。

解决方案

方案1:确保视图启动时获得焦点

在__main__块中添加view.setFocus(),让视图初始化时就具备焦点,触发鼠标跟踪:

if __name__ == '__main__':
    app = QApplication([])
    scene = MyTestScene()
    scene.setSceneRect(-2000, -2000, 4000, 4000)
    view = MyGraphicsView()
    view.setScene(scene)
    view.setGeometry(100, 100, 900, 600)
    view.setFocus()  # 添加这行
    view.setVisible(True)
    # view.setInteractive(False)
    app.exec()

方案2:手动计算缩放锚点(不依赖Qt内置锚点)

完全绕过AnchorUnderMouse,手动计算鼠标对应的场景坐标,缩放后调整视图位置确保锚点不变,不受焦点和交互模式影响:

class MyGraphicsView(QGraphicsView):
    def wheelEvent(self, event):
        # 获取鼠标在视图中的位置
        mouse_view_pos = event.position().toPoint()
        # 转换为场景坐标
        mouse_scene_pos = self.mapToScene(mouse_view_pos)
        
        # 计算缩放因子
        scale_factor = 1.1 if event.angleDelta().y() > 0 else 1 / 1.1
        # 执行缩放
        self.scale(scale_factor, scale_factor)
        
        # 计算缩放后鼠标对应的新场景坐标
        new_mouse_scene_pos = self.mapToScene(mouse_view_pos)
        # 计算偏移量,让原场景点回到鼠标下方
        delta = mouse_scene_pos - new_mouse_scene_pos
        # 平移视图修正位置
        self.translate(delta.x(), delta.y())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:53:29