PDA

View Full Version : QwtPlot - problem with date-time label at major ticks



GG2013
11th July 2013, 03:57
Hallo,
My x-axis data is in date-time (eg: 2010-11-27-01-00-00) format and it is taken hourly over 7 days. I would like to have 7 major ticks and at each tick the label would
be like 2010-11-27-00-00-00, 2010-11-27-01-00-00,.....2010-12-03-23-00-00. Somehow I can't figure it out properly- please see the code below & the attached figure.

Also I would like to have a few clarifications:
1. How the value of "v" in label is being passed on? Sorry, this has been asked before in the forum but the answer by Uwe is not clear to me.
2. The values for MajorTick & MinorTick are set as 7 and 24 resp. but the graph shows 5 major and 20 minor ticks.
3. The values of date1 & date2 are correctly set (i.e 27-Nov-2010 & 03-Dec-2010) but x1 & x2 both show a value of 1.29e12- don't understand why?

Any help would be greatly appreciated.

9278


class TimeScaleDraw: public QwtScaleDraw
{
public:
TimeScaleDraw( )
{
setTickLength(QwtScaleDiv::MajorTick, 7);
setTickLength(QwtScaleDiv::MinorTick, 24);
setTickLength(QwtScaleDiv::MediumTick, 0);
setLabelRotation(0);
setLabelAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
setSpacing(20);
}
virtual QwtText label( double v ) const
{
QDateTime time;
time = time.fromTime_t(v);

QDate date (2010, 11, 27);
time.setDate(date);

qDebug()<<"v"<<v;
return time.toString("yyyy-MM-dd-hh-mm-ss"); //MM is important to display months correctly, mm does not work
}


Plot::Plot(QWidget *parent ): QwtPlot( parent )
{
setAutoReplot( false );

QwtPlotCanvas *canvas = new QwtPlotCanvas();
canvas->setBorderRadius( 10 );

setCanvas( canvas );

plotLayout()->setAlignCanvasToScales( true );

QwtLegend *legend = new QwtLegend;
legend->setDefaultItemMode( QwtLegendData::Checkable );
insertLegend( legend, QwtPlot::RightLegend );

setAxisTitle( QwtPlot::xBottom, "Date:Time[yyyy-mm-dd-hh-mm-ss]" );

QDateTime date1(QDate(2010,11,27), QTime(0,0,0));
QDateTime date2(QDate(2010,12,3), QTime(23,0,0));

double x1 = QwtDate::toDouble(date1);
double x2 = QwtDate::toDouble(date2);
double stepSize = 3600;
qDebug()<<date1<<date2<<QwtDate::toDateTime(x1, Qt::LocalTime)<<x1;

//QwtDateScaleDraw *scaleDraw = new DateScaleDraw(Qt::UTC);
//setAxisScaleDraw( QwtPlot::xBottom, scaleDraw );
setAxisScaleDraw( QwtPlot::xBottom, new TimeScaleDraw);

QwtDateScaleEngine *scaleEngine = new QwtDateScaleEngine();
scaleEngine->autoScale(170, x1, x2, stepSize);
//setAxisScaleEngine( QwtPlot::xBottom, scaleEngine );
//setAxisScale( QwtPlot::xBottom, 0, 170 );
setAxisLabelRotation( QwtPlot::xBottom, -50.0 );
setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom );
.....
}

Uwe
11th July 2013, 07:16
1. How the value of "v" in label is being passed on?
msecs since Epoch. You can translate it into a QDateTime value using QwtDate::toDateTime().

Your TimeScaleDraw class doesn't make much sense. Better use a QwtDateScaleDraw object and use setDateFormat() to assign the date format strings you want to have ( by the way: why doesn't mm work ? ).


The values for MajorTick & MinorTick are set as 7 and 24 resp. but the graph shows 5 major and 20 minor ticks.
No the tick length is the length of the line used for drawing a tick in pixels. This has nothing to do with the number of the ticks or where to put them.
If you want to set the position of the ticks explicitly you can use QwtPlot::setAxisScaleDiv(), but usually you want a QwtDateScaleEngine to do this job for you.

Note that most of the work for implementing the QwtDateScaleEngine was for the part that calculates the ticks for a given interval. The implementation of its autoscaler ( aligning an interval ) is a bit poor in Qwt 6.1.0. I have a better one on my disk that will make it into Qwt 6.1.1, but it needs some more testing. So for the moment I recommend to set the interval using QwtPlot::setAxisScale() - if you know the interval in application code.

Uwe

PS: better ask in the Qwt sub forum

GG2013
12th July 2013, 05:29
Thanks a lot Uwe for your time.
Sorry for not posting it in qwt forum (next time).

Now Major/minor ticks & "v" are clear to me.
But I still can't get the label right. Please see the code and attached plot.

If I don't use any scale engine then the label format works fine only with "setDateFormat(QwtDate::Millisecond, QString("yyyy-mm-dd-hh-mm-ss"));" i.e yyyy-mm-dd-hh-mm-ss.
With any other interval type (for ex Seconds) irrespective of any string format it always returns one particular format.
When scale engine is used, this formatting is changed again (same as above) as you see in the figure.

Also, how do I change the start date in x-axis? Even if the data is in mSec (since epoch) it is not reflected in the plot. In otherwords x-data should show "2010-11-27-00-00-00" and not .....1970(epoch).

Best regards,
9281



setAxisTitle( QwtPlot::xBottom, "Date:Time[yyyy-mm-dd-hh-mm-ss]" );
QDateTime date1(QDate(2010,11,27), QTime(0,0,0));
QDateTime date2(QDate(2010,12,03), QTime(23,0,0));

QwtDateScaleDraw *scaleDraw = new QwtDateScaleDraw;
scaleDraw->setDateFormat(QwtDate::Millisecond, QString("yyyy-mm-dd-hh-mm-ss"));
QwtDateScaleEngine *scaleEngine = new QwtDateScaleEngine( Qt::UTC );
setAxisScaleDraw( QwtPlot::xBottom, scaleDraw );
setAxisScaleEngine( QwtPlot::xBottom, scaleEngine );
setAxisScale(QwtPlot::xBottom, (date1.toTime_t())*1000, (date2.toTime_t())*1000, 24*3600*1000);

setAxisLabelRotation( QwtPlot::xBottom, -50.0 );
setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom );

Uwe
12th July 2013, 07:23
If I don't use any scale engine then the label format works fine only with "setDateFormat(QwtDate::Millisecond, QString("yyyy-mm-dd-hh-mm-ss"));"
The Qwt Date/Time scale classes use a classification for time interval ( see QwtDate::IntervalType ). QwtDateScaleDraw simply iterates over the major ticks and returns the largest "interval type", that can be used to display a value without rounding error. F.e. your screenshot looks like an interval, that is aligned to days.

Having a QwtDateScaleEngine is unrelated to this, beside that it will build ticks that are aligned to an interval type and the scale draw object will use a different format then. The 10:00 in your screenshot might have to do with the UTC offset of your local timezone: you have configured the scale engine to use UTC, but the scale draw and your QDateTime values are in local time !

For each interval type you can set the date format individually. The idea is that it doesn't make much sense to show miliseconds f.e. in a scale like in the screenshot. Note that changing the format for a interval of milliseconds is probably the only one, that you don't want to change as it would round away the milliseconds.

Another option is to treat any interval as something related to seconds:


class YourScaleDraw: public QwtDateScaleDraw
{
....

virtual QwtDate::IntervalType QwtDateScaleDraw::intervalType( const QwtScaleDiv &scaleDiv ) const
{
IntervalType intervalType = QwtDateScaleDraw::intervalType( scaleDiv );
if ( intervalType > QwtDate::Seconds )
intervalType = QwtDate::Seconds;

return intervalType;
}
};

Of course you could also change the format strings for all interval types showing the seconds instead.


Also, how do I change the start date in x-axis? Even if the data is in mSec (since epoch) it is not reflected in the plot. In otherwords x-data should show "2010-11-27-00-00-00" and not .....1970(epoch).
Use QwtDate::toDouble() to build a double from a QDateTime - using QDateTime::toTime_t rounds away the milisecond part.
But beside that you would have to multiply with 1000.0 - with the code above ( using 1000 as integer ) results in an integer overflow.

Uwe

GG2013
16th July 2013, 03:27
Thank you so much Uwe for your time and explanations. Sorry for replying late as I was away over the weekend.

Yes, the major problem was that multiplication with 1000. It was giving a wrong value (I could check that with manual calculation). Using QwtDate::toDouble works fine, else need to specify 1000 as double (as used in commented line which works too).

The following code now sets the major ticks correctly (please see attached fig). However, I would like also to set 24 minor ticks, each corresponding to hour (current code shows 4 divisions. I was trying with scaleEngine-divideScale but it didn't have any impact.
Kind regards,
PS. I can set minor ticks now- Thank you.

9292



QDateTime date1(QDate(2010,11,27), QTime(0,0,0));
double x1 = QwtDate::toDouble(date1);
//double x1 = date1.toTime_t() * double(1000);
QDateTime date2(QDate(2010,12,03), QTime(23,0,0));
double x2 = QwtDate::toDouble(date2);
//double x2 = date2.toTime_t() * double(1000) ;

QwtDateScaleDraw *scaleDraw = new QwtDateScaleDraw(Qt::LocalTime);
scaleDraw->setDateFormat(QwtDate::Day, QString("yyyy-MM-dd-hh-mm-ss"));
setAxisScaleDraw(QwtPlot::xBottom, scaleDraw);

QwtDateScaleEngine *scaleEngine = new QwtDateScaleEngine( Qt::LocalTime );
//scaleEngine->divideScale(x1,x2,7,7*24);
setAxisScaleEngine( QwtPlot::xBottom, scaleEngine );

setAxisScale(QwtPlot::xBottom, x1, x2, 24*3600*1000);

//qDebug()<<date1<<date2<<x1<<x2;
setAxisLabelRotation( QwtPlot::xBottom, -50.0 );
setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom );