文章目录
Graphics View框架结构的主要特点
Graphics View框架结构的主要特点如下:
(1) 在Graphics View框架结构中,系统可以利用Qt绘图系统的反锯齿、OpenGL工具来改善绘图性能。
(2) Graphics View支持事件传播体系结构,可以使图元场景(scene)中的交互能力提高1倍,图元能够处理键盘事件和鼠标事件。其中,鼠标事件包括鼠标被按下、移动、释放和双击,还可以跟踪鼠标的移动。
(3) 在Graphics View框架中,通过二元空间划分树(Binary Space Partitioning,BSP)提供快速的图元查找,这样就能够实时地显示包含上百万个图元的大场景。
Graphics View框架结构的三元素
Graphics View框架结构主要包含了场景类(QGraphicsScene)、视图类(QGraphicsView)和图元类(QGraphicsItem)。场景类提供了一个用于管理位于其中的众多图元容器,视图类用于显示场景中的图元,一个场景可以通过多个视图表现,一个场景包括多个几何图形。它们三者之间的关系可用图表示:
1、场景类:QGraphicsScene类
它是一个用于放置图元的容器,本身是不可见的,必须通过与之相连的视图类来显示及与外界进行交互操作。
场景类主要完成的工作包括提供对它包含的图元的操作接口和传递事件、管理各个图元的状态(如选择和焦点处理)、提供无变换的绘制功能(如打印)等。
事件传播体系结构将场景事件发送给图元,同时也管理图元之间的事件传播。如果场景接收了在某一点的鼠标单击事件,场景会将事件传给这一点的图元。
函数 | 功能描述 |
---|---|
QGraphicsScene::addItem( ) | 可以添加一个图元到场景中 |
QGraphicsScene::items( ) | 和一些重载函数能够返回和点、矩形、多边形或向量路径相交的所有图元。 |
QGraphicsScene::itemAt( ) | 返回指定点的最顶层图元。 |
QGraphicsScene::setSelectedAreas() | 选择图元,选择区域可以是任意的形状,使用QPainterpath表示。 |
QGraphicsScene::selectedItems( ) | 得到当前选择的图元列表。 |
QGraphicsScene::setFocusItem( ) 或QGraphicsScene::setFocus( ) | 设置图元的焦点。 |
QGraphicsScene::focusItem( ) | 函数获得当前具有焦点的图元。 |
QGraphicsScene::reader( ) | 函数在绘图设备上绘制场景。 |
2、视图类: QGraphicsView 类
它提供一个可视的窗口,用于显示场景中的图元。在同一个场景中可以有多个视图,也可以为相同的数据集提供几种不同的视图。视图接收键盘和鼠标的输入事件,并将它们翻译为场景事件(将坐标转换为场景的坐标)。
函数 | 功能描述 |
---|---|
QGraphicsView::setViewport( ) | 将视图设置为QGLWidget。 |
QGraphicsView::matrix( ) | 可以变换场景的坐标,实现场景缩放和旋转。 |
QGraphicsView::mapToScene( ) 和QGraphicsView::mapFromScene( ) | 用于场景的坐标进行转换。 |
3、图元类(QGraphicsItem)
它是场景中各个图元的基类,在它的基础上可以继承出各种图元类,Qt已经预置的包括直线(QGraphicsLineItem)、椭圆(QGraphicsEllipseItem)、文本图元(QGraphicsTextItem)、矩形(QGraphicsRectItem)等。当然,也可以在QGraphicsItem类的基础上实现自定义的图元类,即用户可以继承QGraphicsItem实现符合自己需要的图元。
QGraphicsItem主要支持如下功能:
- 鼠标按下、移动、释放、双击、悬停、滚轮和右键菜单事件。
- 处理键盘输入事件。
- 拖 处理拖曳事件。
- 碰撞检测。
此外,图元有自己的坐标系统,也提供场景和图元。图元还可以通过QGraphicsItem::matrix( )函数来进行自身的交换,可以包含子图元。
Graphics Viewk框架结构的坐标系统
Graphics View 坐标基于笛卡儿坐标系,一个图元的场景具有x坐标和y坐标。当使用没有变换的视图观察场景时,场景中的一个单元对应屏幕上的一个像素。
三个Graphics View 基本类有各自不同的坐标系,场景坐标、视图坐标和图元坐标。Graphics View提供了三个坐标系统之间的转换函数。在绘制图形时,GraphicsView 的场景坐标对应QPainter的逻辑坐标、视图坐标和设备坐标。
1.场景坐标
场景坐标是所有图元的基础坐标系统。场景坐标系统描述了项层的图元,每个图元都有场景坐标和相应的包容框。场景坐标的原点在场景中心,坐标原点是x轴正方向向右,y轴正方向向下。
QGraphicsScene类的坐标系以中心为原点(0,0), 如下图所示。
2.视图坐标
视图坐标是窗口部件的坐标。视图坐标的单位是像素。QGraphicsView视口的左上角是(0,0),X轴正方向向右,Y轴正方向向下。所有的鼠标事件最开始都是使用视图坐标。
QGraphicsView类继承自QWidget类,因此它和其他的QWidget类一样以窗口的左上角作为自己坐标系的原点,如下图所示。
3、图元坐标
图元使用自己的本地坐标,这个坐标系统通常以图元中心为原点,这也是所有变换的原点。图元坐标方向是X轴正方向向右,Y轴正方向向下。创建图元后,只需要注意图元坐标就可以了,QGraphicsScene和QGraphicsView会完成所有的变换。
QGraphicsItem类的坐标系,在调用QGraphicsItem类的paint()函数重画图元时则以此坐标系为基准,如下图所示。
根据需要,Qt提供了这三个坐标系之间的互相转换函数,以及图元与图元之间的转换函数,若需从QGraphicstem坐标系中的某点坐标转换到场景中的坐标,则可调用QGraphicstem 的mapToScene( )函数进行映射。而GraphicsItem的mapToParent()函数则可将QGraphicsItem 坐标
系中的某点坐标映射至它的上一级坐标系中,有可能是场景坐标,也有可能是另一个QGraphicsItem坐标。
Graphics View框架提供了多种变换函数,如下表所示:
映射函数 | 转换类型 |
---|---|
QGraphicsView::mapToScene() | 视图到场景 |
QGraphicsView::mapFromScene() | 场景到视图 |
QGraphicsItem:: mapFromScene() | 场景到图元 |
QGraphicsItem:: mapToScene() | 图元到场景 |
QGraphicsItem:: mapToParent() | 子图元到父图元 |
QGraphicsItem:: mapFromParent() | 父图元到子图元 |
QGraphicsItem:: mapToItem() | 本图元到其他图元 |
QGraphicsItem:: mapFromItem() | 其他图元到本图元 |
图形视图实例
设计界面,一只蝴蝶在屏幕上不停地上下飞舞。
操作步骤:
(1) 新建Qt Widgets Application(这里是Qt5,Qt4选Qt GUI应用),项目名为“Buterly", 基类选择”QMainWindow"(这个随便吧,后面用不到),类名命名默认为“MainWindow",取消“创建界面”复选框的选中状态。单击“下一步”按钮,最后单击“完成”按钮,完成该项目工程的建立。
(2)在“Butterfly" 项目名上单击鼠标右键,在弹出的快捷菜单中选择“添加新文件…”选
项,在弹出的对话框中选择“C++类”选项。单击“Choose…”按钮,在弹出的对话框的“Base
class"下拉列表框中选择基类名“QObject",在“类名"文本框中输入类的名称“Butterfly".
(3) 单击“下一步”按钮,单击“完成”按钮,添加文件“buttrfly.h" 和"butterfly.cpp”。
(4) Butterly 类继承自QObject类、QGraphicstem 类(这个自己添加),在头文件Butterfly.h"中完成的代码如下:
butterfly.h
文件:
#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;
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
bool up;
QPixmap pix_up;
QPixmap pix_down;
qreal angle; //qreal是float的类型定义
};
#endif // BUTTERFLY_H
其中,
(a) void timerEvent(QTimerEvent*)
:定时器实现动画的原理是在定时器的 timerEvent() 中对QGraphicsItem进行重绘。
(b) QRectF boundingRect() const
:为图元限定区域范围,所有继承自QGraphicsItem的自定义图元都必须实现此函数。
© bool up
:用于标志蝴蝶翅膀的位置(位于上或下),以便实现动态效果。
butterfly.cpp
文件
#include "butterfly.h"
#include <math.h>
const static double PI = 3.1416;
Butterfly::Butterfly(QObject *parent) :
QObject(parent)
{
up = true; //给蝴蝶位置的变量赋初值
pix_up.load("./image/up.jpg"); //调用Qpixmap的load()函数加载所用到的图片
pix_down.load("./image/down.jpg"); //图片地址看图片写
startTimer(100); //启动定时器,并设置时间间隔为100毫秒
}
boundingRect( ) 函数为图元限定区域范围。此范围是以图元自身的坐标系为基础设定的。具体实现代码如下:
QRectF Butterfly::boundingRect() const
{
qreal adjust = 10;
return QRectF(-pix_up.width()/2-adjust,-pix_up.height()/2-adjust,\
pix_up.width()+adjust*2,pix_up.height()+adjust*2);
}
在重画函数paint() 中,首先判断当前已显示的图片是pix_up还是pix_down。实现蝴蝶翅膀上下飞舞效果时,若当前显示的是pix_up图片,则重绘pix_down图片,反之亦然。具体实现代码如下:
void Butterfly::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if(up)
{
painter->drawPixmap(boundingRect().topLeft(),pix_up);
up=!up;
}
else
{
painter->drawPixmap(boundingRect().topLeft(),pix_down);
up=!up;
}
}
定时器的timerEvent() 函数实现蝴蝶的飞舞,具体实现代码如下:
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().y()>=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));
}
其中,
(a) setPos(mapToParent(dx,dy)):dx、dy完成蝴蝶随机飞行的路径,且dx、dy是相对于蝴蝶的坐标系而言的,因此应使用mapToParent() 函数映射为场景的坐标。
main.cpp
文件
#include <QtGui/QApplication> //注意:Qt5没有QtGui的,我这里是用Qt4建立的
#include <QGraphicsScene>
#include "butterfly.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene *scene = new QGraphicsScene;
scene->setSceneRect(QRectF(-200,-200,400,400));
Butterfly *butterfly = new Butterfly;
butterfly->setPos(-100,0);
scene->addItem(butterfly);
QGraphicsView *view = new QGraphicsView;
view->setScene(scene);
view->resize(400,400);
view->show();
return a.exec();
}
图片:
运行效果: