自学内容网 自学内容网

QCustomplot 多Y轴实现

项目中需要实现实时曲线的多Y轴实现,对比了qwt和 qcustomplot,觉得后者用起来更简单些,简单记录下:
效果如下
在这里插入图片描述

核心代码如下:

class AxisTag : public QObject
{
  Q_OBJECT
public:
  explicit AxisTag(QCPAxis *parentAxis);
  virtual ~AxisTag();
  
  // setters:
  void setPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setText(const QString &text);
  
  // getters:
  QPen pen() const { return mLabel->pen(); }
  QBrush brush() const { return mLabel->brush(); }
  QString text() const { return mLabel->text(); }
  
  // other methods:
  void updatePosition(double value);
  
protected:
  QCPAxis *mAxis;
  QPointer<QCPItemTracer> mDummyTracer;
  QPointer<QCPItemLine> mArrow;
  QPointer<QCPItemText> mLabel;
};

cpp

#include "axistag.h"

AxisTag::AxisTag(QCPAxis *parentAxis) :
  QObject(parentAxis),
  mAxis(parentAxis)
{
  // The dummy tracer serves here as an invisible anchor which always sticks to the right side of
  // the axis rect
  mDummyTracer = new QCPItemTracer(mAxis->parentPlot());
  mDummyTracer->setVisible(true);
  mDummyTracer->position->setTypeX(QCPItemPosition::ptAxisRectRatio);
  mDummyTracer->position->setTypeY(QCPItemPosition::ptPlotCoords);
  mDummyTracer->position->setAxisRect(mAxis->axisRect());
  mDummyTracer->position->setAxes(0, mAxis);
  mDummyTracer->position->setCoords(1, 0);

  mDummyTracer->setStyle(QCPItemTracer::tsCircle);
  mDummyTracer->setSize(10);
  
  // the arrow end (head) is set to move along with the dummy tracer by setting it as its parent
  // anchor. Its coordinate system (setCoords) is thus pixels, and this is how the needed horizontal
  // offset for the tag of the second y axis is achieved. This horizontal offset gets dynamically
  // updated in AxisTag::updatePosition. the arrow "start" is simply set to have the "end" as parent
  // anchor. It is given a horizontal offset to the right, which results in a 15 pixel long arrow.
  mArrow = new QCPItemLine(mAxis->parentPlot());
  mArrow->setLayer("overlay");
  mArrow->setClipToAxisRect(false);
  mArrow->setHead(QCPLineEnding::esSpikeArrow);
  mArrow->end->setParentAnchor(mDummyTracer->position);
  mArrow->start->setParentAnchor(mArrow->end);
  mArrow->start->setCoords(15, 0);
  
  // The text label is anchored at the arrow start (tail) and has its "position" aligned at the
  // left, and vertically centered to the text label box.
  mLabel = new QCPItemText(mAxis->parentPlot());
  mLabel->setLayer("overlay");
  mLabel->setClipToAxisRect(false);
  mLabel->setPadding(QMargins(3, 0, 3, 0));
  mLabel->setBrush(QBrush(Qt::white));
  mLabel->setPen(QPen(Qt::blue));
  mLabel->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter);
  mLabel->position->setParentAnchor(mArrow->start);
}

AxisTag::~AxisTag()
{
  if (mDummyTracer)
    mDummyTracer->parentPlot()->removeItem(mDummyTracer);
  if (mArrow)
    mArrow->parentPlot()->removeItem(mArrow);
  if (mLabel)
    mLabel->parentPlot()->removeItem(mLabel);
}

void AxisTag::setPen(const QPen &pen)
{
  mDummyTracer->setPen(pen);
  mArrow->setPen(pen);
  mLabel->setPen(pen);
}

void AxisTag::setBrush(const QBrush &brush)
{
   mDummyTracer->setBrush(brush);
  mLabel->setBrush(brush);
}

void AxisTag::setText(const QString &text)
{
  mLabel->setText(text);
}

void AxisTag::updatePosition(double value)
{
  // since both the arrow and the text label are chained to the dummy tracer (via anchor
  // parent-child relationships) it is sufficient to update the dummy tracer coordinates. The
  // Horizontal coordinate type was set to ptAxisRectRatio so to keep it aligned at the right side
  // of the axis rect, it is always kept at 1. The vertical coordinate type was set to
  // ptPlotCoordinates of the passed parent axis, so the vertical coordinate is set to the new
  // value.
  mDummyTracer->position->setCoords(1, value);
  
  // We want the arrow head to be at the same horizontal position as the axis backbone, even if
  // the axis has a certain offset from the axis rect border (like the added second y axis). Thus we
  // set the horizontal pixel position of the arrow end (head) to the axis offset (the pixel
  // distance to the axis rect border). This works because the parent anchor of the arrow end is
  // the dummy tracer, which, as described earlier, is tied to the right axis rect border.
  mArrow->end->setCoords(mAxis->offset(), 0);
}

mainwindows.h

/***************************************************************************
**                                                                        **
**  QCustomPlot, an easy to use, modern plotting widget for Qt            **
**  Copyright (C) 2011-2021 Emanuel Eichhammer                            **
**                                                                        **
**  This program is free software: you can redistribute it and/or modify  **
**  it under the terms of the GNU General Public License as published by  **
**  the Free Software Foundation, either version 3 of the License, or     **
**  (at your option) any later version.                                   **
**                                                                        **
**  This program is distributed in the hope that it will be useful,       **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
**  GNU General Public License for more details.                          **
**                                                                        **
**  You should have received a copy of the GNU General Public License     **
**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
**                                                                        **
****************************************************************************
**           Author: Emanuel Eichhammer                                   **
**  Website/Contact: http://www.qcustomplot.com/                          **
**             Date: 29.03.21                                             **
**          Version: 2.1.0                                                **
****************************************************************************/

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qcustomplot.h"
#include "axistag.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT
  
public:
  explicit MainWindow(QWidget *parent = 0);
  ~MainWindow();
  
    void configLegend();

    void configAxis();

    //增加多条Y轴
    void addMultiAxis(QCPAxis::AxisType type,int num);

    void addMultiGraphs();

    void createTags();

    void configXYzoom();

    void configXzoom();

    void configYzoom(QCPAxis *axis);

private slots:
  void timerSlot();
  void startTimer();
  void stopTimer();

  void mousePress1();
  void mouseWheel1();

  void selectionChanged1();
  void setYAxisRange(QCPRange range);
  
private:
  Ui::MainWindow *ui;
  QCustomPlot *mPlot;
  QVector <QPointer<QCPGraph>> mGraphs;
  QVector <AxisTag *> mTags;
  QVector <QColor> mColors;
  QTimer mDataTimer;

};


#endif // MAINWINDOW_H

mainwindows.cpp

/***************************************************************************
**                                                                        **
**  QCustomPlot, an easy to use, modern plotting widget for Qt            **
**  Copyright (C) 2011-2021 Emanuel Eichhammer                            **
**                                                                        **
**  This program is free software: you can redistribute it and/or modify  **
**  it under the terms of the GNU General Public License as published by  **
**  the Free Software Foundation, either version 3 of the License, or     **
**  (at your option) any later version.                                   **
**                                                                        **
**  This program is distributed in the hope that it will be useful,       **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
**  GNU General Public License for more details.                          **
**                                                                        **
**  You should have received a copy of the GNU General Public License     **
**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
**                                                                        **
****************************************************************************
**           Author: Emanuel Eichhammer                                   **
**  Website/Contact: http://www.qcustomplot.com/                          **
**             Date: 29.03.21                                             **
**          Version: 2.1.0                                                **
****************************************************************************/

#include "mainwindow.h"
#include "ui_mainwindow.h"

void MainWindow::configLegend()
{
    mPlot->legend->setVisible(true);
    mPlot->legend->setFont(QFont("Helvetica",9));
    mPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft | Qt::AlignTop);
}

void MainWindow::configAxis()
{
    mPlot->axisRect()->setupFullAxesBox();
    mPlot->yAxis->setTickLabels(false);
    mPlot->yAxis2->setTickLabels(true);
}

void MainWindow::addMultiAxis(QCPAxis::AxisType type, int num)
{
    if(num < 0)
        return;

    mPlot->axisRect()->axis(QCPAxis::atRight, 0)->setPadding(30);

    for(int i = 0; i<num ; i++)
    {
        mPlot->axisRect()->addAxis(type);
        //设置Y轴留白
        mPlot->axisRect()->axis(QCPAxis::atRight, i+1)->setPadding(30);
    }
}

void MainWindow::addMultiGraphs()
{
    QList< QCPAxis * > axis_ys = mPlot->axisRect()->axes(QCPAxis::atRight) ;
    for (int i = 0; i<axis_ys.size();i++)
    {
        QPointer<QCPGraph> tempGraph = mPlot->addGraph(mPlot->xAxis, mPlot->axisRect()->axis(QCPAxis::atRight, i));
        QColor tempColor = mColors.at(i%mColors.size());
        tempGraph->setPen(QPen(QBrush(tempColor),3));
        tempGraph->setLineStyle((QCPGraph::LineStyle((i)%6)));
        tempGraph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ScatterShape(i+1), 10));
        // 设置Y轴的颜色
        mPlot->axisRect()->axis(QCPAxis::atRight, i)->setTickPen(QPen(QBrush(tempColor),2));
        mPlot->axisRect()->axis(QCPAxis::atRight, i)->setSubTickPen(QPen(QBrush(tempColor),2));
        mPlot->axisRect()->axis(QCPAxis::atRight, i)->setTickLabelColor(tempColor);
        mPlot->axisRect()->axis(QCPAxis::atRight, i)->setBasePen(QPen(QBrush(tempColor),2));
        this->mGraphs.append(tempGraph);

    }
}

void MainWindow::createTags()
{
    QList< QCPAxis * > axis_ys = mPlot->axisRect()->axes(QCPAxis::atRight) ;
    for (int i = 0; i<axis_ys.size();i++)
    {
        AxisTag * tempTag = new AxisTag(mGraphs.at(i)->valueAxis());
        tempTag->setPen(mGraphs.at(i)->pen());
        tempTag->setBrush(mGraphs.at(i)->pen().color());
        mTags.append(tempTag);
    }
}

void MainWindow::configXYzoom()
{
    QList<QCPAxis*> horz, vert;
    horz << mPlot->axisRect()->axes(QCPAxis::atBottom);
    vert << mPlot->axisRect()->axes(QCPAxis::atRight);
    mPlot->axisRect()->setRangeZoomAxes(horz,vert);
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    mPlot(0)
{
    ui->setupUi(this);

    mPlot = new QCustomPlot(this);
    mColors << QColorConstants::DarkGreen
            << QColorConstants::Red
            << QColorConstants::DarkGreen
            << QColorConstants::DarkBlue
            << QColorConstants::DarkYellow
            << QColorConstants::DarkCyan
            << QColorConstants::DarkMagenta;
    setCentralWidget(mPlot);
    mPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
                           QCP::iSelectLegend | QCP::iSelectPlottables /*| QCP::iMultiSelect*/);
    //增加图例,设置图例位置,
    configLegend();

    // 配置坐标轴的刻度显示
    configAxis();

    //增加多条Y轴
    addMultiAxis(QCPAxis::atRight,5);

    //配置缩放轴
    configXYzoom();

    //TODO:y轴随滚轮进行放缩,需要将对应的Y轴进行绑定,就可以做到指定曲线进行放缩。
    connect(mPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(setYAxisRange(QCPRange))); // left axis only mirrors inner right axis
    connect(mPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), mPlot->xAxis2, SLOT(setRange(QCPRange))); // left axis only mirrors inner right axis

    // 添加曲线
    addMultiGraphs();

    // 创建游标,跟踪最新值,并显示label
    createTags();

    //动态更新曲线
    connect(&mDataTimer, SIGNAL(timeout()), this, SLOT(timerSlot()));
    mDataTimer.start(100);

    connect(ui->startAction, SIGNAL(triggered()), this, SLOT(startTimer()));
    connect(ui->stopAction, SIGNAL(triggered()), this, SLOT(stopTimer()));

    connect(mPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress1()));
    connect(mPlot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel1()));

    connect(mPlot, SIGNAL(selectionChangedByUser()), this, SLOT(selectionChanged1()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::timerSlot()
{
    // make key axis range scroll with the data:
    mPlot->rescaleAxes(true);
    mPlot->xAxis->setRange(mPlot->xAxis->range().upper, 100, Qt::AlignRight);

    // calculate and add a new data point to each graph:
    for(int i = 0;i<mGraphs.size();i++)
    {
        mGraphs.at(i)->addData(mGraphs.at(i)->dataCount(), qSin(mGraphs.at(i)->dataCount()/(15.0*(i+1)))+qSin(mGraphs.at(i)->dataCount()/50.0/0.3843)*0.05*(i+1));
        double graphValue = mGraphs.at(i)->dataMainValue(mGraphs.at(i)->dataCount()-1);
        mTags.at(i)->updatePosition(graphValue);
        mTags.at(i)->setText(QString::number(graphValue, 'f', 2));
    }
    mPlot->replot();
}

void MainWindow::startTimer()
{
    mDataTimer.start(50);
}

void MainWindow::stopTimer()
{
    mDataTimer.stop();
}

void MainWindow::mousePress1()
{
    if (mPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
    {
        mPlot->axisRect()->setRangeDrag(mPlot->xAxis->orientation());
    }
//    else /*if (mPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis))*/
//    {
//        qDebug()<<"mPlot->yAxis pressed ";
//        QList< QCPAxis * > axis_ys = mPlot->axisRect()->axes(QCPAxis::atRight) ;
//        foreach (QCPAxis * axis_y, axis_ys)
//        {
//            mPlot->axisRect()->setRangeDrag(axis_y->orientation());
//        }
//    }
    else
    {
        //单轴曲线拖拽方法
        mPlot->axisRect()->setRangeDrag(Qt::Horizontal|Qt::Vertical);

        //多轴曲线拖拽方法
        QList<QCPAxis*> horz, vert;
        horz << mPlot->axisRect()->axes(QCPAxis::atBottom);
        vert << mPlot->axisRect()->axes(QCPAxis::atRight);
        mPlot->axisRect()->setRangeDragAxes(horz,vert);
    }
}

void MainWindow::mouseWheel1()
{
    QList< QCPAxis * > axis_ys = mPlot->axisRect()->axes(QCPAxis::atRight) ;
    foreach (QCPAxis * axis_y, axis_ys)
    {
        if(axis_y->selectedParts().testFlag(QCPAxis::spAxis))
        {
            mPlot->axisRect()->setRangeZoom(axis_y->orientation());
            return;
        }
    }

    if (mPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
        mPlot->axisRect()->setRangeZoom(mPlot->xAxis->orientation());
//    else if (mPlot->yAxis2->selectedParts().testFlag(QCPAxis::spAxis))
//        mPlot->axisRect()->setRangeZoom(mPlot->yAxis2->orientation());
    else
    {
        mPlot->axisRect()->setRangeZoom(Qt::Vertical|Qt::Horizontal);
    }
}

void MainWindow::configXzoom()
{
    QList<QCPAxis*> horz, vert;
    horz << mPlot->axisRect()->axes(QCPAxis::atBottom);
    //      vert << mPlot->axisRect()->axes(QCPAxis::atRight);
    mPlot->axisRect()->setRangeZoomAxes(horz,vert);
}

void MainWindow::configYzoom(QCPAxis* axis)
{
    QList<QCPAxis*> horz, vert;
//    horz << mPlot->axisRect()->axes(QCPAxis::atBottom);
    vert << axis;
    mPlot->axisRect()->setRangeZoomAxes(horz,vert);
}

void MainWindow::selectionChanged1()
{
    configXYzoom();

    if (mPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || mPlot->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
            mPlot->xAxis2->selectedParts().testFlag(QCPAxis::spAxis) || mPlot->xAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
    {
        mPlot->xAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
        mPlot->xAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);

        configXzoom();
    }

    //选中相关右侧Y轴后,对应左侧Y轴也同时选中,并显示右侧Y轴的坐标
    QList< QCPAxis * > axis_ys = mPlot->axisRect()->axes(QCPAxis::atRight) ;
    foreach (QCPAxis * axis_y, axis_ys)
    {
        if(axis_y->selectedParts().testFlag(QCPAxis::spAxis) || axis_y->selectedParts().testFlag(QCPAxis::spTickLabels))
        {
            axis_y->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
            mPlot->yAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
            configYzoom(axis_y);
            mPlot->yAxis->setRange(axis_y->range());
            break;
        }
    }

    // synchronize selection of graphs with selection of corresponding legend items:
    for (int i=0; i<mPlot->graphCount(); ++i)
    {
        QCPGraph *graph = mPlot->graph(i);
        QCPPlottableLegendItem *item = mPlot->legend->itemWithPlottable(graph);
        if (item->selected() || graph->selected())
        {
            item->setSelected(true);
            graph->setSelection(QCPDataSelection(graph->data()->dataRange()));
        }
    }
}

void MainWindow::setYAxisRange(QCPRange range)
{
    QList< QCPAxis * > axis_ys = mPlot->axisRect()->axes(QCPAxis::atRight) ;
    foreach (QCPAxis * axis_y, axis_ys)
    {
        if(axis_y->selectedParts().testFlag(QCPAxis::spAxis) || axis_y->selectedParts().testFlag(QCPAxis::spTickLabels))
        {
            axis_y->setRange(range);
            return ;
        }
    }
}

原文地址:https://blog.csdn.net/u011942101/article/details/143769685

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!