@Qt学习与开发
“ 第一次写博客,尝试将自己的思路记录下来。”
Qt环境配置
一个挺巧妙的机会能接触到Qt,Qt是一个构建人机交互界面的很好的工具,首先我们需要配置好Qt环境。Qt下载链接link,一般选择5.9以上版本,然后安装的时候有两种编译方式,直接选择对应64位的MSVC即可。进入之后可以通过运行一个应用实例查看安装是否正确。
植物大战僵尸实战
实战:文件配置
首先创建一个MainWidget应用程序实例,生成Form的ui文件不勾选(界面化编程),后续的语言和工具包Kit选取自己默认的系统版本64位,选定生成文件的路径(尽量不适用中文名)。
我们先考虑整个开发框架的思维导图:
代码和资源文件的基本部署流程:
- 新建src文件夹,将文件初始的.h和.cpp文件以及资源代码中的code文件拷贝进去,回到Qt界面后关闭弹出窗口;
- 拷贝过去之后需要在项目名上右键,添加现有文件,将src中的所有文件添加进项目对应的Headers和Sources文件夹中 ;
- 进入.pro工程内部文件中,查看里面的关于基本核心库以及C++版本等相关信息定义,其中我们需要将Sources和Headers中的无用路径头文件删除,因为头文件路径都被移动到src文件夹中;
- 在项目文件上Add New,新建一个qrc文件(Qt Resources File),然后在该文件上右键添加现有文件夹(Existing Directory),将资源文件中的音频和图片添加进来,弹出窗口选定Yes to All;
- 基本文件配置就结束了,可以运行下查看是否正常。另外我们可以在项目设置中修改Build的构建目录Debug和Release,这样生成的build文件就在项目文件夹之下。接下来就是正式的代码环节。
实战:场景设置
在项目的现有初始化资源文件中,现有的文件中那些坐标、鼠标响应和计时器等文件是资源文件中的,然后我们主要了解基础的开发思路和方法,结合开发导图依次添加视图和场景代码:
- 在MainWidget .cpp文件中添加视图和场景代码
// 将gameView创建并添加到MainWidget文件
QGraphicsView *gameView = new QGraphicsView(this); //给指针变量申请一个内存指针QGraphicsScene *gameScene = new QGraphicsScene;
gameView->setScene(gameScene); //在对应的类中查看有该方法设置scene
//ImageManager文件中load函数载入背景图片
QGraphicsPixmapItem *backgroundItem = new QGraphicsPixmapItem(gImageCache->load("interface/background1.jpg"));
gameScene->addItem(backgroundItem); //添加进场景//需要添加ImageManager和GraphicsItem头文件(声明错误)
可以右键QGraphicsView进入上下文相关帮助查看用法,其中Header--头文件需要添加 和 qmake--在.pro文件中添加配置
- main.cpp文件中添加ImageManager头文件并初始化
InitImageManager(); //调用初始化函数方法
- 这样就实现了游戏视图中的背景设置,但是窗口太小,所以可以设置初始窗口大小
//MainWidget cpp文件中
gameView->setGeometry(0,0,800,600); //设置窗口大小
- 响应C++中的类开发思想,创建GameView类,项目右键新建class文件命名为GameView,基类就是GraphicsView,其中勾选Widget—可直接添加头文件和OBJECT—槽函数和信号的使用操作
//gameview头文件中的构造函数声明
public://形式需要修改,涉及一个父类,模仿mainwidget文件中的构造函数GameView(QWidget *parent = nullptr);//gameview.cpp文件中实现代码,形参和初始化列表
GameView::GameView(QWidget *parent):QGraphicsView(parent){
}
- 同样的方法添加一个场景类GameScene
//gamescene头文件中声明一个Item变量,需添加对应Item头文件QGraphicsPixmapItem *backgroundItem;
- 在现有两个类的基础上,可以在Widget中添加两个类的声明和对应的指针变量实现代码重构,减少头文件的编译
class GameView; //利用类声明,可以不用包含很多头文件
class GameScene; //代码重构!!!
//头文件包含头文件的方式会导致拉出头文件解释
class MainWidget : public QWidget
{Q_OBJECTpublic:MainWidget(QWidget *parent = nullptr); //构造和析构函数~MainWidget();GameView *gameView;GameScene *gameScene;
};
- 上述中的指针变量可以在Widget的类定义中的初始化列表中实现初始化
MainWidget::MainWidget(QWidget *parent): QWidget(parent),gameView(new GameView(this)),gameScene(new GameScene)
{....//视图和场景实现代码
}
- gameview的实现代码就可以重构到自己的类中,同理gamescene类也是
GameView::GameView(QWidget *parent):QGraphicsView(parent)
{setGeometry(0,0,800,600); //设置窗口大小;利用两个变量来设置
}
- 同理gamescene的实现代码的处理也可以重构到自己的类
GameScene::GameScene():backgroundItem(new QGraphicsPixmapItem(gImageCache->load("interface/background1.jpg")))
{addItem(backgroundItem); //添加进场景
}
- 另外考虑gamescene场景是gameview视图中的一个子例,类似于Item是gamescene的一个子例,那么可以将场景设置方法放到gameview的实现中
GameView::GameView(QWidget *parent):QGraphicsView(parent),gameScene(new GameScene)
{setScene(gameScene); //设置场景得放在前面
}
整个场景的设置,综合来说就是界面框架、视图和场景以及元素之间的一个关系,首先是视图类和场景类的自定义声明,以及将原本在界面中实现的视图和场景以及元素(背景图片)设置的代码转换到自身的类定义中;其次,GameView和GameScene就类似于GameScene和GraphicsItem之间的关系,所以设置场景SetScene函数方法就可以添加到GameView中,只需要添加相应的GameScene的初始化列表,类似于背景图的设置addItem函数方法添加到GameScene中以及初始化列表中的声明变量backgroundItem的初始化。
实战:除草机设置(作业)
除草机作为一个场景元素,自然是背景图一样,添加到GameScene的实现中。同理定义场景元素GraphicsPixmapItem,其定义方式同背景图,利用图片的存储路径作为形参进行声明定义。
GameScene::GameScene():backgroundItem(new QGraphicsPixmapItem(gImageCache->load("interface/background1.jpg")))
{addItem(backgroundItem); //添加进场景//方法①:笨方法,直接如此声明定义5个场景元素进行初始化---缺点是代码可读性和重用性差QGraphicsPixmapItem *lawncleaner1 = new QGraphicsPixmapItem(gImageCache->load("interface/LawnCleaner.png"));lawncleaner1->setPos(180,100); //如果有父类,那么就是在父坐标系中;如果没有就是scene坐标系lawncleaner1->setParentItem(backgroundItem); //设置父类元素,隐式地将此图形项添加到父级的场景中//方法②:利用坐标系函数,根据背景图中的行和列格子自己寻找对应的位置#include "Coordinate.h"Coordinate cor; //生成一个坐标系,告诉你行列的存在,一格一格for(int i=0;i<5;++i){QGraphicsPixmapItem *lawncleaner = new QGraphicsPixmapItem(gImageCache->load("interface/LawnCleaner.png"));lawncleaner->setPos(cor.getX(1),cor.getY(i)); //对应的列--X 和行--Ylawncleaner->setParentItem(backgroundItem);}//这样定义不太好:因为除草机需要触发机器开走的效果
其中不同之处在于除草机是在背景图之上呈现的元素,所以给他添加一个父类元素setParentItem,如此可以保证元素的显示不会因为代码顺序问题出现混乱。