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