这段时间为了日后的工作需要,遵循霍亚飞老师的《Qt Creator快速入门(第三版)》学了第一大章基础篇的知识,并根据所学的知识尝试性地将之前的迷宫最短路径问题进行了图形化界面的设计。由于本人学艺不精,暂时只能基本实现原函数的基本功能,日后若有时间再慢慢增补吧。

1. 基本介绍

目前这个程序的功能主要有五个:
(1)输入迷宫的尺寸:为便于调试,目前设计的为正方形的迷宫,即长宽尺寸相同。用户只需要输入一个数字,然后按下回车即可完成输入过程。
(2)显示迷宫:我将其快捷键(Ctrl+1)与加速键(Alt+1)均设计为1,这样便于操作,以下的两个键同理。其用于在TextEdit部件中将生成的迷宫,以图形化的界面显示出来。同时,在计算完最短路径后,用于显示迷宫的寻路过程与最短路径。
(3)计算最短路径:通过BFS的方式,在生成的迷宫中查找最短路径,在查找完成后,将其路径数据储存在一个QList容器中,用于之后的使用。
(4)显示最短路径:用于将储存的最短路径以文字的形式表述出来,其中包括沿途的所有节点的坐标,以及其运动方向。
(5)Reset:其对应左上角工具栏的返回按钮,以及菜单栏操作中的一个选项(目前只有这一个选项)。其用于重置当前程序中所有的与迷宫相关的变量,防止旧迷宫的数据对新迷宫的结果造成干扰。
以下为本程序的界面图(可见很简陋,因为我也不知道怎么把它变得好看起来(⊙o⊙)…)

2. 代码实现

下面是本项目的文件树,其整体构成较为简单,主要是先前的迷宫程序labyrinth.h和labyrinth.cpp,以及本项目的主窗口程序mainwindow.h和mainwindow.cpp,以及自带生成的主程序main.cpp。

2.1 迷宫程序labyrinth

迷宫的实现代码基本上根据上一篇博客修改得来的,为了方便我就直接将所有的声明与定义都放在了头文件里(当然这是不对的,只是因为我太懒而已)。
(1)具体的迷宫实现仍然是借用邓老师的程序,对laby数组进行随机状态分配从而得到迷宫整体,此处由于没有了主函数,所以在随机生成迷宫的开头添加了qsrand函数来根据时间重置随机种子;
(2)最短路径的实现过程仍然是BFS的步步遍历;
(3)最短路径的再现过程仍是通过终点的数据不断反推,不过这次我删除了其中的边推边输出的情况,而是将最短路径储存到了新的shortpath列表中,便于之后的再利用。
同时,我删除了之前的迷宫显示函数,因为这次是需要在ui中直接输出了,为了简便起见,我将输出的过程都转移到了“显示迷宫”的按钮信号槽中(这种方式似乎并不是很棒,但是可以有效地解决问题)。
以下为labyrinth.h文件,labyrinth.cpp文件我嫌麻烦没有将定义转过去。

#ifndef LABYRINTH_H
#define LABYRINTH_H#include <iostream>
#include <deque>
#include <QTime>
#include <QDebug>
#include <QList>
using namespace std;//这是最小路径的实现函数,通过BFS方式步步遍历的到;如果使用DFS算法,需要穷尽所有的到达终点的路径,最后取最短的/*迷宫寻径主流的三大算法:广度/深度优先搜素算法,以及A*算法*/
/*相对而言,深度优先搜索是最适合迷宫最短路径寻径的,通过一轮一轮的扁铝,找到的第一条路径也就是最短的路径*/
typedef enum { AVAILABLE, ROUTE, BACKTRACKED, WALL } Status;
typedef enum { UNKNOWN, EAST, SOUTH, WEST, NORTH, NO_WAY } ESWN;
inline ESWN nextESWN(ESWN eswn) { return ESWN(eswn + 1); }
int labySize;  //此处借用dascpp中邓公的随机迷宫生成程序struct Cell
{int x, y = 0;Status status = AVAILABLE;  //xy的坐标与类型ESWN incoming, outgoing = UNKNOWN;  //进入的方向与出去的方向Cell *prev;               //运行BFS时建立前缀,用于建图形成反推
};#define LABY_MAX 40
Cell laby[LABY_MAX][LABY_MAX];
int ncheck, nback, length;
Cell* startCell;
Cell* goalCell;inline Cell *neighbor(Cell *cell) //移动的探测,即得到当前cell的邻居,根据outgoing确定方向
{switch (cell->outgoing){case EAST:return cell + LABY_MAX;case SOUTH:return cell + 1;case WEST:return cell - LABY_MAX;case NORTH:return cell - 1;default:exit(-1); //如果不是这四个方向,即UNKNOWN和NO_WAY,则直接退出这个switch循环}
}inline Cell* advance(Cell* cell)  //实质性的移动,根据cell的incoming移动当前cell到对应的cell
{Cell *next;switch (cell->outgoing){case EAST:next = cell + LABY_MAX; next->incoming = WEST; next->x = cell->x + 1; break;  //这里的操作意思是,现节点的进入为西,即相当于原节点的出是东case SOUTH:next = cell + 1;       next->incoming = NORTH; next->y = cell->y + 1; break;case WEST:next = cell - LABY_MAX; next->incoming = EAST; next->x = cell->x - 1; break;case NORTH:next = cell - 1;         next->incoming = SOUTH; next->y = cell->y - 1; break;default: exit(-1);}return next;
}QList<Cell*> shortestpath;
void inline shortest_path()  //此函数用于根据传递到终点goalCell的数据,来进行路径反推
{Cell *c = goalCell;c->status = ROUTE;   //所有反推的路径全部将状态改为ROUTE,便于显示路径shortestpath.push_front(c);while (c->incoming)  //一直反推到最短路径的初始点,即起点,起点的incoming是=0的{length++;        //length开始循环计数auto in = c->incoming;c = c->prev;switch (in)        //根据上一个cell的incoming,来反推出当前cell的outgoing,相对取反即可{case EAST:  c->outgoing = WEST;   break;case SOUTH: c->outgoing = NORTH;   break;case WEST: c->outgoing = EAST;   break;case NORTH: c->outgoing = SOUTH;   break;default: exit(-1);}c->status = ROUTE;shortestpath.push_front(c);}//cout << "shortest path's long is " << length + 1 << endl;  //由于终点是在循环外面做的,所以此处需要加1
}bool bfs(Cell Laby[LABY_MAX][LABY_MAX], Cell *s, Cell *t)
{if ((AVAILABLE != s->status) || (AVAILABLE != t->status)) return false;  //首先,起点和终点必须是能访问的deque<Cell*> bfs_path;   //采用BFS算法,所以这里改为使用队列结构s->incoming = UNKNOWN; s->status = ROUTE; bfs_path.push_back(s);  //将起点的进入点设为无,然后状态设为在路径上,最后入队列do{Cell *c = bfs_path.front();bfs_path.pop_front();if (c == t){t = c;    //当达到终点时,将此时的c传递给goalCell,因为其中储存了其所有的prev前缀return true;}while (NO_WAY != (c->outgoing = nextESWN(c->outgoing)))   //此处改为遍历当前cell的所有方向一次{if (AVAILABLE == neighbor(c)->status)  //只要cell的一个方向可以,就将其入队{Cell* temp = advance(c);temp->outgoing = UNKNOWN; temp->status = ROUTE;temp->prev = c;                     //每个从当前cell出去的cell,都将原cell设为前缀,由此实现当前图(树)结构的实现bfs_path.push_back(temp);ncheck++;}}c->status = BACKTRACKED;  //而被bfs过的cell,借用BACKTRACKED状态,表示其已经被扫描过但没有到达终点} while (!bfs_path.empty());return false;
}void randLaby(int size)    //根据输入的size生成随机的迷宫
{qsrand(QTime(0,0,0).secsTo((QTime::currentTime())));labySize = size;  //生成一个输入size的迷宫for (int i = 0; i < labySize; i++)for (int j = 0; j < labySize; j++){laby[i][j].x = i;laby[i][j].y = j;laby[i][j].incoming =laby[i][j].outgoing = UNKNOWN;laby[i][j].status = WALL; //边界格点必须是墙}for (int i = 1; i < labySize - 1; i++)for (int j = 1; j < labySize - 1; j++)if (rand() % 3) laby[i][j].status = AVAILABLE; //75%的格点为空可用,增加迷宫难度在此酌情修改startCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];goalCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];startCell->status = goalCell->status = AVAILABLE; //起始格点必须可用
}#endif // LABYRINTH_H

2.2 主窗口程序mainwindow

主窗口程序,主要是在ui中拉出了各个输入框、按钮以及按键等等,在其中主要做的工作是定义各个按钮的信号到槽的链接,并定义了两个私有变量整数size和reset函数,分别用于存储用户输入的size数值,与实现整体的重置工作。
以下为mainwindow.h的代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QList>namespace Ui {class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:void on_lineEdit_returnPressed();void on_pushButton_clicked();void on_pushButton_2_clicked();void on_pushButton_3_clicked();void on_actionReset_triggered();private:Ui::MainWindow *ui;int size;void reset();
};#endif // MAINWINDOW_H

以下为mainwindow.cpp的代码,其定义了头文件中声明的五个信号槽函数,以及一个重置reset函数。
(1)reset函数:对应迷宫寻路过程中的shortpath最短路径,中心部件TextEdit,以及最短路径长度length,均将其重置。同时对应于迷宫构成数组laby,设置空Cell单元将其全部赋值,从而实现重置过程。
(2)on_pushButton_clicked函数:与按钮1(显示迷宫)点击信号关联的槽函数,其基本沿用了原displaylaby函数的整体类型。但同时也对其做了适当的优化:由于Qt 的TextEdit特殊字符似乎无法显示的问题,我将原函数的显示符号均改成了常规符号,这样导致本次的图形化界面没有之前的好看(我也很无奈。。);同时将行号与列号具体地显示出来;添加了起点的符号显示,不再只有终点的特殊符号。
(3)on_pushButton_clicked2函数:与按钮2(计算最短路径)点击信号关联的槽函数,其基本沿用了原算法的主函数板块,主要是调用bfs函数计算最短路径后使用shortpath来储存。如果可以计算出最短路径,则在中心部件上打出起点终点以及最短路径的长度。否则说明从起点无法走到终点(即路径都不存在)。
(4)on_pushButton_clicked2函数:与按钮3(显示最短路径)点击信号关联的槽函数。其通过遍历shortpath中存储的每个节点,根据“进入方向-> 节点坐标 -> 离开方向”的形式来输出最短路径上的各个节点。
(5)on_actionReset_triggered函数:其对应的是主界面中的动作Reset,主要就是调用已经定义的reset函数来实现对程序的整体重置。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "labyrinth.h"
#include <QFont>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::reset()
{ui->textEdit->clear();shortestpath.clear();length = 0;Cell zero;startCell = &zero;goalCell = &zero;for(int i =0; i<LABY_MAX;i++)for(int j =0; j<LABY_MAX;j++)laby[i][j]=zero;
}void MainWindow::on_lineEdit_returnPressed()
{size = ui->lineEdit->text().toInt();randLaby(size);ui->textEdit->textCursor().insertText(tr("This laby's size is %1 \n").arg(size));//重置最短路径shortestpath.clear();length = 0;
}void MainWindow::on_pushButton_clicked()
{static char* pattern[5][5] ={" +", " +", " +", " +", " +"," +", "  ", " =", " -", " ="," +", " =", "  ", " =", " |"," +", " -", " =", "  ", " ="," +", " =", " |", " =", "  "};ui->textEdit->textCursor().insertText("  ");for (int j = 0; j < labySize; j++)(j < 10) ? ui->textEdit->textCursor().insertText(tr(" %1").arg(j)) : ui->textEdit->textCursor().insertText(tr("%1").arg(j));ui->textEdit->textCursor().insertText("\n");for (int j = 0; j < labySize; j++){(j < 10) ? ui->textEdit->textCursor().insertText(tr(" %1").arg(j)) : ui->textEdit->textCursor().insertText(tr("%1").arg(j));for (int i = 0; i < labySize; i++){if ((goalCell == &laby[i][j])||(startCell == &laby[i][j])){if (goalCell == &laby[i][j])ui->textEdit->textCursor().insertText(" $");elseui->textEdit->textCursor().insertText(" #");}elseswitch (laby[i][j].status){case WALL:  ui->textEdit->textCursor().insertText("[]");   break;case BACKTRACKED: ui->textEdit->textCursor().insertText("<>");   break;case AVAILABLE: ui->textEdit->textCursor().insertText("  ");   break;default: ui->textEdit->textCursor().insertText(tr("%1").arg(pattern[laby[i][j].outgoing][laby[i][j].incoming]));  break;//老师这里的代码%s后面没有空格,需要加上,不然迷宫会乱掉}}ui->textEdit->textCursor().insertText("\n");}
}void MainWindow::on_pushButton_2_clicked()
{if (bfs(laby, startCell, goalCell))  //判断当前迷宫能否从起点走到终点,如果行的话,输出如下{qDebug() << "true";ui->textEdit->textCursor().insertText(tr("This laby's start point is (%1,%2), end point is (%3,%4)\n").arg(startCell->x).arg(startCell->y).arg(goalCell->x).arg(goalCell->y));shortest_path();//输出当前迷宫从起点到终点的最短路径与长度ui->textEdit->textCursor().insertText(tr("The shortest path's distance is %1 \n").arg(length+1));}else{qDebug() << "false";ui->textEdit->textCursor().insertText(tr("This laby can't go out \n"));}
}void MainWindow::on_pushButton_3_clicked()
{foreach (Cell* c, shortestpath){ui->textEdit->textCursor().insertText(tr("%1 -> (%2, %3) -> %4 \n").arg(c->incoming).arg(c->x).arg(c->y).arg(c->outgoing));}
}void MainWindow::on_actionReset_triggered()
{reset();
}

3. 程序的运行实况

如下图所示为本程序的运行情况,通过“输入尺寸”—“显示迷宫”—“计算最短路径”—“显示最短路径”—“显示迷宫”的操作之后,我们得到的程序界面如图所示。可见其基本实现了原迷宫程序的基本功能,同时根据重置功能与不断更新随机数生成不同的迷宫,实现无数个迷宫的最短路径图形化界面的探寻过程。

Qt图形化界面—迷宫最短路径问题相关推荐

  1. 基于Qt的ui图形化界面进行的界面设计

    qt初学者往往会发现这样的一个问题--无论是我买的相关的书,还是网上博客的内容,基本全都是利用纯代码的形式来进行界面设计的.而初学者对各种控件的代码实现并不熟悉,往往在这里耗费大量时间.故本篇文章介绍 ...

  2. 迷宫游戏(图形化界面)

    迷宫游戏 本程序的功能为实现迷宫游戏.打开游戏,系统弹出游戏菜单界面.玩家可以选择开始游戏,游戏设置,退出游戏.玩家选择开始游戏时,系统自动生成一个规格为10*10,入口为左上角,出口为右下角且从入口 ...

  3. python 视频播放界面_PyQt转换显示Python-OpenCV图像实现图形化界面的视频播放

    一.引言 在Python-OpenCV中显示图像时调用的是一个单独的窗口,有时我们需要将这些图像显示在PyQt的图形化界面上,这样就可以将整个图像显示与PyQt图形化界面进行整合.但OpenCV格式的 ...

  4. 现今主流计算机语言,现今主流的Python图形化界面主要有哪些

    现今主流的Python图形化界面主要有哪些 发布时间:2020-10-23 20:08:59 来源:亿速云 阅读:114 作者:小新 这篇文章将为大家详细讲解有关现今主流的Python图形化界面主要有 ...

  5. python图形用户界面pyside_PySide——Python图形化界面入门教程(一)

    标签: PySide--Python图形化界面入门教程(一) --基本部件和HelloWorld 原文链接:http://pythoncentral.io/intro-to-pysidepyqt-ba ...

  6. qpython3可视图形界面_PySide——Python图形化界面入门教程(三)

    PySide--Python图形化界面入门教程(三) --使用内建新号和槽 --Using Built-In Signals and Slots 上一个教程中,我们学习了如何创建和建立交互widget ...

  7. 图形化界面设计软件简要介绍

    图形化界面设计软件简要介绍 [摘要]:面向使用者的系统工程设计,其目的是要准确.快速地在设计者和使用者之间传递信息和实现功能,也是优化产品性能来适应大众的操作能力,减轻使用者的认知负担.成功的系统工程 ...

  8. 如何让树莓派启动实现图形化界面和命令行模式的切换从而解决两个光标的问题

    前言:由于之前烧录到树莓派的镜像文件是图形化界面的,当我把QT工程交叉编译后的可执行文件在树莓派上运行时.有两个界面,重点是有两个光标,会导致当使用触摸屏时会有两个响应,使屏幕错乱.此时想到了两个解决 ...

  9. 13_Python基础_Python图形化界面

    Python图形化界面     一.Python中的图形彷界面开发库 Python中的图形化用户界面开发库有比较多,较为常用的有Tkinter,PyQt,wxPython. Tkinter Tkint ...

最新文章

  1. 熟悉常用的Linux操作
  2. 重新定位svn地址的方法(windows和linux),svn switch(sw)的帮助信息
  3. 谈行业数字化转型,先要搞明白ICT生态的共赢共生
  4. hdu6165(拓扑排序+tarjan缩点)
  5. 最简便的备份MySql数据库方法
  6. 08--MySQL自学教程:DQL(数据库查询)字段控制查询、聚合函数、分组查询、limit(二)
  7. TCP/IP / IP 头
  8. mrc20温控f1什么意思_温控器的“总、高、低”是什么意思?不知道?民熔老电工告诉你...
  9. antd权限管理_推荐6款超好看实用的管理后台模版
  10. 2019-06-04 Sublime Text 中文输入法的问题
  11. 在Vue项目中引入echarts图表的方法(引入cdn)
  12. 威纶触摸屏485轮询通讯_威纶触摸屏Modbus TCP\RTU\ASCII通信视频教程
  13. python程序员待遇如何-程序员工资大揭秘:你拖后腿了吗?
  14. linux etc xdg,Xdg-menu (简体中文)
  15. Latex(katex)csdn 希腊字母表示,数学符号,集合符号,特殊标记
  16. h5 life.html,H5 交互页编辑器 AEditor 介绍
  17. 自动客服功能的微信小程序
  18. AirPods 2支持无限充电只要15分钟充满
  19. 用jar包生成maven依赖
  20. 抢票原理通俗解释,​候补购票是什么?你还在交智商税吗?

热门文章

  1. Python通过MQTT协议上传物联网数据给ThingsBoard
  2. php ses 发送邮件,Amazon SES – 通过PHP sdk发送HTML邮件
  3. 按键按动次数计数c语言,二、Windows按键消息—重复计数、OEM扫描码、扩充键旗标、内容代...
  4. Component Object Model (COM)
  5. SNMP简单网络配置协议
  6. 字典的基础及字典的使用实例
  7. 基于Android的招聘求职网站的设计与实现
  8. linux后台运行服务
  9. 快捷键-vscode-excel
  10. 关于企业软件资质申请流程以及时间规划(一)——软件著作权申请