飞道的博客

QCustomPlot 使用示例

456人阅读  评论(0)

一、项目结构


fileparser.h

#ifndef FILEPARSER_H
#define FILEPARSER_H
#include <QObject>

struct structReferInfo
{
   
    double pitch_input;
    double heading;
    double pitch;
    double roll;
    double airSpeed_indicated;
    double accelerationOverLoad_body_Y;
};

class fileParser: public QObject
{
   
    Q_OBJECT
public:
    fileParser();

    void readFromFile(QString filePath, QVector<QString>& lines);
    void parseFile(std::map<QString, structReferInfo>& referInfoMap, QVector<QString> lines);

private:

    structReferInfo s_referInfo;
};

#endif // FILEPARSER_H

function.h

#ifndef FUNCTION_H
#define FUNCTION_H
#include <QDateTime>
#include <QString>

inline void qstringToTime(QString timeStr, QDateTime& time)
{
   
    QString dateTimeStr = timeStr;
    dateTimeStr = dateTimeStr.trimmed();

    if(!dateTimeStr.isEmpty())
    {
   
        time = QDateTime::fromString(dateTimeStr, "yyyy-MM-dd hh:mm:ss.zzz");
    }
}

#endif // FUNCTION_H

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <fileparser.h>
#include <QDateTime>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui {
    class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
   
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void getData();

private:
    void initUi();

    void setCustomPlotProperty();

    void initVectorSize(int size);

    void setCustomPlotData();

private slots:
    void on_actionOpenFile_triggered();

    void doubleClicked(QMouseEvent *event);

    void on_actionJPG_triggered();

private:
    Ui::MainWindow *ui;

    QDateTime timeStart, timeFinished;

    fileParser m_fileParser;
    QVector<QString> lines;
    std::map<QString, structReferInfo> referInfoMap;

    QLabel *m_statusLabel;
};
#endif // MAINWINDOW_H

fileparser.cpp
#include "fileparser.h"
#include <QFile>
#include <QTextStream>
#include <QMessageBox>

static int count = 0;

fileParser::fileParser()
{
   

}

void fileParser::readFromFile(QString filePath, QVector<QString>& lines)
{
   
    QFile file(filePath);

    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
   
        return;
    }

    QString line;
    QTextStream in(&file);
    line = in.readLine();

    while(!line.isNull())
    {
   
        lines.push_back(line);
        line = in.readLine();
    }
    file.close();
}

void fileParser::parseFile(std::map<QString, structReferInfo>& referInfoMap, QVector<QString> lines)
{
   
    for ( QVector<QString>::const_iterator it = lines.begin(); it != lines.end(); it++ )
    {
   
        QStringList strList = it->split(",");
        if(strList.size() != 7)
        {
   
            count++;
            continue;
        }

        QString time = strList[0];
        s_referInfo.pitch_input = strList[1].toFloat();
        s_referInfo.heading = strList[2].toFloat();
        s_referInfo.pitch = strList[3].toFloat();
        s_referInfo.roll = strList[4].toFloat();
        s_referInfo.airSpeed_indicated = strList[5].toFloat();
        s_referInfo.accelerationOverLoad_body_Y = strList[6].toFloat();
        referInfoMap.insert(std::pair<QString, structReferInfo>(time, s_referInfo));
    }

    if(count > 0)
    {
   
        QString temp = QString::fromLocal8Bit("该文件中共有%1个数据错误,已过滤错误数据!").arg(count);
        QMessageBox::warning(NULL, "warning", temp, QMessageBox::Yes);

    }
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
   
    QApplication a(argc, argv);
    MainWindow w;
    w.setWindowTitle("drawTool");

    //w.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint);


    w.showMaximized();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QCustomPlot/qcustomplot.h"
#include "function.h"

#define SERIES_COUNT                    6

QCustomPlot *customPlot[SERIES_COUNT];
QVector<double> X;
QVector<double> Y[SERIES_COUNT];

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

    initUi();

    setCustomPlotProperty();
}

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

void MainWindow::initUi()
{
   
    for(int i = 0; i < SERIES_COUNT; i++)
    {
   
        customPlot[i] = new QCustomPlot(ui->scrollArea->widget());
        ui->verticalLayout->addWidget(customPlot[i]);
        connect(customPlot[i], SIGNAL(mouseDoubleClick(QMouseEvent *)), this, SLOT(doubleClicked(QMouseEvent *)));
    }

    m_statusLabel = new QLabel(ui->statusbar);
}

void MainWindow::doubleClicked(QMouseEvent *event)
{
   
    Q_UNUSED(event);

    for(int i = 0; i < SERIES_COUNT; i++)
    {
   
        if(customPlot[i]->hasFocus())
        {
   
            customPlot[i]->graph(0)->rescaleAxes();
            customPlot[i]->replot();
        }
    }
}

void MainWindow::setCustomPlotProperty()
{
   
    QSharedPointer<QCPAxisTickerDateTime> timer(new QCPAxisTickerDateTime);
    timer->setDateTimeFormat("hh:mm:ss.zzz");
    timer->setTickCount(20);

    QFont font;
    font.setPixelSize(20);

    for(int i = 0; i < SERIES_COUNT; i++)
    {
   
        customPlot[i]->addGraph();
        //customPlot[i]->setMinimumHeight(600);
        customPlot[i]->setFixedHeight(600);

        customPlot[i]->xAxis->setTicker(timer);
        customPlot[i]->xAxis->setLabel("time");

        // 边框右侧和上侧均显示刻度线,但不显示刻度值:
//        customPlot[i]->xAxis2->setVisible(true);
//        customPlot[i]->xAxis2->setTickLabels(false);
//        customPlot[i]->yAxis2->setVisible(true);
//        customPlot[i]->yAxis2->setTickLabels(false);

        customPlot[i]->xAxis->setTickLabelFont(font);
        customPlot[i]->xAxis->setLabelFont(font);
        customPlot[i]->yAxis->setTickLabelFont(font);
        customPlot[i]->yAxis->setLabelFont(font);

        customPlot[i]->legend->setVisible(true);
        // 使上下两个X轴的范围总是相等,使左右两个Y轴的范围总是相等
        connect(customPlot[i]->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot[i]->xAxis2, SLOT(setRange(QCPRange)));
        connect(customPlot[i]->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot[i]->yAxis2, SLOT(setRange(QCPRange)));
    }

    customPlot[0]->graph(0)->setPen(QPen(Qt::red, 1));
    customPlot[1]->graph(0)->setPen(QPen(Qt::darkGray, 1));
    customPlot[2]->graph(0)->setPen(QPen(Qt::green, 1));
    customPlot[3]->graph(0)->setPen(QPen(Qt::blue, 1));
    customPlot[4]->graph(0)->setPen(QPen(Qt::darkYellow, 1));
    customPlot[5]->graph(0)->setPen(QPen(Qt::cyan, 1));

    customPlot[0]->yAxis->setLabel("value");
    customPlot[1]->yAxis->setLabel("value");
    customPlot[2]->yAxis->setLabel("value");
    customPlot[3]->yAxis->setLabel("value");
    customPlot[4]->yAxis->setLabel("value");
    customPlot[5]->yAxis->setLabel("value");

    customPlot[0]->graph(0)->setName("a");
    customPlot[1]->graph(0)->setName("b");
    customPlot[2]->graph(0)->setName("c");
    customPlot[3]->graph(0)->setName("d");
    customPlot[4]->graph(0)->setName("e");
    customPlot[5]->graph(0)->setName("f");
}


void MainWindow::on_actionOpenFile_triggered()
{
   
    QString fileName = QFileDialog::getOpenFileName(nullptr, "Open File", "", "TXT File(*.txt)");
    if(fileName.isEmpty())
    {
   
        return;
    }

    m_statusLabel->setText(fileName);
    m_statusLabel->adjustSize();

    lines.resize(0);
    referInfoMap.erase(referInfoMap.begin(),referInfoMap.end());

    m_fileParser.readFromFile(fileName, lines);
    m_fileParser.parseFile(referInfoMap, lines);

    getData();
    setCustomPlotData();
}

void MainWindow::getData()
{
   
    int i = 0, size = 0;

    std::map<QString, structReferInfo>::const_iterator iterFirst = referInfoMap.begin();
    std::map<QString,structReferInfo>::reverse_iterator iterLast = referInfoMap.rbegin();       //返回最后一个元素

    size = (int)referInfoMap.size();
    initVectorSize(size);

    qstringToTime(iterFirst->first, timeStart);
    qstringToTime(iterLast->first, timeFinished);

    std::map<QString, structReferInfo>::const_iterator iter = referInfoMap.begin();
    for (iter = referInfoMap.begin(); iter != referInfoMap.end(); iter++)
    {
   
        QDateTime time;
        qstringToTime(iter->first, time);
        double pitch_input = iter->second.pitch_input;
        double heading = iter->second.heading;
        double pitch = iter->second.pitch;
        double roll = iter->second.roll;
        double airSpeed_indicated = iter->second.airSpeed_indicated;
        double accelerationOverLoad_body_Y = iter->second.accelerationOverLoad_body_Y;

        //X[i] = time.toTime_t();                           //没有毫秒数据
        X[i] = time.toMSecsSinceEpoch() / 1000.0;           //有毫秒数据,但是要除以1000,hh::mm::ss.zzz格式化的时候才能识别毫秒
        Y[0][i] = pitch_input;
        Y[1][i] = heading;
        Y[2][i] = pitch;
        Y[3][i] = roll;
        Y[4][i] = airSpeed_indicated;
        Y[5][i] = accelerationOverLoad_body_Y;        

        i++;
    }
}

void MainWindow::setCustomPlotData()
{
   
    // 把已存在的数据填充进graph的数据区
    for(int i = 0 ; i < SERIES_COUNT; i++)
    {
   
        customPlot[i]->graph(0)->setData(X, Y[i]);

        customPlot[i]->graph(0)->rescaleAxes();                          //自动调整XY轴的范围,以便显示出graph(0)中所有的点

        customPlot[i]->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);         // 支持鼠标拖拽轴的范围、滚动缩放轴的范围,左键点选图层(每条曲线独占一个图层)

        customPlot[i]->replot();
    }
}

void MainWindow::initVectorSize(int size)
{
   
    X.resize(size);
    for(int i = 0; i < SERIES_COUNT; i++)
    {
   
        Y[i].resize(size);
    }
}

void MainWindow::on_actionJPG_triggered()
{
   
    QString savePath = QCoreApplication::applicationDirPath();
    savePath += "/outputData/JPG/";

    QDir dir;
    if(!dir.exists(savePath))
    {
   
        dir.mkdir(savePath);
    }

    bool isOK;
    QString text = QInputDialog::getText(NULL, QString::fromLocal8Bit("设置"), QString::fromLocal8Bit("请设置要保存的文件名"), QLineEdit::Normal, "fileName", &isOK);

    if(!isOK)
    {
   
        QMessageBox::warning(NULL, QString::fromLocal8Bit("警告"), QString::fromLocal8Bit("未输入文件名!文件未保存!"), QMessageBox::Yes);
        return;
    }

    QString filePath = savePath+ text + QString("%1.jpg");
    QFileInfo fileInfo(filePath);
    if(fileInfo.isFile())
    {
   
        QMessageBox::warning(NULL, QString::fromLocal8Bit("警告"), QString::fromLocal8Bit("文件已存在!"), QMessageBox::Yes);
        return;
    }

    for(int i = 0; i < SERIES_COUNT; i++)
    {
   
        customPlot[i]->saveJpg(filePath.arg(i), 0, 0);
    }

    if (QMessageBox::Yes == QMessageBox::question(NULL, QString::fromLocal8Bit("完成"), QString::fromLocal8Bit("保存成功,是否打开查看?"), QMessageBox::Yes | QMessageBox::No))
    {
   
        QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(savePath)));
    }
}

ui界面

二、测试用数据格式

文件以txt格式保存

2021-04-30 14:43:01.598,-6.10352e-05,291.097,0.0343483,-0.254098,590.425,-19.7816
2021-04-30 14:43:01.894,-6.10352e-05,291.038,0.0404533,-0.29129,589.273,-18.3748
2021-04-30 14:43:01.896,-6.10352e-05,291.038,0.0404533,-0.29129,589.273,-18.3748
2021-04-30 14:43:01.921,-6.10352e-05,290.778,0.0755395,-0.411844,582.944,-12.3173
2021-04-30 14:43:01.933,-6.10352e-05,290.765,0.0782369,-0.415599,582.477,-11.9309
2021-04-30 14:43:01.944,-6.10352e-05,290.759,0.0795836,-0.417224,582.243,-11.763
2021-04-30 14:43:01.956,-6.10352e-05,290.753,0.080929,-0.418838,582.009,-11.6061
2021-04-30 14:43:01.964,-6.10352e-05,290.747,0.0822826,-0.42044,581.775,-11.4324
2021-04-30 14:43:01.976,-6.10352e-05,290.742,0.0836427,-0.42203,581.542,-11.2519
2021-04-30 14:43:01.994,-6.10352e-05,290.737,0.0850009,-0.423148,581.309,-11.0893
2021-04-30 14:43:02.008,-6.10352e-05,290.729,0.0871385,-0.425021,580.923,-10.8135
2021-04-30 14:43:02.020,-6.10352e-05,290.724,0.0885049,-0.425659,580.739,-10.6823
2021-04-30 14:43:02.035,-6.10352e-05,290.72,0.0898726,-0.42675,580.506,-10.5237
2021-04-30 14:43:02.047,-6.10352e-05,290.712,0.0921791,-0.427633,580.077,-10.2551
2021-04-30 14:43:02.061,-6.10352e-05,290.709,0.0935521,-0.428249,579.882,-10.1267
2021-04-30 14:43:02.079,-6.10352e-05,290.703,0.095621,-0.429162,579.476,-9.87097
2021-04-30 14:43:02.094,-6.10352e-05,290.698,0.097765,-0.429591,579.122,-9.65878
2021-04-30 14:43:02.110,-6.10352e-05,290.694,0.0991455,-0.430185,578.942,-9.54891
2021-04-30 14:43:02.128,-6.10352e-05,290.689,0.101797,-0.430475,578.49,-9.28378
2021-04-30 14:43:02.144,-6.10352e-05,290.686,0.103182,-0.4306,578.27,-9.15764
2021-04-30 14:43:02.162,-6.10352e-05,290.681,0.105896,-0.430396,577.815,-8.89946
2021-04-30 14:43:02.178,-6.10352e-05,290.677,0.108167,-0.430286,577.403,-8.67594
2021-04-30 14:43:02.194,-6.10352e-05,290.675,0.109559,-0.429933,577.215,-8.57507
2021-04-30 14:43:02.210,-6.10352e-05,290.672,0.112049,-0.429291,576.812,-8.34438
2021-04-30 14:43:02.228,-6.10352e-05,290.671,0.113445,-0.428924,576.606,-8.24013
2021-04-30 14:43:02.246,-6.10352e-05,290.668,0.11624,-0.428175,576.148,-8.00886
2021-04-30 14:43:02.260,-6.10352e-05,290.665,0.118687,-0.427503,575.721,-7.80056
2021-04-30 14:43:02.280,-6.10352e-05,290.664,0.120088,-0.427112,575.522,-7.70508
2021-04-30 14:43:02.294,-6.10352e-05,290.663,0.122894,-0.425854,575.068,-7.49047
2021-04-30 14:43:02.312,-6.10352e-05,290.662,0.124298,-0.425446,574.841,-7.38657
2021-04-30 14:43:02.330,-6.10352e-05,290.661,0.126865,-0.424227,574.408,-7.1915
2021-04-30 14:43:02.346,-6.10352e-05,290.66,0.12955,-0.422952,573.987,-7.00661
2021-04-30 14:43:02.364,-6.10352e-05,290.659,0.130957,-0.422519,573.772,-6.91474
2021-04-30 14:43:02.380,-6.10352e-05,290.659,0.133561,-0.421238,573.37,-6.72936
2021-04-30 14:43:02.394,-6.10352e-05,290.659,0.13497,-0.420324,573.161,-6.64398
2021-04-30 14:43:02.414,-6.10352e-05,290.659,0.137406,-0.419067,572.743,-6.47565
2021-04-30 14:43:02.428,-6.10352e-05,290.659,0.139845,-0.417792,572.357,-6.3234
2021-04-30 14:43:02.447,-6.10352e-05,290.659,0.141255,-0.417317,572.165,-6.24904
2021-04-30 14:43:02.462,-6.10352e-05,290.659,0.144076,-0.415881,571.721,-6.08032
2021-04-30 14:43:02.481,-6.10352e-05,290.659,0.145487,-0.414918,571.5,-5.99758
2021-04-30 14:43:02.497,-6.10352e-05,290.66,0.148309,-0.413445,571.058,-5.83732
2021-04-30 14:43:02.511,-6.10352e-05,290.661,0.15069,-0.412109,570.652,-5.69287
2021-04-30 14:43:02.527,-6.10352e-05,290.661,0.152101,-0.41159,570.467,-5.62847
2021-04-30 14:43:02.543,-6.10352e-05,290.662,0.153512,-0.411066,570.28,-5.55045
2021-04-30 14:43:02.557,-6.10352e-05,290.663,0.15623,-0.409563,569.847,-5.40615
2021-04-30 14:43:02.577,-6.10352e-05,290.663,0.157641,-0.408544,569.637,-5.33715
2021-04-30 14:43:02.593,-6.10352e-05,290.664,0.160462,-0.407444,569.201,-5.19601
2021-04-30 14:43:02.613,-6.10352e-05,290.665,0.16264,-0.406098,568.817,-5.0757
2021-04-30 14:43:02.627,-6.10352e-05,290.667,0.165064,-0.405118,568.465,-4.96738
2021-04-30 14:43:02.643,-6.10352e-05,290.667,0.166474,-0.404541,568.28,-4.91124
2021-04-30 14:43:02.661,-6.10352e-05,290.668,0.169084,-0.403457,567.866,-4.78862
2021-04-30 14:43:02.677,-6.10352e-05,290.67,0.171475,-0.40196,567.486,-4.67872
2021-04-30 14:43:02.693,-6.10352e-05,290.67,0.172883,-0.401357,567.337,-4.62411
2021-04-30 14:43:02.713,-6.10352e-05,290.672,0.175429,-0.40025,566.927,-4.51165
2021-04-30 14:43:02.727,-6.10352e-05,290.673,0.17802,-0.399595,566.539,-4.40635
2021-04-30 14:43:02.743,-6.10352e-05,290.674,0.179425,-0.398966,566.344,-4.35419
2021-04-30 14:43:02.761,-6.10352e-05,290.675,0.181752,-0.397911,565.958,-4.25372
2021-04-30 14:43:02.779,-6.10352e-05,290.676,0.183156,-0.397267,565.785,-4.20889
2021-04-30 14:43:02.793,-6.10352e-05,290.677,0.185963,-0.396455,565.367,-4.10293
2021-04-30 14:43:02.811,-6.10352e-05,290.678,0.187365,-0.395794,565.16,-4.05129
2021-04-30 14:43:02.827,-6.10352e-05,290.679,0.190119,-0.394974,564.751,-3.95035
2021-04-30 14:43:02.843,-6.10352e-05,290.68,0.192398,-0.393869,564.413,-3.85975
2021-04-30 14:43:02.859,-6.10352e-05,290.681,0.193798,-0.39368,564.247,-3.82054

三、结果

四、完整示例下载

https://download.csdn.net/download/qq_40754866/18910871
运行环境:qt 5.14.2 win10 64位


转载:https://blog.csdn.net/qq_40754866/article/details/117085017
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场