QT_QCustomPlot学习

QCustomPlot学习

QCustomPlot默认提供了6个层,如下代码所示,分别是:背景层、网格层、主层、坐标轴层、图例层和矩形选择区域层。

1.在一张视图中表现两个线
请添加图片描述

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    demo();
}

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

void Widget::demo()
{
    QVector<double> x,y,z;
    for (double i=0;i<10 ;i+=0.1 ) {
        x.append(i);
        y.append(i);
        z.append(i*i);
    }
    ui->cw->addGraph();
    ui->cw->graph()->setData(x,y);

    ui->cw->addGraph();
    ui->cw->graph()->setData(x,z);
    ui->cw->graph()->setPen(QPen(QColor(Qt::green)));
    
    ui->cw->graph()->setLineStyle(QCPGraph::LineStyle::lsNone);//设置连线的样式
    ui->cw->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCross, 10));//设置散点的样式,ssCross为十字样式
    ui->cw->graph()->setSelectable(QCP::SelectionType::stMultipleDataRanges);//设置图形中可选的元素,这里配合选择框使用多点选择,可以通过选取框选择多个点,也可以按住ctrl多选

    ui->cw->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables|QCP::iMultiSelect);

    ui->cw->replot();
}

//可以把矩形框设置放在外面
void Widget::on_checkBox_2_clicked(bool checked)
{
    if(checked){
        ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmSelect);//添加鼠标选取矩形框,并设置为选择操作,此时鼠标的拖拽功能失效。按住ctrl可以多选
    }else{
        ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmNone);//取消矩形框选取,鼠标拖拽功能返回
        ui->cw->deselectAll();
        ui->cw->replot();
    }
}

2.一张视图中多个图表:使用QCPLayoutGrid

请添加图片描述

void Widget::demo2()
{
    //一张视图中多个图表

    //创建QCPAxisRect
    QCPAxisRect* a = new QCPAxisRect (ui->cw);
    QCPAxisRect* b = new QCPAxisRect (ui->cw);

    //添加到QCPLayoutGrid
    ui->cw->plotLayout()->clear();
    QCPLayoutGrid* lay = ui->cw->plotLayout() ;
    lay->addElement(0,0,a);
    lay->addElement(1,0,b);


    //创建数据
    QVector<QCPGraphData> d1,d2;
    for (double i=0;i<10 ;i+=0.1 ) {
        d1.append(QCPGraphData(i,i));
        d2.append(QCPGraphData(i,i*i));
    }

    //创建数据,添加到QCPGraph
    QCPGraph* ga = ui->cw->addGraph(a->axis(QCPAxis::atBottom),a->axis(QCPAxis::atLeft));
    ga->data()->set(d1);
    ga->rescaleAxes();

    QCPGraph* gb = ui->cw->addGraph(b->axis(QCPAxis::atBottom),b->axis(QCPAxis::atLeft));
    gb->data()->set(d2);
    gb->rescaleAxes();

    //同时改变X轴大小,可以用来进行对比
    ui->cw->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
    
    connect(ga->keyAxis(),SIGNAL(rangeChanged(QCPRange)),gb->keyAxis(),SLOT(setRange(QCPRange)));
    connect(gb->keyAxis(),SIGNAL(rangeChanged(QCPRange)),ga->keyAxis(),SLOT(setRange(QCPRange)));
    
    ui->cw->axisRect(0)->setRangeZoom(Qt::Horizontal);
    ui->cw->axisRect(1)->setRangeZoom(Qt::Horizontal);
}

同步X轴大小:

{  
//同时改变X轴大小,可以用来进行对比
    ui->cw->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);

    for(int i=0 ; i < ui->cw->graphCount(); ++i){
         connect(ui->cw->graph(i)->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(setXRange(QCPRange)));
        ui->cw->axisRect(i)->setRangeZoom(Qt::Horizontal);
    }
}

void Widget::setXRange(QCPRange range)
{
    for(int i=0 ; i < ui->cw->graphCount(); ++i){
        ui->cw->graph(i)->keyAxis()->setRange(range);
    }
}

添加checkbox是否同步:

void Widget::on_checkBox_clicked(bool checked)
{
    if(checked){
        for(int i=0 ; i < ui->cw->graphCount(); ++i){
             connect(ui->cw->graph(i)->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(setXRange(QCPRange)));
             ui->cw->axisRect(i)->setRangeZoom(Qt::Horizontal);
             ui->cw->axisRect(i)->setRangeDrag(Qt::Horizontal);
             ui->cw->graph(i)->rescaleAxes();
        }
    }else{
        for(int i=0 ; i < ui->cw->graphCount(); ++i){
             disconnect(ui->cw->graph(i)->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(setXRange(QCPRange)));
             ui->cw->axisRect(i)->setRangeZoom(Qt::Horizontal|Qt::Vertical);
             ui->cw->axisRect(i)->setRangeDrag(Qt::Horizontal|Qt::Vertical);
        }
    }
}

遗憾的是,现在QCPLayoutGrid这个方法还没找到有QSplitter这样可以分割的部件实现调整窗口的大小。

3.一张视图中多个图表:使用QSplitter

请添加图片描述

使用两个QCustomPlot窗口,加上一个QSplitter实现调整大小

void Widget::demo3()
{
    ui->cw->plotLayout()->clear();
    //创建数据
    QVector<QCPGraphData> d1,d2,d3;
    for (double i=0;i<10 ;i+=0.1 ) {
        d1.append(QCPGraphData(i,i));
        d2.append(QCPGraphData(i,i*i));
        d3.append(QCPGraphData(i,qSqrt(i)));
    }

    QCustomPlot* qcp1 = new QCustomPlot();
    QCustomPlot* qcp2 = new QCustomPlot();
    QCustomPlot* qcp3 = new QCustomPlot();

    qcp1->addGraph()->data()->set(d1);
    qcp2->addGraph()->data()->set(d2);
    qcp3->addGraph()->data()->set(d3);

    qcp1->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
    qcp2->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
    qcp3->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);

    /*QSplitter**/ pSplitter = new QSplitter(Qt::Vertical,this);
    pSplitter->addWidget(qcp1);
    pSplitter->addWidget(qcp2);
    pSplitter->addWidget(qcp3);
    ui->show->addWidget(pSplitter); //注意这里的 ui->show为 QGridLayout类型

    QCustomPlot* q = nullptr;
    for(int i=0;i < pSplitter->count();++i){
        q = static_cast<QCustomPlot*>(pSplitter->widget(i));
        connect(q->graph()->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(set3Range(QCPRange)));
    }

}

void Widget::set3Range(QCPRange range)
{
    QCustomPlot* q = nullptr;
    for(int i=0;i < pSplitter->count();++i){
        q = static_cast<QCustomPlot*>(pSplitter->widget(i));
        q->graph()->keyAxis()->setRange(range);
        q->replot();
    }
}

4.时间轴
请添加图片描述

double now = QDateTime::currentDateTime().toSecsSinceEpoch();
QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
dateTicker->setDateTimeFormat("d MMMM\n yyyy");
ui->cw->xAxis2->setTicker(dateTicker);
ui->cw->xAxis2->setRange(now,now+24*3600*10);
ui->cw->xAxis2->setVisible(true);

5.实现鼠标左键选框,中键平移,右键菜单
请添加图片描述

以下鼠标键都在qcustomplot.cpp中修改,注意不要搞错成员函数所在的类名

左键选框:

把官方的框选功能限制在左键上,在函数void QCustomPlot::mousePressEvent(QMouseEvent *event)中修改如下:

void QCustomPlot::mousePressEvent(QMouseEvent *event)
{
  emit mousePress(event);
  // save some state to tell in releaseEvent whether it was a click:
  mMouseHasMoved = false;
  mMousePressPos = event->pos();
  //添加 event->button() == Qt::LeftButton ,将选取框按键绑定在左键
  if (event->button() == Qt::LeftButton && mSelectionRect && mSelectionRectMode != QCP::srmNone)
  {
    if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
      mSelectionRect->startSelection(event);
  } else if(event->button() != Qt::LeftButton)//添加if
  {//...
  }
}

中键平移:

官方默认左键是平移曲线,我们把平移功能改到中键上去,以便右键菜单。直接在函数
void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)改,把Qt::LeftButton改成Qt::RightButton即可。

void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
{
  Q_UNUSED(details)
  if (event->buttons() & Qt::midButton)//将Qt::LeftButton改成你要绑定的拖拽的按键
  {
      //...
  }
}

右键菜单:

void demo(){
    ui->cw->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->cw,&QCustomPlot::customContextMenuRequested,this,&Widget::MenuRequested);
}

void Widget::MenuRequested(QPoint p)
{
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);
    menu->addAction("resize",this,[=](){
        ui->cw->rescaleAxes();
        ui->cw->replot();
    });
    menu->addAction("zoom",this,[=](){
        ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);
    });
    menu->addAction("select",this,[=](){
        ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmSelect);
    });
    menu->addAction("None",this,[=](){
        ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmNone);
    });
    menu->popup(ui->cw->mapToGlobal(p));//必须要,否则不显示菜单
}

写进新的类:plotView

//hpp
#include "../../plotLib/qcustomplot.h"
class plotView : public QCustomPlot
{
public:
    plotView(QWidget *parent = nullptr);
    void slotMenuRequested(QPoint p);
};

//cpp
#include "plotview.h"

plotView::plotView(QWidget *parent)
    :QCustomPlot(parent)
{
    this->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this,&QCustomPlot::customContextMenuRequested,this,&plotView::slotMenuRequested);
}

void plotView::slotMenuRequested(QPoint p)
{
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);
    menu->addAction("resize",this,[=](){
        this->rescaleAxes();
        this->replot();
    });
    menu->addAction("zoom",this,[=](){
        this->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);
    });
    menu->addAction("select",this,[=](){
        this->setSelectionRectMode(QCP::SelectionRectMode::srmSelect);
    });
    menu->addAction("None",this,[=](){
        this->setSelectionRectMode(QCP::SelectionRectMode::srmNone);
    });
    menu->popup(this->mapToGlobal(p));
}

6.设置游标,显示当前坐标值
请添加图片描述

 //游标设置
    QCPItemTracer* tracer = new QCPItemTracer(ui->cw);
    ui->cw->setMouseTracking(true);//让游标自动随鼠标移动,若不想游标随鼠标动,则禁止
    tracer->setPen(QPen(Qt::DashLine));
    tracer->setStyle(QCPItemTracer::tsPlus);
    tracer->setSize(50);
    tracer->setGraph(ui->cw->graph(0));

    QCPItemText* tracerLabel = new QCPItemText(ui->cw);
    tracerLabel->position->setParentAnchor(tracer->position);//让标签自动随着游标移动

    connect(ui->cw,&QCustomPlot::mouseMove,[=](QMouseEvent* e){
        double x = ui->cw->xAxis->pixelToCoord(e->pos().x());
       tracer->setGraphKey(x);
//       tracer->setInterpolating(true);//自动计算y值,若只想看已有点,不需要这个
       tracer->updatePosition();
       tracerLabel->setText(QString("x:%1,y:%2").arg(tracer->position->key()).arg(tracer->position->value()));
       ui->cw->replot(QCustomPlot::rpQueuedReplot);
    });

    connect(ui->cw, &QCustomPlot::selectionChangedByUser, [=](){
        for (int i = 0; i < ui->cw->graphCount(); ++i)
        {
            QCPGraph *graph = ui->cw->graph(i);
            QCPPlottableLegendItem *item = ui->cw->legend->itemWithPlottable(graph);
            if (item->selected() || graph->selected())//选中了哪条曲线或者曲线的图例
            {
                 tracer->setGraph(graph);
            }
        }
    });

7.显示点信息和其他图片

请添加图片描述

显示图像上点的信息

//使用plottableClick信号获取最近的一个数据点数据
connect(ui->cw,&QCustomPlot::plottableClick,[=](QCPAbstractPlottable* plottable,int dataIndex,QMouseEvent* e){
       double x = plottable->interface1D()->dataMainKey(dataIndex);
       double y = plottable->interface1D()->dataMainValue(dataIndex);
       ui->plainTextEdit->clear();
       ui->plainTextEdit->appendPlainText(QString("x:")+QString::number(x,'f',2));
       ui->plainTextEdit->appendPlainText(QString("y:")+QString::number(y,'f',2));
    });

自制散点样式

    ui->cw->graph(1)->setLineStyle(QCPGraph::LineStyle::lsNone);
//自定义样式,填色原点
    QCPScatterStyle ss(QCPScatterStyle::ssCircle, 10);
    ss.setPen(QPen(QColor(Qt::black)));
    ss.setBrush(QColor(Qt::yellow));

    ui->cw->graph(1)->setScatterStyle(ss);
    ui->cw->graph(1)->setSelectable(QCP::SelectionType::stMultipleDataRanges);

直线

//游标直线设置,使直线随着鼠标移动
{
        //游标直线设置
    _lineV = new QCPItemLine(ui->cw);
    _lineV->setLayer(ui->cw->axisRect()->layer());
    
    QPen pen(QColor(Qt::green),20);
    _lineV->setPen(pen);

    _lineV->start->setTypeY(QCPItemPosition::PositionType::ptAxisRectRatio);
    _lineV->end->setTypeY(QCPItemPosition::PositionType::ptAxisRectRatio);
}

 connect(ui->cw,&QCustomPlot::mouseMove,[=](QMouseEvent* e){
     double x = ui->cw->xAxis->pixelToCoord(e->pos().x());
     //使线起始位置和终点位置在上下
     _lineV->start->setCoords(x,1);
     _lineV->end->setCoords(x,0);
 }
     

使标签固定在直线下面

//游标标签设置
    tracerLabel = new QCPItemText(ui->cw);
    tracerLabel->setLayer(ui->cw->axisRect()->layer());
    tracerLabel->setPadding(QMargins(0,0,0,20));
    tracerLabel->position->setParentAnchor(_lineV->start);//将锚点固定到直线的下面
	tracerLabel->setClipToAxisRect(false);//可以让标签显示在axisRect外面
  • 0
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值