标注工具核心代码解析——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)
相关推荐
Blossom.11818 分钟前
基于深度学习的图像分类:使用Capsule Networks实现高效分类
人工智能·python·深度学习·神经网络·机器学习·分类·数据挖掘
CodeCraft Studio26 分钟前
借助Aspose.HTML控件,在 Python 中将 HTML 转换为 Markdown
开发语言·python·html·markdown·aspose·html转markdown·asposel.html
QQ_43766431427 分钟前
C++11 右值引用 Lambda 表达式
java·开发语言·c++
aramae27 分钟前
大话数据结构之<队列>
c语言·开发语言·数据结构·算法
悠哉悠哉愿意1 小时前
【电赛学习笔记】MaxiCAM 项目实践——与单片机的串口通信
笔记·python·单片机·嵌入式硬件·学习·视觉检测
封奚泽优1 小时前
使用Python实现单词记忆软件
开发语言·python·random·qpushbutton·qtwidgets·qtcore·qtgui
Goona_1 小时前
拒绝SQL恐惧:用Python+pyqt打造任意Excel数据库查询系统
数据库·python·sql·excel·pyqt
liulilittle2 小时前
C++/CLI与标准C++的语法差异(一)
开发语言·c++·.net·cli·clr·托管·原生
daixin88482 小时前
什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?
java·开发语言·redis·缓存
你我约定有三2 小时前
RabbitMQ--消息丢失问题及解决
java·开发语言·分布式·后端·rabbitmq·ruby