《数据结构、算法与应用 —— C++语言描述》学习笔记 — 栈 —— 应用 —— 迷宫老鼠
一、问题描述
迷宫是一个矩形区域,有一个入口和一个出口。迷宫内部包含不能穿越的墙壁或障碍物。这些障碍物沿着行和列防止,与迷宫的边界平行。迷宫的如果在左上角,出口在右下角。
我们可以使用一个 bool 矩阵来描述这个迷宫。当矩阵中某个位置有障碍时,其值为 ture,否则为 false。迷宫老鼠问题是要寻找一条从入口到出口的路径。路径是一个由位置组成的序列。每一个位置都没有障碍,而且除入口之外,路径上的每个位置是前一个位置在东、南、西、北上相邻的一个位置。
二、设计
我们采用自顶向下的方法设计这个程序。不难理解,这个程序有三个部分:输入迷宫,寻找路径和输出路径。书中使用三个模块实现这些部分。我们这里把输入和输出模块作为放在一起,除此之外,我们还有一个模块用于显示欢迎信息、软件名称及作者信息。为了简便,我们这里使用QT6.1.2 开发。
1、思路
该程序主体流程是:进入欢迎界面;让用户设置迷宫形状;根据用户的需要绘制迷宫;在用户点击寻找路径的按钮时绘制路径(支持多次绘制,暂不支持擦除)。
首先思考主体界面。主体界面自身有一个类。其实例中应该还有含有两个子对象。分别用于存放欢迎界面和迷宫界面(可以将设置界面独立)。由于这两个子对象不会同时显示,我们可以考虑使用 QStackedWidget 存放。
然后考虑欢迎界面。我们需要确认采取显示欢迎字样。这里我选择一个较为简单的淡入淡出的 label。由于该类可以由其它类复用,因此我们将公共接口提取并放在 util 文件夹中。
最后考虑迷宫界面。主体的布局采取左边为交互界面,右边为显式界面。比例为1:3。考虑到计算迷宫的策略可以动态切换,因此我们将计算迷宫的方式单独出来放在 business 文件夹中。二者之间的数据传递是一个 QVector<QVector> 用于表示地图每个矩形的状态。
2、类图
3、目录结构
[RatInMaze]
RatInMaze.pro
[document]
[output]
[src]
main.cpp
[business]
CLS_FindPathStack.h
CLS_FindPathStack.cpp
[ui]
CLS_MainWidget.h
CLS_MainWidget.cpp
CLS_MainWidget.ui
CLS_Maze.h
CLS_Maze.cpp
CLS_Maze.ui
CLS_Welcome.h
CLS_Welcome.cpp
CLS_Welcome.ui
[util]
CLS_LabelGradualChange.h
CLS_LabelGradualChange.cpp
三、实现
1、ui文件
CLS_MainWidget.ui
CLS_Welcome.ui
CLS_Maze.ui
2、淡入淡出 label
qt 中的 css 不支持设置动画,因此需要使用定时器或线程实现淡入淡出。我们这里选择使用线程计时。淡入淡出的基本原理就是不断地改变透明度。
类声明为:
#ifndef CLS_LABELGRADUALCHANGE_H
#define CLS_LABELGRADUALCHANGE_H
#include <QLabel>
class CLS_LabelGradualChange : public QLabel
{
Q_OBJECT
public:
explicit CLS_LabelGradualChange(QWidget *parent = nullptr);
~CLS_LabelGradualChange();
/*
* @brief 设置渐变颜色
* @param color 目标颜色
*/
void setColor(QColor color);
/*
* @brief 设置淡入淡出时长
* @param timeout 总时长
*/
void setTimeout(int timeout);
/*
* @brief 设置颜色变化的总次数
* @param timeout 总时长
*/
void setTotalTimes(int totalTimes);
signals:
void sigLoadFinished();
protected:
virtual void showEvent(QShowEvent *event) override;
private:
QString m_qstrStyleSheet; // 预设的样式表,需要使用.arg拼接透明度
int m_iTimeout = 0; // 淡入淡出时长
int m_iTotalTimes = 0; // 颜色变化的总次数
int m_iTimes = 0;
bool m_blShow = true;
void startShowThread(); // 线程中计时
private slots:
/*
* @brief 更新透明度槽
*/
void slotShow();
};
#endif // CLS_LABELGRADUALCHANGE_H
实现文件:
#include "CLS_LabelGradualChange.h"
#include <QColor>
#include <QThread>
const int CONST_SHOW_TIMES = 20; // 默认显示20次
CLS_LabelGradualChange::CLS_LabelGradualChange(QWidget *parent) :
QLabel(parent)
{
m_iTotalTimes = CONST_SHOW_TIMES;
}
CLS_LabelGradualChange::~CLS_LabelGradualChange()
{
}
void CLS_LabelGradualChange::setColor(QColor color)
{
m_qstrStyleSheet = styleSheet() + QString("color:rgba(%2,%3,%4,%5);").arg(color.red())
.arg(color.green()).arg(color.blue());
}
void CLS_LabelGradualChange::setTimeout(int timeout)
{
m_iTimeout = timeout;
}
void CLS_LabelGradualChange::setTotalTimes(int totalTimes)
{
if (totalTimes > 0)
{
m_iTotalTimes = totalTimes;
}
}
void CLS_LabelGradualChange::showEvent(QShowEvent*)
{
if (m_blShow)
{
startShowThread();
m_blShow = false;
}
}
void CLS_LabelGradualChange::startShowThread()
{
double dbInterval = double(m_iTimeout) / m_iTotalTimes;
QThread *pThread = QThread::create([dbInterval](){
QThread::msleep(dbInterval);});
connect(pThread, SIGNAL(finished()), this, SLOT(slotShow()));
pThread->start();
}
void CLS_LabelGradualChange::slotShow()
{
++m_iTimes;
double opacity = 2.0 * double(m_iTimes >= m_iTotalTimes / 2 ? m_iTotalTimes - m_iTimes : m_iTimes) / m_iTotalTimes;
setStyleSheet(m_qstrStyleSheet.arg(opacity));
if (m_iTimes < m_iTotalTimes)
{
startShowThread();
}
else
{
emit sigLoadFinished();
}
}
3、欢迎界面
类声明:
#ifndef CLS_WELCOME_H
#define CLS_WELCOME_H
#include <QWidget>
namespace Ui {
class CLS_Welcome;
}
class CLS_Welcome : public QWidget
{
Q_OBJECT
public:
explicit CLS_Welcome(QWidget *parent = nullptr);
~CLS_Welcome()</