如何实时绘制旅客在地图上的状态

这一部分我想详细地展开叙述,因为自己在实现这个功能上确实是下了很大的功夫,最后却几乎一无所获,导致自己的体会非常深刻,让我认识到了自己的才疏学浅以及眼高手低的毛病,当然,针对此功能,我最大的认识就是,一个你未曾尝试实现过的功能,决不能轻易地下手编写代码,这不仅是对软件的不尊重,更是对自己精力、甚至生命的一种不尊重,当你费了很大功夫完成一半的功能时,你发现下一个功能根本没法在现有基础上添加时,你会崩溃的。
因此,对一个新的功能,有一个很好的顶层设计,以及对全局的思考是至关重要的,决不能眼高手低,盲目下手编写代码,到头来一定是手忙脚乱,发现自己的垃圾代码确实是一无是处。同时,善于参考借鉴别人的思想也不是一件坏事,当然了,有自己的思想最好,但大部分的时间理应放在你自己这个思想的可行性上,而不是无效的代码编写!

个人失败案例

未经过充分的考虑以及判断,我盲目的开始了动画效果的代码编写,当时的想法是,我一定要尽快实现当前旅客的动画状态显示功能(因为当时还没有添加多个旅客,实现多个旅客的旅行,即状态查询),因此我未经思考地开始了一个旅客的该功能实现,我将这部分代码也放在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 = "<begintime = starttime;
//        qDebug()<= 1)
                break;
        }
        j = 0;k++;
    }
    travelerStatus = 3;
    repaint();
    travelerStatus = 0;
    paintFlag = 1;
}
void Widget::moveTraveler2()
{
    QDateTime begintimeinthis;
    begintimeinthis = map[now_traveler]->begintime;
    qDebug()<<"begintimeinthis = "<= 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 = "<= 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 = "<= 1)
                break;
        }
        qDebug()<<"I run.";
        j = 0;

        if(!GetHowToMove())
            break;
        qDebug()<<"i = "<

当正常点击确定时,调用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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//添加新的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 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()<= 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()<

总结

快速的编码是我们要掌握的技能要求之一,但快速编写垃圾代码的能力一定不是。思考永远要放在第一位,但思考的内容也是重点,对全局的思考,对整个程序,整个软件功能的思考才是重中之重!

你可能感兴趣的:(如何实时绘制旅客在地图上的状态)