目录
第二部分 综合应用设计说明 1
1.1 题目 1
1.2 软件功能 1
1.3 设计思想 1
1.4 逻辑结构与物理结构 3
2.4.1 站点 4
2.4.2 线路 4
2.4.3 后端实现类 5
1.5 开发平台 7
1.6 系统的运行结果分析说明 7
2.6.1 调试及开发过程 7
2.6.1.1 调试 8
2.6.1.2 开发 8
2.6.2.1 正确性 9
2.6.2.2 稳定性 15
2.6.2.3 容错能力 15
1.7 操作说明 17
2.7.1 视图查看 17
2.7.2 换乘查询 19
2.7.3 动态添加 22
2.7.4 工具栏和状态栏 26
2.7.5 帮助 26
2.7.6 关于 27
2.7.7 退出 27
第三部分 实践总结 28
2.1 所做的工作 28
2.2 总结和收获 28
第四部分 参考文献 28
第一部分 综合应用设计说明
11.1题目
题号2★★★:上海的地铁交通网络已经基本成型,建成的地铁线十多条,站点上百个,现需建立一个换乘指南打印系统,通过输入起点和终点站,打印出地铁换乘指南,指南内容包括起点站、换乘站、终点站。
(1)图形化显示地铁网络结构,能动态添加地铁线路和地铁站点。
(2)根据输入起点和终点站,显示地铁换乘指南。
(3)通过图形界面显示乘除路径。
1.2软件功能
功能主要功能包含地铁网络线路的查看,地铁换乘指南查询,动态添加线路站点等。
线路图查看:
●查看上海地铁网络线路图,包括通过键盘、鼠标拖放、放大缩小等简易查看操作;
●查看地铁线路信息,包括线路段、包含站点等;
●查看地铁站的详细信息,包括站点地理坐标、所属线路等
换乘指南查询:
●提供地铁换乘查询,可通过视图方便的查看乘坐路线和换乘路线;
●提供最小出行时间的换乘策略指南
●提供最小换乘次数的换乘策略指南
动态添加线路:
●动态添加线路,可根据需要新增线路;
●动态添加站点,可根据需要新增站点;
●动态添加连接,可根据需要新增站点连接;
●文本方式简易添加,方便快捷;
帮助:
●提供软件使用帮助和说明;
关于:
●提供软件制作信息;
上述所有功能采用面向对象的方法通过C++语言程序结合QT框架实现,后面会详细介绍。
1.3设计思想
实现思路
1.系统分析,明确功能要求
在做应用程序之前都会先明确软件的需求是什么,知道了要实现那些功能才能去细化用什么方法实现,实现的系统架构是什么,初步规划程序的基本流程和核心算法。在认真读题后,明确功能要求后,开始下一步操作。
2.MVC模式系统架构
MVC模式即为模型-视图-控制器结构模式,这种软件架构模式是一种常用的设计。在本次系统结构中,大致采用此种模式,前后端实现分离,前端主要考虑与用户的交互,例如设计怎样的视图,地铁网络线路图如何展现等,后端主要是数据和算法的处理,将复杂的功能和数据交由后端处理,前后端实现函数接口,从而完成整个软件系统的架构设计。
3.设计后端数据结构和接口
本次综合应用程序,基本上的数据结构都有涉及,例如网络线路的连接情况需用图结构、算法实现有队列,本文转载自http://www.biyezuopin.vip/onews.asp?id=14821静态存储有线性表,名字到存储位置有哈希映射,分析好数据结构之后,根据需要将后端系统的接口进行设计,例如最短路径的查询、添加线路站点等均需接口。
4.分块实现,逐步求精
系统结构和数据结构、算法等相应设计好后,就开始逐步实施了,从前端开始,对每个功能要求的前端界面和交互进行细化,实现前端代码,对每个功能的前端实现后,完善后端接口函数,完成所有前端后,后端也基本同步完成,从而完成整个程序的实现。
#include "ui_mainwindow.h"
#include "ui_managelines.h"
#include "mainwindow.h"
#include <QGraphicsItem>
#include <QMessageBox>
#include <QColorDialog>
#include <QTimer>
#include <QDateTime>
#include <QFile>
#include <QTextStream>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myView = new Graphics_view_zoom(ui->graphicsView);
myView->set_modifiers(Qt::NoModifier);
ui->graphicsView->setRenderHint(QPainter::Antialiasing);
scene=new QGraphicsScene;
scene->setSceneRect(-LINE_INFO_WIDTH,0,SCENE_WIDTH,SCENE_HEIGHT);
ui->graphicsView->setScene(scene);
ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
initStatusBar();
manageLines=new ManageLines(this);
subwayGraph=new SubwayGraph;
appHelp=new AppHelp();
bool flag = subwayGraph->readFileData(":/data/data/outLine.txt");
if (!flag)
{
QMessageBox box;
box.setWindowTitle(tr("error information"));
box.setIcon(QMessageBox::Warning);
box.setText("读取数据错误!\n将无法展示内置线路!");
box.addButton(tr("确定"), QMessageBox::AcceptRole);
if (box.exec() == QMessageBox::Accepted)
{
box.close();
}
}
myConnect();
updateTranserQueryInfo();
on_actionLineMap_triggered();
}
MainWindow::~MainWindow()
{
delete ui;
delete myView;
delete scene;
delete subwayGraph;
delete manageLines;
delete appHelp;
}
//连接信号和槽函数
void MainWindow::myConnect()
{
connect(manageLines->ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabWidgetCurrentChanged(int)));
connect(manageLines->ui->pushButtonAddLine, SIGNAL(clicked()), this, SLOT(addLine()));
connect(manageLines->ui->pushButtonAddStation, SIGNAL(clicked()), this, SLOT(addStation()));
connect(manageLines->ui->pushButtonConnect, SIGNAL(clicked()), this, SLOT(addConnection()));
connect(manageLines->ui->pushButtonAddByText, SIGNAL(clicked()), this, SLOT(addByText()));
connect(ui->comboBoxStartLine, SIGNAL(currentIndexChanged(QString)),
this, SLOT(transferStartLineChanged(QString)));
connect(ui->comboBoxDstLine, SIGNAL(currentIndexChanged(QString)),
this, SLOT(transferDstLineChanged(QString)));
connect(ui->pushButtonTransfer, SIGNAL(clicked()), this, SLOT(transferQuery()));
QTimer *timer = new QTimer(this);//新建定时器
connect(timer,SIGNAL(timeout()),this,SLOT(timerUpdate()));//关联定时器计满信号和相应的槽函数
timer->start(1000);//定时器开始计时,其中1000表示1000ms即1秒
}
//时间更新槽函数
void MainWindow::timerUpdate()
{
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString("yyyy-MM-dd hh:mm:ss dddd");
statusLabel2->setText(str);
}
//初始状态栏
void MainWindow::initStatusBar()
{
QStatusBar* bar = ui->statusBar;
statusLabel1 = new QLabel;
statusLabel1->setMinimumSize(200,15);
statusLabel1->setFrameShape(QFrame::Box);
statusLabel1->setFrameShadow(QFrame::Sunken);
statusLabel2 = new QLabel;
statusLabel2->setMinimumSize(200,15);
statusLabel2->setFrameShape(QFrame::Box);
statusLabel2->setFrameShadow(QFrame::Sunken);
statusLabel3 = new QLabel;
statusLabel3->setMinimumSize(200,15);
statusLabel3->setFrameShape(QFrame::Box);
statusLabel3->setFrameShadow(QFrame::Sunken);
bar->addWidget(statusLabel1);
bar->addWidget(statusLabel2);
bar->addWidget(statusLabel3);
statusLabel1->setText(tr("made by 1453381"));
statusLabel2->setText(tr("0000-00-00 00:00::00 星期 "));
statusLabel3->setText(tr("欢迎使用地铁换乘指南,详情帮助"));
}
//由线路表计算混合颜色
QColor MainWindow::getLinesColor(const QList<int>& linesList)
{
QColor color1=QColor(255,255,255);
QColor color2;
for (int i=0; i<linesList.size(); ++i)
{
color2=subwayGraph->getLineColor(linesList[i]);
color1.setRed(color1.red()*color2.red()/255);
color1.setGreen(color1.green()*color2.green()/255);
color1.setBlue(color1.blue()*color2.blue()/255);
}
return color1;
}
//获得线路表的名字集
QString MainWindow::getLinesName(const QList<int>& linesList)
{
QString str;
str+="\t";
for (int i=0; i<linesList.size(); ++i)
{
str+=" ";
str+=subwayGraph->getLineName(linesList[i]);
}
// qDebug()<<"tip="<<str<<"\n";
return str;
}
//将站点的经纬度地理坐标转为视图坐标
QPointF MainWindow::transferCoord(QPointF coord)
{
QPointF minCoord=subwayGraph->getMinCoord();
QPointF maxCoord=subwayGraph->getMaxCoord();
double x = (coord.x()-minCoord.x())/(maxCoord.x()-minCoord.x())*NET_WIDTH+MARGIN;
double y = (maxCoord.y()-coord.y())/(maxCoord.y()-minCoord.y())*NET_HEIGHT+MARGIN;
return QPointF(x,y);
}
//绘制网络图的边
void MainWindow::drawEdges(const QList<Edge>& edgesList)
{
for(int i=0; i<edgesList.size(); ++i)
{
int s1=edgesList[i].first;
int s2=edgesList[i].second;
QList<int> linesList=subwayGraph->getCommonLines(s1, s2);
QColor color=getLinesColor(linesList);
QString tip="途经: "+subwayGraph->getStationName(s1)+"--"+subwayGraph->getStationName(s2)+"\n线路:";
tip+=getLinesName(linesList);
QPointF s1Pos=transferCoord(subwayGraph->getStationCoord(s1));
QPointF s2Pos=transferCoord(subwayGraph->getStationCoord(s2));
QGraphicsLineItem* edgeItem=new QGraphicsLineItem;
edgeItem->setPen(QPen(color, EDGE_PEN_WIDTH));
edgeItem->setCursor(Qt::PointingHandCursor);
edgeItem->setToolTip(tip);
edgeItem->setPos(s1Pos);
edgeItem->setLine(0, 0, s2Pos.x()-s1Pos.x(), s2Pos.y()-s1Pos.y());
scene->addItem(edgeItem);
}
}
//绘制网络图的站点节点
void MainWindow::drawStations (const QList<int>& stationsList)
{
for (int i=0; i<stationsList.size(); ++i)
{
int s=stationsList[i];
QString name=subwayGraph->getStationName(s);
QList<int> linesList=subwayGraph->getStationLinesInfo(s);
QColor color=getLinesColor(linesList);
QPointF longiLati=subwayGraph->getStationCoord(s);
QPointF coord=transferCoord(longiLati);
QString tip="站名: "+name+"\n"+
"经度: "+QString::number(longiLati.x(),'f',7)+"\n"+
"纬度: "+QString::number(longiLati.y(),'f',7)+"\n"+
"线路:"+getLinesName(linesList);
QGraphicsEllipseItem* stationItem=new QGraphicsEllipseItem;
stationItem->setRect(-NODE_HALF_WIDTH, -NODE_HALF_WIDTH, NODE_HALF_WIDTH<<1, NODE_HALF_WIDTH<<1);
stationItem->setPos(coord);
stationItem->setPen(color);
stationItem->setCursor(Qt::PointingHandCursor);
stationItem->setToolTip(tip);
if(linesList.size()<=1)
{
stationItem->setBrush(QColor(QRgb(0xffffff)));
}
scene->addItem(stationItem);
QGraphicsTextItem* textItem=new QGraphicsTextItem;
textItem->setPlainText(name);
textItem->setFont(QFont("consolas",4,1));
textItem->setPos(coord.x(),coord.y()-NODE_HALF_WIDTH*2);
scene->addItem(textItem);
}
}
//更新换乘选择信息
void MainWindow::updateTranserQueryInfo()
{
statusLabel3->setText(tr("已更新数据"));
QComboBox* comboL1=ui->comboBoxStartLine;
QComboBox* comboL2=ui->comboBoxDstLine;
comboL1->clear();
comboL2->clear();
QList<QString> linesList=subwayGraph->getLinesNameList();
for(auto &a:linesList)
{
comboL1->addItem(a);
comboL2->addItem(a);
}
transferStartLineChanged(comboL1->itemText(0));
transferDstLineChanged(comboL2->itemText(0));
}
//换乘出发线路改变槽函数
void MainWindow::transferStartLineChanged(QString lineName)
{
QComboBox* comboS1=ui->comboBoxStartStation;
comboS1->clear();
int lineHash=subwayGraph->getLineHash(lineName);
if(lineHash==-1)
{
return ;
}
QList<QString> stationsList=subwayGraph->getLineStationsList(lineHash);
for(auto &a:stationsList)
{
comboS1->addItem(a);
}
}
//换乘目的线路改变槽函数
void MainWindow::transferDstLineChanged(QString lineName)
{
QComboBox* comboS2=ui->comboBoxDstStation;
comboS2->clear();
int lineHash=subwayGraph->getLineHash(lineName);
if(lineHash==-1)
{
return ;
}
QList<QString> stationsList=subwayGraph->getLineStationsList(lineHash);
for(auto &a:stationsList)
{
comboS2->addItem(a);
}
}
//换乘查询槽函数
void MainWindow::transferQuery()
{
int s1=subwayGraph->getStationHash(ui->comboBoxStartStation->currentText());
int s2=subwayGraph->getStationHash(ui->comboBoxDstStation->currentText());
int way=ui->radioButtonMinTime->isChecked()?1:2;
if(s1==-1||s2==-1)
{
QMessageBox box;
box.setWindowTitle(tr("换乘查询"));
box.setWindowIcon(QIcon(":/icon/icon/query.png"));
box.setIcon(QMessageBox::Warning);
box.setText(tr("请选择有站点的线路"));
box.addButton(tr("确定"),QMessageBox::AcceptRole);
if(box.exec()==QMessageBox::Accepted)
{
box.close();
}
}
else
{
QList<int> stationsList;
QList<Edge> edgesList;
bool flag=true;
if(way==1)
{
flag=subwayGraph->queryTransferMinTime(s1, s2, stationsList, edgesList);
}
else
{
flag=subwayGraph->queryTransferMinTransfer(s1, s2, stationsList, edgesList);
}
if(flag)
{
statusLabel3->setText(tr("换乘查询成功!"));
scene->clear();
drawEdges(edgesList);
drawStations(stationsList);
QString text=way==1?("以下线路时间最短,共换乘"+QString::number(stationsList.size()-1)+"个站点\n\n"):
("以下线路换乘最少,共换乘"+QString::number(stationsList.size()-1)+"条线路\n\n");
for(int i=0; i<stationsList.size(); ++i)
{
if(i)
{
text+="\n ↓\n";
}
text+=subwayGraph->getStationName(stationsList[i]);
QString linesStr=getLinesName(subwayGraph->getStationLinesInfo(stationsList[i]));
text+=linesStr;
}
QTextBrowser* browser=ui->textBrowserRoute;
browser->clear();
browser->setText(text);
}
else
{
QMessageBox box;
box.setWindowTitle(tr("换乘查询"));
box.setWindowIcon(QIcon(":/icon/icon/query.png"));
box.setIcon(QMessageBox::Warning);
box.setText(tr("您选择的起始和终止站点暂时无法到达!"));
box.addButton(tr("确定"),QMessageBox::AcceptRole);
if(box.exec()==QMessageBox::Accepted)
{
box.close();
}
}
}
}
//添加列表视图部件变化槽函数
void MainWindow::tabWidgetCurrentChanged(int index)
{
QWidget* widget=manageLines->ui->tabWidget->currentWidget();
if(widget==manageLines->tabWigetsVector[1])
{
manageLines->linesNameList=subwayGraph->getLinesNameList();
manageLines->updateLinesListWidget();
}
else if(widget==manageLines->tabWigetsVector[2])
{
manageLines->linesNameList=subwayGraph->getLinesNameList();
manageLines->stationsNameList=subwayGraph->getStationsNameList();
manageLines->ui->comboBoxConnectStation1->setMaxCount(manageLines->stationsNameList.size());
manageLines->ui->comboBoxConnectStation2->setMaxCount(manageLines->stationsNameList.size());
manageLines->ui->comboBoxConnectLine->setMaxCount(manageLines->linesNameList.size());
manageLines->updateComboBox();
}
Q_UNUSED(index);
}
//添加线路功能函数
void MainWindow::addLine()
{
QMessageBox box;
box.setWindowTitle(tr("添加线路"));
box.setWindowIcon(QIcon(":/icon/icon/subway.png"));
if(manageLines->lineName.isEmpty())
{
box.setIcon(QMessageBox::Warning);
box.setText(tr("请输入线路名称!"));
}
else if(subwayGraph->getLineHash(manageLines->lineName)==-1)
{
box.setIcon(QMessageBox::Information);
box.setText(tr("线路:")+manageLines->lineName+tr(" 添加成功!"));
subwayGraph->addLine(manageLines->lineName, manageLines->lineColor);
updateTranserQueryInfo();
}
else
{
box.setIcon(QMessageBox::Critical);
box.setText(tr("添加失败!\n错误原因:线路名已存在!"));
}
box.addButton(tr("确定"),QMessageBox::AcceptRole);
if(box.exec()==QMessageBox::Accepted)
{
box.close();
}
updateTranserQueryInfo();
}
//添加站点功能函数
void MainWindow::addStation()
{
QMessageBox box;
box.setWindowTitle(tr("添加站点"));
box.setWindowIcon(QIcon(":/icon/icon/station.png"));
if(manageLines->stationName.isEmpty())
{
box.setIcon(QMessageBox::Warning);
box.setText(tr("请输入站点名称!"));
}
else if(manageLines->linesSelected.isEmpty())
{
box.setIcon(QMessageBox::Warning);
box.setText(tr("请选择站点所属线路!"));
}
else
{
if(subwayGraph->getStationHash(manageLines->stationName)!=-1)
{
box.setIcon(QMessageBox::Critical);
box.setText(tr("添加失败!\n错误原因:站点已存在!"));
}
else
{
Station s(manageLines->stationName, manageLines->longitude, manageLines->latitude,
subwayGraph->getLinesHash(manageLines->linesSelected));
subwayGraph->addStation(s);
box.setText(tr("站点:")+manageLines->stationName+tr(" 添加成功!"));
updateTranserQueryInfo();
}
}
box.addButton(tr("确定"),QMessageBox::AcceptRole);
if(box.exec()==QMessageBox::Accepted)
{
box.close();
}
updateTranserQueryInfo();
on_actionLineMap_triggered();
}
//添加连接功能函数
void MainWindow::addConnection()
{
QString station1=manageLines->ui->comboBoxConnectStation1->currentText();
QString station2=manageLines->ui->comboBoxConnectStation2->currentText();
int s1=subwayGraph->getStationHash(station1);
int s2=subwayGraph->getStationHash(station2);
int l=subwayGraph->getLineHash(manageLines->ui->comboBoxConnectLine->currentText());
QMessageBox box;
box.setWindowTitle(tr("添加连接"));
box.setWindowIcon(QIcon(":/icon/icon/connect.png"));
if(s1==-1||s2==-1||l==-1)
{
box.setIcon(QMessageBox::Warning);
box.setText(tr("请选择已有的站点和线路!"));
}
else if(s1==s2)
{
box.setIcon(QMessageBox::Warning);
box.setText(tr("同一站点不需要连接!"));
}
else if(!subwayGraph->getStationLinesInfo(s1).contains(l))
{
box.setIcon(QMessageBox::Critical);
box.setText(tr("连接失败!\n错误原因:所属线路不包含站点1"));
}
else if(!subwayGraph->getStationLinesInfo(s2).contains(l))
{
box.setIcon(QMessageBox::Critical);
box.setText(tr("连接失败!\n错误原因:所属线路不包含站点2"));
}
else
{
box.setIcon(QMessageBox::Information);
box.setText(tr("添加连接成功!"));
subwayGraph->addConnection(s1,s2,l);
}
if(box.exec()==QMessageBox::Accepted)
{
box.close();
}
updateTranserQueryInfo();
on_actionLineMap_triggered();
}
//文本方式添加功能函数
void MainWindow::addByText()
{
QString writeFile="userAdd.txt";
QFile file(writeFile);
if(!file.open(QIODevice::WriteOnly|QIODevice::Text))
{
QMessageBox::critical(NULL, "提示", "无法创建添加文件");
return ;
}
QTextStream out(&file);
out<<manageLines->ui->textEdit->toPlainText();
file.close();
QMessageBox box;
box.setWindowTitle(tr("文本添加"));
box.setWindowIcon(QIcon(":/icon/icon/add.png"));
bool flag=subwayGraph->readFileData(writeFile);
if(flag)
{
box.setIcon(QMessageBox::Information);
box.setText(tr("添加成功"));
}
else
{
box.setIcon(QMessageBox::Critical);
box.setText(tr("添加失败,数据被擦除!"));
}
box.addButton(tr("确定"),QMessageBox::AcceptRole);
if(box.exec()==QMessageBox::Accepted)
{
box.close();
}
updateTranserQueryInfo();
on_actionLineMap_triggered();
return ;
}
//视图放大槽函数
void MainWindow::on_toolEnlarge_triggered()
{
statusLabel3->setText(tr("已放大"));
ui->graphicsView->scale(1.5,1.5);
}
//动作视图缩小槽函数
void MainWindow::on_toolShrink_triggered()
{
statusLabel3->setText(tr("已缩小"));
ui->graphicsView->scale(2.0/3,2.0/3);
}
//动作添加所有槽函数
void MainWindow::on_actionAddAll_triggered()
{
statusLabel3->setText(tr("添加线路、站点、连接关系"));
manageLines->setAllVisible();
manageLines->show();
}
//动作添加线路槽函数
void MainWindow::on_actionAddLine_triggered()
{
statusLabel3->setText(tr("添加线路"));
manageLines->setAddLineVisible();
manageLines->show();
}
//动作添加站点槽函数
void MainWindow::on_actionAddStation_triggered()
{
statusLabel3->setText(tr("添加站点"));
manageLines->setAddStationVisible();
manageLines->show();
}
//动作添加连接槽函数
void MainWindow::on_actionAddConnect_triggered()
{
statusLabel3->setText(tr("添加连接关系"));
manageLines->setAddConnectionVisible();
manageLines->show();
}
//动作文本方式添加槽函数
void MainWindow::on_actionAddByText_triggered()
{
statusLabel3->setText(tr("文本方式简易添加"));
manageLines->setAddByTextVisible();
manageLines->show();
}
//动作查看所有线路图槽函数
void MainWindow::on_actionLineMap_triggered()
{
statusLabel3->setText(tr("图示:上海地铁网络线路图"));
scene->clear();
QList<int> stationsList;
QList<Edge> edgesList;
subwayGraph->getGraph(stationsList,edgesList);
drawEdges(edgesList);
drawStations(stationsList);
// qDebug()<<"stations.size()="<<stationsList.size()<<" edges.size()="<<edgesList.size();
}
//动作是否显示状态栏槽函数
void MainWindow::on_actionstatusBar_triggered(bool checked)
{
if(checked)
{
ui->statusBar->show();
}
else
{
ui->statusBar->hide();
}
}
//动作是否显示工具栏槽函数
void MainWindow::on_actiontoolBar_triggered(bool checked)
{
if(checked)
{
ui->mainToolBar->show();
}
else
{
ui->mainToolBar->hide();
}
}
//动作关于Qt槽函数
void MainWindow::on_actionQt_triggered()
{
QMessageBox::aboutQt(this,tr("关于Qt"));
}
//动作关于作者槽函数
void MainWindow::on_actionAuthor_triggered()
{
QMessageBox box;
box.setWindowTitle(tr("关于制作者"));
box.setIcon(QMessageBox::Information);
box.setText(tr("Author : 1453381 \n"
"School : TJ \n"
"Major : Computer Science \n"
"Emai : 767089181@qq.com \n"));
box.addButton(tr("确定"),QMessageBox::AcceptRole);
if(box.exec() == QMessageBox::Accepted)
box.close();
}
//动作帮助菜单槽函数
void MainWindow::on_actionuseHelp_triggered()
{
appHelp->show();
}
//动作关闭程序槽函数
void MainWindow::on_actionClose_triggered()
{
close();
}