第一次C++实训报告
第一次实训工作报告
本周工作小结:
对用户地图进行了文件存储操作
开始自学QT,实现了一个简单的GUI界面,包含主页面和多个子页面
初步添加了流星动画和登录功能
实现了主页面和子页面之间的跳转
本项目基于QT进行开发,操作系统为Windows 11,开发环境为Qt Creator 13.0.0
前置工作
进行了QT的学习,主要包括
Qt 概述
创建 Qt 项目
2.1 使用向导创建
2.2. 手动创建
2.3 pro 文件
2.4 一个最简单的 Qt 应用程序
信号和槽机制 (listener)
Qt 窗口系统
Qt 消息机制和事件
1.【项目架构与设计思路】
整体项目采用了MVC(Model-View-Controller)设计模式,这是一种经典的软件架构模式,有助于代码的组织和维护。
Model: 负责数据存储和处理,例如用户信息、游戏设置和排名数据的管理。
View: 作为用户界面的表示,用Qt的UI组件来构建和展示,包括主页面、设置页面、练习模式、比赛模式和玩家排行页面。
Controller: 负责处理用户输入,更新Model和View的数据状态,实现业务逻辑。例如,按钮点击事件会触发Controller的相应方法,进行页面切换或功能执行。
2.【关键技术点】
界面切换: 利用QStackedWidget组件实现页面切换,用户可以轻松地在不同页面间导航。
页面切换接口说明;
- showSettingsPage(): 切换到设置页面。
- on_ReturnToMainPage(): 返回主页面。
- showPracticePage(): 切换到练习模式页面。
- showCompetitionPage(): 切换到比赛模式页面。
- showPlayerRankingPage(): 切换到玩家排行页面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void Widget::showSettingsPage()
{
stackedWidget->setCurrentIndex(1); // 切换到设置页面
}
void Widget::on_ReturnToMainPage()
{
stackedWidget->setCurrentIndex(0); // 切换回主页面
}
void Widget::showPracticePage()
{
stackedWidget->setCurrentIndex(2); // 切换到 PracticePage
}
void Widget::showCompetitionPage()
{
stackedWidget->setCurrentIndex(3); // 切换到 CompetitionPage
}
void Widget::showPlayerRankingPage()
{
stackedWidget->setCurrentIndex(4); // 切换到 PlayerRankingPage
}
连接按钮点击事件与连接返回主页面信号:
1
2
3
4
5
6
7
8
9
10// 连接按钮点击事件
connect(settingsButton, &QPushButton::clicked, this, &Widget::showSettingsPage);
connect(practiceButton, &QPushButton::clicked, this, &Widget::showPracticePage);
connect(competitionButton, &QPushButton::clicked, this, &Widget::showCompetitionPage);
connect(playerRankingButton, &QPushButton::clicked, this, &Widget::showPlayerRankingPage);
// 连接返回主页面信号
connect(playerRankingPage, &PlayerRankingPage::returnToMainPage, this, &Widget::on_ReturnToMainPage);
connect(settingsPage, &SettingsPage::returnToMainPage, this, &Widget::on_ReturnToMainPage);
connect(practicePage, &PracticePage::returnToMainPage, this, &Widget::on_ReturnToMainPage);
connect(competitionPage, &CompetitionPage::returnToMainPage, this, &Widget::on_ReturnToMainPage);
小结:通过这些连接,用户可以通过点击不同的按钮来切换到相应的页面,并且在子页面中可以轻松地返回到主页面,实现了界面的流畅交互和导航功能。
流星动画: 使用QTimer定时器不断更新QPixmap的位置,通过QPainter进行绘制,实现流星动画效果。
初始化流星位置: 在
Widget
构造函数中,使用QRandomGenerator
随机生成了5个流星的初始位置,并存储在meteorPositions
列表中。1
2
3
4
5
6
7cppCopy codeconst int numMeteors = 5;
for (int i = 0; i < numMeteors; ++i)
{
qreal x = QRandomGenerator::global()->bounded(width());
qreal y = QRandomGenerator::global()->bounded(height());
meteorPositions.append(QPointF(x, -meteorPixmap.height()));
}定时器更新流星位置: 在构造函数中,创建一个
QTimer
,并设置每100毫秒触发一次的超时信号连接到animateMeteors
槽函数。在这个函数中,更新了所有流星的位置。1
2
3cppCopy codemeteorTimer = new QTimer(this);
connect(meteorTimer, &QTimer::timeout, this, &Widget::animateMeteors);
meteorTimer->start(100);流星位置更新与边界检测: 在
animateMeteors
函数中,遍历所有的流星位置,并更新它们的坐标。如果流星移出了窗口或超出了窗口的底部,重新设置它们的位置。1
2
3
4
5
6
7
8
9
10
11
12
13cppCopy codefor (int i = 0; i < meteorPositions.size(); ++i)
{
QPointF &pos = meteorPositions[i];
pos.setY(pos.y() + 10);
pos.setX(pos.x() - 6);
if (pos.x() < -meteorPixmap.width() || pos.y() > height())
{
qreal x = QRandomGenerator::global()->bounded(width());
pos.setX(x);
pos.setY(-meteorPixmap.height());
}
}绘制流星: 在
paintEvent
函数中,使用QPainter
绘制所有流星。为了使绘制更加流畅,设置了抗锯齿功能。1
2
3
4
5
6
7
8
9
10
11
12cppCopy codevoid Widget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
for (int i = 0; i < meteorPositions.size(); ++i)
{
QPointF pos = meteorPositions[i];
painter.drawPixmap(pos.toPoint(), meteorPixmap);
}
}
不过流星的流动效果和适配的图片还不是很令人满意后期会进行修改
用户登录: 使用QString存储当前登录的用户名和登录时间,为用户提供个性化体验。
定义成员变量: 在
Widget
类的私有成员中定义了username
和loginTime
两个变量,用于存储当前登录的用户名和登录时间。1
2
3cppCopy codeprivate:
QString username;
QString loginTime;登录按钮创建: 在构造函数中,创建了一个名为
loginButton
的QPushButton
,并设置了其样式和位置。1
2
3
4cppCopy codeQPushButton *loginButton = new QPushButton("登录", this);
loginButton->setStyleSheet("font-family: \"Microsoft YaHei\"; font-size: 30px; background-color: transparent; color: black;");
loginButton->setFixedSize(100, 50);
loginButton->move(1400, 80);登录对话框:
showLoginDialog
函数被用作登录按钮的槽函数。当用户点击登录按钮时,该函数将弹出一个输入对话框,提示用户输入用户名。1
2
3
4
5
6
7
8
9
10
11cppCopy codevoid Widget::showLoginDialog()
{
bool ok;
QString text = QInputDialog::getText(this, tr("登录"), tr("用户名:"), QLineEdit::Normal, "", &ok);
if (ok && !text.isEmpty())
{
username = text;
loginTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
updateSidebar();
}
}更新侧边栏内容:
updateSidebar
函数用于更新侧边栏上显示的用户名和登录时间。当用户成功登录后,这些标签的文本将被更新以显示新的用户名和登录时间。1
2
3
4
5
6
7
8cppCopy codevoid Widget::updateSidebar()
{
if (!username.isEmpty())
{
usernameLabel->setText("用户名: " + username);
loginTimeLabel->setText("登录时间: " + loginTime);
}
}
登录窗口效果展示:
侧边效果一览:
麻烦的问题:
- 虽然本知道万事开头难,但一开始还是毫无进展了好几天,卡在了创建项目时候设置按钮的按钮重叠问题上,后来经过2天的排查才发现是创建项目时候时候选择基类是QMainWindow导致的,这种游戏理应是选择更为简洁的QWidget,其次在学习QT时候也是使用的QWidget,因此真是一个不该犯的错误啊。
- 第二个难关就是实现了按钮的跳转了,一开始采取的是
this->show
和this->hide
的写法,但始终不满意,因为窗口之间的切换不够平滑,经过多种方法的尝试后最后使用了QStackedWidget的方法,终于满足了自己的要求。 - 此外就是各种奇怪的布局问题了,由于对各个布局的深入理解不够,导致一开始的布局奇丑无比,后来经过各种修图和布局的更换,才有了上方那个较为满意的效果。