qt5-入门-2D绘图-Graphics View 架构

参考:
Qt Graphics View Framework_w3cschool
https://www.w3cschool.cn/learnroadqt/4mvj1j53.html

C++ GUI Programming with Qt 4, Second Edition

本地环境:
win10专业版,64位,Qt 5.12


基础知识

QPainter比较适合少量绘图的情况,如果一个图里包含成千上百个单元,同时要让用户可以点击、拖动、选择,那么应该使用grahics view。
这个架构包含:一个scene(对应QGraphicsScene类),里面包含很多item(对应QGraphicsItem类)。用户要看到这个scene,需要借助view(对应QGraphicsView类)。一个scene可以对应多个view,比如不同的view可以看到scene的不同部分,或者不同的view可以看到经过不同transformation后的scene。如下图所示:
在这里插入图片描述
一个scene包含三层:foreground layer, background layer和item layer。前两个可以用QBrush修改,也可以通过重写drawBackground()drawForeground()实现完全的控制。举个例子,如果我们想用一个pixmap作为背景,那可以用QBrush制作一个基于这个pixmap的纹理。

scene可以告诉我们哪些item冲突了,哪些被选择了,或者哪些在哪里这样的信息。item也可以嵌套,此时如果对这个item做transformation,那么它包含的item也会自动被transform。

graphics view架构提供了两种item分组方式:1是前面提到的嵌套,就是让一个item是另一个item的孩子;2是使用QGraphicsItemGroup,加入group的item不会像嵌套一样传递transformation,它们每一个都像独立的个体(类似集合?)

item有预定义的(见下图),也可以重写。
在这里插入图片描述

QGraphicsView实际是一个组件(widget),提供滚动条(如果需要),并且通过transformation改变scene的呈现效果。它默认使用Qt内置的2D绘制引擎,但是也可以用OpenGL组件(构造后调用setViewport(),用到再写)。

graphics view架构使用三种坐标系:(同时提供了三种坐标系互转的函数)

  • viewport坐标系:QGraphicsView的viewport
  • scene坐标系:是逻辑坐标系,用来放置scene顶层的item
  • item坐标系:只与item相关,用来放置子item和绘制item
    在实际操作中,我们通常只关心后两个坐标系。

简单例子

如下图所示,在窗口中绘制一条线,无论怎么放缩窗口,这条线始终居中。如果使用QPainter来画,保持居中就需要一些计算,但是用graphics view架构,代码非常短,也没有计算。
在这里插入图片描述

#include <QGraphicsScene>
#include <QGraphicsView>QGraphicsScene *scene = new QGraphicsScene;
scene->addLine(10, 10, 150, 300);
QGraphicsView *view = new QGraphicsView(scene);
view->resize(500, 500);
view->setWindowTitle("Graphics View");
view->show();

进阶例子:自定义item

效果

在这里插入图片描述
在这里插入图片描述

例子来自ch8-Item-Based Rendering with Graphics View-diagram(实在找不到书和代码的可以私信我,虽然我觉得挺好找的)。大概是构造一个软件,用户可以放置一些Node,并且彼此相连,同时可以拖动上面的元素,并且设置一些属性。

知识储备

  • QGraphicsLineItem不是QObject的子类,如果要添加信号需要另外继承QObject(多继承)
  • graphics-view架构使用bounding rectangle技术。通过这个,可以判断某个点是否在item只中,也可以判断两个item是否碰撞,或者某个item是否需要被绘制(如果scene很大,当只需要绘制一部分时,如果某个item不在可视范围,那通过这个矩形是可以快速判断的,从而提升效率)
  • 因此如果要完全自定义item,需要重新实现boundingRect()paint()(也许还有shape()) 。其中shape是比boundingrect更精确的形状,可以应对细粒度碰撞检测(fine-grained collision detection)的情况,特别是当外形是圆角矩形时。比如如果重写了shape(),那么当点击夹在shape和bounding rectangle之间的区域时,是不会选中item的。也可以不重写它。
  • 根据前面的知识可知,如果图形要重绘需要调用update(),那么如果bounding rectangle可能会改变,就要调用prepareGeometryChange()

设计思路

此处不展开完整的实现,只说明文档结构。
在这里插入图片描述
link是两个node之间的连线,也是最简单的item,因为只需要考虑起点、终点和线条颜色(线宽默认),所以使用QGraphicsLineItem的一些函数就可以写好。Node是包含文字的圆角矩形框,需要完整地绘制,包括bounding rectangle,也包括一些互动(双击快速修改文字,修改属性)。propertiesdialog是属性设置窗口,写的是窗口上那些按钮的响应。最后diagramwindow就是主窗口,写的是主窗口上那些工具的布局、QAction和元素的初始化等。

实现

这里只展示 Node 和 Link。

Link

#ifndef LINK_H
#define LINK_H#include <QGraphicsLineItem>QT_BEGIN_NAMESPACE
class Node;
QT_END_NAMESPACE// QGraphicsLineItem不是QObject的子类,如果要添加信号需要另外继承QObject
// 不用额外的QColor变量记录颜色是因为颜色设置很简单,只是用一下QPen
class Link : public QGraphicsLineItem
{
public:Link(Node *fromNode, Node *toNode);~Link();// 获取两个端点Node *fromNode() const;Node *toNode() const;// 线的颜色void setColor(const QColor &color);QColor color() const;// 连线操作void trackNodes();private:Node *myFromNode;Node *myToNode;
};#endif// -------------------------------------------------------#include <QtWidgets>#include "link.h"
#include "node.h"Link::Link(Node *fromNode, Node *toNode)
{myFromNode = fromNode;myToNode = toNode;myFromNode->addLink(this);myToNode->addLink(this);// 选中是为了可以删除setFlags(QGraphicsItem::ItemIsSelectable);// z表示离窗口的远近,-1就是放在两个node下面,这样虽然是// 连接两个Node中心,但是看起来好像连接的是两个最近的边缘一样setZValue(-1);// 设置线的颜色setColor(Qt::darkRed);// 连线trackNodes();
}Link::~Link()
{myFromNode->removeLink(this);myToNode->removeLink(this);
}Node *Link::fromNode() const
{return myFromNode;
}Node *Link::toNode() const
{return myToNode;
}void Link::setColor(const QColor &color)
{// 1.0是线宽setPen(QPen(color, 1.0));
}QColor Link::color() const
{return pen().color();
}void Link::trackNodes()
{/* QGraphicsLineItem::pos()返回的是相对于父的坐标,这个父可以是* scene(如果这个item是top-level的),也可以是item(嵌套的情况)* QLineF是创建一条2D线段* QGraphicsLineItem::setLine*/setLine(QLineF(myFromNode->pos(), myToNode->pos()));
}

Node

#ifndef NODE_H
#define NODE_H#include <QApplication>
#include <QColor>
#include <QGraphicsItem>
#include <QSet>QT_BEGIN_NAMESPACE
class Link;
QT_END_NAMESPACEclass Node : public QGraphicsItem
{// 允许用tr(),不用再写QObject::tr()或者QCoreApplication::translate()Q_DECLARE_TR_FUNCTIONS(Node)public:Node();~Node();// 一些setter, gettervoid setText(const QString &text);QString text() const;void setTextColor(const QColor &color);QColor textColor() const;void setOutlineColor(const QColor &color);QColor outlineColor() const;void setBackgroundColor(const QColor &color);QColor backgroundColor() const;void addLink(Link *link);void removeLink(Link *link);QRectF boundingRect() const;QPainterPath shape() const;void paint(QPainter *painter,const QStyleOptionGraphicsItem *option, QWidget *widget);protected:// 双击以快速修改textvoid mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);// 处理node被移动的情况,此时需要更新相关的Link// itemChange是在图形项的任何属性变化时都会被调用,而 mouseMoveEvent 只在鼠标移动时才会触发QVariant itemChange(GraphicsItemChange change,const QVariant &value);private:QRectF outlineRect() const;int roundness(double size) const;QSet<Link *> myLinks;QString myText;QColor myTextColor;QColor myBackgroundColor;QColor myOutlineColor;
};#endif// -------------------------------------------------------#include <QtWidgets>#include "link.h"
#include "node.h"Node::Node()
{myTextColor = Qt::darkGreen;myOutlineColor = Qt::darkBlue;myBackgroundColor = Qt::white;setFlags(ItemIsMovable | ItemIsSelectable);
}Node::~Node()
{// 删除node时候要删掉所有关联的linkforeach (Link *link, myLinks)delete link;
}void Node::setText(const QString &text)
{// 表示bounding rectangle可能会改变prepareGeometryChange();myText = text;// 重绘update();
}QString Node::text() const
{return myText;
}// color类的setter getter就不展示了,注意setter需要调用update()重绘void Node::addLink(Link *link)
{myLinks.insert(link);
}void Node::removeLink(Link *link)
{myLinks.remove(link);
}QRectF Node::boundingRect() const
{const int Margin = 1;// 左上右下// 表示对outlineRect()返回的矩形进行调整,向左、上、右、下各移动Margin像素// margin的取值应该至少是pen宽度的一半return outlineRect().adjusted(-Margin, -Margin, +Margin, +Margin);
}QPainterPath Node::shape() const
{QRectF rect = outlineRect();QPainterPath path;path.addRoundRect(rect, roundness(rect.width()),roundness(rect.height()));return path;
}void Node::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget * /* widget */)
{QPen pen(myOutlineColor);if (option->state & QStyle::State_Selected) {pen.setStyle(Qt::DotLine);pen.setWidth(2);}painter->setPen(pen);painter->setBrush(myBackgroundColor);QRectF rect = outlineRect();painter->drawRoundRect(rect, roundness(rect.width()),roundness(rect.height()));painter->setPen(myTextColor);painter->drawText(rect, Qt::AlignCenter, myText);
}void Node::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{QString text = QInputDialog::getText(event->widget(),tr("Edit Text"), tr("Enter new text:"),QLineEdit::Normal, myText);if (!text.isEmpty())setText(text);
}QVariant Node::itemChange(GraphicsItemChange change,const QVariant &value)
{// 每次拖动Node,位置就发生变化了,此时需要重新画线if (change == ItemPositionHasChanged) {foreach (Link *link, myLinks)link->trackNodes();}return QGraphicsItem::itemChange(change, value);
}QRectF Node::outlineRect() const
{const int Padding = 8;QFontMetricsF metrics = static_cast<QFontMetricsF>(qApp->font());QRectF rect = metrics.boundingRect(myText);rect.adjust(-Padding, -Padding, +Padding, +Padding);// 转换坐标是为了让text放在中心,本来默认的坐标是左上角(0,0)rect.translate(-rect.center());return rect;
}int Node::roundness(double size) const
{const int Diameter = 12;return 100 * Diameter / int(size);
}

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

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

相关文章

10个使用NumPy就可以进行的图像处理步骤

图像处理是一种数学计算。数字图像由称为像素的彩色小点组成。每个像素由红、绿、蓝(RGB)三个独立的颜色组成。每个像素中的主色由每个RGB分量的数值决定。 本文将介绍10个使用使用NumPy就可以进行的图像处理步骤&#xff0c;虽然有更强大的图像处理库&#xff0c;但是这些简单…

光伏管理系统:降本增效解决方案。

现在是光伏发展的重要节点&#xff0c;如何在众多同行中脱颖而出并且有效的达到降低成本、提高效率也是很多企业都在考虑的问题&#xff0c;鹧鸪云的团队研发出了光伏管理系统&#xff0c;通过更高效、更智能、更全面的管理方式来帮助企业实现降本增效的转型&#xff0c;小编带…

公考学习平台|基于SprinBoot+vue的公考学习平台(源码+数据库+文档)

公考学习平台目录 目录 基于SprinBootvue的公考学习平台 一、前言 二、系统设计 三、系统功能设计 5.1用户信息管理 5.2 视频信息管理 5.3公告信息管理 5.1论坛信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…

笔记-word导出PDF老是更新域导致图片和表格题注发生变化

问题描述&#xff1a;微软word 导出PDF时&#xff0c;老是更新域&#xff0c;导致图片和表格题注否跟着变化 以下是解决方法的具体描述。 目录 一、准备工作二、操作步骤 一、准备工作 1、工具版本&#xff1a;微软 word 2016&#xff08;其他微软word版本也OK&#xff09; …

【自研网关系列】过滤器链 -- 鉴权过滤器

&#x1f308;Yu-Gateway&#xff1a;&#xff1a;基于 Netty 构建的自研 API 网关&#xff0c;采用 Java 原生实现&#xff0c;整合 Nacos 作为注册配置中心。其设计目标是为微服务架构提供高性能、可扩展的统一入口和基础设施&#xff0c;承载请求路由、安全控制、流量治理等…

智能化未来:Agent AI智能体的崛起与全球挑战

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

Redis源码学习记录:列表 (ziplist)

ziplist redis 源码版本&#xff1a;6.0.9。ziplist 的代码均在 ziplist.c / ziplist.h 文件中。 定义 ziplist总体布局如下&#xff1a; <zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend> zlbytes&#xff1a;uin…

高德优评项目,一单29.9,拷贝+评价,日入500

项目概述&#xff1a; 下载 地 址 &#xff1a; laoa1.cn/1836.html 本项目的核心非常简单&#xff0c;即在高德地图上撰写评论。每条评论完成后&#xff0c;参与者将获得8元的奖励。 此外&#xff0c;高德地图还会提供视频会员作为奖励。我们可以将这些视频会员进行变现…

LLM 构建Data Multi-Agents 赋能数据分析平台的实践之③:数据分析之二(大小模型协同)

一、概述 随着新一代信息技术在产业数字化中的应用&#xff0c;产生了大量多源多模态信息以及响应的信息处理模式&#xff0c;数据孤岛、模型林立的问题也随之产生&#xff0c;使得业务系统臃肿、信息处理和决策效率低下&#xff0c;面对复杂任务及应用场景问题求解效率低。针…

做外贸如何主动开发外贸客户

在外贸业务中&#xff0c;主动开发客户是至关重要的一步&#xff0c;它能够帮助你扩大市场覆盖范围&#xff0c;建立稳定的客户基础。以下是一些有效的策略和方法&#xff0c;可以帮助你更有效地主动开发外贸客户&#xff1a; 明确目标市场&#xff1a;首先&#xff0c;你需要确…

【快速入门】数据库的增删改查与结构讲解

文章的操作都是基于小皮php study的MySQL5.7.26进行演示 what 数据库是能长期存储在计算机内&#xff0c;有组织的&#xff0c;可共享的大量数据的集合。数据库中的数据按照一定的数据模型存储&#xff0c;具有较小的冗余性&#xff0c;较高的独立性和易扩展性&#xff0c;并为…

【docker】Docker开启远程访问

将构建的镜像自动上传到服务器。 需要开放 Docker 的端口&#xff0c;让我们在本地能连接上服务器的 Docker&#xff0c;这样&#xff0c;才能上传构建的镜像给 Docker。 开启远程访问 首先在服务器打开 Docker 的服务文件 vim /usr/lib/systemd/system/docker.service修改…

LiveGBS user/save 逻辑缺陷漏洞复现(CNVD-2023-72138)

0x01 产品简介 LiveGBS是安徽青柿信息科技有限公司研发的一款国标(GB28181)流媒体服务软件,可提供提供用户管理及Web可视化页面管理,开源的前端页面源码;提供设备状态管理,可实时查看设备是否掉线等信息等。 0x02 漏洞概述 LiveGBS user/save 接口处存在逻辑缺陷漏洞,未…

浅论汽车研发项目数字化管理之道

随着汽车行业竞争不断加剧&#xff0c;汽车厂商能否快速、高质地推出贴合市场需求的新车型已经成为车企竞争的重要手段&#xff0c;而汽车研发具备流程复杂、专业领域多、协作难度大、质量要求高等特点&#xff0c;企业如果缺少科学健全的项目管理体系&#xff0c;将会在汽车研…

java-springmvc 01 补充 javaweb 三大组件Servlet,Filter、Listener(源码都是tomcat8.5项目中的)

01.JavaWeb三大组件指的是&#xff1a;Servlet、Filter、Listener,三者提供不同的功能 这三个在springmvc 运用很多 Servlet 01.Servlet接口&#xff1a; public interface Servlet {/*** 初始化方法* 实例化servlet之后&#xff0c;该方法仅调用一次 * init方法必须执行完…

区块链 | 由外部实体导致的 NFT 安全问题

&#x1f98a;原文&#xff1a; Understanding Security Issues in the NFT Ecosystem &#x1f98a;警告&#xff1a; 本文只记录了原文的第 6 节。 1 问题描述 NFT 所指向的数字资产&#xff08;图片、视频等&#xff09;必须是可以访问的&#xff0c;这样 NFT 才具有意义…

flake8,一个超强的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超强的 Python 库 - flake8。 Github地址&#xff1a;https://github.com/PyCQA/flake8 Flake8是一个流行的Python库&#xff0c;用于检查代码质量和风格一致性&#xff0c;它集成了PyFlakes、…

powershell 注册全局热键——提升效率小工具

powershell 注册全局热键 01 前言 在处理一些重复工作问题的时候&#xff0c;想搞一个小工具&#xff0c;配合全局快捷键来提高效率。因为是Windows系统&#xff0c;想到C#&#xff0c;但是又不想用VS开发&#xff0c;因为那样不够灵活&#xff0c;没办法随时修改随时用&…

Windows系统下安装Mosquitto的步骤(2)

接前一篇文章&#xff1a;Windows系统下安装Mosquitto的步骤&#xff08;1&#xff09; 本文内容参考&#xff1a; Windows10上安装Mosquitto的步骤(win10、win11 安装mqtt) - IPS99技术分享 MQTT&#xff1a;windows环境下配置MQTT服务器&#xff08;mosquitto&#xff09;_…

2024.阳光能源追光计划暨大陆考察团交流分享会

近日大陆考察团抵达香港&#xff0c;受到了本司热情接待和安排。公司于4月27日下午举办了阳光能源追光计划主题交流会。 会上公司营销部总监张超&#xff0c;分享了阳光能源近几年的能源发展之路及公司新推出的追光计划&#xff0c;得到了大陆考察交流团团长杨国均先生的高度赞…