飞舞的蝴蝶(GraphicsView框架)
一、简介
GraphicsView框架结构主要包含三个主要的类QGraphicsScene(容器)、QGraphicsView(视图)、QGraphicsItem(图形项)。QGraphicsScene本身不可见必须通过与之相连的QGraphicsView视口类来显示及与外界进行互操作,主要提供项目的操作接口、传递事件和管理各个项目状态;QGraphicsView提供一个可视的窗口,用于显示场景中的项目,一个场景中可以有多个视口;QGraphicsItem是场景中各个项目的基础类。
二、关系图
(1)三者间的关系
(2)坐标系统
QGraphicsScene坐标系是以中心为原点(0,0),QGraphicsView继承自QWidget以窗口的左上角作为自己坐标系的原点,而QGraphicsItem则有自己的坐标系其paint()函数重画时以此坐标系为基准。
(3)坐标映射
三个坐标系之间的相互转换函数及图形项与图形项之间的转换函数。
三、详解
1、运行图
2、解析
(1)利用定时器实现QGraphicsItem不停上下飞舞的蝴蝶的动画效果
- #include <QGraphicsItem>
- #include <QObject>
- class Butterfly : public QObject, public QGraphicsItem
- {
- Q_OBJECT
- public:
- Butterfly();
- 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;
- };
- static const double PI = 3.14159265358979323846264338327950288419717;
- Butterfly::Butterfly()
- {
- setFlag(QGraphicsItem::ItemIsMovable);
- pix_up.load(":/images/butterfly1.PNG");
- pix_down.load(":/images/butterfly2.PNG");
- up = true;
- startTimer(100);
- }
- QRectF Butterfly::boundingRect() const
- {
- qreal adjust = 8;
- return QRectF(-pix_up.width()/2-adjust,-pix_up.height()/2-adjust,
- pix_up.width()+adjust*2,pix_up.height()+2*adjust);
- }
- 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;
- }
- // painter->setPen(Qt::NoPen);
- // painter->setBrush(Qt::darkGray);
- // painter->drawEllipse(-7,-7,40,40);
- // painter->setPen(QPen(Qt::black,0));
- // painter->setBrush(flash ? (Qt::red):(Qt::yellow));
- // painter->drawEllipse(-10,-10,40,40);
- }
- void Butterfly::timerEvent(QTimerEvent *)
- {
- // edge controll
- qreal edgex = scene()->sceneRect().right()+boundingRect().width()/2;
- qreal edgetop = scene()->sceneRect().top()+boundingRect().height()/2;
- qreal edgebottom = scene()->sceneRect().bottom()+boundingRect().height()/2;
- //qDebug() << scene()->itemsBoundingRect();
- 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));
- update();
- }
分析:在定时器的timeEvent()中对QGraphicsItem进行重画,重画paint()函数中飞舞的蝴蝶是由两幅图片组成,蝴蝶的移动边界做一个限定,dx和dy是相对于蝴蝶的坐标系而言的,调用setPos函数时使用mapToParent()函数映射成场景的坐标。setFlag(QGraphicsItem::ItemIsMovable);使蝴蝶可以通过鼠标移动。
(2)来回移动的星星
- class StarItem : public QGraphicsItem
- {
- public:
- StarItem();
- QRectF boundingRect() const;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
- private:
- QPixmap pix;
- };
- StarItem::StarItem()
- {
- pix.load(":/images/star.png");
- }
- QRectF StarItem::boundingRect() const
- {
- return QRectF(-pix.width()/2,-pix.height()/2,pix.width(),pix.height());
- }
- void
- StarItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
- {
- painter->drawPixmap(boundingRect().topLeft(),pix);
- }
- {
- StarItem *star = new StarItem;
- QGraphicsItemAnimation *anim = new QGraphicsItemAnimation;
- anim->setItem(star);
- QTimeLine *timeLine = new QTimeLine(4000);
- timeLine->setCurveShape(QTimeLine::SineCurve);
- timeLine->setLoopCount(0);
- anim->setTimeLine(timeLine);
- int y = (qrand()%400) - 200;
- for (int i=0; i<400; i++)
- {
- anim->setPosAt(i/400.0, QPointF(i-200,y));
- }
- timeLine->start();
- scene->addItem(star);
- }
分析:利用QGraphicsItemAnimation类和QTimeLine实现项目的动画效果(也可使用定时器QTimer结合重绘函数来实现)。
(3)简单正方形
- {
- QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0,0,60,60));
- QPen pen;
- pen.setWidth(3);
- pen.setColor(QColor(qrand()%256,qrand()%256,qrand()%256));
- item->setPen(pen);
- item->setBrush(QColor(qrand()%256,qrand()%256,qrand()%256));
- item->setFlag(QGraphicsItem::ItemIsMovable);
- scene->addItem(item);
- //item->setPos((qrand()%int(scene->sceneRect().width()))-200,(qrand()%int(scene->sceneRect().height()))-200);
- item->setPos(-200, -200);
- }
分析:利用继承QGraphicsItem的类QGraphicsRectItem添加一个正方形的图形项。
注意编译时会出现如下警告:
Warning::Class Butterfly imlements the interface QGraphicsItem but does not list it in Q_INTERFACES ;qobject_cast to QGraphicsItem will not work
原因:
QGraphicsItem的paint方法是在item被重绘时调用的,除了调用这个接口函数外,他还需要调用另外几个接口函数,你是不是在类中没有写呀?
需要添加的函数列表: QRectF boundingRect() const; QPainterPath shape() const; virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent * event); void hoverLeaveEvent(QGraphicsSceneHoverEvent * event);
一、简介
GraphicsView支持事件传播体系结构,可以使图元在场景scene中得到提高了已被的精确交互能力。图形视图框架中的事件都是首先由视图进行接收,然后传递给场景,再由场景给相应的图形项。
对于键盘鼠标事件,scene会传递给获得焦点的图形项。如果场景没有获得焦点,那键盘事件会丢弃;如果调用场景setFocus()或者场景中的一个图形项获得了焦点,那么场景会自动获得焦点;如果场景丢失了焦点(如调用clearFocus())而其中一个图形项获得焦点那场景会保存这个图形项的焦点信息。
图形项默认无法接收悬停事件,可以使用QGraphicsItem的setAcceptHoverEvents()函数使图形项可以接收悬停事件。
二、运行图
(1)五个图形项的运行图如下图所示。
三、详解
1、QGraphicsScene
- #ifndef MYSCENE_H
- #define MYSCENE_H
- #include <QGraphicsScene>
- #include <QGraphicsSceneMouseEvent>
- #include <QPaintEvent>
- #include <QKeyEvent>
- class MyScene : public QGraphicsScene
- {
- Q_OBJECT
- public:
- explicit MyScene(QObject *parent = 0);
- protected:
- void keyPressEvent(QKeyEvent *event);
- void mousePressEvent(QGraphicsSceneMouseEvent *event);
- signals:
- public slots:
- };
- #endif // MYSCENE_H
- #include "myscene.h"
- MyScene::MyScene(QObject *parent) :
- QGraphicsScene(parent)
- {
- clearFocus();
- }
- void MyScene::keyPressEvent(QKeyEvent *event)
- {
- qDebug("*********MyScene::keyPressEvent***************");
- return QGraphicsScene::keyPressEvent(event);
- }
- void MyScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
- {
- qDebug("*********MyScene::mousePressEvent***************");
- QGraphicsScene::mousePressEvent(event);
- }
2、QGraphicsView
- #ifndef MYVIEW_H
- #define MYVIEW_H
- #include <QGraphicsView>
- class MyView : public QGraphicsView
- {
- Q_OBJECT
- public:
- explicit MyView(QWidget *parent = 0);
- protected:
- void keyPressEvent(QKeyEvent *event);
- void mousePressEvent(QMouseEvent *event);
- void paintEvent(QPaintEvent * event);
- void mouseMoveEvent(QMouseEvent *event);
- signals:
- public slots:
- };
- #endif // MYVIEW_H
- #include "myview.h"
- #include <QKeyEvent>
- MyView::MyView(QWidget *parent) :
- QGraphicsView(parent)
- {
- }
- void MyView::keyPressEvent(QKeyEvent *event)
- {
- qDebug("*********MyView::keyPressEvent***************");
- switch (event->key())
- {
- case Qt::Key_Left :
- scale(1.2, 1.2);
- break;
- case Qt::Key_Right :
- scale(1 / 1.2, 1 / 1.2);
- break;
- case Qt::Key_Up :
- rotate(30);
- break;
- }
- QGraphicsView::keyPressEvent(event);
- }
- void MyView::mousePressEvent(QMouseEvent *event)
- {
- qDebug("************MyView::mousePressEvent*****************");
- QGraphicsView::mousePressEvent(event);
- }
- void MyView::paintEvent(QPaintEvent *event)
- {
- qDebug("************MyView::paintEvent*****************");
- QGraphicsView::paintEvent(event);
- }
- void MyView::mouseMoveEvent(QMouseEvent *event)
- {
- //qDebug("************MyView::mouseMoveEvent*****************");
- QGraphicsView::mouseMoveEvent(event);
- }
3、QGraphicsItem
- #ifndef MYITEM_H
- #define MYITEM_H
- #include <QGraphicsItem>
- #include <QGraphicsSceneEvent>
- class MyItem : public QGraphicsItem
- {
- public:
- MyItem();
- QRectF boundingRect() const;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
- QWidget *widget);
- void setColor(const QColor &color) { brushColor = color; }
- protected:
- void keyPressEvent(QKeyEvent *event);
- void mousePressEvent(QGraphicsSceneMouseEvent *event);
- void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
- void hoverLeaveEvent (QGraphicsSceneHoverEvent * event);
- void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
- void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
- private:
- QColor brushColor;
- };
- #endif // MYITEM_H
- #include "myitem.h"
- #include <QPainter>
- #include <QCursor>
- #include <QKeyEvent>
- #include <QGraphicsSceneHoverEvent>
- #include <QGraphicsSceneContextMenuEvent>
- #include <QMenu>
- MyItem::MyItem()
- {
- brushColor = Qt::red;
- setFlag(QGraphicsItem::ItemIsFocusable);
- setFlag(QGraphicsItem::ItemIsMovable);
- //setAcceptHoverEvents(true);
- }
- QRectF MyItem::boundingRect() const
- {
- qreal adjust = 0.5;
- return QRectF(-10 - adjust, -10 - adjust,
- 20 + adjust, 20 + adjust);
- }
- void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
- QWidget *widget)
- { qDebug("************MyItem::paint*****************");
- if(hasFocus()) {
- painter->setPen(QPen(QColor(255,255,255,200)));
- } else {
- painter->setPen(QPen(QColor(100,100,100,100)));
- }
- painter->setBrush(brushColor);
- painter->drawRect(-10, -10, 20, 20);
- }
- // 鼠标按下事件处理函数,设置被点击的图形项获得焦点,并改变光标外观
- void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
- {
- qDebug("************MyItem::mousePressEvent*****************");
- setFocus();
- setCursor(Qt::ClosedHandCursor);
- }
- // 键盘按下事件处理函数,判断是否是向下方向键,如果是,则向下移动图形项
- void MyItem::keyPressEvent(QKeyEvent *event)
- {
- qDebug("************MyItem::keyPressEvent*****************");
- if(event->key() == Qt::Key_Down)
- moveBy(0, 10);
- }
- // 悬停事件处理函数,设置光标外观和提示
- void MyItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
- {
- qDebug("************MyItem::hoverEnterEvent*****************");
- setCursor(Qt::OpenHandCursor);
- setToolTip("I am item");
- }
- void MyItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
- {
- qDebug("************MyItem::hoverLeaveEvent*****************");
- setCursor(Qt::ArrowCursor);
- }
- // 右键菜单事件处理函数,为图形项添加一个右键菜单
- void MyItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
- {
- QMenu menu;
- QAction *moveAction = menu.addAction("move back");
- QAction *actAction = menu.addAction("test");
- QAction *selectedAction = menu.exec(event->screenPos());
- if(selectedAction == moveAction) {
- setPos(0, 0);
- }
- }
- void MyItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
- {
- qDebug("************MyItem::mouseMoveEvent*****************");
- QGraphicsItem::mouseMoveEvent(event);
- }
4、main及运行
- #include <QApplication>
- #include "myitem.h"
- #include "myview.h"
- #include "myscene.h"
- #include <QTime>
- int main(int argc,char* argv[ ])
- {
- QApplication app(argc,argv);
- qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
- MyScene scene;
- scene.setSceneRect(-200, -150, 400, 300);
- for(int i = 0; i < 5; ++i) {
- MyItem *item = new MyItem;
- item->setColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
- item->setPos(i * 50 - 90, -50);
- scene.addItem(item);
- }
- MyView view;
- view.setScene(&scene);
- view.setBackgroundBrush(QPixmap(":/background.png"));
- view.show();
- return app.exec();
- }
分析:mousePressEven鼠标按下事件由View—Scene—Item
分析:事件项Item没有获得焦点时,mousePressEven鼠标按下事件只由View传递到Scene。
分析:事件项Item的悬停事件,在构造函数中设置了setAcceptHoverEvents(true)。