qt uses QCustomplot to draw cpu and memory usage graphs

1. Introduction to QCustomPlot

QCustomPlot is an open source Qt C++ charting library for visualizing data. The library provides many types of customizable charts, including scatter plots, line plots, histograms, and contour plots. It also supports custom drawing, allowing you to create elements of any shape and size and make them interact with other elements. QCustomPlot is easy to integrate into existing Qt applications and supports common chart interactions like mouse selection, zooming and panning. Additionally, it can generate high-quality image and PDF output. QCustomplot’s documentation details its usage and API, making it a powerful and convenient tool for developers who need to add data visualization capabilities to their applications.

2. Interface preview

3. Code implementation

1. Qcustomplot download

You can download QCustomPlot from the QCustomPlot official website (https://www.qcustomplot.com/index.php/download). On the website you can find the latest stable version as well as download links for all historical versions.

2. Add Qcustomplot to your project

Unzip the downloaded Qcustomplot compressed package, and then copy qcustomplot.cpp and qcustomplot.h to your project

Then right-click on the project -> Add existing files to your project.

3. Project file modification

Open the project.pro file and add printsupport component support.
Note: If your compiler version is 6.5, you also need to add the sentence QMAKE_CXXFLAGS + = -Wa,-mbig-obj,, as follows

4. Code writing

a. Create a new class

First, create a new designer interface class, then place a widget in the designer interface, and then upgrade it to QCustomPlot, as follows:

4. Project source code

.h file

#ifndef SYSTEMSTATISTICSWIDGET_H
#define SYSTEMSTATISTICSWIDGET_H

#include <QWidget>
#include <QPixmap>
#include <QTextEdit>
#include <QObject>
#include <QTextObjectInterface>
#include <QPicture>
#include <QVariant>
#include <QPainter>

#include "qcustomplot.h"

namespace Ui {<!-- -->
class SystemStatisticsWidget;
}

class AxisTag : public QObject
{<!-- -->
public:
    explicit AxisTag(QCPAxis *parentAxis):QObject(parentAxis),mAxis(parentAxis)
    {<!-- -->
        mDummyTracer = new QCPItemTracer(mAxis->parentPlot());
        mDummyTracer->setVisible(false);
        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);

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

        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);
    }

    virtual ~AxisTag()
    {<!-- -->
        if (mDummyTracer)
            mDummyTracer->parentPlot()->removeItem(mDummyTracer);
        if(mArrow)
            mArrow->parentPlot()->removeItem(mArrow);
        if (mLabel)
            mLabel->parentPlot()->removeItem(mLabel);
    }

    // setters:
    void setPen(const QPen & amp;pen)
    {<!-- -->
        mArrow->setPen(pen);
        mLabel->setPen(pen);
    }
    void setBrush(const QBrush & brush)
    {<!-- -->
        mLabel->setBrush(brush);
    }
    void setText(const QString & amp;text)
    {<!-- -->
        mLabel->setText(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)
    {<!-- -->
        mDummyTracer->position->setCoords(1, value);
        mArrow->end->setCoords(mAxis->offset(), 0);
    }


protected:
    QCPAxis *mAxis;
    QPointer<QCPItemTracer> mDummyTracer;
    QPointer<QCPItemLine> mArrow;
    QPointer<QCPItemText> mLabel;
};


class SystemStatisticsWidget : public QWidget
{<!-- -->
    Q_OBJECT

public:
    enum {<!-- -->
        PlotTextFormat = QTextFormat::UserObject + 3902
    };
    enum {<!-- -->
        PicturePropertyId = 1
    };
    explicit SystemStatisticsWidget(QWidget *parent = nullptr);
    ~SystemStatisticsWidget();

    void addData(double cpuUsage,double memoryUsage);
    void popWindow(int x,int y,int width,int height);
protected:
    void showEvent(QShowEvent *event) override;
private:
    void controlInit();
    void setLabelText(double cpuUsage,double memoryUsage);
private slots:
    void btnClickedSlot();
private:
    Ui::SystemStatisticsWidget *ui;

    QPointer<QCPGraph> graphCpu;
    QPointer<QCPGraph> graphMemory;
    AxisTag *tagCpu;
    AxisTag *tagMemory;

    QString filePath;
};




#endif // SYSTEMSTATISTICSWIDGET_H

The AxisTag class is the little label on the right side of the image that displays the single value.
.cpp file:

#include "systemStatisticsWidget.h"
#include "ui_systemStatisticsWidget.h"

SystemStatisticsWidget::SystemStatisticsWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SystemStatisticsWidget),
    tagCpu(0),
    tagMemory(0)
{<!-- -->
    ui->setupUi(this);
    this->controlInit();
}

SystemStatisticsWidget::~SystemStatisticsWidget()
{<!-- -->
    delete ui;
}

void SystemStatisticsWidget::controlInit()
{<!-- -->
    this->ui->widgetPlot->yAxis->setTickLabels(false);
    connect(this->ui->widgetPlot->yAxis2, SIGNAL(rangeChanged(QCPRange)), this->ui->widgetPlot->yAxis, SLOT(setRange(QCPRange)));


    this->ui->widgetPlot->yAxis2->setVisible(true);
    this->ui->widgetPlot->axisRect()->addAxis(QCPAxis::atRight);
    this->ui->widgetPlot->axisRect()->axis(QCPAxis::atRight, 0)->setPadding(30);
    this->ui->widgetPlot->axisRect()->axis(QCPAxis::atRight, 1)->setPadding(30);


    graphCpu = this->ui->widgetPlot->addGraph(this->ui->widgetPlot->xAxis, this->ui->widgetPlot->axisRect()->axis(QCPAxis::atRight, 0));
    graphMemory = this->ui->widgetPlot->addGraph(this->ui->widgetPlot->xAxis, this->ui->widgetPlot->axisRect()->axis(QCPAxis::atRight, 1));
    graphCpu->setPen(QPen(QColor(250, 120, 0)));
    graphMemory->setPen(QPen(QColor(0, 180, 60)));

    this->graphCpu->setName(tr("CPU usage"));
    this->graphMemory->setName(tr("Memory usage"));
    this->ui->widgetPlot->legend->setVisible(true);


    tagCpu = new AxisTag(graphCpu->valueAxis());
    tagCpu->setPen(graphCpu->pen());
    tagMemory = new AxisTag(graphMemory->valueAxis());
    tagMemory->setPen(graphMemory->pen());


    connect(this->ui->btnClearData, & amp;QPushButton::clicked,this, & amp;SystemStatisticsWidget::btnClickedSlot);
    connect(this->ui->btnScreenshotAndExport, & amp;QPushButton::clicked,this, & amp;SystemStatisticsWidget::btnClickedSlot);
    connect(this->ui->btnPause, & amp;QPushButton::clicked,this, & amp;SystemStatisticsWidget::btnClickedSlot);
}

void SystemStatisticsWidget::setLabelText(double cpuUsage, double memoryUsage)
{<!-- -->
#define WARNING_VALUE 70
#define ERROR_VALUE 90

    if(cpuUsage > 90)
        this->ui->labelCpuUasge->setStyleSheet("color: rgb(255, 0, 0);font: 700 11pt "Microsoft YaHei UI";");
    else if(cpuUsage >= 70)
        this->ui->labelCpuUasge->setStyleSheet("color: rgb(255, 255, 0);font: 700 11pt "Microsoft YaHei UI";");
    else
        this->ui->labelCpuUasge->setStyleSheet("color: rgb(255, 255, 255);font: 700 11pt "Microsoft YaHei UI";");
    this->ui->labelCpuUasge->setText(QString::number(cpuUsage,'f',2));
    if(memoryUsage > 90)
    {<!-- -->
        this->ui->labelMemoryUsage->setStyleSheet("color: rgb(255, 0, 0);font: 700 11pt "Microsoft YaHei UI";");
    }
    else if(memoryUsage >= 70)
    {<!-- -->
        this->ui->labelMemoryUsage->setStyleSheet("color: rgb(255, 255, 0);font: 700 11pt "Microsoft YaHei UI";");
    }
    else
    {<!-- -->
        this->ui->labelMemoryUsage->setStyleSheet("color: rgb(255, 255, 255);font: 700 11pt "Microsoft YaHei UI";");
    }
    this->ui->labelMemoryUsage->setText(QString::number(cpuUsage,'f',2));
}

void SystemStatisticsWidget::addData(double cpuUsage, double memoryUsage)
{<!-- -->
    if(this->isHidden())
        return;
    if(this->ui->btnPause->isChecked())
        return;

    graphCpu->addData(graphCpu->dataCount(), cpuUsage);
    graphMemory->addData(graphMemory->dataCount(), memoryUsage);

    this->ui->widgetPlot->xAxis->rescale();
    graphCpu->rescaleValueAxis(false, true);
    graphMemory->rescaleValueAxis(false, true);
    this->ui->widgetPlot->xAxis->setRange(this->ui->widgetPlot->xAxis->range().upper, 100, Qt::AlignRight);


    double graphCpuValue = graphCpu->dataMainValue(graphCpu->dataCount()-1);
    double graphMemoryValue = graphMemory->dataMainValue(graphMemory->dataCount()-1);
    tagCpu->updatePosition(graphCpuValue);
    tagMemory->updatePosition(graphMemoryValue);
    tagCpu->setText(QString::number(graphCpuValue, 'f', 2));
    tagMemory->setText(QString::number(graphMemoryValue, 'f', 2));

    this->ui->widgetPlot->replot();

    this->setLabelText(cpuUsage,memoryUsage);
}

void SystemStatisticsWidget::popWindow(int x, int y, int width, int height)
{<!-- -->
    //Instance shadow shadow
    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this->ui->frame);
    shadow->setOffset(0, 0);
    shadow->setColor(QColor(32, 101, 165));
    shadow->setBlurRadius(10);
    this->ui->frame->setGraphicsEffect(shadow);

    this->ui->frame->setStyleSheet("QFrame#frame{border:1px groove gray;"
                                   "border-radius:10px;padding:5px;"
                                   "background-color: rgb(255, 255, 255);}");


    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
    this->setAttribute(Qt::WA_TranslucentBackground, true);

    //Calculate display position
#define DIST_TO_MOUISE 10
    int screenWidth = QGuiApplication::screenAt(QCursor().pos())->geometry().width();
    int screenHeight = QGuiApplication::screenAt(QCursor().pos())->geometry().height();

    int showX,showY;

    if(x + width + DIST_TO_MOUISE > screenWidth)
        showX = x-width-DIST_TO_MOUISE;
    else
        showX = x + DIST_TO_MOUISE;
    if(y + height + DIST_TO_MOUISE > screenHeight)
        showY = y - height - DIST_TO_MOUISE;
    else
        showY = y + DIST_TO_MOUISE;

    this->setGeometry(showX,showY,width,height);

    this->show();
}

void SystemStatisticsWidget::showEvent(QShowEvent *event)
{<!-- -->
    Q_UNUSED(event);
    this->graphCpu->data().data()->clear();
    this->graphMemory->data().data()->clear();
}

void SystemStatisticsWidget::btnClickedSlot()
{<!-- -->
    QPushButton *btn = static_cast<QPushButton *>(sender());

    if( btn == this->ui->btnClearData)
    {<!-- -->
        this->graphCpu->data().data()->clear();
        this->graphMemory->data().data()->clear();
        this->ui->widgetPlot->replot();
    }
    else if(btn == this->ui->btnScreenshotAndExport)
    {<!-- -->
// if(this->filePath.isEmpty())
// {<!-- -->
// this->filePath = QFileDialog::getSaveFileName(this, "Save document...", qApp->applicationDirPath(), "*.pdf");
// }
// if(this->filePath.isEmpty())
// return;
        QScreen *screen = QGuiApplication::primaryScreen();

        int x = this->ui->widgetPlot->mapToGlobal(QPoint(0,0)).x();
        int y = this->ui->widgetPlot->mapToGlobal(QPoint(0,0)).y();

        QPixmap pixmapGrab = screen->grabWindow(0,x,y,this->ui->widgetPlot->width(),this->ui->widgetPlot->height());
        QString fileName = QDateTime::currentDateTime().toString("yyyy.MM.dd.hh.mm.ss") + ".png";
        pixmapGrab.save(fileName);
// // Printing and drawing objects
// QPrinter printer;
// QPainter painter;
// printer.setOutputFormat(QPrinter::PdfFormat);
// printer.setOutputFileName(this->filePath);
// QMargins pageMargins(20, 20, 20, 20);
// QPageLayout pageLayout;
// pageLayout.setMode(QPageLayout::StandardMode);
// pageLayout.setOrientation(QPageLayout::Portrait);
// pageLayout.setPageSize(QPageSize(QPageSize::A4));
// pageLayout.setUnits(QPageLayout::Millimeter);
// pageLayout.setMargins(QMarginsF(pageMargins));
// printer.setPageLayout(pageLayout);

// // QPrinter is associated with QPainter
// painter.begin( & amp;printer);
// painter.setFont(QFont("Microsoft Yahei", 20));

// // The size is scaled according to the page width of the pdf
// if(pixmapGrab.width() > printer.width())
// {<!-- -->
// pixmapGrab = pixmapGrab.scaledToWidth(printer.width(), Qt::TransformationMode::SmoothTransformation);
// }
// // Generate a new page and draw it
// //printer.newPage();
// painter.drawPixmap(0, 0, pixmapGrab.width(), pixmapGrab.height(), pixmapGrab);

// QString str = "\\
" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
// QRect textRect(0,pixmapGrab.height(),printer.width(),20);
// painter.drawText(textRect, Qt::AlignCenter, str);
// // Close drawing
// painter.end();
    }
    else if(btn == this->ui->btnPause)
    {<!-- -->

    }
}

5. Test code writing

1. First define an interface and timer in mainwindow.h

 SystemStatisticsWidget *systemStatisticsWidget;
    QTimer mDataTimer;

2. Instantiate and connect the slot in mainwindow.cpp, and add data regularly

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    connect( & amp;mDataTimer, SIGNAL(timeout()), this, SLOT(timeroutSlot()));
    mDataTimer.start(40);

    this->ui->btnPopwindow->installEventFilter(this);
}

MainWindow::~MainWindow()
{<!-- -->
    delete ui;
}


void MainWindow::on_btnShow_clicked()
{<!-- -->
    this->systemStatisticsWidget->show();
}

void MainWindow::timeroutSlot()
{<!-- -->
    if(!systemStatisticsWidget->isHidden())
    {<!-- -->
        static uint64_t dataPoint = 0;
        dataPoint + + ;
        double cpu = qSin(dataPoint/50.0) + qSin(dataPoint/50.0/0.3843)*0.25;
        double memory = qCos(dataPoint/50.0) + qSin(dataPoint/50.0/0.4364)*0.15;
        this->systemStatisticsWidget->addData(cpu,memory);
    }
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{<!-- -->
    if(obj == this->ui->btnPopwindow)
    {<!-- -->
        if(event->type() == QEvent::Enter)
        {<!-- -->
           // auto evt = dynamic_cast<QEnterEvent *>(event);
          // systemStatisticsWidget->popWindow(evt->globalPosition().x(),evt->globalPosition().y(),500,400);

        }
        else if(event->type() == QEvent::Leave)
        {<!-- -->
           // systemStatisticsWidget->close();
        }
    }
    return QMainWindow::eventFilter(obj,event);
}

6. Complete project download

Click the link to download
build version