标注工具核心代码解析——class AnnotationVie【canvas.py]

详细解析

🔧 构造函数解析

python 复制代码
def __init__(self, parent=None):
    # 🏗️ 继承链调用:调用QGraphicsView的构造函数进行基础初始化
    super(AnnotationView, self).__init__(parent)
    
    # 🖱️ 鼠标跟踪设置:启用连续鼠标位置监听
    # 默认情况下,只有按下鼠标时才触发移动事件
    # 设置为True后,鼠标悬停移动也会触发事件
    self.setMouseTracking(True)
    
    # 📜 滚动条策略:强制显示滚动条
    # ScrollBarAlwaysOn = 始终显示,不管内容是否超出视图
    self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
    self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
    
    # 🖐️ 拖拽模式设置:启用手动拖拽滚动
    # ScrollHandDrag = 鼠标变成手形,可以拖拽移动视图内容
    self.setDragMode(QtWidgets.QGraphicsView.DragMode.ScrollHandDrag)
    
    # 🔍 缩放参数:定义缩放步长
    self.factor = 1.2  # 每次缩放20%

🎯 核心方法解析

🖱️ 滚轮事件处理

python 复制代码
def wheelEvent(self, event: QtGui.QWheelEvent):
    # 📐 获取滚轮滚动信息
    angel = event.angleDelta()  # 返回QPoint对象,包含x和y方向的角度增量
    angelX, angelY = angel.x(), angel.y()  # 分别提取水平和垂直滚动量
    
    # 📍 获取鼠标当前位置(视图坐标系)
    point = event.pos()
    
    # 🔍 根据滚动方向进行缩放
    if angelY > 0:  # 向上滚动 = 放大
        self.zoom(self.factor, point)
    else:  # 向下滚动 = 缩小
        self.zoom(1 / self.factor, point)

继承调用链

复制代码
用户滚动鼠标 → Qt事件系统 → QGraphicsView.wheelEvent() → AnnotationView.wheelEvent()

🔍 缩放功能方法

python 复制代码
def zoom_in(self):
    # 🔍 放大:直接调用zoom方法,不指定鼠标位置
    self.zoom(self.factor)

def zoom_out(self):
    # 🔍 缩小:使用缩放因子的倒数
    self.zoom(1 / self.factor)

def zoomfit(self):
    # 📐 自适应缩放:调用QGraphicsView的内置方法
    # fitInView继承自QGraphicsView,用于自动调整视图以完全显示指定区域
    self.fitInView(
        0, 0,  # 起始坐标
        self.scene().width(), self.scene().height(),  # 场景的宽度和高度
        QtCore.Qt.AspectRatioMode.KeepAspectRatio  # 保持宽高比
    )

🎨 核心缩放方法深度解析

复制代码
def zoom(self, factor, point=None):
    # 📍 坐标转换:将视图坐标转换为场景坐标
    # mapToScene()是QGraphicsView的方法,用于坐标系转换
    mouse_old = self.mapToScene(point) if point is not None else None

    # 🧮 计算缩放后的像素比例
    # transform()获取当前变换矩阵
    # scale()创建新的缩放变换
    # mapRect()应用变换到矩形
    pix_widget = self.transform().scale(factor, factor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width()
    
    # 🚫 缩放限制检查
    if pix_widget > 30 and factor > 1:  # 防止过度放大
        return
    if pix_widget < 0.01 and factor < 1:  # 防止过度缩小
        return

    # 🔍 执行缩放:调用QGraphicsView的scale方法
    self.scale(factor, factor)
    
    # 📐 以鼠标为中心的缩放调整
    if point is not None:
        mouse_now = self.mapToScene(point)  # 缩放后的鼠标场景坐标
        
        # 获取当前视图中心点的场景坐标
        center_now = self.mapToScene(self.viewport().width() // 2, self.viewport().height() // 2)
        
        # 🧮 计算新的视图中心:确保鼠标位置保持不变
        center_new = mouse_old - mouse_now + center_now
        
        # 🎯 调整视图中心:调用QGraphicsView的centerOn方法
        self.centerOn(center_new)

🔄 方法调用关系图

复制代码
用户操作
    ↓
┌─────────────────────┐
│   wheelEvent()      │ ← Qt事件系统
│   zoom_in()         │ ← 工具栏按钮
│   zoom_out()        │ ← 工具栏按钮  
│   zoomfit()         │ ← 菜单选项
└─────────────────────┘
            ↓
    ┌─────────────┐
    │   zoom()    │ ← 核心缩放逻辑
    └─────────────┘
            ↓
┌─────────────────────┐
│  QGraphicsView方法   │
│  • mapToScene()     │ ← 坐标转换
│  • transform()      │ ← 获取变换矩阵
│  • scale()          │ ← 执行缩放
│  • centerOn()       │ ← 调整视图中心
│  • fitInView()      │ ← 自适应显示
└─────────────────────┘

🧠 设计思路分析

🎯 核心设计模式

  1. *� 模板方法模式
    • 重写wheelEvent()自定义滚轮行为
    • 保留Qt的事件处理机制
  1. *� 适配器模式
    • 封装复杂的Qt Graphics Framework
    • 提供简洁的缩放接口
  1. *� 策略模式
    • 不同的缩放策略(以鼠标为中心 vs 以视图中心)

💡 关键技术点

复制代码
# 🔄 坐标系转换
视图坐标 → mapToScene() → 场景坐标

# 🎯 变换矩阵操作  
当前变换 → scale() → 新变换 → mapRect() → 像素计算

# 📐 几何计算
鼠标位置保持 = 旧鼠标坐标 - 新鼠标坐标 + 当前中心

🛡️ 错误处理机制

  • 缩放限制:防止过度放大/缩小导致性能问题
  • 坐标验证:确保变换矩阵计算正确
  • 边界检查:避免视图超出合理范围

这个类完美地展示了Qt Graphics Framework的高级用法面向对象设计的继承机制

源代码

python 复制代码
class AnnotationView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(AnnotationView, self).__init__(parent)
        self.setMouseTracking(True)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
        self.setDragMode(QtWidgets.QGraphicsView.DragMode.ScrollHandDrag)
        self.factor = 1.2

    def wheelEvent(self, event: QtGui.QWheelEvent):
        angel = event.angleDelta()
        angelX, angelY = angel.x(), angel.y()
        point = event.pos()
        if angelY > 0:
            self.zoom(self.factor, point)
        else:
            self.zoom(1 / self.factor, point)

    def zoom_in(self):
        self.zoom(self.factor)

    def zoom_out(self):
        self.zoom(1 / self.factor)

    def zoomfit(self):
        self.fitInView(0, 0, self.scene().width(), self.scene().height(), QtCore.Qt.AspectRatioMode.KeepAspectRatio)

    def zoom(self, factor, point=None):
        mouse_old = self.mapToScene(point) if point is not None else None

        pix_widget = self.transform().scale(factor, factor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width()
        if pix_widget > 30 and factor > 1: return
        if pix_widget < 0.01 and factor < 1: return

        self.scale(factor, factor)
        if point is not None:
            mouse_now = self.mapToScene(point)
            center_now = self.mapToScene(self.viewport().width() // 2, self.viewport().height() // 2)
            center_new = mouse_old - mouse_now + center_now
            self.centerOn(center_new)
相关推荐
随意0231 分钟前
Qt 事件
开发语言·qt
Gyoku Mint8 分钟前
深度学习×第4卷:Pytorch实战——她第一次用张量去拟合你的轨迹
人工智能·pytorch·python·深度学习·神经网络·算法·聚类
鸥梨菌Honevid9 分钟前
Qt自定义控件(1)——QPaintEvent
开发语言·qt
Code季风12 分钟前
深入比较 Gin 与 Beego:Go Web 框架的两大选择
开发语言·golang·go·gin·beego
专注VB编程开发20年1 小时前
javascript的类,ES6模块写法在VSCODE中智能提示
开发语言·javascript·vscode
郭庆汝6 小时前
pytorch、torchvision与python版本对应关系
人工智能·pytorch·python
黄雪超9 小时前
JVM——函数式语法糖:如何使用Function、Stream来编写函数式程序?
java·开发语言·jvm
ThetaarSofVenice9 小时前
对象的finalization机制Test
java·开发语言·jvm
思则变9 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
lijingguang9 小时前
在C#中根据URL下载文件并保存到本地,可以使用以下方法(推荐使用现代异步方式)
开发语言·c#