使用QGraphicsView思想做一个简单图片查看器

news2024/11/15 4:58:31

使用QGraphicsView思想做一个简单图片查看器

如果要做一个图片查看器,支持放大、滚动操作,比较直接的方法是,使用QWidget来显示完整图片,将QWidget放入QScrollArea。缩放时调整QWidget的尺寸,QScrollArea会自动调整滚动范围,超出视口区域图片自然就不会显示。

如果要使用QGraphicsView的思想呢?

原理

QGraphicsScenes是固定不变的,QGraphicsView使用一个变换矩阵来实现QWidget区域与QGraphicsScene区域之间的转换。缩放和滚动相对直接,任意角度旋转涉及到的问题还是挺麻烦的,这里不考虑。
场景和视口关系
所以只要能实现图像坐标、区域,到QWidget的坐标、区域转换,和反向转换,剩下就非常简单了。只考虑缩放和滚动,只需要维护变换矩阵(QTransform)和滚动距离,交互上还需要考虑滚动范围,避免超出。

主要代码

以下代码全部使用了浮点值,防止精度损失和溢出

  1. 根据缩放,重设变换矩阵

    void resetView()
    {
    	// QSizeF scrollRange; //缓存滚动范围
    	// qreal scale; //当前缩放
    	QRectF rect(this->rect());
        scrollRange = imageRect.size() / scale - rect.size();
        QPointF offset(0, 0);	// 当图片显示小于视口,用于居中
        if(scrollRange.width() < 0)
        {
            // 水平居中
            offset.rx() = - scrollRange.rwidth() / 2;
            scrollRange.rwidth() = 0;
        }
    
        if(scrollRange.height() < 0)
        {
            // 垂直居中
            offset.ry() = - scrollRange.height() / 2;
            scrollRange.rheight() = 0;
        }
        // 变换矩阵
        transform = QTransform().scale(scale, scale).translate(-offset.x(), -offset.y());
    }
    
  2. 坐标与矩阵映射
    QGraphicsView内部,滚动范围值是场景区域经过变换后的区域范围,并非从0起始。
    由于滚动代表实际的偏移位置,直接写入transform不方便

    // 	QPointF scrollValue; // 当前滚动,为了方便使用坐标点
    QPointF mapToImage(QPointF pos){
        return transform.map(pos + scrollValue);
    }
    QPointF mapFromImage(QPointF pos){
        return transform.inverted().map(pos) - scrollValue;
    }
    QRectF mapToImage(QRectF rect){
        rect.moveTopLeft(rect.topLeft() + scrollValue);
        return transform.mapRect(rect);
    }
    QRectF mapFromImage(QRectF rect){
        rect = transform.inverted().mapRect(rect);
        rect.moveTopLeft(rect.topLeft() - scrollValue);
        return rect;
    }
    
  3. 绘制图片

    QRectF rect(this->rect());
    QRectF img_rect = mapToImage(rect).intersected(imageRect);
    QRectF paint_rect = mapFromImage(img_rect);
    
    QPainter painter(this);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    painter.drawImage(paint_rect, image, img_rect);
    

    绘制时,先将窗口区域变换到图片区域,求取交集,再反算到视口区域。QPainter支持将图片重某区域绘制到指定区域。

  4. 滚动和缩放图片
    滚动相对简单,监听鼠标事件,修改当前滚动。
    缩放直接修改scale,调用resetView重新计算滚动范围、变换矩阵。

    void scollView(QPointF dp){
        scrollValue.rx() = qBound(0.0, scrollValue.x() + dp.x(), scrollRange.width());
        scrollValue.ry() = qBound(0.0, scrollValue.y() + dp.y(), scrollRange.height());
    }
    

最终效果

在这里插入图片描述

其他细节

上述代码基本包含了主要逻辑,一些细节可能需要根据实际需要再增加逻辑。

  1. 缩放限制
    尽管浮点数的运算能最大程度保留精度,但最好考虑在修改scale时,限定范围。

  2. 缩放时同步缩放图片
    很少有软件会做这样的支持,毕竟支持滚动了。
    但Windows自带的照片有这样功能,具体原理可以再研究。

  3. 缩放或者调整窗口时,锚定某个坐标不动
    当变换矩阵变化、窗口resize时,QGraphicsView支持锚定某个坐标在视口中不变。具体可以文档QGraphicsView::ViewportAnchor。
    可以简单这样实现:

    // QPointF view_pos; //指定一个视口坐标
    QPointF anch_pos = mapToImage(view_pos);
    // ...
    // 其他触发变换矩阵变化的逻辑
    // ...
    // 调整前后差异,重新滚动对齐
    QPointF new_pos = mapFromImage(anch_pos);
    scollView(new_pos - view_pos);
    
  4. 旋转、翻转
    如果只支持90°倍数旋转,直接对原图修改应该比较简单(变换矩阵是否可以做到)

  5. 高分屏适配
    将原始图像的信息除以缩放,与QWidget一致,在最后即将绘制时再换算到实际像素区域

  6. 抗锯齿
    即便QPainter开启抗锯齿和平滑缩放图片,依然得不到好的效果……属于Qt问题,可以复制一份图片待绘制区域的像素,并手动缩放再绘制。这就需要优化效率了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2053878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++_基本语法笔记_仿函数和算法接口

函数对象 概念和使用 切记&#xff1a;仿函数&#xff0c;本质是一个类因为是一个类&#xff0c;所以要把operator() 放在一个类里面 像普通函数&#xff08;需要通过某个类使用&#xff09;一样使用&#xff0c;并且有返回值&#xff1a; 内置当前函数对象的状态&#xff1…

基于Springboot的多功能智能点餐小程序/基于微信小程序的点餐系统

摘要 计算机网络如果结合使用信息管理系统&#xff0c;能够提高管理员管理的效率&#xff0c;改善服务质量。优秀的智能点餐系统能够更有效管理用户智能点餐业务规范&#xff0c;帮助管理者更加有效管理用户智能点餐&#xff0c;可以帮助提高克服人工管理带来的错误等不利因素。…

牛客网SQL进阶135 :每个6/7级用户活跃情况

每个67级用户活跃情况_牛客题霸_牛客网 0 问题描述 基于用户信息表user_info、、试卷作答记录表exam_record、题目练习记录表practice_record&#xff0c;统计 每个6/7级用户总活跃月份数、2021年活跃天数、2021年试卷作答活跃天数、2021年答题活跃天数&#xff0c;结果 按照总…

C语言典型例题41

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 习题3.1 写出下列各个表达式的值。设a3&#xff0c;b4&#xff0c;c5。 (1) ab>c && bc (2) a||bc && b-c (3) !(a>b) && !c || 1 (4) …

遗传算法原理与实战(python、matlab)

遗传算法 1.什么是遗传算法 遗传算法&#xff08;Genetic Algorithm&#xff0c;简称GA&#xff09;是一种基于生物进化论和遗传学原理的全局优化搜索算法。它通过模拟自然界中生物种群的遗传机制和进化过程来解决复杂问题&#xff0c;如函数优化、组合优化、机器学习等。遗传…

CTFHUB-技能树-Web题-RCE(远程代码执行)-远程包含-命令注入-综合过滤练习

CTFHUB-技能树-Web题-RCE&#xff08;远程代码执行&#xff09;-远程包含-命令注入-综合过滤练习 根据题目提示 以及代码 法1&#xff1a; 分隔符可以使用%0a代替 若直接使用文本框上传命令会导致字符被转义&#xff0c;直接访问URL payload&#xff1a; /?ip127.0.0.1%…

docker部署Mongodb后输入命令报错?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

vue实现动画

方法一&#xff1a; 先理解事件发生的过程&#xff0c;v-enter,v-enter-to,v-leave,v-leave-to。其中v-enter,v-leave-to代表开始以及结束时的状态。v-enter-active,v-leave-active代表动画的过程。 定义动画第一步把要做动画的语句添加到transition中&#xff0c;其中name可…

使用docxtemplater-image-module-free时支持动态获取图片大小

使用docxtemplater-image-module-free时支持动态获取图片大小 1、问题背景 在使用docxtemplater-image-module-free生成模板图片时&#xff0c;写死了其中一个函数 getSize() {return [150, 150]; },导致图片都是一个大小&#xff0c;且被拉扯的变形了 2、报错信息 在去掉…

C++——入门基础(上)

目录 一、C参考文档 二、C在工作领域的应用 三、C学习书籍 四、C的第一个程序 五、命名空间 &#xff08;1&#xff09;namespace的定义 (2)命名空间的使用 六、C的输入和输出 七、缺省函数 八、函数重载 九、写在最后 一、C参考文档 &#xff08;1&#xff09;虽…

二叉树《数据结构》

二叉树 1. 树概念及结构1.1 树的概念1.2树的概念1.3 树的表示 2. 二叉树概念及结构2.1 二叉树概念2.4 二叉树的性质练习 3. 二叉树顺序结构及实现3.1 二叉树的顺序结构3.2堆的结构及概念练习3.3堆的实现3.3.1堆的向下调整算法3.3.2堆的创建3.3.3 堆的插入3.3.4 堆的删除3.3.5堆…

C2M商业模式分析与运营平台建设解决方案(五)

C2M商业模式通过直接对接消费者需求与制造商&#xff0c;实现了生产与市场需求的精准匹配&#xff0c;本文提出的解决方案重点在于构建一个智能化运营平台&#xff0c;通过集成先进的大数据分析、人工智能技术和灵活的供应链管理系统&#xff0c;全面提升需求预测的准确性、生产…

【若依框架】代码生成详细教程,15分钟搭建Springboot+Vue3前后端分离项目,基于Mysql8数据库和Redis5,管理后台前端基于Vue3和Element Plus,开发小程序数据后台

今天我们来借助若依来快速的搭建一个基于springboot的Java管理后台&#xff0c;后台网页使用vue3和 Element Plus来快速搭建。这里我们可以借助若依自动生成Java和vue3代码&#xff0c;这就是若依的强大之处&#xff0c;即便你不会Java和vue开发&#xff0c;只要跟着石头哥也可…

loadlibrary failed with error 126:找不到指定模块

买了一张w4300回来装到nas上&#xff0c;核显HD630输出的 打开cad的时候发现图形显卡是hd630&#xff0c;w4300一直没有被驱动&#xff0c;于是去设置里面将cad强行用高性能打开 结果一直报错&#xff1a;loadlibrary failed with error 126:找不到指定模块 网上搜索了半天&am…

招生简章不会设计?这个网站可以供你参考

招生简章是学校与潜在学生之间的第一座桥梁&#xff0c;它的设计直接影响到学校的形象和招生效果。如果你在设计招生简章时感到困惑&#xff0c;不妨参考以下几个要点&#xff0c;让你的招生简章更加吸引人。 1.明确目标受众&#xff1a;在设计招生简章之前&#xff0c;首先要明…

项目实战:Qt+Opencv相机标定工具v1.3.0(支持打开摄像头、视频文件和网络地址,支持标定过程查看、删除和动态评价误差率,支持追加标定等等)

若该文为原创文章&#xff0c;转载请注明出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/141334834 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、Op…

运维小技能:通过调整JVM的默认内存配置来解决内存溢出(‌OutOfMemoryError)‌或栈溢出(‌StackOverflowError)‌等错误

文章目录 引言I 调整JVM的默认堆内存配置1.1 java命令启动jar包时配置JVM 的内存参数1.2 基于Tomcat服务器部署的java应用,配置JVM 的内存参数II 案例: Linux 操作系统设置tomcat的 JVM 的内存参数查找Tomcat位置: 快速定位服务状态和部署位置具体配置步骤扩展: 监测Nginx访…

配置stm32cubemx采集stm32H743IIT6,通过DMA实现多通道和多模块ADC的采集,亲测有效!

之前写到stm32cubemx通过阻塞实现单通道和多通道的ADC的采集。 本文分享通过DMA实现单模块多通道和多模块多通道的ADC采集。 stm32cubemx的版本6.10.0。 一、DMA采集多通道ADC数据 阻塞采集是每次采集adc数据&#xff0c;cpu死等&#xff0c;直到采集完或者在设定时间超时没…

不能使用乘除法、for、while、if、else、switch、case求1+2+3+...+n

求123...n_牛客题霸_牛客网 (nowcoder.com) 描述 求123...n&#xff0c;要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句&#xff08;A?B:C&#xff09;。 数据范围&#xff1a; 0<n≤2000<n≤200 进阶&#xff1a; 空间复杂度 O(1)O(…

PCRNet: Point Cloud Registration Network using PointNet Encoding 论文解读

目录 一、导言 二、先导知识 1、Frobenius范数 三、相关工作 1、点云配准工作 2、PointNet 3、基于深度学习的点云配准 四、PCRNet 1、PCRNet 2、Iterative PCRNet 3、损失函数 五、实验 一、导言 本论文收录于CVPR2019&#xff0c;本论文提出了一种依赖PointNet网…