QCustomPlot Discussion and Comments

QCPColorMap spectrogramReturn to overview

The QCPColorMap can already be used to create nice-looking spectrograms, but it requires some tedious work like shifting all existing data before inserting next line/row. Perhaps a lineOffset field could be added to QCPColorMapData and updateMapImage() modified from

    for (int line=0; line<lineCount; ++line)
    {
      QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-line));
      mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
    }
to
    for (int line=mMapData->lineOffset; line<lineCount; ++line)
    {
      QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-(line-mMapData->lineOffset)));
      mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
    }
    for (int line=0; line<mMapData->lineOffset; ++line)
    {
      QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-(line +lineCount-mMapData->lineOffset)));
      mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
    }

and data/cell getters/setters changed to
double QCPColorMapData::data(double key, double value)
{
  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
  int valueCell = (1.0-(value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower))*(mValueSize-1)+0.5;
  valueCell = (valueCell + lineOffset)%valueSize();
  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
    return mData[valueCell*mKeySize + keyCell];
  else
    return 0;
}

double QCPColorMapData::cell(int keyIndex, int valueIndex)
{
  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
  {
    valueIndex = (valueIndex + lineOffset)%valueSize();
    return mData[valueIndex*mKeySize + keyIndex];
  }
  else
    return 0;
}

void QCPColorMapData::setData(double key, double value, double z)
{
  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;

  valueCell = (valueCell + lineOffset)%valueSize();
  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
  {
    mData[valueCell*mKeySize + keyCell] = z;
    if (z < mDataBounds.lower)
      mDataBounds.lower = z;
    if (z > mDataBounds.upper)
      mDataBounds.upper = z;
     mDataModified = true;
  }
}

void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
{
  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
  {
      valueIndex = (valueIndex + lineOffset)%valueSize();

    mData[valueIndex*mKeySize + keyIndex] = z;
    if (z < mDataBounds.lower)
      mDataBounds.lower = z;
    if (z > mDataBounds.upper)
      mDataBounds.upper = z;
     mDataModified = true;
  }
}

Some helper functions like

void QCPColorMapData::shift(bool down)
{
    if(down)
        lineOffset = ++lineOffset%valueSize();
    else
        lineOffset = (--lineOffset +valueSize())%valueSize();
    mDataModified = true;
}

and
void QCPColorMapData::insertValues(QVector<double> values, bool top)
{
    shift(top);
    for(int i = 0; i < values.size(); ++i)
        setCell(i, top?mValueSize-1:0, values[i]);
}

could then easily be introduced.

Oh Thanks, It's exactly what I am looking for! It could be cool to have a setData function that takes QVector<QVector<double>> too. I don't know if it's doable...

Thanks again!

Thank Henrik,

I am implemented as you described and it is working amazingly good.

Thank you

Hi Henrik and thanks a lot.

It works perfectly and without additional CPU usage. Congratulation ;).
But can you propose a code version to shift and insert values by row and not by line (as it was described in our post).

Thank you

(Not see that the first post was to old :( )