Hi,

I'm just getting my feet wet with Qwt and the first thing I've attempted is a waterfall plot that shows frequency bins (from an FFT) on the vertical axis, time horizontally and represents intensity as Color. From reading a few posts here and following the rasterview and cpuplot examples I've got the orientation and scaling of the data within the plot... I'm using QwtMatrixRasterData (like in the rasterview example) with newest records at the end (like a vertical reverse waterfall) but Displaying the newest data on the rightmost vertical.

That works fine except that I only get as many records as there are frequency bins.... That is it only plots a square matrix, stopping at the number of frequency bins instead of the number of records in the data. I'm not quite sure I understand how (for which coordinates) the value method is called because it seems to only get called for that many records.

Is it possible to re-orient the data in the plot in this way using QwtMatrixRasterData just re-implementing value(), or will I have to implement my own QwtRasterData? Or can anyone suggest another type of plot item that would work for this?

Here's my code (so far just a small self-contained example but this will eventually be getting updates around 4 times per sec):

-----------------------------------------specplot.h --------------------------------------------------
#ifndef SPECPLOT_H
#define SPECPLOT_H
#include <qwt_plot.h>

class QwtPlotSpectrogram;

class SpectrumPlot: public QwtPlot
{
Q_OBJECT

public:
SpectrumPlot( QWidget * = NULL );

private:
QwtPlotSpectrogram *d_spectrogram;
};


#endif // SPECPLOT_H



-----------------------------------------specplot.cpp --------------------------------------------------
#include <QApplication>
#include <QVBoxLayout>
#include <QTime>

#include <qwt_plot_layout.h>
#include <qwt_scale_draw.h>
#include <qwt_scale_widget.h>
#include <qwt_plot_canvas.h>
#include <qwt_color_map.h>
#include <qwt_plot_spectrogram.h>
#include <qwt_matrix_raster_data.h>
#include <qwt_plot_magnifier.h>
#include <qwt_plot_panner.h>

#include <iostream>

#include "specplot.h"

#define HISTORY 60 // seconds

//================================================== ============================
class RasterData: public QwtMatrixRasterData
{
public:
RasterData() :
numBins(10),
numRecs(36)
{
const double matrix[] =
{
100,100,100,100,100,100,100,100,100,100,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
63, 69, 98, 79, 75, 75, 73, 71, 69, 70,
63, 63, 99, 77, 76, 73, 71, 69, 68, 70,
62, 63, 96, 75, 75, 70, 66, 65, 66, 66,
60, 60, 95, 75, 76, 72, 67, 65, 66, 66,
60, 60, 91, 70, 72, 68, 67, 67, 67, 68,
61, 60, 91, 65, 65, 62, 64, 65, 66, 66,
64, 63, 91, 63, 62, 63, 65, 66, 66, 68,
64, 63, 90, 60, 61, 61, 63, 65, 66, 67,
63, 61, 90, 63, 63, 62, 64, 66, 65, 66,
60, 60, 90, 60, 61, 61, 63, 65, 65, 66,
68, 71, 95, 80, 76, 71, 71, 71, 70, 69,
64, 65, 99, 75, 72, 69, 68, 68, 67, 67,
60, 60, 90, 65, 66, 63, 64, 65, 66, 66,
64, 67, 92, 82, 81, 73, 73, 76, 76, 76,
72, 76, 98, 87, 86, 79, 79, 82, 79, 76,
70, 74, 96, 83, 82, 78, 79, 80, 78, 77,
67, 75, 96, 93, 91, 88, 89, 83, 77, 76,
64, 77, 98, 93, 91, 87, 84, 77, 76, 77,
70, 74, 92, 88, 86, 83, 80, 74, 72, 75,
68, 73, 99, 85, 82, 79, 79, 75, 72, 70,
60, 60, 95, 70, 72, 70, 68, 69, 68, 69,
60, 60, 90, 63, 64, 65, 65, 65, 66, 66,
60, 60, 90, 62, 63, 64, 65, 66, 66, 67,
60, 60, 90, 60, 61, 63, 66, 67, 67, 66,
60, 60, 90, 65, 64, 62, 64, 66, 66, 66,
60, 60, 90, 64, 64, 63, 64, 66, 66, 66,
60, 60, 90, 60, 61, 61, 63, 65, 66, 66,
60, 60, 90, 60, 61, 61, 63, 65, 65, 65,
60, 60, 90, 63, 65, 63, 64, 65, 66, 66,
60, 60, 90, 60, 62, 62, 63, 65, 66, 66,
60, 60, 90, 60, 60, 61, 63, 64, 65, 66,
60, 60, 90, 60, 61, 62, 63, 65, 66, 66,
60, 60, 90, 60, 60, 61, 63, 64, 65, 66,
100,100,100,100,100,100,100,100,100,100
};

QVector<double> values;

for ( uint i = 0; i < sizeof( matrix ) / sizeof( double ); i++ )
values += matrix[i];
std::cout << "Added " << std::to_string(values.size()/numBins) << " recs" << std::endl;

// fill up the values matrix to match the size of the plot...
// can I avoid this??
for ( int i = values.size(); i < (HISTORY*numBins); i++ )
values += -1;
std::cout << "Padded to " << std::to_string(values.size()/numBins) << " recs" << std::endl;

setValueMatrix( values, numBins );

setInterval( Qt::XAxis,
QwtInterval( 0, numBins, QwtInterval::ExcludeMaximum ) );
setInterval( Qt::YAxis,
QwtInterval( 0, HISTORY, QwtInterval::ExcludeMaximum ) );
setInterval( Qt::ZAxis, QwtInterval( 1.0, 100.0 ) );


//setResampleMode(ResampleMode::BilinearInterpolatio n);
setResampleMode(ResampleMode::NearestNeighbour);
}

// x = plot freq bin
// y = plot time
virtual double value( double x, double y ) const
{
double dataX, dataY;

// frequency bins are columns in data, rows on plot
dataX = y;

// time - newest on the right
dataY = numRecs - x;

return QwtMatrixRasterData::value( dataX , dataY);;
}

private:
const int numBins;
int numRecs;
};


//================================================== ============================
class ColorMap: public QwtLinearColorMap
{
public:
ColorMap():
QwtLinearColorMap( Qt::black, Qt::darkRed )
{
addColorStop( 0.2, Qt::blue );
addColorStop( 0.4, Qt::cyan );
addColorStop( 0.5, Qt::green);
addColorStop( 0.6, Qt::yellow );
addColorStop( 0.8, Qt::red );
}
};

//================================================== ============================
class TimeScaleDraw: public QwtScaleDraw
{
public:
TimeScaleDraw( const QTime &base ):
baseTime( base )
{
}
virtual QwtText label( double v ) const
{
QTime upTime = baseTime.addSecs( static_cast<int>( HISTORY - v ) );
return upTime.toString();
}
private:
QTime baseTime;
};

//================================================== ============================
SpectrumPlot::SpectrumPlot( QWidget *parent ):
QwtPlot( parent )
{
QwtPlotCanvas *canvas = new QwtPlotCanvas();
canvas->setBorderRadius( 0 );
setCanvas( canvas );

d_spectrogram = new QwtPlotSpectrogram();
d_spectrogram->setRenderThreadCount( 1 ); // 0 = use system specific thread count

d_spectrogram->setColorMap( new ColorMap() );

d_spectrogram->setData( new RasterData() );
d_spectrogram->attach( this );
d_spectrogram->setXAxis(QwtPlot::xBottom);
d_spectrogram->setYAxis(QwtPlot::yRight);

// Color scale map on left
const QwtInterval zInterval = d_spectrogram->data()->interval( Qt::ZAxis );
QwtScaleWidget *leftAxis = axisWidget( QwtPlot::yLeft );
leftAxis->setColorBarEnabled( true );
leftAxis->setColorBarWidth( 10 );
leftAxis->setColorMap( zInterval, new ColorMap() );

setAxisScale( QwtPlot::yLeft, zInterval.minValue(), zInterval.maxValue() );
enableAxis( QwtPlot::yLeft, true );


enableAxis( QwtPlot::yRight );

plotLayout()->setAlignCanvasToScales( true );

setAxisTitle( QwtPlot::xBottom, "Time [h:m:s]" );
setAxisScaleDraw( QwtPlot::xBottom,
new TimeScaleDraw( QTime::currentTime() ) );
setAxisScale( QwtPlot::xBottom, HISTORY, 0);
setAxisLabelRotation( QwtPlot::xBottom, -50.0 );
setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom );

setAxisScale( QwtPlot::yRight, 0.0, 10.0 );
setAxisMaxMinor( QwtPlot::yRight, 10 );

QwtPlotMagnifier *magnifier = new QwtPlotMagnifier( canvas );
magnifier->setAxisEnabled( QwtPlot::yLeft, false );
magnifier->setAxisEnabled( QwtPlot::yRight, false );

QwtPlotPanner *panner = new QwtPlotPanner( canvas );
panner->setAxisEnabled( QwtPlot::yLeft, false );
panner->setAxisEnabled( QwtPlot::yRight, false );
}

//== MAIN ================================================== ==================

int main( int argc, char **argv )
{
QApplication a( argc, argv );

QWidget vBox;
vBox.setWindowTitle( "Horizontal Waterfall" );

SpectrumPlot *plot = new SpectrumPlot(&vBox);

QVBoxLayout *layout = new QVBoxLayout(&vBox);
layout->addWidget(plot);

vBox.resize( 800, 600 );
vBox.show();

return a.exec();
}