QT开发技术【qcustomplot 曲线与鼠标十字功能】

一、效果

二、代码

cpp 复制代码
#include "obsersingle.h"
#include "ui_obsersingle.h"
#pragma execution_character_set("utf-8")

 右键菜单acion回调 
// 适应窗口大小
void ObserSingle::RescaleActionFun::fun(ObserSingle *form)
{
    // 自适应y轴数据范围,每次 setData 之后都要调用
    form->customPlot->rescaleAxes(true);
    form->customPlot->replot(QCustomPlot::rpQueuedReplot); // 重绘
}

// 十字游标
void ObserSingle::CursorActionFun::fun(ObserSingle *form)
{
    // 显示十字游标
    if(form->cursorShow){
        form->lineY->point1->setCoords(0, 0);
        form->lineY->point2->setCoords(0, 0);
        form->lineX->point1->setCoords(0, 0);
        form->lineX->point2->setCoords(0, 0);
        form->cursorShow = false;
    }else{
        form->cursorShow = true;
    }
    form->customPlot->replot(QCustomPlot::rpQueuedReplot); // 重绘
}

 ObserSingle 
ObserSingle::ObserSingle(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ObserSingle)
    , clickMenu(new QMenu)
    , lineY(nullptr)
    , lineX(nullptr)
    , cursorShow(false)
    , midButtonPress(false)
    , customPlot(new QCustomPlot)
{
    ui->setupUi(this);

    // 创建画布
    QGridLayout *layout = new QGridLayout();
    layout->setContentsMargins(2, 2, 2, 2);
    layout->addWidget(customPlot);
    ui->plotWindow->setLayout(layout);
    // 轴设置
    customPlot->xAxis->setRange(1, 1, Qt::AlignRight);          // 设置轴刻度
    customPlot->xAxis->ticker()->setTickCount(8);               // 设置轴刻度数目
    customPlot->xAxis->setLabel(tr("xAxis"));                       // 设置轴标签
    customPlot->yAxis->setRange(2, 2, Qt::AlignRight);
    customPlot->yAxis->ticker()->setTickCount(5);
    customPlot->yAxis->setLabel(tr("yAxis"));
    // 图例
    customPlot->legend->setVisible(true);                       // 显示
    customPlot->legend->setBrush(QColor(255, 255, 255, 50));   // 灰色透明
    customPlot->legend->setBorderPen(Qt::NoPen);                // 边框隐藏
    customPlot->legend->setWrap(4);                             // 设置4个图例自动换行
    customPlot->legend->setFillOrder(QCPLayoutGrid::foRowsFirst);    // 图例优先排序(foRowsFirst foColumnsFirst) foRowsFirst
//    customPlot->plotLayout()->addElement(0, 0, customPlot->legend);     // 图例位置     0 0
    customPlot->plotLayout()->setRowStretchFactor(0, 0.001);            // 显示比例     0 0.001
    // 设置基本坐标轴(左侧Y轴和下方X轴)可拖动、可缩放、曲线可选、legend可选、设置伸缩比例,使所有图例可见
    customPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectPlottables);
//    customPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes );
    // 设置legend只能选择图例
    customPlot->legend->setSelectableParts(QCPLegend::spItems);
    // 设置图例与曲线同步选中
    QObject::connect(customPlot, SIGNAL(selectionChangedByUser()), this, SLOT(selectionChanged()));

    // 右键action 适应窗口大小
    clickMenuAddAction(tr("适应窗口大小"), ":/reference/image/plot.png", new RescaleActionFun);
    // 右键action 十字游标
    clickMenuAddAction(tr("十字游标"), ":/reference/image/plot.png", new CursorActionFun);
    // 鼠标右键菜单栏
    QObject::connect(customPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(onMousePress(QMouseEvent *)));

    // 提示线
    lineY = new QCPItemStraightLine(customPlot);
    lineY->setLayer("overlay");
    lineY->setPen(QPen(Qt::gray, 1, Qt::DashLine));
    lineY->setClipToAxisRect(true);
    lineY->point1->setCoords(0, 0);
    lineY->point2->setCoords(0, 0);
    lineX = new QCPItemStraightLine(customPlot);
    lineX->setLayer("overlay");
    lineX->setPen(QPen(Qt::gray, 1, Qt::DashLine));
    lineX->setClipToAxisRect(true);
    lineX->point1->setCoords(0, 0);
    lineX->point2->setCoords(0, 0);
    // 鼠标移动
    QObject::connect(customPlot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(onMouseMove(QMouseEvent *)));
    // 鼠标释放
    QObject::connect(customPlot, SIGNAL(mouseRelease(QMouseEvent*)), this,  SLOT(onMouseRelease(QMouseEvent *)));

    // 自适应y轴数据范围,每次 setData 之后都要调用
    customPlot->rescaleAxes(true);
    customPlot->replot(QCustomPlot::rpQueuedReplot); // 重绘

//    customPlot->setOpenGl(true);
//    qDebug()<<"opengle="<<customPlot->openGl();
}

ObserSingle::~ObserSingle()
{
    ui->plotWindow->layout()->deleteLater();
    QList<QString> keyList = actionFunMap.keys();
    for(int i=0; i<keyList.size(); i++){
        if(actionFunMap[keyList[i]]){
            delete actionFunMap[keyList[i]];
        }
    }
    QList<QAction *> actionList = actionMap.keys();
    for(int i=0; i<actionList.size(); i++){
        if(actionList.at(i)){
            delete actionList.at(i);
        }
    }
    if(clickMenu){
        clickMenu->deleteLater();
    }
    if(customPlot){
        customPlot->deleteLater();
    }
    delete ui;
}

/** 鼠标移动 */
void ObserSingle::onMouseMove(QMouseEvent *event)
{
    if(midButtonPress){
        // posPress 与 QCursor::pos() 比较
        QPoint pos = QCursor::pos();
        int xLen = pos.x() - posPress.x();
        int yLen = pos.y() - posPress.y();
        if(qAbs(xLen) - qAbs(yLen) > 0){
            // 拉伸x轴
            if(xLen > 0){
                double xSize = customPlot->xAxis->range().upper - customPlot->xAxis->range().lower;
                customPlot->xAxis->setRangeLower(customPlot->xAxis->range().lower + xSize/20);
                customPlot->xAxis->setRangeUpper(customPlot->xAxis->range().upper - xSize/20);
            }else{
                double xSize = customPlot->xAxis->range().upper - customPlot->xAxis->range().lower;
                customPlot->xAxis->setRangeLower(customPlot->xAxis->range().lower - xSize/20);
                customPlot->xAxis->setRangeUpper(customPlot->xAxis->range().upper + xSize/20);
            }
        }else{
            // 拉伸y轴
            if(yLen > 0){
                double ySize = customPlot->yAxis->range().upper - customPlot->yAxis->range().lower;
                customPlot->yAxis->setRangeLower(customPlot->yAxis->range().lower - ySize/20);
                customPlot->yAxis->setRangeUpper(customPlot->yAxis->range().upper + ySize/20);
            }else{
                double ySize = customPlot->yAxis->range().upper - customPlot->yAxis->range().lower;
                customPlot->yAxis->setRangeLower(customPlot->yAxis->range().lower + ySize/20);
                customPlot->yAxis->setRangeUpper(customPlot->yAxis->range().upper - ySize/20);
            }
        }
        posPress = pos;
        customPlot->replot(QCustomPlot::rpQueuedReplot);
    }else{
        // 当前鼠标位置(像素坐标)
        int x_pos = event->pos().x();
        int y_pos = event->pos().y();
        // 像素坐标转成实际的x,y轴的坐标
        float x_val = customPlot->xAxis->pixelToCoord(x_pos);
        float y_val = customPlot->yAxis->pixelToCoord(y_pos);
        if(cursorShow){
            lineY->point1->setCoords(x_val, customPlot->yAxis->range().lower);
            lineY->point2->setCoords(x_val, customPlot->yAxis->range().upper);
            lineX->point1->setCoords(customPlot->xAxis->range().lower, y_val);
            lineX->point2->setCoords(customPlot->xAxis->range().upper, y_val);
            customPlot->replot(QCustomPlot::rpQueuedReplot);
        }
        QToolTip::hideText();
        QToolTip::showText(event->globalPos(),
                           QString("x:%1 y:%2").arg(x_val).arg(y_val),
                           this, rect());
    }
}

/** 图例与曲线同步选中 */
void ObserSingle::selectionChanged()
{
    // make top and bottom axes be selected synchronously, and handle axis and tick labels as one selectable object:
    if (customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || customPlot->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
        customPlot->xAxis2->selectedParts().testFlag(QCPAxis::spAxis) || customPlot->xAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
    {
        customPlot->xAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
        customPlot->xAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
    }
    // make left and right axes be selected synchronously, and handle axis and tick labels as one selectable object:
    if (customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis) || customPlot->yAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
        customPlot->yAxis2->selectedParts().testFlag(QCPAxis::spAxis) || customPlot->yAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
    {
        customPlot->yAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
        customPlot->yAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
    }

    // 将图形的选择与相应图例项的选择同步
    for (int i=0; i<customPlot->graphCount(); ++i)
    {
        QCPGraph *graph = customPlot->graph(i);
        QCPPlottableLegendItem *item = customPlot->legend->itemWithPlottable(graph);
        if (item->selected() || graph->selected())
        {
            item->setSelected(true);
            // 注意:这句需要Qcustomplot2.0系列版本
            graph->setSelection(QCPDataSelection(graph->data()->dataRange()));
            // 这句1.0系列版本即可
            // graph->setSelected(true);
        }
    }
}

/** 右键菜单 */
void ObserSingle::onMousePress(QMouseEvent *event)
{
    if(event->button() == Qt::RightButton){
        clickMenu->exec(QCursor::pos());
    }else if(event->button() == Qt::MiddleButton){
        midButtonPress = true;
        posPress = QCursor::pos();
    }
}

/** 鼠标释放 */
void ObserSingle::onMouseRelease(QMouseEvent *event)
{
    if(event->button() == Qt::MiddleButton){
        midButtonPress = false;
    }
}

/** 右键菜单action点击 */
void ObserSingle::_on_action_create_triggered(void)
{
    QAction *action = static_cast<QAction *>(this->sender());

    if(actionMap.contains(action)){
        if(actionFunMap.contains(actionMap[action])){
            actionFunMap[actionMap[action]]->fun(this);
        }
    }
}

/** 创建右键菜单action */
void ObserSingle::clickMenuAddAction(QString name, QString icon, MenuActionFun *fun)
{
    QAction *action = new QAction(QIcon(icon), name, this);
    actionMap.insert(action, name);
    QObject::connect(action, SIGNAL(triggered()), this, SLOT(_on_action_create_triggered()));
    clickMenu->addAction(action);
    actionFunMap.insert(name, fun);
}

/** 绘制曲线 */
void ObserSingle::drawCurve(const QString name, const QVector<double> x, const QVector<double> y, QPen pen)
{
    if(curveMap.contains(name)){
        curveMap[name]->setData(x, y, true);
    }else{
        QCPGraph *graph = customPlot->addGraph();
        graph->setAntialiased(false);               // 关闭抗锯齿可加快绘制速度
        graph->setAntialiasedFill(false);
        graph->setAntialiasedScatters(false);
        graph->setPen(pen);
        graph->setName(name);
        graph->setData(x, y, true);
        curveMap.insert(name, graph);
    }
    customPlot->replot(QCustomPlot::rpQueuedReplot); // 重绘
}

/** 清除曲线 */
void ObserSingle::clearCurve(const QString name)
{
    if(curveMap.contains(name)){
        QVector<double> x, y;
        // 删除所有曲线
//        customPlot->clearGraphs();
        QCPGraph *graph;
        // 移除map曲线
        curveMap.remove(name);
        QList<QString> nameList = curveMap.keys();
        // 重新绘画所有曲线
        QList<QString> nameListSave;
        QList<QVector<double>> xList;
        QList<QVector<double>> yList;
        QList<QPen> penList;
        for(int i=0; i<nameList.size(); i++){
            graph = curveMap[nameList[i]];
            curveMap.remove(nameList[i]);
            // 保存曲线数据
            x.clear();
            y.clear();
            for(int data_i=0; data_i<graph->data()->size(); data_i++){
                x.append(graph->data()->at(data_i)->key);
                y.append(graph->data()->at(data_i)->value);
            }
            // 保存曲线颜色
            QColor color = graph->pen().color();
            // 重画曲线
//            DrawCurve(nameList[i], x, y, QPen(color));
//            DrawCurve(nameList[i], x, y);
            nameListSave.append(nameList[i]);
            xList.append(x);
            yList.append(y);
            penList.append(QPen(color));
        }
        customPlot->clearGraphs();
        for(int i=0; i<nameListSave.size(); i++){
            drawCurve(nameListSave[i], xList[i], yList[i], penList[i]);
        }

    }
    customPlot->replot(QCustomPlot::rpQueuedReplot); // 重绘
}

/** 自适应屏幕 */
void ObserSingle::replot(void)
{
    customPlot->rescaleAxes(true);
}

/** 设置运行状态 */
void ObserSingle::setRunStatus(bool b)
{
    if(b){
        customPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes );
        // 将图形的选择与相应图例项的选择同步取消
        for (int i=0; i<customPlot->graphCount(); ++i)
        {
            QCPGraph *graph = customPlot->graph(i);
            QCPPlottableLegendItem *item = customPlot->legend->itemWithPlottable(graph);
            if (item->selected() || graph->selected())
            {
                item->setSelected(false);
                QCPDataSelection select;
                graph->setSelection(select);
            }
        }
    }else{
        customPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectPlottables);
    }
}
相关推荐
__lost1 小时前
MATLAB画出3d的常见复杂有机分子和矿物的分子结构
开发语言·人工智能·matlab·化学·分子结构
夜夜敲码2 小时前
C语言教程(十八):C 语言共用体详解
c语言·开发语言
大学生亨亨2 小时前
go语言八股文(五)
开发语言·笔记·golang
raoxiaoya2 小时前
同时安装多个版本的golang
开发语言·后端·golang
whoarethenext3 小时前
初始https附带c/c++源码使用curl库调用
服务器·c++·qt·https·curl
enyp804 小时前
麒麟系统(基于Ubuntu)上使用Qt编译时遇到“type_traits文件未找到”的错误
linux·qt·ubuntu
cloues break.4 小时前
C++进阶----多态
开发语言·c++
我不会编程5554 小时前
Python Cookbook-6.10 保留对被绑定方法的引用且支持垃圾回收
开发语言·python
刘婉晴4 小时前
【环境配置】Mac电脑安装运行R语言教程 2025年
开发语言·macos·r语言