通过上篇笔记我们知道可以采用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