其他分享
首页 > 其他分享> > Qt 2D绘图之五:图形视图框架的结构和坐标系统

Qt 2D绘图之五:图形视图框架的结构和坐标系统

作者:互联网

一、图形视图框架的结构

在前面讲的基本绘图中,我们可以自己绘制各种图形,并且控制它们。但是,如果需要同时绘制很多个相同或不同的图形,并且要控制它们的移动、检测它们的碰撞和叠加;或者我们想让自己绘制的图形可以拖动位置、进行缩放和旋转等操作。实现这些功能,要是还使用以前的方法,那么会十分困难。解决这些问题,可以使用Qt提供的图形视图框架。

图形视图(Graphics View)框架结构的主要特点如下:


图形视图结构主要包含三部分:

img


1.1 场景(Scene)

场景是图形项QGraphicsItem对象的容器,其主要完成的工作包括:
(1)提供用于管理大量图形项的快速接口;
(2)传播事件给每一个图形项;
(3)管理图形项的状态(如选择和焦点处理);
(4)提供无变换的渲染功能,主要用于打印。


下面是一些QGraphicsScene的常用函数:


下面先来看一个最简单的例子。新建空的Qt项目(Empty qmake Project),项目名称为myscene。然后在这个项目中添加新的C++源文件,命名为main.cpp。添加完成后首先在myscene.pro文件中添加一行代码:

QT += widgets

然后将main.cpp的内容更改如下。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QDebug>

int main(int argc,char* argv[ ])
{
    QApplication app(argc,argv);

    //新建场景
    QGraphicsScene scene;
    //创建矩形图形项
    QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 100, 100);
    //将图形项添加到场景中
    scene.addItem(item);
    //输出(50, 50)点处的图形项
    qDebug() << scene.itemAt(50,50,QTransform());

    return app.exec();
}

这里先创建了一个场景,然后创建了一个矩形图形项,并且将该图形项添加到了场景中。然后使用itemAt()函数来返回指定坐标处最顶层的图形项,这里返回的就是刚才添加的矩形图形项。现在可以运行程序,不过因为还没有设置视图,所以不会出现任何图形界面,这时可以在应用程序输出栏中看到输出的项目的信息如下:

QGraphicsItem(0x161015c8, pos=0,0)


1.2 视图(View)

QGraphicsView提供了视图部件,它用来使场景中的内容可视化。可以连接多个视图到同一个场景来为相同的数据集提供多个视口。

下面是一些QGraphicsView:的常用函数:


在前面的程序中先添加头文件# include <QGraphicsView>,然后在主函数中 “return app. exec();”一行代码前继续添加如下代码:

//为场景创建视图
QGraphicsView view(&scene);
//设置场景的前景色
view.setForegroundBrush(QColor(255, 255, 0, 100));
//设置场景的背景图片
view.setBackgroundBrush(QPixmap("../myScene/background.png"));
view.resize(400, 300);
view.show();

这里新建了视图部件,并指定了要可视化的场景。然后为该视图设置了场景前景色和背景图片。一个场景分为3层:图形项层(ItemLayer)、前景层(ForegroundLayer)和背景层(BackgroundLayer)。场景的绘制总是从背景层开始,然后是图形项层,最后是前景层。前景层和背景层都可以使用QBrush进行填充,比如使用渐变和贴图等。这里的前景色设置为半透明的黄色,当然也可以设置为其他的填充。这里要提示一下,其实使用好前景色可以实现很多特殊的效果,比如使用半透明的黑色便可以实现夜幕降临的效果。

代码中使用了 QGraphicsView类中的函数来设置场景中的背景和前景,其实也可以使用QGraphicsScene中的同名函数来实现,不过它们的效果并不完全 一样。如果使用QGraphicsScene对象设置了场景背景或者前景,那么对所有关联了该场景的视图都有效,而QGraphicsView对象设置的场景的背景或者前景,只对它本身对应的视图有效。

运行程序,效果如下图所示。可以看到矩形图形项和背景图片都是在视图中间部分进行绘制的,这个问题会在后面的坐标系统部分详细讲解。


1.3 图形项

QGraphicsItem是场景中图形项的基类。图形视图框架为典型的形状提供了标准的图形项,比如矩形(QGraphicsRectlem)、椭圆(QGraphicsEllipseltem)和文本项(QGraphicsTextltem)。不过,只有编写自定义的图形项时才能发挥QGraphicsItem的强大功能。

QGraphicsItem主要支持以下功能:

除此之外,图形项还可以存储自定义的数据,可以使用setData()进行数据存储,然后使用data()获取其中的数据。下面自定义图形项。


在前面的程序中添加新文件,模板选择C+ +类,类名为Myltem,基类为 QGraphicsItem,类型信息选择“无”。添加完成后,在myitem.h文件中添加两个函数的声明:

#ifndef MYITEM_H
#define MYITEM_H

#include <QGraphicsItem>

class MyItem : public QGraphicsItem
{
public:
    MyItem();

    //返回要绘制图形项的矩形区域
    QRectF boundingRect() const;

    //用来执行实际的绘图操作
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);
};

#endif // MYITEM_H

再到myitem.cpp文件中添加头文件# include <QPainter>,然后定义添加的两个函数:

#include "myitem.h"
#include <QPainter>

MyItem::MyItem()
{
}

//返回要绘制图形项的矩形区域
QRectF MyItem::boundingRect() const
{
    qreal penWidth = 1;
    return QRectF(0 - penWidth / 2, 0 - penWidth / 2,
                  20 + penWidth, 20 + penWidth);
}

//用来执行实际的绘图操作
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                   QWidget *widget)
{
    Q_UNUSED(option); //标明该参数没有使用
    Q_UNUSED(widget);

    painter->setBrush(Qt::red);
    painter->drawRect(0, 0, 20, 20);
}

要实现自定义的图形项,那么首先要创建一个QGraphicsItem的子类,然后重新实现它的两个纯虚公共函数:boimdingRect()和paint(),前者用来返回要绘制图形项的矩形区域,后者用来执行实际的绘图操作。其中,boimdingRect()函数将图形项的外部边界定义为一个矩形,所有的绘图操作都必须限制在图形项的边界矩形之中。而且,QGraphicsView要使用这个矩形来剔除那些不可见的图形项,另外QGraphicsItem的碰撞检测机制也需要使用到这个边界矩形。


下面到main.cpp中添加#include "myitem.h",然后将以前那个图形项的定义语句改为:

MyItem *item = new MyItem;

这时运行程序,效果如下图所示。可以看到,自定义的红色小方块出现在了视图的正中间,背景图片的位置也有所变化,这些问题都会在后面的坐标系统中讲到。如果只想添加简单的图形项,那么也可以直接使用图形视图框架提供的8种标准图形项。


下面是图形视图框架的事件传递示意图:

img


二、图形视图框架的坐标系统

图形视图框架是基于笛卡尔坐标系统的,一个图形项在场景中的位置和几何形状由x坐标和y坐标来表示。当使用一个没有变换的视图来观察场景时,场景中的一个单元代表屏幕上的一个像素。在图形视图框架中有3个有效的坐标系统:图形项坐标、场景坐标和视图坐标。为了方便应用,图形视图框架中提供了一些便捷函数来完成3个坐标系统之间的映射。当进行绘图时,场景坐标对应QPainter的逻辑坐标,视图坐标对应设备坐标。


2.1 场景坐标

场景坐标是所有图形项的基础坐标系统。场景坐标系统描述了每一个顶层图形項的位置,也形成了所有从视图传到场景上的事件的基础。场景坐标的原点在场景的中心,x轴正方向向右,y轴正方向向下。

每一个在场景中的图形项除了拥有一个图形項的本地坐标和边界矩形外,还都拥有一个场景坐标(QGraphicsItem: :scenePos())和一个场景中的边界矩形(QGraphicsItem::sceneBoundingRect())。场景坐标用来描述图形项在场景坐标系统中的位置,而图形项的场景边界矩形用于QGraphicsScene判断场景中的哪些区域进行了更改。

QGraphicsScene类的坐标系以中心为原点(0,0),如下图所示。

img


2.2 视图坐标

视图的坐标就是窗口部件的坐标。视图坐标的每一个单位对应一个像素。QGraphicsView视图的左上角是(0,0),x轴正方向向右,y轴正方向向下。

所有的鼠标事件最开始都是使用视图坐标。 QGraphicsView类继承自QWidget类,因此它与其他的QWidget类一样,以窗口的左上角作为自己坐标系的原点,如图所示。

img


2.3 图形项坐标

图形项使用自己的本地坐标,这个坐标系统通常以图形项中心为原点,这也是所有变换的原点。图形项坐标方向是x轴正方向向右,y轴正方向向下。创建图形项后,只需注意图形项坐标就可以了,QGraphicsScene和QGraphicsView会完成所有的变换。

QGraphicsItem类的坐标系,若在调用QGraphicsItem类的paint()函数重绘图元时,则以此坐标系为基准,如下图所示。

img


2.4 坐标映射

当处理场景中的图形项时,将坐标或者一个任意的形状从场景映射到图形项、或者从一个图形项映射到另一个图形项、或者从视图映射到场景,这些坐标变换都是非常有用的。例如:

不仅可以在视图、场景和图形项之间使用坐标映射,还可以在子图形项和父图形项或者图形项和图形项之间进行坐标映射 。图形视图框架提供的所有映射函数如下表所列,所有的映射函数都可以映射点、矩形、多边形和路径。

映 射 函 数 转 换 类 型
QGraphicsView::mapToScene() 视图到场景
QGraphicsView::mapFromScene() 场景到视图
QGraphicsItem:: mapFromScene() 场景到图元
QGraphicsItem:: mapToScene() 图元到场景
QGraphicsItem:: mapToParent() 子图元到父图元
QGraphicsItem:: mapFromParent() 父图元到子图元
QGraphicsItem:: mapToItem() 本图元到其他图元
QGraphicsItem:: mapFromItem() 其他图元到本图元


参考:

74 QT图形视图框架(Graphics View)

标签:QGraphicsView,场景,Qt,视图,2D,坐标,图形,QGraphicsItem
来源: https://www.cnblogs.com/linuxAndMcu/p/11064489.html