Qt实现思维导图功能6『鹰眼视图』

news2024/11/14 20:28:18

前文链接:Qt实现思维导图功能5『纵向分布模式』

百度网盘体验地址:
链接:https://pan.baidu.com/s/1xotlkSPfG7E_37y_XPfDng 
提取码:5li7

效果图
1、动态演示效果:

思维导图-鹰眼视图


2、静态展示图片:
在这里插入图片描述

新增功能如下

序号简述具体功能
1显示全场景保持场景纵横比不变的情况下最大化显示全场景导图信息
1矩形导航框显示场景具体大小,随主视图缩放/移动而变化
1视野跳转鹰眼视图支持鼠标点击后立即跳转到指定位置,该位置将呈现在主视图视野中心
1视野移动鹰眼视图支持鼠标移动时触发主视图视野伴随移动,移动过程丝滑无卡顿

新增UI如下

序号简述具体功能
1鹰眼视图窗口显示和操控主视图中场景图元位置

核心代码

#pragma once

/*
 * 鹰眼视图窗口
 * 1、最大化显示全场景图元
 * 2、矩形导航框自适应场景缩放与移动
 */

#include <QWidget>
#include "ui_EagleWidget.h"

class QGraphicsView;
class QGraphicsPixmapItem;
class QGraphicsRectItem;

class EagleWidget : public QWidget
{
	Q_OBJECT

public:
	EagleWidget(QWidget *parent = nullptr);
	~EagleWidget();

	// 设置主视图
	void setMainView(QGraphicsView *view);

protected:
	bool eventFilter(QObject *watched, QEvent *event);

private slots:
	void on_btnClose_clicked();

private:
	Ui::EagleWidget ui;

	QGraphicsView *m_mainView;				// 主视图
	QGraphicsPixmapItem *m_bgPixmapItem;	// 背景图元
	QGraphicsRectItem *m_rectItem;			// 矩形导航框
};
#include "EagleWidget.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QGraphicsRectItem>
#include <QMouseEvent>

EagleWidget::EagleWidget(QWidget *parent /*= nullptr*/)
	: QWidget(parent)
	, m_mainView(nullptr)
{
	ui.setupUi(this);
	setAttribute(Qt::WA_DeleteOnClose);
	m_bgPixmapItem = new QGraphicsPixmapItem();
	m_rectItem = new QGraphicsRectItem;
	m_rectItem->setPen(QPen(Qt::red));
	ui.graphicsView->setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing);
	ui.graphicsView->viewport()->installEventFilter(this);

	ui.graphicsView->setCursor(QCursor(Qt::CrossCursor));
}

EagleWidget::~EagleWidget()
{

}

void EagleWidget::setMainView(QGraphicsView *view)
{
	m_mainView = view;
	m_mainView->installEventFilter(this);
	m_mainView->viewport()->installEventFilter(this);

	QGraphicsScene *scene = new QGraphicsScene(ui.graphicsView->viewport()->rect());
	ui.graphicsView->setScene(scene);
	scene->setBackgroundBrush(m_mainView->scene()->backgroundBrush());
	scene->addItem(m_bgPixmapItem);
	scene->addItem(m_rectItem);
}

bool EagleWidget::eventFilter(QObject *watched, QEvent *event)
{
	if (m_mainView == nullptr)
		return QWidget::eventFilter(watched, event);

	if (watched == m_mainView)
	{
		if (event->type() == QEvent::Paint || event->type() == QEvent::Wheel)
		{		
			QGraphicsScene *scene = m_mainView->scene();
			// 设置图像大小与场景图元边界大小一致
			QRectF itemsBoundingRect = scene->itemsBoundingRect();
			QPixmap	pixmap(itemsBoundingRect.width(), itemsBoundingRect.height());
			// 渲染图像
			QPainter painter;
			painter.begin(&pixmap);
			painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing);
			scene->render(&painter, pixmap.rect(), itemsBoundingRect);
			painter.end();
			// 调整图像大小
			QSize eagleViewportSize = ui.graphicsView->viewport()->rect().size();
			QPixmap scaledPixmap = pixmap.scaled(eagleViewportSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
			m_bgPixmapItem->setPixmap(scaledPixmap);
			// 调整图像位置
			if (scaledPixmap.height() <= ui.graphicsView->viewport()->height())
				m_bgPixmapItem->setY((ui.graphicsView->viewport()->height() - scaledPixmap.height()) / 2.0);
			if (scaledPixmap.width() <= ui.graphicsView->viewport()->width())
				m_bgPixmapItem->setX((ui.graphicsView->viewport()->width() - scaledPixmap.width()) / 2.0);

			// 计算缩放后的图元大小与主视窗大小的水平/垂直比率
			QRectF bgPixmapRect = m_bgPixmapItem->boundingRect();
			QSize mainViewportSize = m_mainView->viewport()->rect().size();
			qreal scaleFactor = m_mainView->matrix().m11();
			double scaleH = itemsBoundingRect.width() * scaleFactor / mainViewportSize.width();
			double scaleV = itemsBoundingRect.height() * scaleFactor / mainViewportSize.height();
			double scaleMax = qMax(scaleH, scaleV);
			if (scaleMax <= 1)
			{// 当比率<=1时,矩形导航框为整个鹰眼视图中的背景图元大小
				m_rectItem->setRect(bgPixmapRect);
				m_rectItem->setPos(m_bgPixmapItem->pos());
			}
			else// scaleMax > 1
			{// 当比率>1时,矩形导航框为主视窗中图元占整个场景图元的比率所对应的鹰眼视图大小
				QPointF viewportTopLeftToScenePos = m_mainView->mapToScene(QPoint(0, 0));
				double offsetXRatio = (viewportTopLeftToScenePos.x() - itemsBoundingRect.x()) / itemsBoundingRect.width();
				double offsetYRatio = (viewportTopLeftToScenePos.y() - itemsBoundingRect.y()) / itemsBoundingRect.height();
				m_rectItem->setRect(0, 0, bgPixmapRect.width() / scaleH, bgPixmapRect.height() / scaleV);
				m_rectItem->setPos(offsetXRatio * bgPixmapRect.width() + m_bgPixmapItem->x(), offsetYRatio * bgPixmapRect.height() + m_bgPixmapItem->y());
			}

			// 显示缩放比率
			if (scaleMax != 0)
				ui.lblScale->setText(QString("%1%").arg((int)(scaleMax * 100)));
		}
	}
	else if (watched == m_mainView->viewport())
	{
		if (event->type() == QEvent::MouseMove)
		{
			QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event);
			if (mouseEvent->buttons() != Qt::LeftButton)
				return QWidget::eventFilter(watched, event);

			QGraphicsScene *scene = m_mainView->scene();
			QRectF itemsBoundingRect = scene->itemsBoundingRect();

			// 计算缩放后的图元大小与主视窗大小的水平/垂直比率
			QRectF bgPixmapRect = m_bgPixmapItem->boundingRect();
			QSize mainViewportSize = m_mainView->viewport()->rect().size();
			qreal scaleFactor = m_mainView->matrix().m11();
			double scaleH = itemsBoundingRect.width() * scaleFactor / mainViewportSize.width();
			double scaleV = itemsBoundingRect.height() * scaleFactor / mainViewportSize.height();
			double scaleMax = qMax(scaleH, scaleV);
			if (scaleMax > 1)
			{// 当比率>1时,矩形导航框为主视窗中图元占整个场景图元的比率所对应的鹰眼视图大小
				QPointF viewportTopLeftToScenePos = m_mainView->mapToScene(QPoint(0, 0));
				double offsetXRatio = (viewportTopLeftToScenePos.x() - itemsBoundingRect.x()) / itemsBoundingRect.width();
				double offsetYRatio = (viewportTopLeftToScenePos.y() - itemsBoundingRect.y()) / itemsBoundingRect.height();
				m_rectItem->setRect(0, 0, bgPixmapRect.width() / scaleH, bgPixmapRect.height() / scaleV);
				m_rectItem->setPos(offsetXRatio * bgPixmapRect.width() + m_bgPixmapItem->x(), offsetYRatio * bgPixmapRect.height() + m_bgPixmapItem->y());
			}
		}
	}
	else if (watched == ui.graphicsView->viewport())
	{
		if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove)
		{
			QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event);
			QRectF bgPixmapRect = m_bgPixmapItem->boundingRect();
			QGraphicsScene *scene = m_mainView->scene();
			QRectF itemsBoundingRect = scene->itemsBoundingRect();

			// 换算鼠标点击处在场景中的位置,并将该位置设置为视野中心
			double offsetX = mouseEvent->x() - m_bgPixmapItem->x();
			double offsetY = mouseEvent->y() - m_bgPixmapItem->y();
			double scale = bgPixmapRect.width() / itemsBoundingRect.width();
			double sceneOffsetX = itemsBoundingRect.x() + offsetX / scale;
			double sceneOffsetY = itemsBoundingRect.y() + offsetY / scale;
			m_mainView->centerOn(sceneOffsetX, sceneOffsetY);

			// 计算缩放后的图元大小与主视窗大小的水平/垂直比率
			QSize mainViewportSize = m_mainView->viewport()->rect().size();
			qreal scaleFactor = m_mainView->matrix().m11();
			double scaleH = itemsBoundingRect.width() * scaleFactor / mainViewportSize.width();
			double scaleV = itemsBoundingRect.height() * scaleFactor / mainViewportSize.height();
			double scaleMax = qMax(scaleH, scaleV);
			if (scaleMax > 1)
			{// 当比率>1时,设置矩形导航框位置
				QPointF scenePos = ui.graphicsView->mapToScene(mouseEvent->pos());
				m_rectItem->setPos(scenePos.x() - m_rectItem->rect().width() / 2, scenePos.y() - m_rectItem->rect().height() / 2);
			}
		}
	}

	return QWidget::eventFilter(watched, event);
}

void EagleWidget::on_btnClose_clicked()
{
	close();
}

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

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

相关文章

Econ3107-econ5116-小组作业知识点精讲

对本文有疑问可以加微信 Tutor_0914联系。也可以访问我的个人辅导网站 &#xff1a; tutoryou 基本概念 option期权 期权&#xff08;Option&#xff09;&#xff0c;是一种选择权&#xff0c;指是一种能在未来某特定时间以特定价格买入或卖出一定数量的某种特定商品的权利…

Linux 知识学习总结

常用命名 du 作用&#xff1a;显示指定目录或文件所占磁盘空间大小。 示例&#xff1a; du -h 以K&#xff0c;M&#xff0c;G为单位自动适配显示 lxlx-virtual-machine:~/test/video$ du -h 1.2G du -m 指定以1MB为单位显示 lxlx-virtual-machine:~/test/video$ du -m 12…

Python潮流周刊#11:如何使用 Golang 运行 Python 代码?

你好&#xff0c;我是猫哥。这里每周分享优质的 Python 及通用技术内容&#xff0c;大部分为英文&#xff0c;已在小标题注明。&#xff08;标题取自其中一则分享&#xff0c;不代表全部内容都是该主题&#xff0c;特此声明。&#xff09; 本周刊精心筛选国内外的 250 信息源&a…

​注意力机制中的掩码详解

注意力机制的掩码允许我们发送不同长度的批次数据一次性的发送到transformer中。在代码中是通过将所有序列填充到相同的长度&#xff0c;然后使用“attention_mask”张量来识别哪些令牌是填充的来做到这一点&#xff0c;本文将详细介绍这个掩码的原理和机制。 我们先介绍下如果…

(简单)设计哈希集合 Java

为了实现哈希集合这一数据结构&#xff0c;有以下几个关键问题需要解决&#xff1a; 哈希函数&#xff1a;能够将集合中任意可能的元素映射到一个固定范围的整数值&#xff0c;并将该元素存储到整数值对应的地址上冲突处理&#xff1a;由于不同元素可能映射到相同的整数值&…

SpringBoot读取配置的方式

读取配置的几种方式 Spring Boot提供了多种方式来读取配置&#xff0c;下面是其中几种常用的方式&#xff1a; 使用application.properties或application.yml文件&#xff1a;在Spring Boot项目的classpath根目录下&#xff0c;可以创建一个名为application.properties或appli…

oc基本控件3

UIButton // // ViewController.m // OcDemoTest // // Created by Mac on 2023/7/14. //#import "ViewController.h"interface ViewController ()endimplementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 1 创建按钮对象UIButton *button…

涂鸦智能打造专业家庭智能生活助手,实现人机交互升级

近年来&#xff0c;智能家居设备的品类不断拓展&#xff0c;同时&#xff0c;人们对AI与智能家居的联动愈发憧憬。自然语言交互是未来人机交互的主要趋势之一&#xff0c;其关键在于使AI具备主动理解信息的能力&#xff0c;让用户的交互更轻松。如何将智能场景的交互变得更“善…

MySQL-DDL-表结构操作-创建-案例

案例 根据页面原型/需求创建表&#xff08;设计合理的数据类型、长度、约束&#xff09; 具体操作 在idea中使用可视化图形界面创建 具体操作如下&#xff1a; 在该界面中进行属性的创建&#xff0c;进行属性名称、数据类型、约束、描述等信息的填写最终运行结果如下&…

800V高压电驱动系统盘点

2023年上海车展共有23家厂商的63个电驱动产品&#xff0c;经过梳理&#xff0c;本次展出的800V高压电驱动共有13款&#xff0c;可以说电驱动全面进入高压化。800V电驱动是一个系统性的话题&#xff0c;对于电机而言&#xff0c;挑战的方向主要围绕高速、高压、散热&#xff0c;…

替换空格

替换空格 请实现一个函数&#xff0c;把字符串 s 中的每个空格替换成"%20"。 题目给的测试用例里有以下限制&#xff1a; 0 < s.length < 14。 split() 把字符串分割为子字符串数组 例如&#xff1a; var txt"ABCD EFGH IJKL MNOP QRSTU VWXYZ"; v…

微信小程序下拉选择

微信小程序中下拉框选择一般的交互方式有以下两种 直接下拉选择点击选择框后&#xff0c;弹出浮层进行选择 下边分别介绍两种方式的实现。在微信小程序中&#xff0c;这两种实现都需要修改三个文件 js 文件&#xff1a;下拉选择逻辑的具体实现 wxml 文件&#xff1a;下拉组件…

C#正则表达式校验某个字符串是否是合格的email

C#正则表达式校验某个字符串是否是合格的email 可以借助正则表达式校验某个字符串是否是合规的电子邮箱。对于邮箱的正则表达式有严格的模式&#xff0c;如&#xff1a;^[a-zA-Z0-9_&*-](?:\\.[a-zA-Z0-9_&*-])*(?:[a-zA-Z0-9-]\\.)[a-zA-Z]{2,7}$ 对应的C#实现如下…

TCP编程流程和粘包

目录 1、TCP编程流程 2、粘包 1、TCP编程流程 socket() 是创建套接字&#xff0c;返回值为监听套接字描述符&#xff0c;有了套接字才能通过网络进行数据的传输。创建套接字的参数要指定服务类型&#xff0c;TCP协议使用的是流式服务&#xff08;SOCK_STREAM&#xff09;。 b…

用Matlab听音乐 - 动态频谱

文章目录 高帧率版本效果: 定时器版本music_play主函数&#xff1a;定时器回调函数&#xff1a;效果: 高帧率版本 由于matlab这款科学计算软件本身庞大略显笨重&#xff0c;执行代码的速度受当前系统影响&#xff0c;很难做到严格定时仿真&#xff08;造成音画不同步&#xff…

互联网行业真的不行了吗?

文章目录 前言一、起因二、互联网真的完了吗&#xff1f;三、是不是要转行&#xff1f;四、十年磨一剑五、统一回复 前言 英雄算法联盟 - 七月集训 已经开始 16 天&#xff0c;八月算法集训 将于 08月01日 正式开始&#xff0c;目前已经提前开始报名&#xff0c;报名方式参见&a…

英国24所顶尖大学撤销禁令,更新AI使用规定!

自从ChatGPT展现了其高超的AI技术后&#xff0c;备受全球年轻人的喜爱。ChatGPT功能多样化&#xff0c;可以节省查阅复杂文献的时间、编写简单的Python代码、辅助学生理解知识点... 同时&#xff0c;ChatGPT引发的学术不诚信问题也让各大院校头疼不已。 连续数月以来&#xff…

js 浮点位数超过17位乘以10^18,精度丢失问题

我有一个浮点型 var num 9.963407954080194743 用num * (10 ** 18) 计算得出的结果是9963407954080195000, 但是我想要得到的结果是9963407954080194743 问ChatGPT问题得以解决&#xff1a; GPT提供的代码&#xff1a; import Big from big.js;const num1 new Big(9.9634…

从输入URL到页面渲染的整个过程

从输入URL到页面渲染的整个过程 1.DNS解析&#xff0c;把url中的域名解析成对应的IP地址。如果本地DNS缓存没有响应的记录&#xff0c;则会向DNS发送请求&#xff0c;获取相应的IP地址。 2.浏览器使用获取到的目标服务器的IP地址&#xff0c;通过TCP/IP协议与服务器建立连接&a…

python-web开发(Djaongo)课程基本内容

python-web开发&#xff08;Djaongo&#xff09;课程基本内容及其前置技术 参考内容&#xff1a; 【最新Python的web开发全家桶&#xff08;django前端数据库&#xff09;】 https://www.bilibili.com/video/BV1rT4y1v7uQ/?share_sourcecopy_web&vd_source84fd4883bb478d0…