QCustomPlot Discussion and Comments

Several stacked yAxis for one AxisRectReturn to overview

Hi everyone,

Just want to know if there is a built-in way to stack vertically several yAxis and still allowing the graphs affected to a specific axis to use the whole height of the axisRect.

If someone has already faced this need, any help would be appreciated.

Thanks

Do you mean like in the
https://www.qcustomplot.com/index.php/demos/advancedaxesdemo
demo, the left axis? Or really on top of each other? you could try playing with the setOffset or perhaps modifying the QCPAxis to report a minimum margin of 0.

Thanks for the reply.

When adding a new valueAxis, the axes are horizontally layed out, allowing graphs to use the whole height of the axisRect.
I would like to lay out value axes vertically.

The closest examples of what i want to achieve are seismogram or F1 lap telemetry plot.

I already tried to hack qcp and achieved to set the axis height and position, but before going further, just want to know if someone has faced this need and found a nice solution.

I managed to quickly do what i want.
Here is the result : https://postimg.cc/ZWRGQbFV/84b3bc7a

To do so, i create fake visible axes aligned vertically in a QCPLayoutGrid.
For each graph, we need to create specific hidden axis.
And after updating the layout, the ranges of hidden axes are calculated based on the range/height/position of the associated fake axis.

Code balow, based on the plot-examples project :



void MainWindow::setupStackedAxis(QCustomPlot *customPlot)
{
    demoName = "Stacked Axes Demo";

    customPlot->plotLayout()->clear(); // clear default axis rect so we can start from scratch
    QCPAxisRect *wideAxisRect = new QCPAxisRect(customPlot, false);

    wideAxisRect->setMinimumMargins(QMargins(15, 15, 5, 15));
    wideAxisRect->setupFullAxesBox(true);

    wideAxisRect->axis(QCPAxis::atLeft)->setVisible(false);
    wideAxisRect->axis(QCPAxis::atTop)->setVisible(false);
    wideAxisRect->axis(QCPAxis::atRight)->grid()->setVisible(true);
    wideAxisRect->axis(QCPAxis::atBottom)->grid()->setVisible(true);

    QCPLayoutGrid *subLayout = new QCPLayoutGrid;
    subLayout->setFillOrder(QCPLayoutGrid::foRowsFirst);
    customPlot->plotLayout()->addElement(0, 0, wideAxisRect); // insert axis rect in first row
    customPlot->plotLayout()->addElement(0, 1, subLayout); // sub layout in second row (grid layout will grow accordingly)

    customPlot->plotLayout()->setColumnStretchFactor(0,1);
    customPlot->plotLayout()->setColumnStretchFactor(1, 0.001);

    // create fake axis
    QCPAxisRect *subRectTop = new QCPAxisRect(customPlot, false);
    auto topAxis = subRectTop->addAxis(QCPAxis::atRight);
    topAxis->setTickLabelColor(Qt::blue);
    topAxis->setTickPen(QPen(Qt::blue));
    topAxis->setSubTickPen(QPen(Qt::blue));
    topAxis->setBasePen(QPen(Qt::blue));
    topAxis->ticker()->setTickCount(3);

    subRectTop->setMinimumSize(0,0);
    subRectTop->setMinimumMargins(QMargins(5, 15, 40, 5));
    subLayout->addElement(subRectTop);

    //Create real axis - not visible
    auto firstAxis = wideAxisRect->addAxis(QCPAxis::atRight);
    firstAxis->setVisible(false);


    QCPAxisRect *subRectMiddle = new QCPAxisRect(customPlot, false);
    auto middleAxis = subRectMiddle->addAxis(QCPAxis::atRight);
    middleAxis->setTickLabelColor(Qt::red);
    middleAxis->setTickPen(QPen(Qt::red));
    middleAxis->setSubTickPen(QPen(Qt::red));
    middleAxis->setBasePen(QPen(Qt::red));
    middleAxis->ticker()->setTickCount(3);

    subRectMiddle->setMinimumSize(0,0);
    subRectMiddle->setMinimumMargins(QMargins(5, 5, 40, 5));
    subLayout->addElement(subRectMiddle);

    auto secondAxis = wideAxisRect->addAxis(QCPAxis::atRight);
    secondAxis->setVisible(false);

    QCPAxisRect *subRectBottom = new QCPAxisRect(customPlot, false);
    auto bottomAxis = subRectBottom->addAxis(QCPAxis::atRight);
    bottomAxis->setTickLabelColor(Qt::green);
    bottomAxis->setTickPen(QPen(Qt::green));
    bottomAxis->setSubTickPen(QPen(Qt::green));
    bottomAxis->setBasePen(QPen(Qt::green));
    bottomAxis->ticker()->setTickCount(3);

    subRectBottom->setMinimumSize(0,0);
    subRectBottom->setMinimumMargins(QMargins(5, 5, 40, 15));
    subLayout->addElement(subRectBottom);

    auto thirdAxis = wideAxisRect->addAxis(QCPAxis::atRight);
    thirdAxis->setVisible(false);

    foreach (QCPAxisRect *rect, customPlot->axisRects())
    {
        foreach (QCPAxis *axis, rect->axes())
        {
            axis->setLayer("axes");
            axis->grid()->setLayer("grid");
        }
    }

    QCPGraph *graphCosFirst = customPlot->addGraph(wideAxisRect->axis(QCPAxis::atBottom), firstAxis);
    graphCosFirst->setPen(QPen(Qt::blue));

    QCPGraph *graphCosSecond = customPlot->addGraph(wideAxisRect->axis(QCPAxis::atBottom), secondAxis);
    graphCosSecond->setPen(QPen(Qt::red));

    QCPGraph *graphCosThird = customPlot->addGraph(wideAxisRect->axis(QCPAxis::atBottom), thirdAxis);
    graphCosThird->setPen(QPen(Qt::green));

    QVector<QCPGraphData> dataCos(51);
    for (int i=0; i<dataCos.size(); ++i)
    {
        auto key = i/(double)(dataCos.size()-1)*10-5.0;
        auto value = qCos(key);

        graphCosFirst->addData(key, value);
        graphCosSecond->addData(key, value);
        graphCosThird->addData(key, value);
    }

    wideAxisRect->axis(QCPAxis::atBottom)->rescale();

    topAxis->setRange(0,1);
    middleAxis->setRange(-0.5,0.5);
    bottomAxis->setRange(-2,0);

    QCPMarginGroup *marginGroup = new QCPMarginGroup(customPlot);
    subRectTop->setMarginGroup(QCP::msTop, marginGroup);
    subRectBottom->setMarginGroup(QCP::msBottom, marginGroup);
    wideAxisRect->setMarginGroup(QCP::msTop | QCP::msBottom, marginGroup);

    connect(customPlot, &QCustomPlot::afterLayout, this, [=](){

        {
            auto pixelRatio = topAxis->range().size() / (topAxis->coordToPixel(topAxis->range().lower) - topAxis->coordToPixel(topAxis->range().upper));

            auto topOffset = (topAxis->coordToPixel(topAxis->range().upper) - wideAxisRect->rect().top()) * pixelRatio;
            auto bottomOffset = (wideAxisRect->rect().bottom() - topAxis->coordToPixel(topAxis->range().lower)) * pixelRatio;

            firstAxis->setRange(topAxis->range().lower - bottomOffset,
                                 topAxis->range().upper + topOffset);
        }

        {
            auto pixelRatio = middleAxis->range().size() / (middleAxis->coordToPixel(middleAxis->range().lower) - middleAxis->coordToPixel(middleAxis->range().upper));

            auto topOffset = (middleAxis->coordToPixel(middleAxis->range().upper) - wideAxisRect->rect().top()) * pixelRatio;
            auto bottomOffset = (wideAxisRect->rect().bottom() - middleAxis->coordToPixel(middleAxis->range().lower)) * pixelRatio;
            secondAxis->setRange(middleAxis->range().lower - bottomOffset,
                                 middleAxis->range().upper + topOffset);
        }

        {
            auto pixelRatio = bottomAxis->range().size() / (bottomAxis->coordToPixel(bottomAxis->range().lower) - bottomAxis->coordToPixel(bottomAxis->range().upper));

            auto topOffset = (bottomAxis->coordToPixel(bottomAxis->range().upper) - wideAxisRect->rect().top()) * pixelRatio;
            auto bottomOffset = (wideAxisRect->rect().bottom() - bottomAxis->coordToPixel(bottomAxis->range().lower)) * pixelRatio;

            thirdAxis->setRange(bottomAxis->range().lower - bottomOffset,
                                 bottomAxis->range().upper + topOffset);
        }
    });
}