QCustomPlot Discussion and Comments

Empty selection with stDataRange yield errorsReturn to overview

Hi,

if I use a QCPGraph with setSelectable(QCP::stDataRange); and afterwards submit an empty selection, I get errors on the console:

double QCPAbstractPlottable1D<DataType>::dataMainValue(int) const [with DataType = QCPGraphData] Index out of bounds -1
double QCPAbstractPlottable1D<DataType>::dataMainKey(int) const [with DataType = QCPGraphData] Index out of bounds -1

I guess the reason is, that QCPDataSelection::enforceType(QCP::SelectionType type) generates a strange QCPDataSelection([0..-1]) via the span() method.

The following code demonstrates the error.

int main(int argc, char *argv[])
{
  QCPDataSelection selection;
  qDebug() << selection;
  qDebug() << selection.span();
  selection.enforceType(QCP::stDataRange);
  qDebug() << selection;
}

This results in:

QCPDataSelection()
[0..-1]
QCPDataSelection([0..-1] )

This is at least not what I would expect...

Best,
Olaf

Thanks for reporting!
Will be fixed in next patch.

I can't reproduce this bug. Are you sure this error message doesn't come from your own code calling dataMainKey and dataMainValue with invalid indices (-1)? Maybe in a slot that reacts to selection changes and then tries to operate on the empty range with something like end()-1 without checking whether that's valid?

Note: The debug output when piping a QCPDataRange into qDebug is a bit unfortunate, this will change in the next patch. It is meant as "first data point"..."last data point", and that makes it look funny for empty ranges. I've changed the debug output to a more intuitive display "QCPDataRange(begin, end)" style, so it would be "QCPDataRange(0, 0)" for an empty data range.

Hi Manu,

Sorry, my description of the error was not accurate. I have a graph in a plot and deselect it by clicking in an empty spot. Thus the graph is unselected. The selection decorator is a QCPSelectionDecoratorBracket, which is, where exactly the error occurs.

It is generated from the call chain
double QCPAbstractPlottable1D<DataType>::dataMainKey called from
QCPSelectionDecoratorBracket::getPixelCoordinates called from
QCPSelectionDecoratorBracket::drawDecoration

The critial section is:

    foreach (const QCPDataRange &dataRange, selection.dataRanges())
    {
      // determine position and (if tangent mode is enabled) angle of brackets:
      int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1;
      int closeBracketDir = -openBracketDir;
      QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin());
      QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1);


In the last line, an empty QCPDataRange yields an index of -1. The emptyness is not cheched for.

But this is only part of the problem. Let selection be an empty QCPDataSelection. isEmpty() checks the QList<QCPDataRange> for elements. If there are none, the selection is empty. selection.enforceType(QCP::stDataRange) now takes selection.span(), which returns an empty QCPDataRange for an empty selection, and inserts that in mDataRange.
So now, selection.isEmpty() returns false, because its mDataRange does contain an (empty) QCPDataRange. This is not the answer I would expect!

My bugfix for void QCPDataSelection::enforceType(QCP::SelectionType type) is (only the relevant case is shown here)

    case QCP::stDataRange:
    {
      mDataRanges.clear();
      QCPDataRange range = span();
      if (!range.isEmpty())
        mDataRanges << range;
      break;


mfg.
Olaf Schumann

Hi All,

I have to give an erratum, the second code block ist unfortunately not correct. Line 3 clears the mDataRanges that are needed in the span() function. So these lines have to be swapped. The correct code is:

case QCP::stDataRange:
    {
      QCPDataRange range = span();
      mDataRanges.clear();
      if (!range.isEmpty())
        mDataRanges << range;
      break;
    }

Best,
Olaf