-
图形视图框架提供了一个基于图形项的模型视图编程方法,它主要由场景、视图和图形项这三个部分组成,这三个部分分别由QGraphicsScene、QGraphicsView和QGraphicsItem三个类表示。多个视图可以查看同一个场景,场景包含各种各样的几何形状的图形项。
-
在我们平时绘图中,如果我们在一块画布上绘制多个不规则图形并且还要监控每一个图形的行为(比如移动、叠加、碰撞、拖动、缩放、旋转等 *** 作)时,我们就要用到Qt里的图形视图框架,QGraphicScene(场景)可以管理多个图形项QGraphicsItem(比如:QGraphicsRectItem(矩形的图形项,也就是图元)),QGraphicsView(视图)关联场景可以让场景中的所有图形项可视化,其次还提供了缩放和旋转等方法。
分别新建了一个场景,一个矩形图形项和一个视图,并将图形项添加到场景中,将视图与场景关联,最后显示视图就行了,场景是管理图形项的,所有的图形项必须添加到一个场景中,但是场景本身无法可视化,要想看到场景上的内容,必须使用视图,代码如下:
#includeint main(int argc,char* argv[ ]) { QApplication app(argc,argv); QGraphicsScene *scene = new QGraphicsScene; //场景 QGraphicsRectItem *item = new QGraphicsRectItem(100,100,50,50); //矩形项 scene->addItem(item); //项添加到场景 QGraphicsView *view = new QGraphicsView; //视图 view->setScene(scene); //视图关联场景 view->show(); //显示视图 return app.exec(); }
运行如下:
QGraphicsItem类是所有图形项的基类。图形视图框架对一些典型的形状提供了一些标准的图形项。比如上面我们使用的矩形(QGraphicsRectItem)、椭圆(QGraphicsEllipseItem)、文本(QGraphicsTextItem)等多个图形项。但只有继承QGraphicsItem 类实现我们自定义的图形项时,才能显示出这个类的强大。QGraphicsItem支持以下功能:
- 鼠标的按下、移动、释放和双击事件,也支持鼠标悬停、滚轮和右键菜单事件。
- 键盘输入焦点和键盘事件
- 拖放
- 利用QGraphicsItemGroup进行分组
- 碰撞检测
自定义图形项
我们继承QGraphicsItem类实现自定义的图形项,必须先实现两个纯虚函数boundingRect()和paint(),前者用于定义Item的绘制范围,后者用于绘制图形项,首先我们新增MyItem类,代码如下:
#ifndef MYITEM_H #define MYITEM_H #includeclass MyItem : public QGraphicsItem { public: MyItem(); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; }; #endif // MYITEM_H
#include "MyItem.h" #includeMyItem::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::yellow); painter->drawEllipse(0,0,50,50); }
#include "MyItem.h" #include#include int main(int argc,char* argv[ ]) { QApplication app(argc,argv); // 场景 QGraphicsScene *scene = new QGraphicsScene; // 椭圆项 MyItem *item = new MyItem; // QGraphicsRectItem *item = // new QGraphicsRectItem(150,150,50,50); // 项添加到场景 scene->addItem(item); // 视图 QGraphicsView *view = new QGraphicsView; // 视图关联场景 view->setScene(scene); // 显示视图 view->show(); return app.exec(); }
运行结果如下:
碰撞检测
collidingItems函数返回与该项目冲突的所有项目的列表,下面我们用它做个例子:
将MyItem.cpp中paint函数设置画刷的代码改成如下:
//如果与其他图形项碰撞则显示红色,否则显示绿色 painter->setBrush(!collidingItems().isEmpty()?Qt::red : Qt::green);
然后再main.cpp文件中在场景中添加一个直线图形项:
QGraphicsLineItem *line = newQGraphicsLineItem(0,50,300,50); scene->addItem(line);
运行如下,起初方块时绿色的,当我们拖动它与直线接触时会变成红色:
在QGraphicsItem类中有三个碰撞检测函数,分别是collidesWithItem()、collidesWithPath()和collidingItems(),我们使用的是第三个。第一个是该图形项是否与指定的图形项碰撞,第二个是该图形项是否与指定的路径碰撞,第三个是返回所有与该图形项碰撞的图形项的列表。在帮助中我们可以查看它们的函数原型和介绍,这里要说明的是,这三个函数都有一个共同的参数Qt::ItemSelectionMode,它指明了怎样去检测碰撞。我们在帮助中进行查看,可以发现它是一个枚举类型,一共有四个值,分别是:
- Qt::ContainsItemShape :只有图形项的shape被完全包含时;
- Qt::IntersectsItemShape :当图形项的shape被完全包含时,或者图形项与其边界相交;
- Qt::ContainsItemBoundingRect : 只有图形项的bounding rectangle被完全包含时;
- Qt::IntersectsItemBoundingRect :图形项的bounding
rectangle被完全包含时,或者图形项与其边界相交。
移动
对于图形项的移动,我们有很多办法实现,也可以在很多层面上对其进行控制,比如说在View上控制或者在Scene上控制。但是对于大量的不同类型的图形项,怎样能一起控制呢?在图形视图框架中提供了advance()槽函数,这个函数在QGraphicsScene和QGraphicsItem中都有,在图形项类中它的原型是advance(int phase)。它的实现流程是,我们利用QGraphicsScene类的对象调用QGraphicsScene的advance()函数,这时就会执行两次该场景中所有图形项的advance(int phase)函数,第一次phase为0,告诉所有图形项即将要移动;第二次phase的值为1,这时执行移动。下面我们看一个例子。
我们在myitem.h中的protected中声明函数:
void advance(int phase) override;
然后在myitem.cpp中对其进行定义:
void MyItem::advance(int phase) { if(!phase) return; //如果phase为0,表示将开始移动则返回 moveBy(0,10); }
在到main.cpp中添加以下定时器代码:
QTimer timer; connect(&timer, &QTimer::timeout, scene, &QGraphicsScene::advance); timer.start(1000);
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)