QCustomPlot 多个坐标系

对多坐标系感兴趣的可以看下,附有运行效果图和下载链接

三个水平的坐标系,如图
在这里插入图片描述
首先说明,本案列是直接继承QCustomPlot,如果是需要通过非继承方式,自行将其中的this关键字替换为QCustomPlot* XX;文中有些代码省略了this,自行判断。
一、添加多个坐标系
步骤如下:

this->setInteractions(QCP::iRangeDrag //可平移
                          | QCP::iRangeZoom //可滚轮缩放
                          | QCP::iSelectPlottables //可选中曲线
                          | QCP::iSelectLegend );//可选中图例
//! 1.清空布局中默认的坐标系及其他控件
this->plotLayout()->clear();

/*  在.h文件中定义好的需要使用的控件
QCPAxisRect * A0_5;			//坐标系 1
QCPAxisRect * A0_50;		//坐标系 2
QCPAxisRect * V0_150;		//坐标系 3
QCPItemTracer* tracer;		//游标
QCPItemText* tracerLabel;	//游标标签
*/
V0_150 = nullptr;
A0_50 = nullptr;
A0_5 = nullptr;
legend = nullptr;
tracer = nullptr;
tracerLabel = nullptr;

//! 2.创建游标及游标标签  
tracer_init();
/* 创建代码就在此处注释中描述,实际使用中写在对应方法中
    if(tracer == nullptr)
    {
        tracer = new QCPItemTracer(this);
        tracer->setInterpolating(false);//禁用插值
        tracer->setPen(QPen(QBrush(QColor(Qt::red)),Qt::DashLine));//虚线游标
        tracer->setStyle(QCPItemTracer::tsCrosshair);//游标样式:十字星、圆圈、方框


        tracerLabel = new QCPItemText(this);
        tracerLabel->setLayer("overlay");
        tracerLabel->setPen(QPen(Qt::black));
        tracerLabel->setBrush(Qt::cyan);
        tracerLabel->setPadding(QMargins(2,2,2,2));//边界宽度
        tracerLabel->setPositionAlignment(Qt::AlignLeft | Qt::AlignTop);// 标签显示位置,左上
        tracerLabel->position->setParentAnchor(tracer->position); // 标签跟随游标
    }
*/

//! 3. 添加坐标系  三个坐标系的创建、样式属性分别在不同方法进行,方便维护
V0_150_init();
A0_50_init();
A0_5_init();
/* 坐标系的创建过程就不多余的描述,以注释中代码为例
	// 以智能指针方式定义dateTicker
    QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime); 
    dateTicker->setDateTimeFormat("yyyy-MM-dd\nhh:mm:ss"); // 设置以时间为X轴单位
    V0_150 = new QCPAxisRect(this, true);	// 创建坐标系
    V0_150->axis(QCPAxis::atBottom)->setTicker(dateTicker); // 将时间轴设置在坐标系中
    V0_150->axis(QCPAxis::atLeft)->setRange(0,150);	// 设置Y轴范围
    V0_150->setupFullAxesBox(true);    // 让四个轴关联,并全部显示出来
    V0_150->setRangeDrag(Qt::Horizontal | Qt::Vertical); //水平方向拖动 | 垂直方向移动
    V0_150->setRangeZoom(Qt::Horizontal | Qt::Vertical); //水平方向缩放 | 垂直方向缩放
    //如果是固定坐标系数量,可直接将坐标系添加如布局,\ 此处是动态添加和删除坐标系,所有魏加入布局
    //this->plotLayout()->addElement(axisRectCount(),0,V0_150); //axisRectCount()获取布局中坐标系个数
//! 4. legend
/*  if(legend == nullptr)
    {
        legend = new QCPLegend;
        legend->setVisible(true);
//        plotLayout()->addElement(legend); //加入布局 
//! 注释:此处未加入布局和设置拉伸比的原因如下:由于初始第一个坐标系和legend中都没有数据,legend高度会在布局中影响坐标系的高度。
所有在添加图层前将legend加入布局即可(如果有方法解决这个问题,可直接添加如布局)
//        plotLayout()->setColumnStretchFactor(1, 0.001); //设置行拉伸比例,将legend的宽度最小化
        connect(this, SIGNAL(selectionChangedByUser()), this, SLOT(when_selectionChangedByUser()));
        connect(this, SIGNAL(legendDoubleClick(QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)), this, SLOT(when_legendDoubleClick(QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)));
    }
*/

//! 5. 坐标系某些轴的隐藏
//axis_style_init(); //每添加或删除一个坐标系 或 同时添加或删除多个坐标系 调用一次
*/
//! MyCustomPlot::QCustomPlot
//! 以3个坐标系为例
void MyCustomPlot::axis_style_init()
{
    int index = 0;

    if(axisRectCount() == 2)
    {
        foreach (auto *rect, axisRects())
        {
            index++;
            if(index == 1)
            {
                rect->axis(QCPAxis::atTop)->setVisible(true);
                rect->axis(QCPAxis::atBottom)->setVisible(false);
            }
            else if(index == 2){
                rect->axis(QCPAxis::atTop)->setVisible(false);
                rect->axis(QCPAxis::atBottom)->setVisible(true);
            }
        }


    }
    else if(axisRectCount() == 3)
    {
        foreach (auto *rect, axisRects())
        {
            index++;
            if(index == 1)
            {
                rect->axis(QCPAxis::atTop)->setVisible(true);
                rect->axis(QCPAxis::atBottom)->setVisible(false);
            }
            else if(index == 2){
                rect->axis(QCPAxis::atTop)->setVisible(false);
                rect->axis(QCPAxis::atBottom)->setVisible(false);
            }
            else if(index == 3)
            {
                rect->axis(QCPAxis::atTop)->setVisible(false);
            }
        }
    }
}
void MyCustomPlot::add_axis(int index) // 0 1 3 分别区别三个坐标系
{
    if(axisRectCount() >= 3) return;
    switch (index) {
    case 0:
        if(V0_150 == nullptr){	//若为空指针,调用函数
            V0_150_init();
            if(A0_50 != nullptr) //若不为空指针,建立信号槽,同步X轴,注意建立双向连接
            {
                connect(V0_150->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_50->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
                connect(A0_50->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), V0_150->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
            }
            if(A0_5 != nullptr)	// 同理
            {
                connect(V0_150->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_5->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
                connect(A0_5->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), V0_150->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
            }
        }
        plotLayout()->addElement(axisRectCount(),0,V0_150); // 将坐标系加入布局
        break;
    case 1:
        if(A0_50 == nullptr){
            A0_50_init();
            if(V0_150 != nullptr){
                connect(A0_50->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), V0_150->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
                connect(V0_150->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_50->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
            }
            if(A0_5 != nullptr){
                connect(A0_50->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_5->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
                connect(A0_5->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_50->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
            }
        }
        plotLayout()->addElement(axisRectCount(),0,A0_50);
        break;
    case 2:
        if(A0_5 == nullptr){
            A0_5_init();
            if(A0_50 != nullptr){
                connect(A0_5->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_50->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
                connect(A0_50->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_5->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
            }
            if(V0_150 != nullptr){
                connect(A0_5->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), V0_150->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
                connect(V0_150->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), A0_50->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
            }
        }
        plotLayout()->addElement(axisRectCount(),0,A0_5);
        break;
    default:
        break;
    }

    legend_init(); // 创建legend,根据自己需要创建
    axis_style_init();// 调整坐标系样式
    plotLayout()->simplify(); //简化布局
    replot(QCustomPlot::rpQueuedReplot); //刷新
}
void MyCustomPlot::delete_axis(int index)
{
    if(axisRectCount() <= 0) return;//axisRectCount()布局中坐标系数量
    switch (index) {
    case 0:
        if(V0_150 == nullptr) return;
        clearDataAndDeleteGraph(0);                 // 1. 清除数据,移除图层 不清除数据和图层,legend会保留信息,在legend点击事件发送时会程序崩溃
        plotLayout()->remove(axisRect(index));      // 2. 从布局中删除坐标系  并 delete
        plotLayout()->simplify();                   // 3. 简化布局 
													// 2.3步骤是从布局中删除坐标系的关键,可自行测试无某一步骤造成的影响
        V0_150 = nullptr;  //避免成为野指针
        break;
    case 1:
        if(A0_50 == nullptr) return;
        clearDataAndDeleteGraph(1);
        plotLayout()->remove(axisRect(index));
        plotLayout()->simplify();

        A0_50 = nullptr;
        break;
    case 2:
        if(A0_5 == nullptr) return;
        clearDataAndDeleteGraph(2);
        plotLayout()->remove(axisRect(index));
        plotLayout()->simplify();

        A0_5 = nullptr;
        break;
    default:
        break;
    }

    if(axisRectCount() <= 0 && legend != nullptr) // customplot中无坐标系,删除legend
    {
        plotLayout()->remove(legend);
        plotLayout()->simplify();
        legend = nullptr;
    }
    else if(axisRectCount() > 0 && legend != nullptr)
    {
        plotLayout()->take(legend);
        plotLayout()->simplify();
        plotLayout()->addElement(legend);
        plotLayout()->setColumnStretchFactor(1, 0.001);
    }
    axis_style_init();
    replot(QCustomPlot::rpQueuedReplot);
}
void MyCustomPlot::clearDataAndDeleteGraph(int index)
{	//! graph_Map保存了每一个图层指针graph_Map的key为 坐标系的type*10+idx, type就等于此函数中的index
	//void MyCustomPlot::addData(int idx, double key, double value, int type)
    for(QMap<int,QCPGraph*>::Iterator it = graph_Map.begin(); it != graph_Map.end(); it++)
    {
        if(it.key()/10 == index)
        {
            if(it.value() != nullptr)
            {
                QCPGraph* graph = it.value();
                graph->data().data()->clear(); //清除数据
                graph_Map.remove(it.key()); //在移除Map
                removeGraph(graph); //移除图层,不要自己delete,调用此函数自动delete
                replot(QCustomPlot::rpQueuedReplot);
            }
        }
    }
}
// 判断数据加入的坐标系
void MyCustomPlot::addData(int idx, double key, double value, int type)
{
    if(!plotLayout()->hasElement(0,1) && legend != nullptr) // 将legend加入布局,一样的道理,自己选中合适的位置加入
    {
        plotLayout()->addElement(legend);
        plotLayout()->setColumnStretchFactor(1, 0.001);
    }
    switch (type) {
    case 0:
        if(V0_150 == nullptr) return;
        addData(idx+type*10, key, value, V0_150);
        break;
    case 1:
        if(A0_50 == nullptr) return;
        addData(idx+type*10, key, value, A0_50);
        break;
    case 2:
        if(A0_5 == nullptr) return;
        addData(idx+type*10, key, value, A0_5);
        break;
    default:
        break;
    }
}

void MyCustomPlot::addData(int idx, double key, double value,QCPAxisRect* rect)
{
    QCPGraph* _graph;
    if(!graph_Map.contains(idx)) // 判断当前数据是否已有图层
    {	//创建图层并加入Map
        graph_Map.insert(idx, addGraph(rect->axis(QCPAxis::atBottom),rect->axis(QCPAxis::atLeft)));

        _graph = graph_Map.value(idx);
        _graph->setLineStyle(QCPGraph::lsStepLeft);        //设置连线类型
        _graph->setPen(QPen(QColor(qrand()%256, qrand()%256, qrand()%256),1)); //随机曲线颜色,qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); 提前添加
        _graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 3));        //显示散点数据
        _graph->addData(key, value); //添加数据
        if(!isMove){
            rect->axis(QCPAxis::atBottom)->setRange(key-1,key+1);
            rect->axis(QCPAxis::atLeft)->setRange(value-1,value+1);
        }
        LengedName_Map.insert(idx,QString("Curver%1").arg(idx));
        _graph->setName(LengedName_Map.value(idx));
        XRange = rect->axis(QCPAxis::atBottom)->range();
        YRange = rect->axis(QCPAxis::atLeft)->range();
    }
    else
    {
        _graph = graph_Map.value(idx);
        _graph->addData(key, value);
        if(!isMove){
            rect->axis(QCPAxis::atBottom)->setRange(key-1,key+1);
            rect->axis(QCPAxis::atLeft)->setRange( value-1 < rect->axis(QCPAxis::atLeft)->range().lower ? value-1 : rect->axis(QCPAxis::atLeft)->range().lower, value+1 > rect->axis(QCPAxis::atLeft)->range().upper ? value+1 : rect->axis(QCPAxis::atLeft)->range().upper);
        }
        _graph->setName(QString("%1  %2").arg(LengedName_Map.value(idx)).arg(value));
    }
    replot(QCustomPlot::rpQueuedReplot);                 //刷新图表
}
void MyCustomPlot::mousePressEvent(QMouseEvent *event)
{
    QCustomPlot::mousePressEvent(event);
    isMove = true;

}

void MyCustomPlot::mouseMoveEvent(QMouseEvent *event)
{
    QCustomPlot::mouseMoveEvent(event);

    if(tracer == nullptr || tracerLabel == nullptr || axisRectCount() <= 0 || tracer->graph() == nullptr) return;
    if(tracer->visible())
    {
        if(tracerGraph){

            QCPAxisRect* rect = nullptr;

            int key = graph_Map.key(tracerGraph,-1);
            if(key == -1) return;
            else if(key/10 == 0){
                rect = V0_150;
            }
            else if(key/10 == 1){
                rect = A0_50;
            }
            else if(key/10 == 2){
                rect = A0_5;
            }
            else
                return;

            double x = rect->axis(QCPAxis::atBottom)->pixelToCoord(event->pos().x());//像素坐标转plot坐标
            tracer->setGraphKey(x);
            tracer->updatePosition(); //使得刚设置游标的横纵坐标位置生效
            double y = rect->axis(QCPAxis::atLeft)->pixelToCoord(event->pos().y());
            QString xTime = QDateTime::fromMSecsSinceEpoch(x * 1000.0).toString("hh:mm:ss.zzz");
            tracerLabel->setText(QString("x = %1\ny=%2").arg(xTime).arg(y));
            replot(QCustomPlot::rpQueuedReplot);
        }
    }
}

void MyCustomPlot::mouseReleaseEvent(QMouseEvent *event)
{
    QCustomPlot::mouseReleaseEvent(event);
    isMove = false;
}

void MyCustomPlot::when_selectionChangedByUser()
{
    for (QMap<int,QCPGraph*>::iterator it = graph_Map.begin(); it != graph_Map.end();it++)
    {
        QCPGraph *graph = it.value();
        if(graph == nullptr) return;
        QCPPlottableLegendItem *item = legend->itemWithPlottable(graph);
        if (item->selected() || graph->selected())//选中了哪条曲线或者曲线的图例
        {
            tracerGraph = graph;
            if(tracer != nullptr){
                tracer->setGraph(tracerGraph);
            }

            item->setSelected(true);

            QPen pen;
            pen.setWidth(3);
            pen.setColor(Qt::blue);
            graph->selectionDecorator()->setPen(pen);

            graph->setSelection(QCPDataSelection(graph->data()->dataRange()));

        }

    }
}

/*
双击图例修改曲线颜色
*/
void MyCustomPlot::when_legendDoubleClick (QCPLegend *legend, QCPAbstractLegendItem *legendItem, QMouseEvent *event)
{
    Q_UNUSED(legend);
    Q_UNUSED(event);
    qDebug()<<"when_legendDoubleClick (QCPLegend *legend, QCPAbstractLegendItem *legendItem, QMouseEvent *event)";

    for (int i = 0; i < this->graphCount(); ++i)
    {
        QCPGraph *pGraph = this->graph(i);
        if(legendItem == this->legend->itemWithPlottable(pGraph))//查询被点击的图例项是哪个graph的图例项
        {
            QColor newColor = QColorDialog::getColor(Qt::red,this,"选择新颜色");
            if(newColor.isValid())//用户点击了确定
            {
                pGraph->setPen(QPen(newColor,1));
                replot(QCustomPlot::rpQueuedReplot);
            }
        }
    }
}
  • 12
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值