Qt开发总结(18)——Graphics View绘图架构

通过上篇笔记我们知道可以采用QPainter在绘图设备的paintEvent事件里编写绘图的程序,这种绘图方式效率相对较低,绘制的图形比较简单。Qt为绘制复杂的可交互图形提供了Graphics View绘图架构,是一种基于图形选项(Graphics Item)Model/View框架(可参见本人博客Qt开发总结(14)——Model/View框架),而且其核心试图组件QGraphicsView也作为常用显示控件在Qt开发总结(13)——控件之显示控件中提及。使用Graphics View架构可以绘制复杂的有几万个基本图形元件的图形,并且每个图形元件是可选择、可拖放和可修改的,这类似与绘图软件的功能。

架构组成

Graphics View架构主要由3个部分组成,即场景、视图和图形项。

1.场景

QGraphicsScene提供绘图场景(Scene)。场景是不可见的,是一个抽象的图形项管理容器,可以向场景中添加图形项,获取场景中的某个图形项等,主要功能有:

  • 提供管理大量图形项的快速接口,如QGraphicsScene::addItem(),QGraphicsScene::items()。
  • 将事件传播给每个图形项,例如将鼠标点击事件传递给鼠标位置的图形项。
  • 管理每个图形项的状态,例如选择状态QGraphicsScene::setSelectionArea(),焦点状态QGraphicsItem::setFocus()等。
  • 管理未经变换的渲染功能QGraphicsScene::render(),主要用于打印。

2.视图

QGraphicsView提供绘图的视图(View)控件,用于显示场景中的内容。可以为一个场景设置几个视图,用于对同一数据集提供不同的视口。视图控件是一个带滚动条的控件,当视图小于场景时,自动提供滚动条以使得整个场景在视图中移动。

  QGraphicsScene scene;
  myPopulateScene(&scene);

  QGraphicsView view(&scene);
  view.show();

 

同样,视图也会接收来自鼠标和键盘的事件,以实现一些坐标变化,比如缩放或旋转。

3.图形项

图形项(Graphics Item)就是一些基本的图形元件,图形项的基类是QGraphicsItem。Qt提供了一些基本的图形项,如绘制椭圆的QGraphicsEllipseItem类,绘制矩形的QGraphicsRectItem,绘制文字的QGraphicsTextItem。QGraphicsItem支持以下一些操作:

  • 支持鼠标事件,包括按下、移动、释放、双击,还包括鼠标停留、滚轮、快捷菜单等事件。
  • 支持键盘输入、按键事件。
  • 支持拖放操作。
  • 支持组合,可以是父子项关系组合,也可以通过QGraphicsItemGroup类进行组合。
  • 支持碰撞检测,即是否与其他图形项碰撞。

综上,场景是图形项的容器,可以在场景上绘制很多图形项,每个图形项就是一个对象,这些图形项可以被选择、拖动等。视图是显示场景的一部分区域的视口,一个场景可以多个视图,一个视图显示尝尽的部分或者全部区域,或从不同的角度观察场景。

坐标系统

Graphics View有3个坐标系,分别对应为图形项坐标(图中3),场景坐标(图中2)、视图坐标(图中1)。绘图的时候,场景的坐标系等价于QPainter的逻辑坐标系,一般以场景的中心为原点;视图坐标与设备坐标相同,是物理坐标系,缺省以左上角为原点;图形项坐标是局部逻辑坐标系,一般以图形项元件的中心为原点。

图形项的鼠标事件的坐标是局部坐标表示的,创建和绘制图形项时只需考虑其局部坐标,QGraphicsScene和QGraphicsView会自动进行坐标转换。一个图形项的位置就是其局部坐标原点在父坐标系统中的坐标,对于没有父图形项的图形项,其父对象就是场景。如果父对象进行坐标变换,则子项都会做同样的变换。大部分QGraphicsItem的接口函数都是局部坐标系计算的,只有pos函数返回的是子项在父项(或场景)中的坐标。

视图坐标就是窗体控件上的物理坐标,单位是像素。视图坐标只与widget或视口有关,与场景无关。所有的鼠标事件、拖放事件首先都是由视图坐标定义的,然后用户需要将这些坐标映射为场景坐标,以便和图形项交互。

场景坐标是所有图形项的基础坐标,场景坐标描述了每个顶层图形项的位置,用QGraphicsItem::scenePos()给出。

在场景中操作图形项时,进行场景到图形项、图形项到图形项,或者视图到场景之间的坐标变换是比较有用的,即坐标映射(Mapping)。例如,在QGraphicsView上单机鼠标时,通过函数QGraphicsView::mapToScene()可以将视图坐标映射为场景坐标,然后用QGraphicsScene::itemAt()函数可以获取场景中鼠标光标处的图形项。

相关的类

类名

描述

QGraphicsEffect

The base class for all graphics effects

QGraphicsAnchor

Represents an anchor between two items in a QGraphicsAnchorLayout

QGraphicsAnchorLayout

Layout where one can anchor widgets together in Graphics View

QGraphicsGridLayout

Grid layout for managing widgets in Graphics View

QAbstractGraphicsShapeItem

Common base for all path items

QGraphicsEllipseItem

Ellipse item that you can add to a QGraphicsScene

QGraphicsItem

The base class for all graphical items in a QGraphicsScene

QGraphicsItemGroup

Container that treats a group of items as a single item

QGraphicsLineItem

Line item that you can add to a QGraphicsScene

QGraphicsObject

Base class for all graphics items that require signals, slots and properties

QGraphicsPathItem

Path item that you can add to a QGraphicsScene

QGraphicsPixmapItem

Pixmap item that you can add to a QGraphicsScene

QGraphicsPolygonItem

Polygon item that you can add to a QGraphicsScene

QGraphicsRectItem

Rectangle item that you can add to a QGraphicsScene

QGraphicsSimpleTextItem

Simple text path item that you can add to a QGraphicsScene

QGraphicsTextItem

Text item that you can add to a QGraphicsScene to display formatted text

QGraphicsLayout

The base class for all layouts in Graphics View

QGraphicsLayoutItem

Can be inherited to allow your custom items to be managed by layouts

QGraphicsLinearLayout

Horizontal or vertical layout for managing widgets in Graphics View

QGraphicsProxyWidget

Proxy layer for embedding a QWidget in a QGraphicsScene

QGraphicsScene

Surface for managing a large number of 2D graphical items

QGraphicsSceneContextMenuEvent

Context menu events in the graphics view framework

QGraphicsSceneDragDropEvent

Events for drag and drop in the graphics view framework

QGraphicsSceneEvent

Base class for all graphics view related events

QGraphicsSceneHelpEvent

Events when a tooltip is requested

QGraphicsSceneHoverEvent

Hover events in the graphics view framework

QGraphicsSceneMouseEvent

Mouse events in the graphics view framework

QGraphicsSceneMoveEvent

Events for widget moving in the graphics view framework

QGraphicsSceneResizeEvent

Events for widget resizing in the graphics view framework

QGraphicsSceneWheelEvent

Wheel events in the graphics view framework

QGraphicsTransform

Abstract base class for building advanced transformations on QGraphicsItems

QGraphicsView

Widget for displaying the contents of a QGraphicsScene

QGraphicsWidget

The base class for all widget items in a QGraphicsScene

QStyleOptionGraphicsItem

Used to describe the parameters needed to draw a QGraphicsItem

QGraphicsSvgItem

QGraphicsItem that can be used to render the contents of SVG files

关键特性

1.缩放和旋转

QGraphicsView支持和QPainter同样的坐标转换,底层通过QGraphicsView::setMatrix()实现。一个简单的缩放和旋转信号响应:

 

  class View : public QGraphicsView
  {
  Q_OBJECT
      ...
  public slots:
      void zoomIn() { scale(1.2, 1.2); }
      void zoomOut() { scale(1 / 1.2, 1 / 1.2); }
      void rotateLeft() { rotate(-10); }
      void rotateRight() { rotate(10); }
      ...
  };

 

2.打印

Graphics View通过QGraphicsScene::render() 和 QGraphicsView::render()实现了打印技术。这两个函数都可以实现渲染全部或者部分图形内容,通过QPainter输出到打印设备:

  

  QGraphicsScene scene;
  scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

  QPrinter printer;
  if (QPrintDialog(&printer).exec() == QDialog::Accepted) {
      QPainter painter(&printer);
      painter.setRenderHint(QPainter::Antialiasing);
      scene.render(&painter);
  }

 

注意,这两个函数区别就在于坐标系不同。所以,QGraphicsScene::render()通常用来打印全部信息,比如全本的文本;而QGraphicsView::render()用来打印当前视图中内容,比如截屏。

  

  QGraphicsScene scene;
  scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

  QPixmap pixmap;
  QPainter painter(&pixmap);
  painter.setRenderHint(QPainter::Antialiasing);
  scene.render(&painter);
  painter.end();

  pixmap.save("scene.png");

 

3.拖放

由于QGraphicsView不是由QWidget直接派生,所以它自己已经提供了和QWidget一样的拖放接口,并且适用于场景和每一个图形项。当视图接收到拖放事件后,把它转化为QGraphicsSceneDragDropEvent,场景接管这个事件后,把它发送给鼠标当前聚焦的第一个图形项。

可能图形项同时被多个视图显示,但是只有一个视图可以开始移动这个图形项。由于大部分情况下我们都是用过鼠标点击来实现拖放,所以可以在mousePressEvent() 或者 mouseMoveEvent()接口中指定发起控件:

 

  void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
      QMimeData *data = new QMimeData;
      data->setColor(Qt::green);

      QDrag *drag = new QDrag(event->widget());
      drag->setMimeData(data);
      drag->start();
  }

 

4.光标提示

与QWidget一样,QGraphicsItem同样支持光标(QGraphicsItem::setCursor())和提示(QGraphicsItem::setToolTip())。当鼠标光标进入Item的区域后就会激发QGraphicsView光标提示。

5.动画

Graphics View支持动画装配。使用Animation Framework中的QPropertyAnimation可以实现QGraphicsObject对象的动画生成。另外一种方法是创建一个QObject and QGraphicsItem图形项,通过定时器驱动它。

6. OpenGL渲染

简单的创建一个QGLWidget作为QGraphicsView的视场,通过QGraphicsView::setViewport()实现即可,具体参见Model/View架构详解。

7.图形项组

通过将一个图形项设置为另一个的子项,可以实现一些更方便的方法:这些图形项会一起移动,父项所有的变换都会传递到子项上。另外,QGraphicsItemGroup是很好的图形项组类,方便管理组内的子项。

8.布局

QGraphicsLayout是适用于QGraphicsWidget的布局管理类,接口函数和QLayout几乎一样。具体可以用QGraphicsLinearLayout and QGraphicsGridLayout管理具体布局。

例子

一个简单的动画例子:collidingmice https://download.csdn.net/download/bjtuwayne/12032726

发布了76 篇原创文章 · 获赞 63 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/bjtuwayne/article/details/103553384