December 3, 2011

koahnig koahnig
Mad Scientist
2193 posts

Qwt x-axis should indicate hh:mm:ss

 

Hi,

I have started to use Qwt 6.0.1. At the moment I have a similar problem as described in this thread [developer.qt.nokia.com] . My value are measured in time. The x-axis should show hh:mm:ss. The QwtScaleDraw and a solution with reimplementation of QwtScaleDraw::label is certainly an option. Currently, I am still looking to find how define the values for different ticks.

Can anyoneelse point me towards to look at in more detail?

Thanks in advance.

10 replies

December 3, 2011

Scylla Scylla
Lab Rat
238 posts

Take a look at cpuplot example in the qwt source. This should be a good starting point.

December 4, 2011

koahnig koahnig
Mad Scientist
2193 posts

Thanks for pointing me to the example. This is certainly a good start.
However, it shows also what I want to avoid. The labels drawn are:

  1. 02:38:46
  2. 02:38:56
  3. 02:39:06
  4. ...

It would be more logical to plot instead:
  1. 02:38:40
  2. 02:38:50
  3. 02:39:00
  4. ...

or for larger intervals:
  1. 02:30:00
  2. 02:40:00
  3. 02:50:00
  4. ...

This must be handled somewhere in qwt_scale_engine and qwt_interval, but without detailed background on the implementation it is a bit of a pain.

December 4, 2011

Scylla Scylla
Lab Rat
238 posts

No, it depends on the x-achsis values. Put the the x-values in a 10s step and you will see the right scale.

  1. QTime upTime = baseTime.addSecs((int)v);

convert your x-values from seconds to a QTime.

December 4, 2011

koahnig koahnig
Mad Scientist
2193 posts

hmmm ??
I came to this statement here:

  1. class TimeScaleDraw: public QwtScaleDraw
  2. {
  3. public:
  4.     TimeScaleDraw(const QTime &base):
  5.         baseTime(base)
  6.     {
  7.     }
  8.     virtual QwtText label(double v) const
  9.     {
  10.         QTime upTime = baseTime.addSecs((int)v);
  11.         return upTime.toString();
  12.     }
  13. private:
  14.     QTime baseTime;
  15. };

The step size of v is in tens.

I have added also the stepsize in CpuPlot constructor to this statement:

  1.     setAxisScale(QwtPlot::xBottom, 0, HISTORY, 10);

But still the same.

[edit] Update because some information was deleted prior to posting :-(

December 4, 2011

Scylla Scylla
Lab Rat
238 posts

Can you show how you use the “TimeScaleDraw”, show more code.
Anyway, may be you should take look at setAxisMajor(…) and setAxisMinor(…) methods of the QwtPlot.

December 4, 2011

koahnig koahnig
Mad Scientist
2193 posts

I am simply using the cpuplot example and apply some changes in order to find out how to do it.

However, before we talking about different implementations. I have compiled the examples downloaded together with Qwt 6.0.1. In another thread I have read that there are significant differences to previous versions.

This is the section I have changed with no success:

  1. CpuPlot::CpuPlot(QWidget *parent):
  2.     QwtPlot(parent),
  3.     dataCount(0)
  4. {
  5.     setAutoReplot(false);
  6.  
  7.     canvas()->setBorderRadius( 10 );
  8.  
  9.     plotLayout()->setAlignCanvasToScales(true);
  10.  
  11.     QwtLegend *legend = new QwtLegend;
  12.     legend->setItemMode(QwtLegend::CheckableItem);
  13.     insertLegend(legend, QwtPlot::RightLegend);
  14.  
  15.     setAxisTitle(QwtPlot::xBottom, " System Uptime [h:m:s]");
  16.     setAxisScaleDraw(QwtPlot::xBottom,
  17.         new TimeScaleDraw(cpuStat.upTime()/*.addSecs ( -6 )*/ ) );
  18.     setAxisScale(QwtPlot::xBottom, 0, HISTORY, 10);
  19.     setAxisMaxMajor ( QwtPlot::xBottom, 10 );
  20.     setAxisMaxMinor ( QwtPlot::xBottom, 10 );
  21.     setAxisLabelRotation(QwtPlot::xBottom, -50.0);
  22.     setAxisLabelAlignment(QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom);
  23.  
  24.     /*
  25.      In situations, when there is a label at the most right position of the
  26.      scale, additional space is needed to display the overlapping part
  27.      of the label would be taken by reducing the width of scale and canvas.
  28.      To avoid this "jumping canvas" effect, we add a permanent margin.
  29.      We don't need to do the same for the left border, because there
  30.      is enough space for the overlapping label below the left scale.
  31.      */
  32.  
  33.     QwtScaleWidget *scaleWidget = axisWidget(QwtPlot::xBottom);
  34.     const int fmh = QFontMetrics(scaleWidget->font()).height();
  35.     scaleWidget->setMinBorderDist(0, fmh / 2);
  36.  
  37.     setAxisTitle(QwtPlot::yLeft, "Cpu Usage [%]");
  38.     setAxisScale(QwtPlot::yLeft, 0, 100, 10);

setAxisMaxMinor and setAxisMaxMajor has been added now.

In

  1.     setAxisScaleDraw(QwtPlot::xBottom,
  2.         new TimeScaleDraw(cpuStat.upTime()/*.addSecs ( -6 )*/ ) );

you see commented out addSecs. This certainly makes the labels look nicely rounded to every 10 seconds. Unfortunately it is changing basically the x-values now shifted by 6 seconds. So the graph is actually wrong.

December 5, 2011

koahnig koahnig
Mad Scientist
2193 posts

I have found a solution for finding better fitting tick labels.

I have introduced following class:

  1. class SecondsLinearScaleEngine : public QwtLinearScaleEngine
  2. {
  3. public:
  4.     virtual void autoScale( int maxSteps,
  5.         double &x1, double &x2, double &stepSize ) const;
  6.  
  7. protected:
  8.     double divideInterval( double interval, int numSteps ) const;
  9.     double ceil60 ( double v ) const;
  10. };

You need to copy autoScale from its base class, since divideInterval is not virtual.
  1. double SecondsLinearScaleEngine::divideInterval(
  2.     double intervalSize, int numSteps ) const
  3. {
  4.     if ( numSteps <= 0 )
  5.         return 0.0;
  6.  
  7.     double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
  8.     if ( v < 1.0 )
  9.         return QwtScaleArithmetic::ceil125( v );
  10.     return ceil60( v );
  11. }

This is basically the same as of the base class. Only the return line has been substituted with the three last lines.
ceil60 does return multiples of 1,2,3,5 up to 30 seconds, minutes.

The equivalent of cpuplot requires two additional statements.

  1.     setAxisScaleDraw ( QwtPlot::xBottom, new TimeScaleDraw ( QTime() ) );
  2.     setAxisScaleEngine ( QwtPlot::xBottom, new SecondsLinearScaleEngine );

Thanks to Scylla for playing the sparing partner.

December 5, 2011

Scylla Scylla
Lab Rat
238 posts

Thank you for sharing your solution. I’m sure that I can use this in the next project where I have to use qwt havily. So may be I will/must have a deeper look in the qwt source. By the way, qwt is a very nice lib ;-)

December 6, 2011

Andre Andre
Area 51 Engineer
6075 posts

Did you send your class to the QWT maintainer? He might be interested in adding this functionality to the library itself?

 Signature 

Looking for Qt developers to join our team @ i-Optics: https://qt-project.org/forums/viewthread/25393/

December 6, 2011

koahnig koahnig
Mad Scientist
2193 posts

Thanks for feedback.
at Scylla: Yes, it is a very nice lib. Even so, I grumbled a bit to myself about the documentation in the beginning, it was not so hard to extend. ;-)
at Andre: No. This is a good suggestion. However, at the time being it is a bit too much “hand-knitten”. After some clean-up I will contact them.

 
  ‹‹ [Solved]More qwt plot questions      Absolutely new to Qt..need help with QWT library structure ››

You must log in to post a reply. Not a member yet? Register here!