1. Introducción a QCustomPlot
QCustomPlot es una biblioteca de gráficos Qt C++ de código abierto para visualizar datos. La biblioteca proporciona muchos tipos de gráficos personalizables, incluidos diagramas de dispersión, diagramas de líneas, histogramas y diagramas de contorno. También admite dibujos personalizados, lo que le permite crear elementos de cualquier forma y tamaño y hacerlos interactuar con otros elementos. QCustomPlot es fácil de integrar en aplicaciones Qt existentes y admite interacciones de gráficos comunes como selección del mouse, zoom y panorámica. Además, puede generar imágenes de alta calidad y resultados en PDF. La documentación de QCustomplot detalla su uso y API, lo que la convierte en una herramienta poderosa y conveniente para los desarrolladores que necesitan agregar capacidades de visualización de datos a sus aplicaciones.
2. Vista previa de la interfaz
3. Implementación del código
1. Descarga de Qcustomplot
Puede descargar QCustomPlot desde el sitio web oficial de QCustomPlot (https://www.qcustomplot.com/index.php/download). En el sitio web puede encontrar la última versión estable, así como enlaces de descarga para todas las versiones históricas.
2. Agregue Qcustomplot a su proyecto
Descomprima el paquete comprimido Qcustomplot descargado, luego cópielo qcustomplot.cpp
y qcustomplot.h
a su proyecto
, luego haga clic derecho en el proyecto -> Agregar archivos existentes a su proyecto.
3. Modificación de archivos de proyecto
Abra el archivo .pro del proyecto y agregue printsupport
soporte para componentes.
Nota: Si la versión de su compilador es 6.5, también debe agregar QMAKE_CXXFLAGS += -Wa,-mbig-obj,
esta oración, de la siguiente manera
4. Escritura de código
a. Crear una nueva clase
Primero, cree una nueva clase de interfaz de diseñador, luego coloque un widget en la interfaz de diseñador y luego actualícelo a QCustomPlot
, de la siguiente manera:
4. Código fuente del proyecto
archivo .h
#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 &pen)
{
mArrow->setPen(pen);
mLabel->setPen(pen);
}
void setBrush(const QBrush &brush)
{
mLabel->setBrush(brush);
}
void setText(const QString &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
La clase AxisTag es la pequeña etiqueta en el lado derecho de la imagen que muestra el valor único.
Archivo .cpp:
#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使用率"));
this->graphMemory->setName(tr("内存使用率"));
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,&QPushButton::clicked,this,&SystemStatisticsWidget::btnClickedSlot);
connect(this->ui->btnScreenshotAndExport,&QPushButton::clicked,this,&SystemStatisticsWidget::btnClickedSlot);
connect(this->ui->btnPause,&QPushButton::clicked,this,&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)
{
//实例阴影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);
//计算显示位置
#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);
// // 打印和绘图对象
// 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 和 QPainter关联
// painter.begin(&printer);
// painter.setFont(QFont("微软雅黑", 20));
// // 尺寸根据pdf的页面宽度缩放
// if(pixmapGrab.width() > printer.width())
// {
// pixmapGrab = pixmapGrab.scaledToWidth(printer.width(), Qt::TransformationMode::SmoothTransformation);
// }
// // 生成新的一页并绘制上去
// //printer.newPage();
// painter.drawPixmap(0, 0, pixmapGrab.width(), pixmapGrab.height(), pixmapGrab);
// QString str = "\n"+QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
// QRect textRect(0,pixmapGrab.height(),printer.width(),20);
// painter.drawText(textRect, Qt::AlignCenter, str);
// // 关闭绘制
// painter.end();
}
else if(btn == this->ui->btnPause)
{
}
}
5. Escribir código de prueba
1. Primero defina una interfaz y un temporizador en mainwindow.h
SystemStatisticsWidget *systemStatisticsWidget;
QTimer mDataTimer;
2. Cree una instancia y conecte la ranura en mainwindow.cpp y agregue datos con regularidad.
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
,systemStatisticsWidget(new SystemStatisticsWidget())
{
ui->setupUi(this);
connect(&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. Descarga completa del proyecto
Haga clic en el enlace para descargar
la versión de compilación.