一、概述
本次博客是为了记录大三时期做的一个实训,用QT开发的,感觉这个小软件用到了不少QT里面的东西,现在记录一下,以后需要用的时候看一下就很快能上手了。这里尤其是对信号槽,绘制更新等用得比较多。
二、功能和算法分析
本次小程序实现的功能有:
1. 输入行数和列数,自动生成网格棋盘;
2. 通过交互来设置起点和终点;
3. 寻找到最短的路径并一步一步动态的显示路径;
4. 可以读取棋盘文件来自动生成棋盘。
算法:
要找最短路径,很简单的就是使用广度优先搜索算法,利用队列来寻找最短路径,使用数组来存储棋盘,同时标记是否已经遍历过。
三、工程结构和目录
使用的基本框架为dialog,基本所有的代码都是在dialog.cpp文件里编写。dialog.h里要声明很多函数和变量,还有很重要的槽函数,这些函数都要在dialog.cpp文件里实现出来。dialog.ui就是通过QT的界面绘制方式自动生成的文件,下面的Resources文件里存储的都是在界面中用到的图片文件。
四、代码
这里就直接上代码吧,最主要的目的还是为了找个地方存一下这些代码,为了以后用到相似的功能时可以更加快速的上手。
1. .pro文件(QT)
#-------------------------------------------------
#
# Project created by QtCreator 2018-01-08T09:39:37
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = AlgorithmAnalisys
TEMPLATE = app
SOURCES += main.cpp\
dialog.cpp
HEADERS += dialog.h
FORMS += dialog.ui
RESOURCES += \
icon_1.qrc \
start.qrc \
final.qrc \
jump.qrc \
file.qrc \
finish.qrc
这里注意到,要在.pro文件中添加RESOURCES的路径。
2. dialog.h文件
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QQueue>
#include <QStack>
#include <QTime>
#include <QVector>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
int rows=0;
int cols=0;
int N;
int flag=-1;
int rect_num1=-1;
int rect_num2=-1;
QQueue<int> queue;
QStack<int> stack;
int rect_jumpNo=-1;
int l=0;
QTimer *timer;
int flag1=0;
private:
Ui::Dialog *ui;
QRect rect[1000];
int a[1000];
int b[500];
void mousePressEvent(QMouseEvent *);
void paintEvent(QPaintEvent *);
private slots:
void finish();
void final();
void start();
void jump();
void update_jumpNo();
void selectFile();
};
#endif // DIALOG_H
3. dialog.cpp文件
#include "dialog.h"
#include "ui_dialog.h"
#include <QRect>
#include <QPainter>
#include <iostream>
#include <QMouseEvent>
#include <QTimer>
#include <QString>
#include <QFileDialog>
#include <QMessageBox>
#include <QFile>
#include <QTextStream>
#include <QChar>
#include <QDebug>
using namespace std;
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(finish()));
connect(ui->start,SIGNAL(clicked()),this,SLOT(start()));
connect(ui->final_2,SIGNAL(clicked()),this,SLOT(final()));
connect(ui->btn_jump,SIGNAL(clicked()),this,SLOT(jump()));
connect(ui->btn_inputfile,SIGNAL(clicked()),this,SLOT(selectFile()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::LeftButton)
{
for(int i=0;i<N;i++)
{
if(rect[i].contains(e->pos()))
{
if(flag==0)
{
rect_num1=i;
a[(rect_num1/cols+2)*(cols+4)+rect_num1%cols+2]=-2;
}
if(flag==1)
{
rect_num2=i;
a[(rect_num2/cols+2)*(cols+4)+rect_num2%cols+2]=-3;
}
break;
}
}
}
update();
}
void Dialog::paintEvent(QPaintEvent *)
{
QPainter paint(this);
if(flag==-1)
{
paint.setBrush(Qt::white);
paint.drawRects(rect,N);
}
if(flag==0)
{
paint.setBrush(Qt::white);
paint.drawRects(rect,N);
paint.setBrush(Qt::blue);
paint.drawRect(rect[rect_num1]);
}
if(flag==1)
{
paint.setBrush(Qt::white);
paint.drawRects(rect,N);
paint.setBrush(Qt::blue);
paint.drawRect(rect[rect_num1]);
paint.setBrush(Qt::white);
paint.setBrush(Qt::red);
paint.drawRect(rect[rect_num2]);
}
if(flag==4)
{
paint.setBrush(Qt::black);
paint.drawRect(rect[rect_jumpNo]);
}
//cout<<"Hello"<<endl;
}
void Dialog::finish()
{
stack.clear();
disconnect(timer,SIGNAL(timeout()),this,SLOT(update_jumpNo()));
if(flag1==0)
{
rows=ui->lineEdit->text().toInt();
cols=ui->lineEdit_2->text().toInt();
}
else
flag1=0;
N=rows*cols;
int width=ui->widget->width()/cols;
int height=ui->widget->height()/rows;
int x=ui->widget->x();
int y=ui->widget->y();
for(int i=0;i<2;i++)
{
for(int j=0;j<cols+4;j++)
a[i*(cols+4)+j]=-1;
}
for(int i=rows+2;i<rows+4;i++)
{
for(int j=0;j<cols+4;j++)
a[i*(cols+4)+j]=-1;
}
for(int i=2;i<rows+2;i++)
{
for(int j=0;j<2;j++)
a[i*(cols+4)+j]=-1;
for(int j=cols+2;j<cols+4;j++)
a[i*(cols+4)+j]=-1;
}
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
rect[i*cols+j]=QRect(x,y,width,height);
a[(i+2)*(cols+4)+j+2]=0;
x+=width;
}
x=ui->widget->x();
y+=height;
}
flag=-1;
update();
}
void Dialog::start()
{
flag=0;
}
void Dialog::final()
{
flag=1;
}
void Dialog::jump()
{
queue.clear();
stack.clear();
disconnect(timer,SIGNAL(timeout()),this,SLOT(update_jumpNo()));
bool finish=false;
int start=-2;
int final=-3;
int current_pos;
for(int i=0;i<(rows+4)*(cols+4);i++)
{
if(a[i]==-2)
{
start=i;
queue.push_back(i);
break;
}
}
for(int i=0;i<(rows+4)*(cols+4);i++)
{
if(a[i]==-3)
{
final=i;
break;
}
}
ui->label_3->setText("正在搜寻路径...");
while(!queue.isEmpty())
{
int pos=queue.dequeue();
// queue.pop_front();
current_pos=pos;
cout<<pos<<" ";
if(pos==final)
{
stack.push(final);
int lastStep=final;
while(true)
{
lastStep=a[lastStep];
if(lastStep==start)
{
finish=true;
break;
}
else
{
stack.push(lastStep);
}
}
break;
}
int next_pos1=(current_pos/(cols+4)-2)*(cols+4)+(current_pos%(cols+4)+1);
if(a[next_pos1]==0||a[next_pos1]==-3)
{
queue.enqueue(next_pos1);
a[next_pos1]=current_pos;
}
int next_pos2=(current_pos/(cols+4)-1)*(cols+4)+(current_pos%(cols+4)+2);
if(a[next_pos2]==0||a[next_pos2]==-3)
{
queue.enqueue(next_pos2);
// queue.push_back(next_pos2);
a[next_pos2]=current_pos;
}
int next_pos3=(current_pos/(cols+4)+1)*(cols+4)+(current_pos%(cols+4)+2);
if(a[next_pos3]==0||a[next_pos3]==-3)
{
queue.enqueue(next_pos3);
a[next_pos3]=current_pos;
}
int next_pos4=(current_pos/(cols+4)+2)*(cols+4)+(current_pos%(cols+4)+1);
if(a[next_pos4]==0||a[next_pos4]==-3)
{
queue.enqueue(next_pos4);
a[next_pos4]=current_pos;
}
int next_pos5=(current_pos/(cols+4)+2)*(cols+4)+(current_pos%(cols+4)-1);
if(a[next_pos5]==0||a[next_pos5]==-3)
{
queue.enqueue(next_pos5);
a[next_pos5]=current_pos;
}
int next_pos6=(current_pos/(cols+4)+1)*(cols+4)+(current_pos%(cols+4)-2);
if(a[next_pos6]==0||a[next_pos6]==-3)
{
queue.enqueue(next_pos6);
a[next_pos6]=current_pos;
}
int next_pos7=(current_pos/(cols+4)-1)*(cols+4)+(current_pos%(cols+4)-2);
if(a[next_pos7]==0||a[next_pos7]==-3)
{
queue.enqueue(next_pos7);
a[next_pos7]=current_pos;
}
int next_pos8=(current_pos/(cols+4)-2)*(cols+4)+(current_pos%(cols+4)-1);
if(a[next_pos8]==0||a[next_pos8]==-3)
{
queue.enqueue(next_pos8);
a[next_pos8]=current_pos;
}
}
if(finish)
{
timer=new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(update_jumpNo()));
timer->start(1000);
flag=4;
// int i=0;
// flag==4;
// while(!stack.isEmpty())
// {
// b[i]=stack.top();
// stack.pop();
// // rect_jumpNo=(b[i]/(cols+4)-2)*cols+b[i]%(cols+4)-2;
// cout<<b[i]<<" ";
// }
// cout<<endl;
}
else
ui->label_3->setText("无法顺利到达终点");
}
void Dialog::update_jumpNo()
{
if(!stack.isEmpty())
{
b[l]=stack.top();
stack.pop();
rect_jumpNo=(b[l]/(cols+4)-2)*cols+b[l]%(cols+4)-2;
update(QRegion(rect[rect_jumpNo]));
l++;
}
else
{
QString str=QString::number(l);
ui->label_3->setText(("顺利到达终点,最短需要\n跳"+str+"步"));
disconnect(timer,SIGNAL(timeout()),this,SLOT(update_jumpNo()));
ui->lineEdit->setText("");
ui->lineEdit_2->setText("");
l=0;
}
}
void Dialog::selectFile()
{
flag1=1;
QString fileName = QFileDialog :: getOpenFileName(this, NULL, NULL, "*.h *.cpp *.txt");
//---打开文件并读取文件内容
QFile file(fileName);
//--打开文件成功
if (file.open(QIODevice ::ReadOnly | QIODevice ::Text))
{
QTextStream textStream(&file);
QString str;
rows=0;
cols=0;
str=textStream.readLine();
rows++;
for( int i=0;i<str.length();i++)
{
if(str.at(i)==' ')
{
cols++;
}
}
while(!textStream.atEnd())
{
str=textStream.readLine();
rows++;
}
ui->label_3->setText("已读入文件");
}
else //---打开文件失败
{
QMessageBox ::information(NULL, NULL, "open file error");
}
}
4. main.cpp文件
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
5. UI界面
五、效果演示
1. 设置行数和列数,绘制棋盘;设置起点和终点,起点为蓝色,终点为红色。
先输入行数和列数,然后点击绘制棋盘按钮。然后点击设置起点按钮,并在右侧棋盘中选择一个方格作为起点,同样再选择一个终点。
2. 点击开始跳按钮
3. 路径寻找完毕