这一部分我想详细地展开叙述,因为自己在实现这个功能上确实是下了很大的功夫,最后却几乎一无所获,导致自己的体会非常深刻,让我认识到了自己的才疏学浅以及眼高手低的毛病,当然,针对此功能,我最大的认识就是,一个你未曾尝试实现过的功能,决不能轻易地下手编写代码,这不仅是对软件的不尊重,更是对自己精力、甚至生命的一种不尊重,当你费了很大功夫完成一半的功能时,你发现下一个功能根本没法在现有基础上添加时,你会崩溃的。
因此,对一个新的功能,有一个很好的顶层设计,以及对全局的思考是至关重要的,决不能眼高手低,盲目下手编写代码,到头来一定是手忙脚乱,发现自己的垃圾代码确实是一无是处。同时,善于参考借鉴别人的思想也不是一件坏事,当然了,有自己的思想最好,但大部分的时间理应放在你自己这个思想的可行性上,而不是无效的代码编写!
个人失败案例
未经过充分的考虑以及判断,我盲目的开始了动画效果的代码编写,当时的想法是,我一定要尽快实现当前旅客的动画状态显示功能(因为当时还没有添加多个旅客,实现多个旅客的旅行,即状态查询),因此我未经思考地开始了一个旅客的该功能实现,我将这部分代码也放在widget模块中,因为绘图就是在widget中实现的。
实现方法:
1、查询旅行方案并点击确定后,算法得出旅客的旅行信息;
2、将该信息转化为一行字符串,传递给widget中;
3、widget监控子窗口中何时点击了确定,点击时进入移动乘客的函数,并根据当前时间较之旅客出发时间来判断他现在应处于什么状态(停留、运动、还是到达状态)
4、针对如何实现停留,我产生了思考,如何让该乘客的状态保持在当前一段时间(即停留这张图片绘制在地图上几“小时”),我决定采用sleep的方式,直接根据参数sleep一定时间;
5、针对如何实现运动,我参考了example中别人移动乘客的算法,见附1,虽然实现了高帧率运动(如60帧),但是每两帧之间的时间又如何停止呢?(不停止的话,函数直接执行完,即图像直接绘制完,根本看不到运动效果),我又采用了sleep;
最后效果:完全ok,非常完美,单个乘客的运动效果完全达到需求。
而后,问题接踵而至,要求必须实现多个乘客同时在旅行系统中进行查询,并同时保存他们的状态,并可以实现随时切换,我面临着如何实现保存旅客当前位置,并根据该位置在切换回时再推算出旅客此时所在的位置并进行移动,事实上,根据我先前设计的算法,我认为这一功能根本无法实现。以下是我的LJ代码(可跳过):
(大致思想就是切换旅客时,我判断当前他应该是停留状态还是运动状态,若是停留状态,再停留一段时间,进入循环进行当次运动,最后进入主循环进行剩余城市间的停留和运动;若是运动状态,则找出他应该从哪儿开始运动,进入循环进行当次运动,最后进入主循环进行剩余城市间的停留和运动)
//移动旅客,在地图上显示其位置及当前状态
void Widget::moveTraveler()
{
int j = 0,k = 0;
i = 0;
qDebug()<<"how many times i come";
while(paintFlag != 0)
{
starttime = QDateTime::currentDateTime();//获取开始时间
qDebug()<<"starttime = "<<starttime;
if(k == 0)
map[now_traveler]->begintime = starttime;
// qDebug()<<begintime;
GetHowToMove();
travelerStatus = 1;
QWidget::repaint();
sleep(stay_time * 10000);
//sleep(1000);
travelerStatus = 2;
for(int i = 1;i != time*1000/SPEED;i++,j++)
{
if(i == 1&&j == 0)
{
departuredatetime = QDateTime::currentDateTime();//获取离开的时间
cityarrivaltime = departuredatetime.addSecs(time);//获取到达的时间
start2Begin = getSplitTime(starttime, departuredatetime);
start2End = getSplitTime(starttime, cityarrivaltime);
// qDebug()<<departuredatetime<<cityarrivaltime;
}
repaint();
sleep(SPEED);
if(increaseRatio >= 1)
break;
}
j = 0;k++;
}
travelerStatus = 3;
repaint();
travelerStatus = 0;
paintFlag = 1;
}
void Widget::moveTraveler2()
{
QDateTime begintimeinthis;
begintimeinthis = map[now_traveler]->begintime;
qDebug()<<"begintimeinthis = "<<begintimeinthis;
int sub = (int)getTimeDifference(begintimeinthis,ctime);
qDebug()<<sub;
int wheretobegin = sub/10000;
int sum = 0;
for(int i = 0;;i++) { ******剪枝掉不需要的字符
qDebug()<<StrFromTra;
int timebetween2city = StrFromTra.section("-",1,1).toInt()+StrFromTra.section("-",3,3).toInt();
qDebug()<<timebetween2city;
if(timebetween2city * 10000 < sub && timebetween2city != 0)
{
qDebug()<<StrFromTra.section("-",1,1).toInt()+StrFromTra.section("-",3,3).toInt();
sum += StrFromTra.section("-",1,1).toInt()+StrFromTra.section("-",3,3).toInt();
int num = 0;
for(int j = 0;j < 4;j++)
num += StrFromTra.section("-",j,j).length() + 1;
StrFromTra = StrFromTra.mid(num);
sub -= timebetween2city * 10000;
}
else {
break;
}
}
begintimeinthis = begintimeinthis.addSecs(sum * 10);
sub = (int)getTimeDifference(begintimeinthis,ctime);
wheretobegin = sub/10000;
qDebug()<<"sub = "<<sub<<"wheretobegin = "<<wheretobegin;
qDebug()<<"strFromTra = "<<StrFromTra;
int total = 0;
for(int i = 1;StrFromTra.section("-",i,i) != nullptr;i += 2)
{
total += StrFromTra.section("-",i,i).toInt();
}
qDebug()<<"total = "<<total;
if(wheretobegin >= total)//若返回的时间大于总时间
{
dest2 = CityToNum(StrFromTra);
qDebug()<<"dest2 = "<<dest2;
travelerStatus = 3;
repaint();
travelerStatus = 0;
paintFlag = 0;
}
if(wheretobegin < StrFromTra.section("-",1,1).toInt())******若返回的时间处于停留时间内
{
travelerStatus = 1;
depart2 = CityToNum(StrFromTra.section("-",0,0));
update();
sleep(StrFromTra.section("-",1,1).toInt() * 10000 - sub);//停留
int j = 0;
travelerStatus = 2;
for(int i = 1;i != time*1000/SPEED;i++,j++)//运动
{
if(i == 1&&j == 0)
{
departuredatetime = QDateTime::currentDateTime();//获取离开的时间
cityarrivaltime = departuredatetime.addSecs(StrFromTra.section("-",3,3).toInt() * 10);//获取到达的时间
start2Begin = getSplitTime(starttime, departuredatetime);
start2End = getSplitTime(starttime, cityarrivaltime);
// qDebug()<<departuredatetime<<cityarrivaltime;
}
repaint();
sleep(SPEED);
if(increaseRatio >= 1)
break;
}
j = 0;
}
else {//若返回的时间应处于运动状态
depart2 = CityToNum(StrFromTra.section("-",0,0));
int i = 0;
double j;
for(j = 0;j/(time*1000/SPEED) < getTimeDifference(begintimeinthis.addSecs(10 * StrFromTra.section("-",1,1).toInt()),ctime)
(StrFromTra.section("-",3,3).toDouble() * 10000);j++,i++);******找到应该从哪儿开始运动
i = (int)j;
qDebug()<<"i = "<<i;
travelerStatus = 2;
for(int j = 0;i != time*1000/SPEED;i++,j++)
{
if(j == 0)
{
departuredatetime = begintimeinthis.addSecs(StrFromTra.section("-",1,1).toInt() * 10);//获取离开的时间
cityarrivaltime = begintimeinthis.addSecs(StrFromTra.section("-",1,1).toInt() * 10 + StrFromTra.section("-",3,3).toInt() * 10);//获取到达的时间
start2Begin = getSplitTime(starttime, departuredatetime);
start2End = getSplitTime(starttime, cityarrivaltime);
// qDebug()<<departuredatetime<<cityarrivaltime;
}
repaint();
sleep(SPEED);
if(increaseRatio >= 1)
break;
}
j = 0;
}
int num = 0;
if(StrFromTra.section("-",1,1) != nullptr)
{
for(int j = 0;j < 4;j++)
num += StrFromTra.section("-",j,j).length() + 1;
StrFromTra = StrFromTra.mid(num);
qDebug()<<"StrFromTra = "<<StrFromTra;
}
GetHowToMove();
if(paintFlag == 0)
dest2 = CityToNum(StrFromTra);
int j = 0;
while(paintFlag != 0) ******开始剩余的几个城市间正常的停留和运动
{
// starttime = QDateTime::currentDateTime();//获取开始时间
// qDebug()<<starttime;
travelerStatus = 1;
QWidget::repaint();
sleep(stay_time * 10000);
qDebug()<<"I sleep.";
//sleep(1000);
travelerStatus = 2;
for(int i = 1;i != time*1000/SPEED;i++,j++)
{
if(i == 1&&j == 0)
{
departuredatetime = QDateTime::currentDateTime();//获取离开的时间
cityarrivaltime = departuredatetime.addSecs(time);//获取到达的时间
start2Begin = getSplitTime(starttime, departuredatetime);
start2End = getSplitTime(starttime, cityarrivaltime);
// qDebug()<<departuredatetime<<cityarrivaltime;
}
repaint();
sleep(SPEED);
if(increaseRatio >= 1)
break;
}
qDebug()<<"I run.";
j = 0;
if(!GetHowToMove())
break;
qDebug()<<"i = "<<i;
}
travelerStatus = 3;
paintFlag = 0;
repaint();
travelerStatus = 0;
paintFlag = 1;
}
//获取当前轮次旅客从某出发地移动至相应目的地的信息
int Widget::GetHowToMove()
{
paintFlag = 1;
// qDebug()<<"StrFromTra = "<<StrFromTra;
QString depart = StrFromTra .section("-",4*i,4*i);
QString dest = StrFromTra.section("-",4*i+4,4*i+4);
if(dest==nullptr)
{
i = -1;
paintFlag = 0;
}
stay_time = StrFromTra.section("-",4*i+1,4*i+1).toInt();
vehicle = StrFromTra.section("-",4*i+2,4*i+2);
time = StrFromTra.section("-",4*i+3,4*i+3).toInt();
time = time * 10;
qDebug()<<depart<<stay_time<<vehicle<<time<<dest;
depart2 = CityToNum(depart);
dest2 = CityToNum(dest);
i++;
if(dest == nullptr)
return 0;
else {
return 1;
}
}
当正常点击确定时,调用movetraveler()函数,切换旅客时调用movetraveler2()函数,姑且先不提我这个想法是不是bug很难调,想法简单,但代码庞大且效率奇低,单是我在写代码时我就感到了深深的无力感,可是我依然硬着头皮写了好多天,发现莫名其妙的bug让我无所适从,最后发现问题所在:信号与槽的机制,槽内调用函数时还没有执行完(因为有sleep函数),就要切换旅客进入下一个函数(movetraveler2),其内也有sleep函数,若打断函数1后去执行函数2,当函数2执行完后又会返回到函数1继续执行,虽然初衷是彻底打断函数1,只执行函数2 ,但connect机制如此,无法改变,网上也没有相关资料,因此坚持了好多天的思想,一朝彻底放弃,确实很不容易,因为这就像是壮士断腕,更何况临近ddl,害怕自己重头来过没法赶完这个功能。
就是因为我随意使用sleep函数以及将调用其的函数放在槽内,导致了这个很难解决的问题,最终发现功能根本无法实现,真的非常痛苦。前车之鉴,希望大家不要像我一样,对待一件事情,一定实现做好顶层的设计与所有情况的考虑。
正确实现的功能:
(首先要考虑的是,不把正常点击确定以及切换旅客割离开来,不管是哪种调用,都只是改变了当前显示在地图上的旅客的信息而已,这样的需求下,问题就鞥呢迎刃而解)
1、封装mapwidget类,widget窗口上添加一个widget控件并提升为mapwidget,在该控件中实现所有的重绘操作,包括地图绘制。
2、mapwidget类中,构造函数中打开一个定时器,一直不断地重复刷新重绘工作。
3、若当前旅客没有进行过查询操作,将某参数设为-1,mapwidget中监测该值,为-1时不绘制旅客状态图片;
4、当旅客在一次旅行中时,将其出发时间、到达时间等信息存在struct traveler结构体中,mapwidget随时监测,并根据相应的值绘制旅客状态;
以下为代码:
//mapwidget.h
#ifndef MAPWIDGET_H
#define MAPWIDGET_H
#include "widget.h"
#include <QWidget>
#include <QTextEdit>
#include <QBrush>
#include <QPalette>
#include <QPixmap>
#include <QPen>
#include <QDebug>
#include <QRectF>
#include <QDateTime>
#include <QPointF>
#include <QMessageBox>
#include <QTimer>
#include <QThread>
class mapwidget : public QWidget
{
public:
mapwidget(QWidget *);
void paintEvent(QPaintEvent *);
QPixmap setPointGraph();//设置图标
QPointF setPointPos();//设置图标位置
QDateTime getSplitTime(QDateTime former, QDateTime later);//获取两时间点时间间隔
QPointF getCityCor(int city);//获得城市对应坐标
double getTimeDifference(QDateTime shorterDateTime, QDateTime longerDateTime);//获得两时间间隔时间差
QPointF getMoveDistance(QDateTime starttime, QDateTime spentTime, QDateTime start2Begin, QDateTime start2End,
int from, int to);//获得坐标增量
int nextCity();//获得新计划的始发地
int CityToNum(QString);
int VehicleToNum(QString);
private slots:
void update();//刷新画面
private:
int state;
QTimer * paintmstimer;
};
#endif // MAPWIDGET_H
//mapwidget.cpp
#include "mapwidget.h"
#include <QApplication>
#include <QStateMachine>
#include <QPushButton>
#include <QSignalTransition>
#include <QPropertyAnimation>
#include <QPainter>
#include <QState>
#include <QLabel>
//添加新的timer,使得绘图准确
mapwidget::mapwidget(QWidget *parent) :
QWidget(parent),state(-1)
{
this->setAutoFillBackground(true);
qDebug()<<"come mapwidget";
paintmstimer = new QTimer;
paintmstimer->start(1000/60);
QObject::connect(paintmstimer, SIGNAL(timeout()), this, SLOT(update()));
}
//绘图实践,绘制旅行过程
void mapwidget::paintEvent(QPaintEvent *)
{
//设置一个画家 叫painter1
QPainter painter1(this);
//绘制地图map2
QPixmap pix;
pix.load(":/map2.png");
pix = pix.scaled(pix.width()*0.55,pix.height()*0.55);
painter1.drawPixmap(0,150,pix);
QPainter painter(this);
Widget *fatherPtr = (Widget *)parentWidget();
if (fatherPtr->tra.currenttraveler != -1)
{
painter.drawPixmap((setPointPos()), setPointGraph());
}
}
//根据当前状态、交通方式决定图标
QPixmap mapwidget::setPointGraph()
{
QPixmap pointGraph;
switch(state)
{
case -2://arrived destination
pointGraph = QPixmap(":/vehicles/arrive.ico");
break;
case -1://pause waiting
pointGraph = QPixmap(":/vehicles/pause.ico");
break;
case 0:
pointGraph = QPixmap(":/vehicles/car.ico");
break;
case 1:
pointGraph = QPixmap(":/vehicles/train.ico");
break;
case 2:
pointGraph = QPixmap(":/vehicles/plane.ico");
break;
}
return pointGraph;
}
//设置当前图标所处位置
QPointF mapwidget::setPointPos()
{
Widget *fatherPtr = (Widget *)parentWidget();
static QPointF pointPos;
// std::vector<Attribute> path = fatherPtr->travelers[fatherPtr->currentTraveler].getPlan();
QDateTime spenttime = QDateTime::currentDateTime();
QDateTime starttime = fatherPtr->tra.starttime;
QDateTime totaltime = fatherPtr->tra.totaltime;
QString plan = fatherPtr->tra.plan;
//将字符串Plan剪短为需要的串
int sub = (int)getTimeDifference(starttime,spenttime);
int sum = 0;
for(int i = 0;;i++) {
int timebetween2city = plan.section("-",1,1).toInt()+plan.section("-",3,3).toInt();
if(timebetween2city * 10000 < sub && timebetween2city != 0)
{
// qDebug()<<plan.section("-",1,1).toInt()+plan.section("-",3,3).toInt();
sum += plan.section("-",1,1).toInt()+plan.section("-",3,3).toInt();
int num = 0;
for(int j = 0;j < 4;j++)
num += plan.section("-",j,j).length() + 1;
plan = plan.mid(num);
sub -= timebetween2city * 10000;
}
else {
break;
}
}
// qDebug()<<"plan = "<<plan;
//已用时间不小于总时间,当前位置为目的地
if(spenttime >= totaltime)
{
for(int j = 0;plan.section("-",1,1) != nullptr;j++)
{
int num = plan.section("-",0,0).length() + 1;
plan = plan.mid(num);
}
// qDebug()<<"plan = "<<plan;
pointPos = getCityCor(CityToNum(plan));
state = -2;
}
else
while(1)
{
QDateTime departuredatetime = starttime.addSecs(sum * 10 + plan.section("-",1,1).toInt()* 10);
QDateTime cityarrivaltime = departuredatetime.addSecs(plan.section("-",3,3).toInt() * 10);
QDateTime start2Begin = getSplitTime(starttime, departuredatetime);
QDateTime start2End = getSplitTime(starttime, cityarrivaltime);
//已用时间不超过一段路径发车时间,状态为等待
// qDebug()<<getTimeDifference(starttime,spenttime)<<getTimeDifference(starttime,departuredatetime);
if (getSplitTime(starttime,spenttime) <= start2Begin)
{
pointPos = getCityCor(CityToNum(plan.section("-",0,0)));
state = -1;
break;
}
//已用时间不超过一段路径的到站时间,状态为运动中
else if (getSplitTime(starttime,spenttime) <= start2End)
{
pointPos = getCityCor(CityToNum(plan.section("-",0,0)));
pointPos += getMoveDistance(starttime,spenttime, start2Begin, start2End, CityToNum(plan.section("-",0,0)), CityToNum(plan.section("-",4,4)));
state = VehicleToNum(plan.section("-",2,2));
if (spenttime == start2End)
{
qDebug() << "Arriving at " << plan.section("-",4,4);
}
break;
}
}
return pointPos;
}
//获得两时间点之间的时间差,判断当前所处的状态
QDateTime mapwidget::getSplitTime(QDateTime former, QDateTime later)
{
int durationMsec = (later.time().msec() - former.time().msec());
int durationSec = (later.time().second() - former.time().second() - (int)((durationMsec >= 0)?0:1));
int durationMin = (later.time().minute() - former.time().minute() - (int)((durationSec >= 0)?0:1));
int durationHour = (later.time().hour() - former.time().hour() - (int)((durationMin >= 0)?0:1));
int durationDay = (later.date().day() - former.date().day() - (int)((durationHour >= 0)?0:1) + former.date().daysInMonth())
% former.date().daysInMonth();
durationMsec = (durationMsec + 1000) % 1000;
durationSec = (durationSec + 60) % 60;
durationMin = (durationMin + 60) % 60;
durationHour = (durationHour + 24) % 24;
return QDateTime(QDate(1, 1, durationDay+1), QTime(durationHour, durationMin, durationSec, durationMsec));
}
//获得两个时间段的时间差,用于计算坐标增量
double mapwidget::getTimeDifference(QDateTime former, QDateTime later)
{
int durationMsec = (later.time().msec() - former.time().msec());
int durationSec = (later.time().second() - former.time().second() - (int)((durationMsec >= 0)?0:1));
int durationMin = (later.time().minute() - former.time().minute() - (int)((durationSec >= 0)?0:1));
int durationHour = (later.time().hour() - former.time().hour() - (int)((durationMin >= 0)?0:1));
int durationDay = (later.date().day() - former.date().day() - (int)((durationHour >= 0)?0:1) + former.date().daysInMonth())
% former.date().daysInMonth();
durationMsec = (durationMsec + 1000) % 1000;
durationSec = (durationSec + 60) % 60;
durationMin = (durationMin + 60) % 60;
durationHour = (durationHour + 24) % 24;
return (double)(1000 * (durationDay * 86400 + durationHour * 3600 + durationMin * 60 + durationSec) +durationMsec);
}
//计算坐标增量
QPointF mapwidget::getMoveDistance(QDateTime starttime, QDateTime spentTime, QDateTime start2Begin, QDateTime start2End,
int from, int to)
{
// qDebug()<<start2Begin<<start2End<<spentTime;
double increaseRatio = getTimeDifference(start2Begin, getSplitTime(starttime,spentTime))/getTimeDifference(start2Begin, start2End);
double xIncrease = (getCityCor(to) - getCityCor(from)).x() * increaseRatio;
double yIncrease = (getCityCor(to) - getCityCor(from)).y() * increaseRatio;
return QPointF(xIncrease, yIncrease);
}
总结
快速的编码是我们要掌握的技能要求之一,但快速编写垃圾代码的能力一定不是。思考永远要放在第一位,但思考的内容也是重点,对全局的思考,对整个程序,整个软件功能的思考才是重中之重!