相关文章推荐

📢 图形视图框架的介绍

在 Qt 框架内的许多模块,类和子框架下,有一块专门用于简化图形处理的工具,称为 图形视图框架 。 它包含许多类,几乎所有的类都以QGraphics开头,并且所有这些类都可用于处理构建计算机视觉应用时可能遇到的大多数图形任务。 图形视图框架将所有可能的对象简单地分为三个主要类别,即场景类( QGraphicsScene )、视图类( QGraphicsView )和图元类( QGraphicsItem ),统称为“三元素”。随之而来的架构允许轻松地添加,删除,修改以及显示图形对象。

  • 视图( QGraphicsView 小部件):用于 可视化和显示 QGraphicsScene 的内容 。 它还 负责将事件传播到 QGraphicsScene 。 这里要注意的重要一点是 QGraphicsScene QGraphicsView 都具有不同的坐标系。 可以猜到,如果放大,缩小或进行不同的相似变换,则场景上的位置将不同。 QGraphicsScene QGraphicsView 都提供了转换彼此适合的位置值的功能。
  • 场景( QGraphicsScene 类): 管理项目( QGraphicsItem )的实例(其子类) ,包含它们,并将事件(例如,鼠标单击、移动等)传播到项目中。一个场景可以通过多个视图表现,一个场景也可以包括多个几何图形。
  • 图形项目( QGraphicsItem 及其子类)这些项目( QGraphicsItem )子类的实例是 QGraphicsScene 中包含的项目。 它们可以是 线,矩形,图像,文本等

🌺 视图类 — QGraphicsView

QGraphicsView 类是 Qt 窗口小部件类,可以将其放置在窗口上以 显示 QGraphicsScene ,该窗口本身包含许多 QGraphicsItem 子类和/或窗口小部件。 与 QGraphicsScene 类相似,该类还提供大量函数,方法和属性来处理图形的可视化部分。 我们将审核以下列表中的一些最重要的函数,然后我们将学习如何对 QGraphicsView 进行子类化并将其扩展为在我们全面的计算机视觉应用中具有若干重要功能,例如放大,缩小, 项目选择等。因此,这是我们在计算机视觉项目中需要的 QGraphicsView 类的方法和成员:

  • alignment setAlignment 函数可用于设置场景在视图中的对齐方式。 重要的是要注意,只有当视图可以完全显示场景,即视图不需要滚动条时,这才具有可见效果。 一般当场景大于视图会出现滚动条,是不能通过 setAlignment() 函数来设置场景在视图中的对齐方式的。

    左对齐Qt::AlignLeft ; 向上对齐Qt::AlignTop ; 中心对齐Qt::AlignCenter
    eg:setAlignment(Qt::AlignLeft | Qt::AlignTop);
    
  • dragMode setDragMode 函数可用于获取和设置视图的拖动模式。 这是视图的最重要函数之一,它可以决定在视图上单击并拖动鼠标左键时会发生什么。我们将使用 QGraphicsView::DragMode 枚举设置不同的拖动模式。

    QGraphicsView::NoDrag:忽略鼠标事件,不可以拖动。
    QGraphicsView::ScrollHandDrag:光标变为手型,可以拖动场景进行移动。
    QGraphicsView::RubberBandDrag:使用橡皮筋效果,进行区域选择,可以选中一个区域内的所有图形项。
    
  • isInteractive setInteractive 函数允许检索和修改视图的交互行为。 交互式视图会响应鼠标和键盘(如果已实现),否则,所有鼠标和键盘事件都将被忽略,并且该视图只能用于查看并且不能与场景中的项目进行交互。

  • optimizationFlags setOptimizationFlags renderHints setRenderHints viewportUpdateMode setViewportUpdateMode 函数分别用于获取和设置与视图的性能和渲染质量有关的参数。 在下面的示例项目中,我们将在实践中看到这些函数的用例。

  • dragMode 设置为 RubberBandDrag 模式的情况下,可以使用 rubberBandSelectionMode setRubberBandSelectionMode 函数设置视图的项目选择模式。 可以设置以下内容,它们是 Qt::ItemSelectionMode 枚举中的条目:

    Qt::ContainsItemShape、Qt::IntersectsItemShape、Qt::ContainsItemBoundingRect、Qt::IntersectsItemBoundingRect
    
  • sceneRect setSceneRect 函数可用于获取和设置视图中场景的可视化区域。 显然,该值不必与 QGraphicsScene 类的 sceneRect 相同。

  • centerOn 函数可用于确保特定点或项目位于视图中心。

  • ensureVisible 函数可用于将视图滚动到特定区域(具有给定的边距)以确保它在视图中。 此函数适用于点,矩形和图形项目。

  • fitInView 函数与 centerOn ensureVisible 非常相似,但主要区别在于,该函数还使用给定的宽高比处理参数缩放视图的内容以适合视图。 以下:

    Qt::IgnoreAspectRatio、Qt::KeepAspectRatio、Qt::KeepAspectRatioByExpanding
    eg:fitInView(m_view->sceneRect(), Qt::KeepAspectRatio);   //场景大小适应视图View大小  
    
  • itemAt函数可用于在视图中的特定位置检索项目。

我们已经了解到场景中的每个项目和场景中的每个项目都有各自的坐标系,我们需要使用映射函数将位置从一个位置转换到另一个位置,反之亦然。 视图也是如此。 视图还具有自己的坐标系,主要区别在于视图中的位置和矩形等实际上是根据像素进行测量的,因此它们是整数,但是场景和项目的位置使用实数,等等。 这是由于以下事实:场景和项目在视图上被查看之前都是逻辑实体,因此所有实数都将转换为整数,而整个场景(或部分场景)准备在屏幕上显示。下图可以帮助您更好地理解这一点:
在这里插入图片描述

  • mapFromScene mapToScene 函数可用于在场景坐标系之间转换位置。与前面提到的一致, mapFromScene 函数接受实数并返回整数值,而 mapToScene 函数接受整数并返回实数。稍后我们将开发视图的缩放功能时,将使用这些函数。
  • items 函数可用于获取场景中的项目列表。
  • render 函数对于执行整个视图或其一部分的渲染很有用。该函数的用法与 QGraphicsScene 中的render完全相同,只是此函数在视图上执行相同的功能。
  • rubberBandRect 函数可用于获取橡皮筋选择的矩形。如前所述,这仅在拖动模式设置为 rubberBandSelectionMode 时才有意义。
  • setScene scene 函数可用于设置和获取视图场景。
  • setMatrix,setTransform,transform,rotate,scale,shear和translate 函数都可以用于修改或检索视图的几何特性。

QGraphicsScene QGraphicsItem 类相同, QGraphicsView 还提供了许多相同的受保护虚拟成员,可用于进一步扩展视图的功能。 现在,我们将对平时应用 QGraphicsView 扩展类时经常使用到的功能:

1、缩放与旋转

QGraphicsView::scale(xScale, yScale);//在分别在x,y方向上缩放xScale,yScale倍。若为1.0倍,则不进行缩放。
QGraphicsView::rotate(90);//顺时针旋转90度

  功能实现:

//重写QGraphicsView类中滑轮事件,完成缩放功能。MyGraphicsView为继承QGraphicsView的子类
void MyGraphicsView::wheelEvent(QWheelEvent *event) 
	// 获取当前鼠标相对于view的位置;
	QPointF cursorPoint = event->pos();
	// 获取当前鼠标相对于scene的位置;
	QPointF scenePos = this->mapToScene(QPoint(cursorPoint.x(), cursorPoint.y()));	
	// 获取view的宽高;
	qreal viewWidth = this->viewport()->width();
	qreal viewHeight = this->viewport()->height();
	// 获取当前鼠标位置相当于view大小的横纵比例;
	qreal hScale = cursorPoint.x() / viewWidth;
	qreal vScale = cursorPoint.y() / viewHeight;
	// 当前放缩倍数;
	//qreal scaleFactor = this->matrix().m11();
	int wheelDeltaValue = event->delta();
	// 向上滚动,放大
	if (wheelDeltaValue > 0)
		this->scale(1.4, 1.4);
	// 向下滚动,缩小;
		this->scale(1/ 1.4, 1 / 1.4);;
	// 将scene坐标转换为放大缩小后的坐标;
	QPointF viewPoint = this->matrix().map(scenePos);
	// 通过滚动条控制view放大缩小后的展示scene的位置;
	this->horizontalScrollBar()->setValue(int(viewPoint.x() - viewWidth * hScale));
	this->verticalScrollBar()->setValue(int(viewPoint.y() - viewHeight * vScale));

2、其他事件传递

  在上面我们看到必须在事件函数的最后将event参数传递出去,才能执行默认的事件操作。其实不止上面那一种情况,在图形视图框架中,鼠标键盘等事件是从视图View进入的,视图View将它们传递给场景Scene,场景Scene再将事件传递给该点的图形项Item,如果该点有多个图形项,那么就传给最上面的图形项。所以要想使这个事件能一直传播下去,我们就需要在重新实现事件处理函数时,在其最后将event参数传给默认的事件处理函数。比如我们重写了场景的键盘按下事件处理函数,那么我们就在该函数的最后写上QGraphicsView::keyPressEvent(event);

  除了像上面那样通过直接重写QGraphicsView的虚函数的方式来实现事件的传递之外,还可以通过事件过滤器eventFilter的方式来实现:

MyGraphicsView::MyGraphicsView(QWidget *parent)
	:QGraphicsView(parent)
    this->viewport()->installEventFilter(this);	
bool MyGraphicsView::eventFilter(QObject *obj, QEvent *event)
    return false;			//返回false表示不过滤

🌺 场景类 — QGraphicsScene

  场景分为3层:分别是背景层、图形项层、前景层。我们绘制的图形item都是放在图形项层的:

  QGraphicsScene类提供了处理多个图形项(QGraphicsItem)所需的几乎所有方法。其主要作用是作为一个容器放置图元Item,本身是不可见的,必须关联到至少一个QGraphicsView。这两者的关系就画布和画板的关系,View是画板,负责显示;Scene是画布,负责存储图形数据。所以从这个角度出发,我们可以这样认为,一个Scene可以关联到多个View,就好比一份画布贴到不同的画板上来被外界所看到一样。

 
推荐文章