Qt5 图形视图框架QGraphicsView
1、图形视图框架包含三大类:场景类(QGraphicsScene),视图类(QGraphicsView),图元类(QGraphicsItem);
2、对于有很多项的场景,使用轻量级的QGraphicsItem类作为数目众多的自定义项的基础最好,仅对数目较少的项使用QGraphicsObject
3、当一个项发生了变换(transformation),该项所有的子对象自动应用 该变换,递归应用到最深层次的子孙对象,
变换有(拖动,移动。。。。。),通过调用QGraphicsItem::setFlag(QGraphicsItem::ItemIngnoreTransformation)可以使子项忽略父项的变换,通常使用的标志还有可移动,选中,获得焦点等
4、通过把项设置为QGraphicsItemGroup的子项,可以将他们分组,创建项的集合。
5、图形视图类使用三种坐标系统,但是实际应用中只是关心其中的两种。视图使用物理坐标系统,场景使用在构造函数中传入的QRectF定义的逻辑坐标系统。Qt自动进行场景坐标到视图坐标的映射,放置项时使用的是场景的坐标。项的中心是在(0,0)点的逻辑坐标系统,每个项的(0,0)点实际在该项场景中所在位置的中心(文本项除外,原点在项的左上角)。
6、图形项:QGraphicsItem因为有两个纯虚函数而不能实例化对象,这两个纯虚函数是boundingRect()和paint()
paint()函数必须重新实现来绘制项,boundingRect()函数在图形视图框架中为项提供一个边界矩形,用于冲突监测和保证项仅在QGraphicsView的视口(viewport)中可见时才重绘。如果要创建非矩形形状的自定义图形项,最好同时实现shape()方法,这个方法返回一个精确描述项的轮廓的QpainterPath,对于准确检测冲突和鼠标点击非常有用。
7、如果要自定义一个形状,最简单的是使用标准的QGraphicsItem的子类之一,比如QGraphicsPathItem和QGraphicsPolygonItem,如果有定制行为,可以重新实现保护事件的处理函数,如keyPressEvent()。如果只是想要自己绘制,可以从QGraphicsItem派生,重新实现boundingRect()和paint()和shape()方法。
俄罗斯方块的实现
俄罗斯方块的逻辑
小方块的类型
效果图
代码的实现
onePiece.h
#ifndef ONEPIECE_H
#define ONEPIECE_H
/************
*一块方块的绘制
*
*
***************/
#include <QGraphicsObject>
#include <QColor>
class onePiece : public QGraphicsObject
{
public:
onePiece(const QColor &brushColor = Qt::red);
//为项提供一个外围的边界矩形
virtual QRectF boundingRect() const;
//QGraphicsView调用,在本地坐标系统中绘制项
virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
//重新定义项的形状,默认调用boundingRect获取项的矩形形状轮廓
virtual QPainterPath shape() const;
private:
QColor m_brushColor;
};
#endif // ONEPIECE_H
onepiece.c
#include "onepiece.h"
#include <QPainter>
#include "enumHeader.h"
/****
* 为了避免同一个方块组内小方块发生碰撞,
* 小方块的大小实际为19.5*19.5,但是小方块加上笔刷的宽度后为20*20
* 这样看起来是四个小方块连在一起的
* **/
onePiece::onePiece(const QColor &brushColor)
:m_brushColor(brushColor)
{
}
QRectF onePiece::boundingRect() const
{
qreal penWidth = PEN_WIDTH;
return QRectF(-(PIECE_DIAMETER - penWidth)/2, -(PIECE_DIAMETER - penWidth)/2,
PIECE_DIAMETER - penWidth, PIECE_DIAMETER - penWidth);
}
void onePiece::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
//背景贴图
painter->drawPixmap(-PIECE_DIAMETER/2,-PIECE_DIAMETER/2,PIECE_DIAMETER,PIECE_DIAMETER,QPixmap(":/piece/Image/piece/box.png"));
painter->setBrush(m_brushColor);
//将方块的边界的颜色进行透明化
QColor penColor = m_brushColor;
// 将颜色的透明度减小
penColor.setAlpha(200);
painter->setPen(penColor);
//使用当前的笔刷和笔画矩形框
painter->drawRect(-PIECE_DIAMETER/2, -PIECE_DIAMETER/2, PIECE_DIAMETER, PIECE_DIAMETER);
}
QPainterPath onePiece::shape() const
{
QPainterPath path;
//去除笔刷的宽度,这样同一个方块组的方块就不会被检测出碰撞的情况
path.addRect(-(PIECE_DIAMETER-PEN_WIDTH)/2,-(PIECE_DIAMETER-PEN_WIDTH)/2,PIECE_DIAMETER-PEN_WIDTH,PIECE_DIAMETER-PEN_WIDTH);
return path;
}
pieceBox.h
#ifndef PIECEBOX_H
#define PIECEBOX_H
/********
* 方块组
* 4*4
*
*
* ******/
#include <QGraphicsItemGroup>
#include <QKeyEvent>
#include <QTimer>
#include "enumHeader.h"
#include <QTransform>
#include "onepiece.h"
#include <QGraphicsItem>
class pieceBox :public QObject,public QGraphicsItemGroup
{
Q_OBJECT
public:
pieceBox();
//颜色表
static QColor colorTable[7];
//绘制方块组的边框矩形
virtual QRectF boundingRect() const;
//是否发生碰撞
bool isCollding() const;
//获取当前的方块类型
BoxType getCurrentBoxType() const;
//创建方块组
void createBox(const QPointF &point = QPointF(0,0),
BoxType currentBoxType = RandomShape);
//消除方块组
void clearBoxGroup(const bool &isClear = false);
protected:
virtual void keyPressEvent(QKeyEvent * event);
signals:
void signal_needNewBox();
void signal_gameOver();
public slots:
void slot_moveOneStep();
void slot_startTimer(int timeSec);
void slot_stopTimer();
private:
QTimer *m_Timer;
BoxType m_currentBoxType;
QTransform m_oldTransform;
QList<onePiece *> m_pieceBoxList; //存放新方块组的四个方块
};
#