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)!