QCustomPlot Discussion and Comments

Rescale y-axis for visible data only?Return to overview

I have a QCustomPlot widget with multiple graphs to display multiple streams of data on the same plot. My UI has checkboxes that correspond to each graph. When one is clicked, I show or hide that graph as such:

widget->graph(0)->setVisible(showGraph0->isChecked());

I continue adding and removing data regardless of whether or not the graph is visible so that it can be seamlessly toggled on/off.


widget->graph(0)->addData(x, y);
widget->graph(0)->removeDataBefore(x - MAXWIDTH);
widget->xAxis->setRange(x - MAXWIDTH, x);
widget->yAxis->rescale();

The problem I'm trying to solve is with rescaling the y-axis. yAxis->rescale() rescales for ALL the data, including the graphs that may not be visible. I want the yAxis-rescale() to scale for ONLY the visible graph data.

So, if graph(0) & graph(1) are set to false (not visible) and graph(2) & graph(3) are set to true (visible), the widget's yAxis is scaled for graph(2) & graph(3) but NOT graph(0) and graph(1).

Performance is a concern since I'm processing a fairly large real-time data set. What's the optimal way to do this?

Documentation says:

void QCPAxis::rescale ( bool  onlyVisiblePlottables = false) 

Try to use this flag:
widget->yAxis->rescale(true);

I'm having the same issue and calling yAxis->rescale(true) doesn't seem to do the trick. By "onlyVisiblePlottables", it must be referring to the entire visible plottable, including the data points in the plottable that are not currently visible in the axisRect(). This is because if I scroll to the left to view the entire plottable, I'll notice that the scale has been adjusted to accommodate the max and min of the entire graph.

I should probably add that I'm using QCPFinancial as the plottable.

In case anyone else is having that problem, I came up with this solution. it seems to work ok so far. If there is a more efficient way or a built in way then I'd be interested in hearing about it.

//rescales the y axis to fit visible data points
void ChartWindow::rescaleYAxis()
{
    //get visible min and max y values
    if (m_pCandleSticks)
    {
        QCPFinancialDataMap *pDataMap = m_pCandleSticks->data();

        double dHigh = 0;
        double dLow = 999999;

        for (int i = 0; i < m_plot->xAxis->tickVector().size(); ++i)
        {
            double dTick = m_plot->xAxis->tickVector().at(i);
            QCPFinancialDataMap::iterator it = pDataMap->find(dTick);
            if (it != pDataMap->end())
            {
                if (it->high > dHigh)
                    dHigh = it->high;
                if (it->low < dLow && it->low > 0)
                    dLow = it->low;
            }
        }

        m_plot->yAxis->setRange(dLow, dHigh);
    }
}

FYI: A builtin way (a parameter for the rescale functions "inKeyRange") will be provided in QCP2.0

Also, the code you posted above is a bit strange. It will miss any data that's not exactly on ticks. I'd go about it like this: Get the current lower and upper key range (xAxis->range().lower and .upper), then use
dataMap->lowerBound and upperBound on the data map to find the data span with keys in that range. Finally loop over all data points in that span and record the minimum and maximum occuring values.

Here is a possible solution:

        QCPFinancialDataMap *pDataMap = m_ptrCandles->data();
        QCPFinancialDataMap::const_iterator lower = pDataMap->lowerBound(ui->chart->xAxis->range().lower);
        QCPFinancialDataMap::const_iterator upper = pDataMap->upperBound(ui->chart->xAxis->range().upper);
        //TODO: error checking

        double dHigh = std::numeric_limits<double>::min();
        double dLow = std::numeric_limits<double>::max();

        while (lower != upper)
        {
            if (lower.value().high > dHigh) dHigh = lower.value().high;
            if (lower.value().low < dLow) dLow = lower.value().low;
            lower++;
        }

        ui->chart->yAxis->setRange(dLow*0.99, dHigh*1.01);