用Qt的图形视图框架(Graphics View Framework)做了一个拼图游戏DEMO,演示了:

  • QGraphicsView、QGraphicsScene、QGraphicsItem的基本用法
  • drag && drop
  • 自定义QGraphicsItem

先来看看效果吧:

现在,来看下代码了。

项目说明

如上图所示,项目名称为qPuzzle,三个源文件,main.cpp是入口,imageitem.h和imageitem.cpp实现了:

  • PuzzleImageItem,就是界面左上侧那两个可以拖动的碎片,支持拖动
  • PuzzlePart,用于接受拖放的item
  • PuzzlePartManager,管理可拖放的PuzzleImageItem,拖放到位后从QGraphicsScene中移除PuzzleImageItem

项目还有几个图片资源,model.png是带有拼图区域的房子图片,mode_1.png和model_2.png是房子上扣出来的小图片。

源码说明

分开来说吧,main()、PuzzleImageItem和PuzzlePartItem。

入口函数main

先看main.cpp吧:

class GraphicsView : public QGraphicsView
{
public:GraphicsView(QGraphicsScene *scene) : QGraphicsView(scene){}protected:virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE{}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QGraphicsScene scene(0, 0, 480, 360);PuzzleImageItem *image1 = new PuzzleImageItem(":/model_1.png", 60, 60, 1);image1->setPos(4, 4);scene.addItem(image1);PuzzleImageItem *image2 = new PuzzleImageItem(":/model_2.png", 60, 60, 2);image2->setPos(4, 70);scene.addItem(image2);QGraphicsPixmapItem *model = new QGraphicsPixmapItem(QPixmap(":/model.png"));scene.addItem(model);model->setPos(100, 100);PuzzlePartManager mgr(&scene);mgr.addSourceItems(1, image1);mgr.addSourceItems(2, image2);PuzzlePart *part1 = new PuzzlePart(&mgr, 60, 60, 1);part1->setPos(261, 149);part1->setZValue(2);scene.addItem(part1);PuzzlePart *part2 = new PuzzlePart(&mgr, 60, 60, 2);part2->setPos(231, 199);part2->setZValue(2);scene.addItem(part2);GraphicsView view(&scene);view.setRenderHint(QPainter::Antialiasing);view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);view.setBackgroundBrush(QColor(230, 200, 167));view.setWindowTitle("House Puzzle");view.show();return a.exec();
}

main()方法创建了QGraphicsScene实例,构造各种item并添加到场景中,将QGraphicsView与QGraphicsScene关联起来,代码很直接,不多说了。

PuzzleImageItem

再来看看支持拖动的PuzzleImageItem的实现:

class PuzzleImageItem : public QGraphicsObject
{
public:PuzzleImageItem(const QString & imagePath, int w, int h, int partId);QRectF boundingRect() const Q_DECL_OVERRIDE;void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE;protected:void mousePressEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;void mouseMoveEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;protected:QImage m_image;int m_width;int m_height;int m_partId;
};

PuzzleImageItem代表完整图片的一部分,它从QGraphicsObject继承而来,持有一个图片、宽、高以及图片的id(m_partId)。其中m_partId是碎片索引,在拖放到位后,PuzzlePartManager通过它来将界面左上角的碎片从视图中移除。

重写了boundingRect和paint方法,这是自定义QGraphicsItem时通常都需要做的。

重写了mousePressEvent,在它里面将鼠标形状修改为抓紧的小手:

void PuzzleImageItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{setCursor(Qt::ClosedHandCursor);
}

重写了mouseMoveEvent,在它里面组装QDrag和QMimeData:

void PuzzleImageItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()) {return;}QDrag *drag = new QDrag((QObject*)event->widget());QMimeData *mime = new QMimeData;mime->setImageData(m_image);mime->setData(QString(QMetaType::typeName(QMetaType::Int)), QString("%1").arg(m_partId).toLatin1());drag->setMimeData(mime);drag->setPixmap(QPixmap::fromImage(m_image));drag->setHotSpot(QPoint(15, 30));drag->exec();setCursor(Qt::OpenHandCursor);
}

注意我在这里用QMimeData传递m_partId给接受拖放的PuzzlePartItem,这样可以区分碎片该放到哪个目标区域。在PuzzlePartItem的dragEnterEvent方法中有用到,代码如下:

void PuzzlePart::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{const QMimeData *mime = event->mimeData();int partId = mime->data(QMetaType::typeName(QMetaType::Int)).toInt();if(mime->hasImage() && partId == m_partId){event->setAccepted(true);m_dragOver = true;update();}else{event->setAccepted(false);}
}

这里可以留意一下使用QMimeData传递非典型类型数据的做法。

重写了mouseReleaseEvent,在它里面重新设置鼠标形状为打开的小手:

void PuzzleImageItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{setCursor(Qt::OpenHandCursor);
}

PuzzlePartItem

PuzzlePartItem定义了一个区域,用于接受拖放。它同样从QGraphicsObject继承,声明如下:

class PuzzlePart : public QGraphicsObject
{
public:PuzzlePart(PuzzlePartManager *mgr, int w, int h, int partId, QGraphicsItem *parent = 0);QRectF boundingRect() const Q_DECL_OVERRIDE;void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) Q_DECL_OVERRIDE;protected:void dragEnterEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;void dropEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;PuzzlePartManager *m_sourcePartManager;int m_width;int m_height;int m_partId;QImage m_image;bool m_dragOver;
};

这个类也保留了一个id(m_partId),可以用来决定接受哪个PuzzleImageItem。前面贴出来的dragEnterEvent方法的代码里就用到了。dragEnterEvent方法在PuzzleImageItem被拖到PuzzlePartItem所在区域时触发。

当释放鼠标时,会触发dropEvent,代码如下:

void PuzzlePart::dropEvent(QGraphicsSceneDragDropEvent *event)
{m_dragOver = false;const QMimeData *mime = event->mimeData();int partId = mime->data(QMetaType::typeName(QMetaType::Int)).toInt();if(mime->hasImage() && partId == m_partId){m_image = qvariant_cast<QImage>(event->mimeData()->imageData());m_sourcePartManager->removeItem(partId);}update();
}

我们在这里接收QMimeData里的图片,触发重新绘制,还调用PuzzlePartManager的removeItem将源item(PuzzleImageItem)从视图中移除。

重写dragLeaveEvent是为了在鼠标拖着源object离开自己时重绘。


好啦,这个demo基本就这样了。

需要项目源码的,可以关注我的订阅号“程序视界”,回复“qPuzzle”获取下载地址。

用Qt图形视图框架开发拼图游戏相关推荐

  1. Qt图形视图框架详解-安晓辉-专题视频课程

    Qt图形视图框架详解-12227人已学习 课程介绍         介绍Qt中的Graphics View Framework,涉及View.Scene.Item的关系,如何自定义QGraphicsI ...

  2. Qt 图形视图框架中的事件处理和传播

    目录名字 Qt 图形视图框架中的事件处理和传播 要点: Qt 图形视图框架中的事件处理和传播 图形视图框架在一些动画类的应用程序上经常会用到.该框架中的事件处理与传播机制也是经常会涉及. 程序设计核心 ...

  3. Qt 图形视图框架(Qt MVC)

    什么是MVC MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离 ...

  4. Qt图形视图框架图片图元QGraphicsPixmapItem

    文章可能被更新,最新地址:http://www.fearlazy.com/index.php/post/107.html 看这个标题还真有点拗口,一句话里出现了四个图字.这句话的关键词是图片,前面讲了 ...

  5. Qt图形视图框架:QGraphicsScene详解

    一.描述 1.场景提供了一个用于管理大量2D图形项的平面.该类充当图形项的容器. 它与视图一起用于可视化2D曲面上的图形图形项. 2.场景没有自己的视觉外观,只负责管理图形项. 3.场景的最大优势之一 ...

  6. 201112阶段二qt图形视图框架

    目录 一.学习的知识点 一)事件 二)图形视图框架 二.上课没有听懂或者没有理解的地方 三.当天学习的收获 一.学习的知识点 一)事件 鼠标事件的创建 组合键 二)图形视图框架 场景类:坐标系以中心为 ...

  7. Qt图形视图框架--图元总结

    文章目录 一.基类QGraphicsItem 二.内部图元 2.1.椭圆图元--QGraphicsEllipseItem 2.2.线段图元--QGraphicsLineItem 2.3.路径图元--Q ...

  8. Qt图形视图框架:视图增加标尺

    效果: 把视图.水平标尺.垂直标尺用网格布局放置. 每当拖动视图的滚动条时,更新标尺的偏移量: connect(deviceGraphicsView->horizontalScrollBar() ...

  9. Qt图形视图框架:将自定义图形项放入布局

    只有继承布局项QGraphicsLayoutItem的对象才能放入布局.继承QGraphicsLayoutItem后需要重写setGeometry().sizeHint()这两个函数. 以自定义滑动条 ...

  10. QT图形视图框架---场景 advance()函数

    调用场景的advance()函数就会自动调用场景中所有图形项的advance()函数,

最新文章

  1. 独家 | 人工智能和大数据是如何联系在一起的?
  2. I、Q 通道幅相不平衡的影响及改善
  3. pymysql.err.OperationalError: (1045, “Access denied for user ‘system‘@‘::1‘ (using password: YES)“)
  4. CCF NOI1054 扫雷游戏
  5. 爬虫笔记10Scrapy-框架
  6. Android高版本开机广播,android3.1以上,假如程序没有启动过,怎么获取开机广播呢?...
  7. php任务分配思路_PHP执行定时任务的几种方法思路
  8. Instruments性能优化-Core Animation
  9. python_generator生成器
  10. 简记Inception系列
  11. Android界面绘制流程--------How Android Draws Views
  12. 【并行计算-CUDA开发】CUDA shared memory bank 冲突
  13. 解决智慧树考试酷无法复制粘粘的问题
  14. 浅谈大数据思维——一名管科类学生基于《大数据时代》的思考
  15. 计算机地图制图符号制作的心得,计算机地图制图实习报告.docx
  16. Matplotlib绘制多子图+合并图例+子图间距
  17. 2_PY基本数据类型
  18. 计算机毕业设计ssm龙腾集团员工信息管理系统39r5l系统+程序+源码+lw+远程部署
  19. Bugku:分析 手机热点
  20. 华为新平板以美好体验定义青春

热门文章

  1. 图新地球 “请选择目标图层后再下载”
  2. js实现电子签名功能
  3. 「模拟8.19 A嚎叫..(set) B主仆..(DFS) C征程..(DP+堆优化)」
  4. 1. 从WordPress搬家到网易博客!
  5. 【联盛德W806上手笔记】九、DMA
  6. 吴恩达深度学习课程笔记(二):改善深层神经网络
  7. 5G/NR 5G核心网(5GC)之网络功能和实体
  8. java 6 17 32_java学习class6(1)
  9. 从 RTL 到 GDS :基于 OpenLANE
  10. 整站SEO优化方案:整站优化的方案的流行格式