PDA

View Full Version : QwtDateScaleEngine how to set the date range?



alexge233
20th November 2014, 04:11
Hello,

I've read a few questions on the forum and googled for it, yet I can't figure out how to properly set the date range.

I have a CSV file I load into memory, and use boost::gregorian_date, which I then convert into QDate like so:


auto first = csvdata.Rows().front().date;
auto last = csvdata.Rows().back().date;

QDateTime start ( QDate( first.year(), first.month(), first.day() ), QTime( 0, 0, 0 ) );
QDateTime end ( QDate( last.year(), last.month(), last.day() ), QTime( 0, 0, 0 ) );

auto * scaleDraw = new QwtDateScaleDraw( Qt::UTC );
scaleDraw->setDateFormat( QwtDate::Day, QString("dd-mm-yyyy") );

auto * scaleEngine = new QwtDateScaleEngine( Qt::UTC );

plot.setAxisScaleEngine( QwtPlot::xBottom, scaleEngine );
plot.setAxisScale( QwtPlot::xBottom, start.toTime_t(), end.toTime_t() , 24 * 3600 * 30 );
plot.setAxisScaleDraw( QwtPlot::xBottom, scaleDraw );

plot.setAxisLabelRotation( QwtPlot::xBottom, -90.0 );
plot.setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom );


I then plot a simple moving average curve, which is essentialy an

std::pair<double, boost::gregorian_date> sma;



QwtPlotCurve * sma_curve = new QwtPlotCurve();
QPolygonF points;
for ( int i = 0; i < sma.size()-1; i++ )
{
static boost::posix_time::ptime epoch ( boost::gregorian::date( 1970, 1, 1 ) );
auto secs = ( boost::posix_time::ptime ( sma[i].second, boost::posix_time::seconds(0) ) - epoch ).total_seconds();
points << QPointF( secs, sma[i].first );
}

sma_curve->setSamples( points );


Yet, the date range on the bottom is always starting from the unix time (Jan 1970).
I'm obviously doing something wrong.
Also, the date label for the axis, is not set to dd-mm-yyyyy, but rather to hh:mm DD dd MM yyyy

Yet the plotted data is correct, just misaligned to the actual dates (which in this case should be 2012-Feb-01 to 2014-Nov-12).


10754


Can someone please help?

PS: I'm using qwt 6.1.1 from the svn repository.

Uwe
20th November 2014, 07:55
Better use the following methods to convert between QDateTime to double:


QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec );
double QwtDate::toDouble( const QDateTime &dateTime );Concerning the format of the labels: it depends on the interval type, what is a classification of the scale interval - not of a specific value- and in your case the scale is not identified as being in days.
The classification is done in QwtDateScaleDraw::intervalType according to the current major ticks of the axis.

Uwe

alexge233
20th November 2014, 19:24
Uwe,

Thank you very much for the reply.
But I'm still very confused.

plot.setAxisScale( QwtPlot::xBottom, QwtDate::toDouble( start ), QwtDate::toDouble( end ) , 24 * 3600 * 1000 );
Makes no difference.

As far as I understand, the interval type seem to differ?
I was using seconds as the smallest interval.
I've set the setDateFormat as Days, which is what I would rather use.

auto * scaleDraw = new QwtDateScaleDraw( Qt::UTC );
scaleDraw->setDateFormat( QwtDate::Day, QString("dd-mm-yyyy") );
When I try to to set the axis starting date, by using seconds elapsed since the unix epoch:

plot.setAxisScale( QwtPlot::xBottom, start.toTime_t(), end.toTime_t() , 24 * 3600 * 30 );
The interval is wrong, because what I see is a starding date a few seconds after the unix epoch.
Yet
QwtPlot::setAxisScale (int axisId, double min, double max, double step=0) asks for my minimal and maximum value, and the step size, so my minimal step size are the days elapsed from start date since the epoch, and the max size are the days elapsed from end date since the epoch.

So, I re-factored by trying to use days only this time:

QDateTime epoch ( QDate( 1970, 1, 1 ), QTime(0,0,0) );
and then tried to reset the axis, to use days, incremented monthly (every 30 days label):

plot.setAxisScale( QwtPlot::xBottom, epoch.daysTo( start ), epoch.daysTo( end ) , 30 );
To my suprise, I am still getting the epoch in 1970, although I have set it to days, the increment appears to be in miliseconds, as the y axis labels increment only by ms.
Which implies that I cannot set directly the DateScale to use days, because it uses some other interval?
I don't want the second or milliseconds to be the smallest interval, but only a days. How do I achieve that?

QwtDate::IntervalType
Is protected. How do I use it without creating a class that inherits from it? Or is that the way it must be used?

Finally, I populate the plot values, using days elapsed like so:


QPolygonF points;
for ( const auto & p: sma )
{
QDateTime date ( QDate( p.second.year(), p.second.month(), p.second.day() ), QTime(0,0,0) );
points << QPointF( epoch.daysTo( date ), p.first );
}

Which draws a correct curve of my data.

Thank you very much for your help!

#EDIT

Nevermind, I finally figured it out, I had to multiply by the amount of milliseconds in a day, like so:


double x = 86400000.;
plot.setAxisScale( QwtPlot::xBottom, epoch.daysTo( start ) * x, epoch.daysTo( end ) * x , 30 * x );
...
for ( const auto & p: sma )
{
QDateTime date ( QDate( p.second.year(), p.second.month(), p.second.day() ), QTime(0,0,0) );
points << QPointF( epoch.daysTo( date ) * x, p.first );
}

This fixed botht he labels, and the date alignment.