当前位置: 首页 > news >正文

图片展示控件QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo

简介

/*
 * 图片展示控件
 * Graphics View Framework的使用Demo
 * QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo
 * 支持图片的旋转与缩放,自动缩放至接触边框
*/

效果展示

坐标系示意图

Graphics View Framework的使用需要特别注意QGraphicsView、QGraphicsScene、QGraphicsItem的坐标系及其转换方法,下图为本程序的示意图。

黑色表示视图View的坐标系

蓝色表示场景scene的坐标系

紫色表示图元Item的坐标系

源码

pictureDisplayWidget.h

/** 图片展示控件* Graphics View Framework的使用Demo* QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo* 支持图片的旋转与缩放,自动缩放至接触边框
*/#pragma once
#include <QWidget>class QGraphicsScene;
class QGraphicsView;
class QGraphicsTextItem;
class QGraphicsPixmapItem;
class QGraphicsRectItem;
class QGraphicsLineItem;
class QSlider;
class QPushButton;
class PictureDisplayWidget : public QWidget
{Q_OBJECTpublic:explicit PictureDisplayWidget(QWidget *parent = nullptr);~PictureDisplayWidget();public slots:void slot_SliderValueChanged(int value);void slot_btnClicked(bool checked = false);private:void showInfo();void setImage(const QString& strImagesPath);void shrinkImageSizeToInsideFramRect(const std::function<void (void)>& op);void enlargeImageSizeToInsideFramRect();private:QGraphicsView* m_view;QGraphicsScene* m_scene;QGraphicsTextItem* m_infoTextItem;QGraphicsRectItem* m_framRectItem;QList<QGraphicsLineItem*> m_framLineList;QGraphicsPixmapItem* m_pixmapItem;QSlider* m_rotateSlider;QSlider* m_imageSizeSlider;QPushButton* m_btn1;QPushButton* m_btn2;QPushButton* m_btn3;QPushButton* m_btn4;
};

pictureDisplayWidget.cpp

#include "pictureDisplayWidget.h"
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QSlider>
#include <QDebug>
#include <QScrollBar>
#include <QPushButton>
#include <QVBoxLayout>static qreal sliderValueToPixmapScale(int value)
{return value*0.02;
}
static int pixmapScaleToSliderValue(qreal scale)
{return scale/0.02;
}PictureDisplayWidget::PictureDisplayWidget(QWidget *parent): QWidget(parent)
{setWindowTitle("Graphics View Framework的使用Demo");QVBoxLayout* verticalLayout = new QVBoxLayout(this);//创建场景m_scene = new QGraphicsScene;QRectF sceneRect(-200, -200, 400, 400);m_scene->setSceneRect(sceneRect); //设置view显示的场景框//添加左上角文本图元m_infoTextItem = m_scene->addText("", QFont("Times", 12, QFont::Bold));m_infoTextItem->setDefaultTextColor(Qt::yellow);m_infoTextItem->setPos(-200, -200); //设置文本图元位于场景框左上角,该坐标是场景坐标系的坐标m_infoTextItem->setZValue(5); //设置z轴值,使得图元在最上层//添加场景矩形边框图元,用于图片缩小时的碰撞检测m_framRectItem = m_scene->addRect(sceneRect, QPen(Qt::red, 1));//添加场景矩形边框的四条边线图元,用于图片放大时的碰撞检测QPen framLinePen(Qt::blue, 1);//int gap = 1;//QRectF framLineRect = sceneRect.adjusted(gap, gap, -gap, -gap);QRectF framLineRect(sceneRect);m_framLineList.append(m_scene->addLine(QLineF(framLineRect.topLeft(), framLineRect.topRight()), framLinePen));m_framLineList.append(m_scene->addLine(QLineF(framLineRect.topRight(), framLineRect.bottomRight()), framLinePen));m_framLineList.append(m_scene->addLine(QLineF(framLineRect.bottomRight(), framLineRect.bottomLeft()), framLinePen));m_framLineList.append(m_scene->addLine(QLineF(framLineRect.bottomLeft(), framLineRect.topLeft()), framLinePen));//添加图片图元m_pixmapItem = m_scene->addPixmap(QPixmap());//创建视图m_view = new QGraphicsView(m_scene, this);m_view->setMinimumSize(m_scene->sceneRect().width() + m_view->verticalScrollBar()->width(),m_scene->sceneRect().height() + m_view->horizontalScrollBar()->height());m_view->setBackgroundBrush(QBrush(Qt::gray));m_view->show();verticalLayout->addWidget(m_view);//添加图片旋转角度条m_rotateSlider = new QSlider(Qt::Horizontal, this);m_rotateSlider->setTickInterval(1);m_rotateSlider->setMinimum(0);m_rotateSlider->setMaximum(360);m_rotateSlider->setTickPosition(QSlider::TicksBelow);verticalLayout->addWidget(m_rotateSlider);connect(m_rotateSlider, &QSlider::valueChanged, this, &PictureDisplayWidget::slot_SliderValueChanged);//添加图片缩放条m_imageSizeSlider = new QSlider(Qt::Horizontal, this);//m_imageSizeSlider->setTickInterval(1);m_imageSizeSlider->setMinimum(30);m_imageSizeSlider->setMaximum(70);m_imageSizeSlider->setValue(50);m_imageSizeSlider->setTickPosition(QSlider::TicksBelow);verticalLayout->addWidget(m_imageSizeSlider);connect(m_imageSizeSlider, &QSlider::valueChanged, this, &PictureDisplayWidget::slot_SliderValueChanged);//添加按钮m_btn1 = new QPushButton("1", this);m_btn2 = new QPushButton("2", this);m_btn3 = new QPushButton("3", this);m_btn4 = new QPushButton("4", this);verticalLayout->addWidget(m_btn1);verticalLayout->addWidget(m_btn2);verticalLayout->addWidget(m_btn3);verticalLayout->addWidget(m_btn4);connect(m_btn1, &QPushButton::clicked, this, &PictureDisplayWidget::slot_btnClicked);connect(m_btn2, &QPushButton::clicked, this, &PictureDisplayWidget::slot_btnClicked);connect(m_btn3, &QPushButton::clicked, this, &PictureDisplayWidget::slot_btnClicked);connect(m_btn4, &QPushButton::clicked, this, &PictureDisplayWidget::slot_btnClicked);//初始化setImage(":/images/1.png");showInfo();resize(700, 650);
}PictureDisplayWidget::~PictureDisplayWidget()
{}void PictureDisplayWidget::slot_btnClicked(bool checked)
{Q_UNUSED(checked);QPushButton* sender = static_cast<QPushButton*>(this->sender());QString strImagesPath(":/images/1.png");if (sender == m_btn1){strImagesPath = ":/images/1.png";}else if (sender == m_btn2){strImagesPath = ":/images/1.jpeg";}else if (sender == m_btn3){strImagesPath = ":/images/1.jpg";}else if (sender == m_btn4){m_rotateSlider->setValue(0);strImagesPath = ":/images/OIP-C.jfif";}setImage(strImagesPath);
}void PictureDisplayWidget::setImage(const QString& strImagesPath)
{//修改图片大小,使图片大小与场景边框尺寸一致m_pixmapItem->setPixmap(QPixmap(strImagesPath).scaled(m_scene->sceneRect().size().toSize(), Qt::KeepAspectRatio));//设置图元变换(旋转)的原点为图元边框的中心点m_pixmapItem->setTransformOriginPoint(m_pixmapItem->boundingRect().center());QPointF itemBoundingRectCenterPoint = m_pixmapItem->boundingRect().center();qDebug() << m_pixmapItem->boundingRect() << itemBoundingRectCenterPoint; //返回的是图元坐标系坐标//以pixmapItem边界矩形左上角为(0,0)进行移动---关键m_pixmapItem->setPos(0-itemBoundingRectCenterPoint.x(), 0-itemBoundingRectCenterPoint.y());qDebug() << "pos:" << m_pixmapItem->pos();//返回的是父项坐标系坐标qDebug() << "scenePos:" << m_pixmapItem->scenePos();//返回的是场景坐标系坐标qDebug() << "sceneBoundingRectPathPoint:" << m_pixmapItem->mapToScene(m_pixmapItem->boundingRect());//返回的是场景坐标系坐标//缩小图元,使其在场景边框内shrinkImageSizeToInsideFramRect([](){//qDebug() << "setImage-->shrinkImageSizeToInsideFramRect";});//放大图元,使其填满场景边框enlargeImageSizeToInsideFramRect();
}//超出边界矩形时,自动缩小图片尺寸
void PictureDisplayWidget::shrinkImageSizeToInsideFramRect(const std::function<void (void)>& op)
{if (m_imageSizeSlider){//不断缩小图元大小,直至其完全包含在场景边框内int imageSizeSliderValue = m_imageSizeSlider->value();while (!m_pixmapItem->collidesWithItem(m_framRectItem, Qt::ContainsItemShape)) //碰撞检测{m_imageSizeSlider->setValue(--imageSizeSliderValue);op();}}
}//自动放大图片尺寸,至触碰边界线
void PictureDisplayWidget::enlargeImageSizeToInsideFramRect()
{// for (int i = 0; i < m_framLineList.size(); ++i) {//     qDebug() << "ContainsItemShape " << i << ": "<< m_pixmapItem->collidesWithItem(m_framLineList.at(i), Qt::ContainsItemShape);//     qDebug() << "IntersectsItemShape " << i << ": "<< m_pixmapItem->collidesWithItem(m_framLineList.at(i), Qt::IntersectsItemShape);//     qDebug() << "ContainsItemBoundingRect " << i << ": "<< m_pixmapItem->collidesWithItem(m_framLineList.at(i), Qt::ContainsItemBoundingRect);//     qDebug() << "IntersectsItemBoundingRect " << i << ": "<< m_pixmapItem->collidesWithItem(m_framLineList.at(i), Qt::IntersectsItemBoundingRect);//     qDebug() << "--------------------------------";// }if (m_imageSizeSlider){//不断放大图元,直至其碰撞到任意一条边线或者达到最大缩放比int count = 0;do{bool bCollidesLine = false;foreach (auto framLine, m_framLineList){if (bCollidesLine = m_pixmapItem->collidesWithItem(framLine, Qt::IntersectsItemShape)) //碰撞检测{break;}}if (bCollidesLine){break; //当图元碰撞到任意一条边线时退出}int imageSizeSliderValue = m_imageSizeSlider->value();m_imageSizeSlider->setValue(++imageSizeSliderValue); //不断放大图元if (m_imageSizeSlider->value() < imageSizeSliderValue){break; //当图元放大无效时退出}} while (++count < m_imageSizeSlider->maximum());}
}void PictureDisplayWidget::slot_SliderValueChanged(int value)
{QSlider* sender = static_cast<QSlider*>(this->sender());if (sender == m_rotateSlider){m_pixmapItem->setRotation(value);//旋转并检测当超出边界矩形时,自动缩小图片尺寸shrinkImageSizeToInsideFramRect(std::bind(&QGraphicsItem::setRotation, m_pixmapItem, value));//自动放大图片尺寸,至触碰边界线enlargeImageSizeToInsideFramRect();}else if (sender == m_imageSizeSlider){qreal oldScale = m_pixmapItem->scale(); //记录原缩放比m_pixmapItem->setScale(sliderValueToPixmapScale(value)); //设置图元缩放比//当碰撞检测到pixmapItem不完全在边界矩形内时,恢复原缩放比if (!m_pixmapItem->collidesWithItem(m_framRectItem, Qt::ContainsItemShape)){m_pixmapItem->setScale(oldScale);m_imageSizeSlider->setValue(pixmapScaleToSliderValue(oldScale));}}showInfo();
}//左上角信息打印
void PictureDisplayWidget::showInfo()
{QString infoText = QString("rotate:%1\r\nimageSize:%2").arg(m_rotateSlider->value()).arg(m_imageSizeSlider->value());m_infoTextItem->setPlainText(infoText);
}

相关文章:

图片展示控件QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo

简介 /* * 图片展示控件 * Graphics View Framework的使用Demo * QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo * 支持图片的旋转与缩放&#xff0c;自动缩放至接触边框 */ 效果展示 坐标系示意图 Graphics View Framework的使用需要特别注意QGraphicsView、…...

C++仿C#实现事件处理

测试 #include "beacon/beacon.hpp" #include <cstdio> #include <thread>class mouseEvent : public beacon::args { public:mouseEvent(int x, int y) : x(x), y(y) {}int x, y; };class object : public beacon::sender { public:};class mouseHandl…...

SpringBoot-04--整合登录注册动态验证码

文章目录 效果展示1.导入maven坐标2.编写代码生成一个验证码图片3.前端如何拿到验证码4. 后端生成验证码5前端代码 效果展示 效果&#xff0c;每次进入页面展现出来不同的验证码。 技术 使用别人已经写好的验证码生成器&#xff0c;生成图片&#xff0c;转为Base64编码&#x…...

Qt如何打包桌面应用程序

Qt提供了一种便捷的方式来打包桌面应用程序&#xff0c;使其能够在不同操作系统上运行。以下是一些常用的打包工具和步骤&#xff1a; 1. **使用Qt Installer Framework**&#xff1a;Qt提供了一个名为Qt Installer Framework的工具&#xff0c;可以用来创建跨平台的安装程序。…...

AI作画提示词工程:技巧与最佳实践

在AI作画中&#xff0c;提示词工程&#xff08;Prompt Engineering&#xff09;是生成高质量图像的关键一步。以Midjourney为例&#xff0c;通过巧妙设计提示词&#xff0c;AI能够生成更符合预期的图像。本教程将分享如何有效利用提示词&#xff0c;掌握提示词的技巧与最佳实践…...

Ugandan Knuckles

目录 一、题目 二、思路 三、payload 四、思考与总结 一、题目 <!-- Challenge --> <div id"uganda"></div> <script>let wey (new URL(location).searchParams.get(wey) || "do you know da wey?");wey wey.replace(/[<…...

MVI、MVVM、MVP的对比

MVI 特点&#xff1a; 单向数据流&#xff1a;MVI采用单向数据流&#xff0c;从Model到View的数据流动&#xff0c;保证了数据流的可控性和可预测性。响应式编程&#xff1a;通过使用协程与RxJava等响应式编程库&#xff0c;简化了数据流的管理和处理。不可变性&#xff1a;MV…...

基于 Flutter 从零开发一款产品(一)—— 跨端开发技术介绍

前言 相信很多开发者在学习技术的过程中&#xff0c;常常会陷入一种误区当中&#xff0c;就是学了很多技术理论知识&#xff0c;但是仍做不出什么产品出来&#xff0c;往往学了很多干货&#xff0c;但是并无实际的用处。其实&#xff0c;不论是做什么&#xff0c;我们都需要从…...

React + Vite项目别名配置

Node版本&#xff1a;v20.16.0Vite版本&#xff1a;5.4.1 安装 types/node 依赖包 pnpm i types/node -D pnpm ls types/node配置 vite.config.js 文件: resolve: {alias: {"": join(__dirname, "./src/"),}, },使用配置好的别名 &#xff1a; 由上图我们…...

FFmpeg编译与配置 - Linux环境

Linux环境配置 环境&#xff1a;Ubuntu 22.04 step1. 首先下载安装依赖环境 更新软件源 sudo apt update下载依赖软件 sudo apt install \ autoconf \ automake \ build-essential \ cmake \ git-core \ libass-dev \ libfreetype6-dev \ libgnutls28-dev \ libsdl2-dev \…...

MyBatis-Plus 提供的一个通用服务层实现类

一、代码示例 Service public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService{Overridepublic List<CarriageDTO> findAll() {return List.of();} } 在这段代码中&#xff0c;CarriageServiceImpl …...

41-设计规则:线宽规则

1.设置电源线规则和信号线规则 2.设置信号线规则 3.设置电源线规则 如果未生效&#xff1a; ① 提升优先级即可。 ②查看使能选项有没有勾选...

使用MicroApp重构旧项目

前言 随着技术的飞速发展&#xff0c;我们公司内部一个基于“上古神器” jQuery PHP 构建的十年历史老项目已显力不从心&#xff0c;技术非常老旧且维护成本高昂&#xff0c;其实已经无数次想要重构&#xff0c;但是苦于历史遗留原因以及业务的稳定性而一直难以下手&#xff0…...

【Golang】go mod的使用

【1】GO111MODULE有三个值&#xff1a;off, on, auto off&#xff1a;go命令行将不会支持module功能&#xff0c;将会使用旧版本那种通过vendor目录或者GOPATH来查找依赖包的方式。 on&#xff1a;go命令行会使用modules功能&#xff0c;而不…...

Linux内核之网络套接字

文章目录 前言一、TCP4层模型和OSI7层模型OSI 7层模型TCP/IP 4层模型比较 二、套接字概念三、sockaddr_in和sockaddr结构体sockaddr_insockaddr区别 四、协议中的数据划分数据划分和首部添加流程数据接收与解析流程流程图 前言 一、TCP4层模型和OSI7层模型 OSI 7层模型 物理…...

SpringBoot事务-调度-缓存

一.Spring Boot中的事务管理 设置事务 Transactional(isolation Isolation.DEFAULT) Transactional(propagation Propagation.REQUIRED) 开启事务 EnableTransactionManagement 1. 开启事务管理 要开启 Spring 的事务管理&#xff0c;你需要在你的 Spring Boot 应用中添加 …...

社交媒体分析:如何利用Facebook的数据提升业务决

在数字化时代&#xff0c;社交媒体已经成为企业战略中不可或缺的一部分。Facebook&#xff0c;作为全球最大的社交平台之一&#xff0c;提供了丰富的数据资源&#xff0c;这些数据不仅能够帮助企业了解市场趋势&#xff0c;还能提升业务决策的精准度。本文将探讨如何有效利用Fa…...

企业中的流程组织

在每个实施SAP的企业中&#xff0c;除了传统的IT部门(包括SAP顾问所在的部门)&#xff0c;必不可少的是一定形式的流程组织(Process Organization)&#xff0c;流程组织的主要作用有 梳理企业内部业务流程&#xff0c;根据企业发展需要变更企业内部流程确定企业流程规范和部门…...

Redis:查询是否包含某个字符/字符串之二

上一篇&#xff1a;Redis&#xff1a;查询是否包含某个字符/字符串之一-CSDN博客 下一篇&#xff1a;Redis&#xff1a;查询是否包含某个字符/字符串之三-CSDN博客 摘要&#xff1a; 要查询数据&#xff0c;就需要遍历key&#xff0c;遍历value&#xff0c;其中包含存储等辅助…...

算法笔记|Day23贪心算法

算法笔记|Day23贪心算法 ☆☆☆☆☆leetcode 455.分发饼干题目分析代码 ☆☆☆☆☆leetcode 376. 摆动序列题目分析代码 ☆☆☆☆☆leetcode 53. 最大子序和题目分析代码 ☆☆☆☆☆leetcode 455.分发饼干 题目链接&#xff1a;leetcode 455.分发饼干 题目分析 优先考虑饼干…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...