• 为了保证你在浏览本网站时有着更好的体验,建议使用类似Chrome、Firefox之类的浏览器~~
    • 如果你喜欢本站的内容何不Ctrl+D收藏一下呢,与大家一起分享各种编程知识~
    • 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此,生命在于折腾,何不年轻时多折腾一下

QT图形视图框架

Qt admin 3年前 (2016-04-10) 1868次浏览 0个评论 扫描二维码

Graphics View 提供了一个界面,它既可以管理大数量的定制 2D graphical items,又可与它们交互,有一个 view widget 可以把这些项绘制出来,并支持旋转与缩放。这个柜架也包含一个事件传播结构,对于在 scene 中的这些 items,它具有双精度的交互能力。Items 能处理键盘事件,鼠标的按,移动、释放、双击事件,也可以跟踪鼠标移动。Graphics View 使用 BSP 树来提供对 item 的快速查找,使用这种技术,它可以实时地绘制大规模场景,甚至以百万 items 计。Graphics View 在 Qt 4.2 中被引用,它替代了它的前辈 QCanvas。

Graphics View 的体系结构

Graphics View 提供的是一种类似于 Qt model-view 的编程。多个 views 可以监视同一个场景,而场景包含多个具有多种几何外形的 items。

 

场景

QGraphicsScene 表示 Graphics View 中的场景,它有以下职责: 为管理大量的 items 提供一个快速的接口。 传播事件到每个 item。 管理 item 的状态,例如选择,焦点处理。 提供未经变换的渲染功能,主要用于打印。 场景作为 QGraphicsItem 对象的容器。通过调用 QgraphicsScene::addItem()把这些 Items 加入到场景中。可以使用众多的查找函数来获取特定的 items。QGraphicsScene:items()与它的许多重载函数可获取那些与点、矩形,多边形,向量路径等相交或是有包含有关系的 items。QGraphicsScene::itemAt()返回特定上最顶端的 item。所有的 item 查找函数都以出栈序列返回(也就是说,第一个返回的是最顶端的,最后一个返回的是最底端的)。 QGraphicsScene  scene; QGraphicsRectItem  *rect=scene.addRect(QRectF(0,0,100,100)); QGraphicsItem  *item=scene.itemAt(50,50); //item==rect; QGraphicsScene 的事件传播结构会把场景事件投递到 items,也管理多个 items 之间的传递。假如场景收到了鼠标在某个位置 press 事件,场景会把这个事件投递给处在那个位置的 item。QGraphicsScene 也管理某种 item 状态,像选择与焦点。你可以通过调用 QGraphicsScene::setSelectionArea()来选择 items,它需要提供一个任意的形状为参数。这个函数也作为在 QGraphicsView 实现橡皮筋选择功能的一个基础。为得到这些已经被选择的 items,调用 QGraphicsScene::selectedItem()。另一个状态处理是是否一个 item 拥有键盘输入焦点。你可以调用 QGraphicsScene::setFocusItem()或 QGraphics::setFocus()来设定焦点,也可用 QGraphicsScene::focusItem()来得到当前拥有焦点的那个 item。最后,QGraphicsScene 允许你通过调用 QGraphicsScene::render()函数把部分场景送到绘图设备进行渲染。

视图

QGraphicsView 提供了视图部件,它可视化场景中的内容。你可以联结多个视图到同一个场景,对这个相同的数据集提供几个视口。视口部件是一个滚动区域,它提供了滚动条以对大场景进行浏览。为了使用 OpenGL,你应该调用 QGraphicsView::setViewport()来把一个 QGLWidget 设为视口。视图从键盘,鼠标接收输入事件,在发送这些事件到场景之前,会对这些事件进行适当的翻译(把事件坐标转换成对应的场景坐标)。

利用转换矩阵,QGraphicsView::matrix(),视图可变换场景的坐标系统。这允许高级的导航特性,如缩放,旋转。为了方便,QGraphicsView 也提供了在视图与场景之间进行坐标转换的函数:QGraphicsView::mapToScene(),QGraphicsView::mapForScene()。

The Item QGraphicsItem 是场景中图形 items 的基类。Graphics View 提供了一些标准的、用于典型形状的 items。像矩形(QGraphicsRectItem),椭圆(QGraphicsEllipseItem),文本(QGraphicsTextItem),当你写定制的 item 时,那些最有用的一些 QGraphicsItem 特性也是有效的。除此这外,QGraphicsItem 支持以下特性: *鼠标按、移动、释放、双击事件,鼠标悬停事件,滚轮事件,弹出菜单事件。 *键盘输入焦点,键盘事件。 *拖拽 *组,包括父子关系,使用 QGraphicsItemGroup *碰撞检测 Items 如同 QGraphicsView 一样,位于本地坐标系,它也为 item 与场景之间,item 与 item 之间的坐标转换提供许多工具函数。而且,也像 QGraphicsView 一样,它使用矩阵来变换它的坐标系统:QGraphicsItem::matrix()。它对旋转与缩放单个的 Item 比较有用。 Items 可以包含别的 items(孩子)。父 items 的转换被它的子孙所继承。然而,它的所有函数(也就是,QGraphicsItem::contains(),QGraphicsItem::boundingRect(),QGraphicsItem::collidesWith()),不会积累这些转换,依然在本地坐标下工作。 QGraphicsItem 通过 QGraphicsItem::shape(),QGraphicsItem::collideWith())来支持碰撞检测。这两个都是虚函数。从 shape()返回你的 item 的形状(以本地坐标 QPainterPath 表示),QGraphicsItem 会为你处理所有的碰撞检测。假如你想提供自己的碰撞检测,你应该重新实现 QGraphicsItem::collideWith()。

Graphics View 坐标系统

Graphics View 基于笛卡尔坐标系。item 在场景中的位置与几何形状通过 x,y 坐标表示。当使用未经变形的视图来观察场景时,场景中的一个单位等于屏幕上的一个像素。在 Graphics View 中有三个有效的坐标系统:Item 坐标系,场景坐标系,视图坐标系。为了简化你的实现,Graphics View 提供了方便的函数,允许三个坐标系之间相互映射。 当渲染时,Graphics View 的场景坐标对应于 QPainter 的逻辑坐标,视图坐标与设备坐标相同。

Item 坐标

Items 位于它们自己的坐标系中。它的坐标都以点(0,0)为中心点,这也是所有变换的中心点。在 item 坐标系中的几何图元,经常被称为 item 点,item 线,item 矩形。当创建一个定制的 item,item 坐标是所需要考虑的。QGraphicsScene 与 QGraphicsView 可以为你执行所有转换,这使得实现定制的 item 变得容易。举例来说,假如你收到鼠标按或是拖进入事件,事件的位置以 item 坐标的形式给出。QGraphicsItem::contain()虚函数,当某个点的位置在你的 item 范围内时,返回 true,否则返回 false。这个点参数使用 item 坐标,相似地,item 的包围矩形与形状也使用 item 坐标。 Item 位置指的是 item 的中心点在它父亲的坐标系中的坐标。以这种思想来看,场景指的就是那些祖先最少的 item 的“父亲”。最上级的 Item 位置就是在场景中的位置。 子坐标与父坐标之间是相关的,假如孩子未经变换,子坐标与父坐标之间的差值等于在父坐标系下,父 item 与子 item 之间的距离。例如,假如一个未经变换的子 item 位置与其父 item 的中心重合,那么这两个 item 的坐标系统完全相同。如果孩子的位置是(10,0),那么孩子坐标系中的(0,10)点,对应于父坐标系中的(10,10)点。 因为 item 的位置与变换是相对于父 item 的,子 item 的坐标不会被父亲的变换影响,尽管父 item 的变换隐含地对子 item 做了变换。在上面的例子中,即使父 item 旋转,缩放,子 item 的(0,10)点依然对应于父 item 的(10,10)点。然而,相对于场景来讲,子 item 会遵循父 item 的变换。假如父 item 被缩放(2X,2X),子 item 的位置在场景中的坐标是(20,0),它的(10,0)点则与场景中的(40,0)对应 。除了 QGraphicsItem::pos(),QGraphicsItem 的函数以 Item 坐标工作,如一个 item’s 包围矩形总是以 item 坐标的形式给出。

场景坐标

场景坐标系统描述了每个最顶级 item 的位置,也是从视图向场景投递场景事件的基础。场景中的每个 item 有场景位置与包围矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect()), 另外,它有自己本地 item 位置与包围矩形。场景位置描述了 item 在场景坐标下的位置,它的场景包围矩形则用于 QGraphicsScene 决定场景中哪块区域发生了变化。场景中的变化通过 QGraphicsScene::changed()信号来通知,它的参数是场景矩形列表。

视图坐标

视图坐标是 widget 的坐标,视图坐标中每个单位对应一个像素。这种坐标的特殊之处在于它是相对于 widget 或是视口的,不会被所观察的场景所影响。QGraphicsView 的视口的左上角总是(0,0),右下角总是(视口宽,视口高)。所有的鼠标事件与拖拽事件,最初以视图坐标表示,就应该把这些坐标映射到场景坐标以便与 item 交互。

坐标映射

经常,处理场景中 item 时,在场景与 item 之间,item 与 item 之间,视图与场景之间进行坐标映射,形状映射是非常有用的。举例来讲,当你在 QGraphicsView 的视口中点击鼠标时,你应该通过调用 QGraphicsView::mapToScence()与 QGraphicsScene::itemAt()来获知光标下是场景中的哪个 item。假如你想获知一个 item 位于视口中的什么位置,你应该先在 item 上调用 QGraphicsItem::mapToScene(),然后调用 QGraphicsView::mapFromScene()。最后,假如你想在一个视图椭圆中有哪些 items,你应该把 QPainterPath 传递到 mapToScene(),然后再把映射后的路径传递到 QGraphicsScene::items()。 你可以调用 QGraphicsItem::mapToScene()与 QGraphicsItem::mapFromScene()在 item 与场景之间进行坐标与形状的映射。也可以在 item 与其父 item 之间通过 QGraphicsItem::mapToParent()与 QGraphicsItem::mapFromItem()进行映射。所有映射函数可以包括点,矩形,多边形,路径。视图与场景之间的映射也与此类似。对于从视图与 item 之间的映射,你应该首先映射到场景,然后再从场景向 item 进行映射。

关键特性

缩放与旋转

QGraphicsView 通过 QGraphicsView::setMatrix()支持同 QPainter 一样的仿射变换,通过对一个视图应用变换,你可以很容易地支持普通的导航特性如缩放与旋转。下面是一个例子: 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);} }; 这些槽应与 QToolButtons 联接,并使 autoRepeat 有效。当对视图变换时,QGraphicsView 会对视图中心进行校正。

拖拽

因为 QGraphicsView 继承自 QWidget,它也提供了像 QWidget 那样的拖拽功能,另处,为了方便,Graphics View 柜架也为场景,每个 item 提供拖拽支持。当视图接收到拖拽事件,它可翻译为 QGraphicsSceneDragDropEvent,再发送到场景。场景接管这个事件,把它发送到光标下接受拖拽的第一个 item。 从一个 item 开始拖拽时,创建一个 QDrag 对象,传递开始拖拽的那个 widget 的指针。Items 可以同时被多个视图观察,但只有一个视图可以开始拖拽。拖拽在多数情况下是从按下鼠标或是移动鼠标开始的,因此,在 mousePressEvent()或 mouseMoveEvent()中,你可以从事件中得到那个原始的 widget 指针,例如: void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { QMimeData *data=new QMimeData; data->setColor(Qt::green); QDrag *drag=new QDrag(event->widget()); drag->setMimeData(data); drag->start(); } 为了在场景中载取拖拽事件,你应重新实现 QGraphicsScene::dragEnterEvent()和在 QGraphicsItem 的子类里任何与你特定场景需要的事件处理器。items 也可以通过调用 QGraphicsItem::setAcceptDrops()获得拖拽支持,为了处理将要进行的拖拽,你需要重新实现 QGraphicsItem::dragEnterEvent(),QGraphicsItem::dragMoveEvent(),QGraphicsItem::dragLeaveEvent()和 QGraphicsItem::dropEvent()。

光标与工具提示

像 QWidget 一样,QGraphicsItem 也支持光标(QgraphicsItem::setCursor)与工具提示(QGraphicsItem::setToolTip())。当光标进入到 item 的区域,光标与工具提示被 QGraphicsView 激活(通过调用 QGraphicsItem::contains()检测)。你也可以直接在视图上设置一个缺省光标(QGraphicsView::setCursor)。

动画

Graphics View 支持几种级别的动画。你可以很容易地通过把 QGraphicsItemAnimatoin 与你的 item 联结来 装配出动画路径,这允许以时间线来控制动画,在所有平台上以稳定的速率运作。QGraphicsItemAnimation 允许你为 item 的位置,旋转,缩放,剪切,变换等产生一条路径,动画可以用 QSlider 来控制,或更为普遍使用的 QTimeLine。 另一种是从 QObject 和 QGraphicsItem 继承,item 可以设置自己的定时器,以在 QObject::timeEvent()中增加步进的方式来控制动画。 第三种,是通过调用 QGraphicsScene::advance()来推进场景,它又依次调用 QGraphicsItem::advance().

OpenGL 渲染

为了使用 OpenGL 渲染,你要设置一个新的 QGLWidget 作为 QGraphicsView 的视口:QGraphicsView::setViewPort()。假如你让 OpenGL 提供反锯齿功能,你需要 OpenGL 采样缓冲支持。 QGraphicsView view(&scene); view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));

Item 组

通过把一个 item 做为另一个 item 的孩子,你可以得到 item 组的大多数本质特性:这些 items 会一起移动,所有变换 会从父到子传递。QGraphicsItem 也可以为它的孩子处理所有的事件,这样就允许以父亲代表它所有的孩子,可以有效地把所有的 items 看作一个整体。 另外,QGraphicsItemGroup 是一个特殊的 item,它既对孩子事件进行处理又有一个接口把 items 从一个组中增加和删除。把一个 item 加到 QGraphicsItemGroup 仍会保留 item 的原始位置与变换,而给一个 item 重新指定父 item 则会让 item 根据其新的父亲重新定位。可以用 QGraphicsScene::createItemGroup()建组。


 

下面是一个蝴蝶动画的实现:

#ifndef BUTTERFLY_H
#define BUTTERFLY_H
#include <QObject>
#include<QGraphicsItem>
#include<QPainter>
#include<QGraphicsScene>
#include<QGraphicsView>
class butterfly : public QObject,public QGraphicsItem
 { Q_OBJECT public: explicit butterfly(QObject *parent = 0); 
void timerEvent(QTimerEvent *); //重写定时事件
QRectF boundingRect()const; //必须设定相应的作用范围
signals: 
public slots:
 protected: 
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);//重写绘图函数
 private:
 bool up; 
QPixmap pixup; 
QPixmap pixdown; 
qreal angle;
 }; 
#endif // BUTTERFLY_H
#include "butterfly.h"
#include<math.h>
const static double PI=3.1415926;
butterfly::butterfly(QObject *parent) : QObject(parent)
{
        up=true;//加载图片的标志位
        pixup.load("up.png");
        pixdown.load("down.png");
        startTimer(100);
}

QRectF butterfly::boundingRect()const
{

    qreal adjust=2;
    return QRectF(-pixup.width()/2-adjust,-pixup.height()/2-adjust,pixup.width()+adjust*2,pixup.height()+adjust*2);



}
void butterfly::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)//图片的动画就是通过定时和重写绘图函数来实现
{
    if(up)//up 为 true 时则加载当前图片
    {
        painter->drawPixmap(boundingRect().topLeft(),pixup);
        up=!up;
    }
    else
    {
        painter->drawPixmap(boundingRect().topLeft(),pixdown);
        up=!up;
    }
}
void butterfly::timerEvent(QTimerEvent *)
{
    qreal edgex=scene()->sceneRect().right()+boundingRect().width()/2;//移动右边界
    qreal edgetop=scene()->sceneRect().top()+boundingRect().height()/2;//移动上边界
    qreal edgebottom=scene()->sceneRect().bottom()+boundingRect().height()/2;//移动下边界
    if(pos().x()>edgex)
    {
        setPos(scene()->sceneRect().left(),pos().y());
    }
    if(pos().y()<edgetop)    
    {         
      setPos(pos().x(),scene()->sceneRect().bottom());
    }
    if(pos().x()>edgebottom)
    {
        setPos(pos().x(),scene()->sceneRect().top());
    }
    angle+=(qrand()%10)/20.0;
    qreal dx=fabs(sin(angle*PI))*10.0;
    qreal dy=(qrand()%20)-10.0;
    setPos(mapToParent(dx,dy));//映射到场景坐标之中

}

#include "mainwindow.h"
#include <QApplication>
#include"butterfly.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QGraphicsScene *scene=new QGraphicsScene;
    scene->setSceneRect(QRectF(-200,-200,400,400));//设定场景坐标与大小,场景中心为(0,0),左上角坐标为(-200,-200),长宽为(400,400)
    butterfly *butter=new butterfly;
    butter->setPos(-100,0);//设定位置
    scene->addItem(butter);
    QGraphicsView *view=new QGraphicsView;
    view->setScene(scene);//视图绑定场景
    view->resize(400,400);//视图显示大小
    view->show();
    return a.exec();
}


Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明QT 图形视图框架
喜欢 (0)
admin
关于作者:
互联网行业码农一枚/业余铲屎官/数码影音爱好者/二次元

您必须 登录 才能发表评论!