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.hqwc.cn/news/660727.html

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

相关文章

【webrtc】MessageHandler 2: 基于线程的消息处理:以PeerConnectionClient为例

PeerConnectionClient 前一篇 nullaudiopoller 并么有场景线程,而是就是在当前线程直接执行的, PeerConnectionClient 作为一个独立的客户端,默认的是主线程。 PeerConnectionClient 同时维护客户端的信令状态,并且通过OnMessage实现MessageHandler 消息处理。 目前只处理一…

【分享】如何将word格式文档转化为PDF格式

在日常的办公和学习中&#xff0c;我们经常需要将Word文档转换为PDF格式。PDF作为一种通用的文件格式&#xff0c;具有跨平台、易读性高等优点&#xff0c;因此在许多场合下都更为适用。那么&#xff0c;如何实现Word转PDF呢&#xff1f;本文将介绍几种常用的方法&#xff0c;帮…

Aker(安碁科技)晶振产品应用和选型

一、石英晶体振荡器简介 在电子电路系统中&#xff0c;特定的动作需要严格按照一定的顺序进行&#xff0c;以确保数据被正确处理和操作&#xff0c;时钟信号就成了系统工作的重要引导者。而且在多模块复杂电路系统中&#xff0c;为了确保不同功能模块能协调一致地工作&#xf…

怎么用微信小程序实现远程控制台球室

怎么用微信小程序实现远程控制台球室呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制台球室&#xff0c;控制球台上方的照明灯&#xff0c;单台设备可控制多张球台的照明灯。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 …

Matlab生成txt文件导入到Vivado仿真

Matlab处理数据并将其写入txt文件 %% Txt Generate pre_RS_datadec2bin(simDataIn,8); %将数据转化为8bit的二进制 fidfopen("F:\FPGA\Xilinx_vivado\project\dvbstestbench\dbvs\matlab\pre_RS_data.txt","wt"); for i1:n*nMessages %数据…

【webrtc】MessageHandler 1: 基于线程的消息处理:以10毫秒处理音频为例

基于m98 G:\CDN\rtcCli\m98\src\audio\null_audio_poller.h分发的消息由MessageHandler 类通过其抽象接口OnMessage 实现处理 NullAudioPoller NullAudioPoller 是一个处理audio的消息的分发器 poll 启动:

Mybatis-Plus扩展接口InnerInterceptor

InnerInterceptor 接口就是 MyBatis-Plus 提供的一个拦截器接口&#xff0c;用于实现一些常用的 SQL 处理逻辑&#xff0c;处理 MyBatis-Plus 的特定功能,例如PaginationInnerInterceptor、OptimisticLockerInnerInterceptor 等,都实现了 InnerInterceptor 接口&#xff0c;并添…

SpringCloud 学习笔记 —— 六、Ribbon:负载均衡(基于客户端)

SpringCloud 学习笔记 —— 一、背景-CSDN博客 SpringCloud 学习笔记 —— 二、微服务与微服务架构-CSDN博客 SpringCloud 学习笔记 —— 三、SpringCloud 入门概述-CSDN博客 SpringCloud 学习笔记 —— 四、SpringCloud Rest 学习环境搭建&#xff1a;服务提供者-CSDN博客 …

Visual studio 2019 编程控制CH341A芯片的USB设备

1、硬件 买了个USB可转IIC、或SPI、或UART的设备&#xff0c;主芯片是CH341A 主要说明USB转SPI的应用&#xff0c;绿色跳线帽选择IIC&SPI&#xff0c;用到CS0、SCK、MOSI、MISO这4个引脚 2、软件 2.1、下载CH341A的驱动 点CH341A官网https://www.wch.cn/downloads/CH34…

JavaScript百炼成仙自学笔记——1

我要成神&#xff0c;成为前后端都会的神 《JavaScript百炼成仙》是清华大学出版社出版的一部图书&#xff0c;作者是杨逸飞JavaScript百炼成仙_百度百科《JavaScript百炼成仙》是清华大学出版社出版的一部图书&#xff0c;作者是杨逸飞。https://baike.baidu.com/item/JavaSc…

Flink checkpoint 源码分析- Checkpoint barrier 传递源码分析

背景 在上一篇的博客里&#xff0c;大致介绍了flink checkpoint中的触发的大体流程&#xff0c;现在介绍一下触发之后下游的算子是如何做snapshot。 上一篇的文章: Flink checkpoint 源码分析- Flink Checkpoint 触发流程分析-CSDN博客 代码分析 1. 在SubtaskCheckpointCoo…

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

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