相随与欢
长安大学 信息工程学院 人工智能系
大一下-面向对象课程设计-彩色泡泡机题目详解
与欢行
一、题目与需求分析:
基于Qt框架的GUI设计与实现
需求:
1、绘制泡泡(颜色、大小等配置信息随机产生)
2、点击游戏区域即可产生泡泡。
3、泡泡会向上漂浮离开(速度随机)。
二、具体实现:
A、泡泡绘制类Bubble的设计
bubble.h
#ifndef BUBBLE_H
#define BUBBLE_H
//泡泡的具体绘制实现
//不辞青山,相随与欢
#include <QWidget>
#include<QPropertyAnimation> //属性动画 泡泡漂浮
#include<QPainter> //绘图工具
#include<QDebug> //调试信息输出打印
#include<QPainterPath> //绘图路径(笔画) 生成圆形泡泡
#include<QTime> //获得时间 用于获得随机数
#include<QMouseEvent> //鼠标点击事件
namespace Ui {
class Bubble;
}
class Bubble : public QWidget
{
Q_OBJECT
public:
//radius
explicit Bubble(int r,int x1,int y1,int newV,QWidget *parent = nullptr); //参数分别为半径、横纵坐标、速度大小
~Bubble();
void setColor(QColor color); //用于设置泡泡线条的颜色
void paintEvent(QPaintEvent *event); //绘图事件 用于绘制泡泡
void own_show(); //自己写的展示函数
private:
Ui::Bubble *ui;
QPropertyAnimation *bubbleAnimation; //泡泡漂浮的动画
int x; //泡泡横坐标 点击的位置
int y; //泡泡纵坐标 点击的位置
int radius; //泡泡半径 随机
int v; //泡泡的漂浮速度(也就是动画持续的时间) 周期 生存期 随机
QColor bubbleColor; //泡泡的颜色 随机
};
#endif // BUBBLE_H
bubble.cpp
#include "bubble.h"
#include "ui_bubble.h"
Bubble::Bubble(int r,int x1,int y1,int newV,QWidget *parent) :
QWidget(parent),
ui(new Ui::Bubble),
v(newV),
x(x1),
y(y1),
radius(r)
{
ui->setupUi(this); //初始化UI
this->setFixedSize(QSize(80,80)); //设置泡泡所在窗口的最大值,从而限定泡泡的大小
//设置窗口标志(属性)
this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); //去除泡泡所在窗口的标题栏
this->setAttribute(Qt::WA_TranslucentBackground); //使泡泡所在窗口(已去除标题栏)背景透明化,只出现泡泡的图形
this->bubbleAnimation=new QPropertyAnimation(this,"pos"); //初始化泡泡动画 参数分别为动画作用的对象:泡泡 涉及更改的属性:位置 position
}
Bubble::~Bubble()
{
delete ui;
}
void Bubble::setColor(QColor color) //改变泡泡的颜色
{
this->bubbleColor=color;
}
void Bubble::paintEvent(QPaintEvent *event)
{
QPainter painter(this); //创建画笔,作用于自身
painter.setRenderHint(QPainter::Antialiasing); //固定语法,用于图形反走样,抗锯齿
painter.setPen(bubbleColor); //设置画笔,勾勒泡泡,颜色通过随机RGB通道值产生
QPainterPath bubblePath; //泡泡绘制路径 QPainterPath提供容器用于绘制路径
//ellipse椭圆
//参数为圆心的坐标 半径的大小 圆心坐标由鼠标点击的位置决定 半径为随机产生
bubblePath.addEllipse(QPoint(40,40),radius,radius); //在绘制路径中生成圆形泡泡在窗口的初始位置,后期随机生成半径
painter.drawPath(bubblePath); //绘制泡泡路径 进行绘制
}
void Bubble::own_show() //展示泡泡颜色,大小,漂浮速度,方向
{
QWidget::show(); //显示泡泡
//问的 产生随机
QTime time;
time=QTime::currentTime(); //获得当前时间用以产生随机数
qsrand(time.msec()+time.second()*1000);
int exc_x=400+qrand()%1000;
this->bubbleAnimation->setDuration(v); //用随机速度设置动画时长(同随机) 设置动画的时长
//两点确定一线
this->bubbleAnimation->setStartValue(QPoint(x,y)); //设置动画的开始位置 泡泡产生的位置就是鼠标点击的位置
this->bubbleAnimation->setEndValue(QPoint(x+exc_x,-500)); //设置结束位置 随机的偏移量
this->bubbleAnimation->start(); //动画开始 动画开始
}
B、游戏界面game类的设计与实现
game.h
#ifndef GAME_H
#define GAME_H
#include <QWidget>
#include<QMouseEvent>
#include"bubble.h"
namespace Ui {
class Game;
}
class Game : public QWidget
{
Q_OBJECT
public:
explicit Game(QWidget *parent = nullptr);
~Game();
void mousePressEvent(QMouseEvent *event); //鼠标事件
private:
Ui::Game *ui;
};
#endif // GAME_H
game.cpp
#include "game.h"
#include "ui_game.h"
Game::Game(QWidget *parent) :
QWidget(parent),
ui(new Ui::Game)
{
ui->setupUi(this);
}
Game::~Game()
{
delete ui;
}
void Game::mousePressEvent(QMouseEvent *event)
{
QPoint Pos = event->pos(); //显示鼠标点击的位置 以坐标形式呈现
qDebug() << "当前鼠标的位置:" << Pos; //输出鼠标所点击位置的坐标
//产生一堆随机数
QTime time;
time= QTime::currentTime();
qsrand(time.msec()+time.second()*1000);
int newR = 20+qrand() % 20; //产生40以内的随机数
int R = qrand() % 255; //产生255以内的随机数
int G = qrand() % 255; //产生255以内的随机数
int B = qrand() % 255; //产生255以内的随机数
//泡泡信息写入日志文件
QFile file;
file.setFileName("C:/Users/Administrator/Desktop/欢欢_彩色泡泡机/泡泡信息.txt");
QString colorInfo="颜色:RGB:("+QString::number(R)+","+QString::number(G)+","+QString::number(B)+")";
QString posInfo="坐标:("+QString::number(Pos.x())+QString::number(Pos.y())+")";
if(file.open(QIODevice::WriteOnly |QIODevice::Text | QIODevice::Append))
{
QTextStream stream(&file);
stream<<colorInfo<<endl;
stream<<posInfo<<endl;
stream<<endl;
file.close();
}
Bubble* paopao = new Bubble(newR,Pos.x(),Pos.y()); //动态申请一个泡泡对象
paopao->move(this->mapToGlobal(Pos)); //将泡泡移动到点击的位置
//paopao->move(Pos);
paopao->setColor(QColor(R,G,B)); //RGB数值设置颜色 同样采用随机数机制
paopao->own_show(); //展示 调用动画
}
三、核心功能具体实现解析
1、如何绘制泡泡?
首先,Qt中有一个绘图事件方法
virtual void QWidget::paintEvent ( QPaintEvent * )
该接口(方法)主要用于各种图形的绘制
a、paintEvent会在何时调用?
paintEvent函数执行:
1、在构建窗体的时候执行
2、在窗体update的时候执行
3、系统认为窗口需要重新绘制的时候执行
b、具体绘制实现
在bubble类中创建成员函数
void paintEvent(QPaintEvent *event); //绘制事件 泡泡的主要绘制算法实现在此
在源文件bubble.cpp中对其实现进行定义:
//绘制泡泡的图案 画画函数
void Bubble::paintEvent(QPaintEvent *event) //绘图事件函数 在构造本类对象时自动调用 用于图形的绘制
{
//画画需要工具(笔和画刷) 笔用于勾勒线条 画刷用于上色 同时还要知道画布是什么
//QPainter 画笔名(绘图位置)
QPainter painter(this); //Qpainter是一个绘图工具 相当于画笔/画刷
painter.setRenderHint(QPainter::Antialiasing); //这一句是固定用法 用于图形反走样、抗锯齿 百度查到的
//void QPainter::setPen ( const QPen & pen )
//设置新的绘图工具画笔。
//pen定义了如何绘制线和轮廓,并且它也定义了文本颜色。
painter.setPen(QPen(m_bubbleColor,10)); //设置画笔 如果是NoPen的话 绘制出来的图案没有边框(界) 勾勒
//void QPainter::setBrush ( BrushStyle style )
//设置绘图工具的画刷为黑色和特定的style。
//painter.setBrush(QBrush(m_bubbleColor)); //设置画刷 为画刷设置颜色 用于给泡泡上色 色彩通过随机的RGB通道值实现
//联想笔画 写一个字是不是需要笔画和顺序
//QPainterPath 类(绘图路径)提供了一个容器,用于绘图操作,可以创建和重用图形形状。
QPainterPath bubblePath; //泡泡绘制路径
//path.drawEllipse(x, y, r, r);
//Ellipse 椭圆(圆)
bubblePath.addEllipse(QPoint(40, 40), radius, radius); //将一个圆形添加到绘图路径中
painter.drawPath(bubblePath); //绘制图形路径
}
c、绘画工具QPainter
画笔:
QPainter 可以创建画笔 画笔可以设置颜色以及粗细大小等等属性
画笔主要用于勾勒
创建方法:
//QPainter 工具名(画布位置)
QPainter p(this);
painter.setPen(QPen(m_bubbleColor,10));
通过setPen成员函数设置画笔的颜色以及粗细
画刷:
QPainter也可以设置画刷 画刷也有对应的颜色等属性
画刷主要用于图案核心区域的上色
//void QPainter::setBrush ( BrushStyle style )
//设置绘图工具的画刷为黑色和特定的style。
//painter.setBrush(QBrush(Color));
d、绘图路径QPainterPath
QPainterPath相当于我们的笔画
QPainterPath 类(绘图路径)提供了一个容器,用于绘图操作,可以创建和重用图形形状。
path.addEllipse(x,y,r,r) 将一个圆形路径添加到绘图路径中 参数依次为圆心的x,y坐标以及半径
e、下笔draw
painter.drawPath(bubblePath); //绘制图形路径
让画笔按照规定的路径去进行绘制图形
2、背景图片如何绘制?
也是通过paintEvent进行绘制
具体实现:
void login::paintEvent(QPaintEvent *)
{
//绘制背景图片
QPainter p(this);
p.drawPixmap(0,0,width(),height(),QPixmap(":/files/snow.png"));
p.end();
}
QPixmap将背景图片读出
3、疑问:Bubble类的本质是什么?
可以看到Bubble类的本质是一个QWidget,是一个窗体。
而窗体除了其内部图形外还包含窗体边框、窗体标题、以及一些未绘图区域,这就需要我们去进行处理。
4、如何让Bubble窗体只包含泡泡?
通过前面的分析,我们知道Bubble类对象的本质仍是一个窗体,那么我们就需要去除其边框,并让其除泡泡外其余部分均为透明。
未处理前:
观察Bubble的构造函数:
Bubble::Bubble(int r,int x1,int y1,int newV,QWidget *parent) :
QWidget(parent),
ui(new Ui::Bubble),
v(newV),
x(x1),
y(y1),
radius(r)
{
ui->setupUi(this); //初始化UI
this->setFixedSize(QSize(80,80)); //设置泡泡所在窗口的最大值,从而限定泡泡的大小
//设置窗口标志(属性)
//去除泡泡所在窗口的标题栏
this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
this->setAttribute(Qt::WA_TranslucentBackground);
//使泡泡所在窗口(已去除标题栏)背景透明化,只出现泡泡的图形
this->bubbleAnimation=new QPropertyAnimation(this,"pos");
//初始化泡泡动画 参数分别为动画作用的对象:泡泡 涉及更改的属性:位置 position
}
1、去除窗体边框标题栏
通过设置窗体属性(标志)
this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); //设置窗口标志 不带标题栏边框
2、让窗体大小与泡泡一致
//setFixedSize() 设置窗口固定大小
this->setFixedSize(QSize(80, 80)); //设置固定的大小 泡泡的最大值
3、让除泡泡外其余部分透明
//setAttribute 设置窗体属性 Qt::WA_TranslucentBackground使其透明
this->setAttribute(Qt::WA_TranslucentBackground); //使窗体透明
处理后:
5、如何让泡泡进行漂浮
漂浮的本质是什么:无非就是位置的改变
通过Qt中的属性动画类 QPropertyAnimation //属性动画头文件 property:属性
Qt中的动画类关系如下:
将其定义在泡泡的展示函数 own_show()里
void Bubble::own_show() //展示泡泡颜色,大小,漂浮速度,方向
{
QWidget::show(); //显示泡泡
//问的 产生随机
QTime time;
time=QTime::currentTime(); //获得当前时间用以产生随机数
qsrand(time.msec()+time.second()*1000);
int exc_x=400+qrand()%1000;
this->bubbleAnimation->setDuration(v); //用随机速度设置动画时长(同随机) 设置动画的时长
//两点确定一线
this->bubbleAnimation->setStartValue(QPoint(x,y)); //设置动画的开始位置 泡泡产生的位置就是鼠标点击的位置
this->bubbleAnimation->setEndValue(QPoint(x+exc_x,-500)); //设置结束位置 随机的偏移量
this->bubbleAnimation->start(); //动画开始 动画开始
}
动画的初始化
将初始化放在bubble的构造函数中
this->BubbleAnimation = new QPropertyAnimation(this, "pos"); //初始化动画成员
参数为 动画作用的对象,需要变动的属性
1、设置动画时长
this->BubbleAnimation->setDuration(v);
2、设置开始位置
this->BubbleAnimation->setStartValue(QPoint(x, y));
3、设置结束位置
this->BubbleAnimation->setEndValue(QPoint(x, y));
4、开始动画
this->BubbleAnimation->start(); //动画开始
6、如何实现鼠标点击即可产生泡泡
通过鼠标点击事件
void mousePressEvent(QMouseEvent *event); //鼠标点击事件
该成员函数会在每一次鼠标点击游戏区时自动调用
void Game_1::mousePressEvent(QMouseEvent *event) //点击时自动调用
{
QPoint Pos=event->pos(); //用坐标形式显示鼠标点击的位置(即泡泡产生的位置)
qDebug()<<"泡泡产生的位置是"<<Pos;
QTime time;
time=QTime::currentTime();
qsrand(time.msec()+time.second()*1000);
int r=20+qrand()%20; //随机的半径
int R=qrand()%255; //随机的R通道值
int G=qrand()%255; //RGB颜色通道,通过改变数值调整颜色[0,255]
int B=qrand()%255; //随机的B通道值
int v=2000+qrand()%3000; //获得泡泡随机速度
qDebug()<<"泡泡颜色(RGB值)为:("<<R<<","<<G<<","<<B<<")"<<endl;
qDebug()<<"泡泡大小(半径)为:"<<r<<endl;
Bubble * bubble1=new Bubble(r,Pos.x(),Pos.y(),v); //动态申请一个泡泡类对象
bubble1->move(this->mapToGlobal(Pos)); //将泡泡直接移动到鼠标在页面点击的位置
bubble1->setColor(QColor(R,G,B)); //设置颜色 RGB规则
bubble1->own_show(); //展示泡泡
//将泡泡的信息写入文件
QFile file("C://Users//fuqilingbuxiu//Desktop//课设//泡泡信息.txt");
if(! file.open(QIODevice::Append|QIODevice::Text)) //append追加,不会覆盖之前的文件
{
// QMessageBox::critical(this,"错误","文件打开失败,信息没有保存!","确定");
// return;
}
QString size,colour,position,speed;
size=" 泡泡的大小"+QString::number(r);
colour=" 泡泡颜色(RGB)值:("+QString::number(R)+","+QString::number(G)+","+QString::number(B)+");";
position=" 泡泡产生位置:("+QString::number(Pos.x())+","+QString::number(Pos.y())+");";
speed=" 泡泡的速度(时间表示):"+QString::number(v);
QTextStream out(&file);//写入
out<<size<<endl;
out<<colour<<endl;
out<<position<<endl;
out<<speed<<endl;
out<<endl;
file.close();
}
捕获坐标位置
event->pos(); //event即为鼠标点击事件
产生随机数(泡泡的配置信息)
QTime time;
time=QTime::currentTime();
qsrand(time.msec()+time.second()*1000);
int r=20+qrand()%20; //随机的半径
int R=qrand()%255; //随机的R通道值
int G=qrand()%255; //RGB颜色通道,通过改变数值调整颜色[0,255]
int B=qrand()%255; //随机的B通道值
int v=2000+qrand()%3000; //获得泡泡随机速度
产生泡泡
观察泡泡类Bubble的构造函数
Bubble(int r,int x1,int y1,int newV,QWidget *parent = nullptr);
//参数分别为半径、横坐标、纵坐标、速度大小
产生泡泡
Bubble * bubble1=new Bubble(r,Pos.x(),Pos.y(),v); //动态申请一个泡泡类对象
将泡泡移动至鼠标点击处
bubble1->move(this->mapToGlobal(Pos)); //将泡泡直接移动到鼠标在页面点击的位置
设置颜色
//通过bubble的setColor接口
bubble1->setColor(QColor(R,G,B)); //设置颜色 RGB规则
泡泡的显示
bubble1->own_show(); //展示泡泡
7、如何实现页面跳转
首先,要明确每一个页面的本质是什么?
没错,每一个页面的本质是一个对象,那么我们可以通过类的组合机制,将不同的页面对象作为另外一个类的数据成员,再设置相应的接口槽函数以进行跳转
举例:
login.h
#include <QWidget>
#include<QPainter> //绘图工具 画笔 画刷
#include<QString> //字符串
#include<QDebug> //调试,输出打印
#include<QMessageBox> //确认是否退出
#include"select1.h" //功能选择页面
#include"select.h"
#include<QMediaPlayer> //用于播放bgm
QT_BEGIN_NAMESPACE
namespace Ui { class Login; }
QT_END_NAMESPACE
class Login : public QWidget
{
Q_OBJECT
public:
Login(QWidget *parent = nullptr);
~Login();
void paintEvent(QPaintEvent *event); //背景图片的绘制 drawpixmap
private slots:
void on_Login_button_clicked();
void on_Login_return_Button_2_clicked();
private:
Ui::Login *ui;
select1 s1; //功能选择页面对象
select s;
};
#endif // LOGIN_H
其中select1为页面类 s1为对应的页面对象
在接口槽函数中进行以下实现
void Login::on_Login_button_clicked()
{
QString id="mxdxcd";
QString key="4826";
QString input_ID=this->ui->login_zh_Edit->text(); //获得单行编辑框的文本值
QString input_KEY=this->ui->login_key_Edit->text();
if(input_ID==id&&input_KEY==key)
{
qDebug()<<"登录成功" <<endl;
//获得当前时间 CSDN所学
QDateTime current_date_time =QDateTime::currentDateTime();
QString time=current_date_time.toString("yyyy.MM.dd hh:mm:ss.zzz ddd")+"登录成功";
//将登陆时间写入配置文件
QFile file("C://Users//fuqilingbuxiu//Desktop//课设//泡泡信息.txt");
if(! file.open(QIODevice::Append|QIODevice::Text)) //append追加,不会覆盖之前的文件
{
qDebug()<<"打开失败";
}
QTextStream out(&file);//写入
out<<time<<endl;
out<<endl;
file.close();
//BGM的播放设置
QMediaPlayer * player = new QMediaPlayer; //创建文件对象,播放bgm
//player->setMedia(QUrl::fromLocalFile("C:\\Users\\fuqilingbuxiu\\Desktop\\music\\y.mp3")); //绝对路径设置媒体文件
player->setMedia(QUrl::fromLocalFile("./y.mp3")); //相对路径设置媒体文件 .表示程序当前运行目录 ..表示上一级目录
player->setVolume(30); //设置音量
player->play(); //播放
this->hide();
s1.setWindowTitle("功能选择");
s1.setFixedSize(800,600); //设置固定大小
s1.setWindowIcon(QIcon(":/images/bubble.png"));
s1.show();
}
else{
s.show();
}
}
进行信息的校验,如果账号和密码都正确,那么关闭登录页面,并跳转至功能选择页面。
8、如何将相关的信息写入文件?
//将登陆时间写入配置文件
QFile file("C://Users//fuqilingbuxiu//Desktop//课设//泡泡信息.txt");
if(! file.open(QIODevice::Append|QIODevice::Text)) //append追加,不会覆盖之前的文件
{
qDebug()<<"打开失败";
}
QTextStream out(&file);//Qt文本流 与cout异曲同工
out<<time<<endl;
out<<endl;
file.close();
QFile为Qt中的文件类库
创建文件对象,指定其具体路径(绝对路径),设置打开模式为打开写。
通过QTextStream Qt文本流将字符串写入文件中。
9、如何获得当前时间?
QDateTime current_date_time =QDateTime::currentDateTime();
QString time=current_date_time.toString("yyyy.MM.dd hh:mm:ss.zzz ddd")+"登录成功";
10、点击按钮更换游戏区主题
首先在游戏区页面类中添加一个私有数据成员bcg作为控制参数
class Game_1 : public QWidget
{
Q_OBJECT
public:
explicit Game_1(QWidget *parent = nullptr);
~Game_1();
void mousePressEvent(QMouseEvent *event); //鼠标事件
void paintEvent(QPaintEvent *event); //设置背景图
private slots:
void on_btn_update_clicked(); //更改背景图按钮对应的槽函数
private:
Ui::Game_1 *ui;
int bcg=0; //背景图片的控制变量
};
相应的按钮槽函数
void Game_1::on_btn_update_clicked()
{
this->update(); //因为paintEvent函数会在页面刷新时自动调用
}
借助QPaintEvent的特性:在页面每次刷新时会自动调用!
void Game_1::paintEvent(QPaintEvent *event)
{
//背景图设置
//paintEvent事件函数在窗体构建和每次刷新时(update)自动调用
//bcg为游戏类内的私有数据成员 用于控制背景图片的切换
QPainter p(this);
if(this->bcg==0)
{
p.drawPixmap(0,0,width(),height(),QPixmap(":/images/d.jpg")); //绘制背景图片 CSDN
p.end();
this->bcg+=1;
}
else if(this->bcg==1)
{
p.drawPixmap(0,0,width(),height(),QPixmap(":/images/f.jpg"));
p.end();
this->bcg+=1;
}
else if(this->bcg==2)
{
p.drawPixmap(0,0,width(),height(),QPixmap(":/images/n.jpg"));
p.end();
this->bcg=3;
}
else if(this->bcg==3)
{
p.drawPixmap(0,0,width(),height(),QPixmap(":/images/p.jpg"));
p.end();
this->bcg=0;
}
}
这个逻辑虽然看似简单,但实则甚妙~!
四、程序后期处理及优化
1、如何播放BGM
本程序在登录成功后自动播放BGM
首先。播放BGM依托于QMediaPlayer
在项目的配置文件中加上
QT +=multimedia
在登录按钮槽函数下添加以下代码
//BGM的播放设置
QMediaPlayer * player = new QMediaPlayer; //创建文件对象,播放bgm
//player>setMedia(QUrl::fromLocalFile("C:\\Users\\fuqilingbuxiu\\Desktop\\music\\y.mp3")); //绝对路径设置媒体文件
player->setMedia(QUrl::fromLocalFile("./y.mp3"));
//相对路径设置媒体文件 .表示程序当前运行目录 ..表示上一级目录
player->setVolume(30); //设置音量
player->play(); //播放
2、如何更改程序的图标
在项目配置文件中加上
RC_ICONS=bubble.ico
其中bubble.ico为相应的ico图标文件,该文件需要放置在程序文件相应目录下
3、Qt资源文件的添加
在QtCreator中右键项目文件夹->add new->Qt->Qt resources file
Add Prefix 为添加前缀
Add Files 为添加文件
4、如何更改每一个窗体页面的图标以及文字
Login w; //创建登录页面对象
w.setWindowTitle("登录"); //设置页面标题
w.setWindowIcon(QIcon(":/images/bubble.png")); //更改页面的图标
w.show(); //展示页面
5、程序如何打包?
先将运行模式更改为release,然后打开项目下的build相关文件夹
将其中的exe文件拖拽至单独文件夹
打开Qt相关编译环境的命令行
先cd至前面的新文件夹的位置
然后运行 windeployqt 程序名.exe
再借助相关的打包封装工具即可成功
五、程序测试
不辞青山,相随与欢!