When developing system monitoring applications, visualizing hardware metrics like CPU temperature is crucial. While specialized tools like CPU-Z or HWMonitor use low-level drivers for hardware access, Qt provides a more accessible approach through system APIs and libraries like QCustomPlot for data visualization. This article demonstrates how to create real-time CPU temperature monitoring graphs using QCustomPlot in a Windows environment.
Introduction to QCustomPlot
QCustomPlot is a comprehensive 2D plotting libray for Qt applications that enables developers to create high-quality graphs and charts with minimal effort. It supports various chart types, real-time data updates, and extensive customization options.
Key features of QCustomPlot include:
- Multiple chart types (line, scatter, bar, pie, histogram)
- Advenced data visualization capabilities
- Customizable axes and grid
- Interactive elements (zoom, pan, selection)
- Export functionality for various image formats
- Layer management for complex visualizations
- Real-time data streaming support
The library requires only the qcustomplot.h and qcustomplot.cpp files and the QT += printsupport directive in the project file.
System Setup
To implement CPU temperature monitoring, we'll need:
- Qt Creator with MSVC2022 compiler
- QCustomPlot library integration
- System API access for temperature data
- Timer mechanism for periodic updates
Visualization Style 1: Basic Temperature Graph
This style creates a clean, informative temperature graph with time-based x-axis and gradient backgrounds.
Plot Configuration
void configurePlot(QCustomPlot* plot) {
// Configure grid appearance
plot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
plot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
plot->xAxis->grid()->setSubGridVisible(true);
plot->yAxis->grid()->setSubGridVisible(true);
// Enable interactions
plot->setInteractions(QCP::iSelectLegend | QCP::iSelectPlottables);
// Set axis ranges
plot->xAxis->setRange(QDateTime::currentSecsSinceEpoch() - 120, QDateTime::currentSecsSinceEpoch());
plot->yAxis->setRange(0, 100);
// Configure axis labels
plot->xAxis->setLabel("Time (S)");
plot->yAxis->setLabel("Temperature (°C)");
// Add title
plot->plotLayout()->insertRow(0);
QCPTextElement* title = new QCPTextElement(plot, "CPU Temperature Monitor", QFont("sans", 17, QFont::Bold));
plot->plotLayout()->addElement(0, 0, title);
// Enable legend
plot->legend->setVisible(true);
plot->legend->setSelectableParts(QCPLegend::spItems);
plot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignTop|Qt::AlignLeft);
}
Time-Based X-Axis
void setupTimeAxis(QCustomPlot* plot) {
QSharedPointer<QCPAxisTickerDateTime> ticker(new QCPAxisTickerDateTime);
ticker->setDateTimeSpec(Qt::LocalTime);
ticker->setTickCount(5);
ticker->setDateTimeFormat("yyyy-MM-dd \n hh:mm:ss");
plot->xAxis->setTicker(ticker);
}
Interactive Elements
void addInteractiveElements(QCustomPlot* plot) {
// Vertical line for mouse tracking
QPointer<QCPItemLine> verticalLine = new QCPItemLine(plot);
verticalLine->start->setType(QCPItemPosition::ptPlotCoords);
verticalLine->start->setCoords(QDateTime::currentSecsSinceEpoch() + 5, 0);
verticalLine->end->setType(QCPItemPosition::ptPlotCoords);
verticalLine->end->setCoords(QDateTime::currentSecsSinceEpoch() + 5, 100);
verticalLine->setPen(QPen(Qt::red, 2));
// Text box for displaying values
QPointer<QCPItemText> infoBox = new QCPItemText(plot);
infoBox->setPositionAlignment(Qt::AlignRight|Qt::AlignTop);
infoBox->position->setType(QCPItemPosition::ptAxisRectRatio);
infoBox->position->setCoords(0.4, 0.05);
infoBox->setText("Temperature: -- °C");
infoBox->setPen(QPen(Qt::white));
infoBox->setPadding(QMargins(2,2,2,2));
infoBox->position->setParentAnchorX(verticalLine->anchor("start"));
infoBox->setColor(Qt::white);
infoBox->setBrush(QColor("#383F5B"));
infoBox->setVisible(false);
}
Gradient Effects
QPen createGradientPen(const QSize& size) {
QLinearGradient gradient(0, 15, 0, size.height() - 25);
gradient.setColorAt(0, QColor(255, 255, 255, 0));
gradient.setColorAt(0.5, Qt::black);
gradient.setColorAt(1, QColor(255, 255, 255, 0));
QBrush brush(gradient);
QPen pen;
pen.setColor(Qt::black);
pen.setWidth(3);
pen.setBrush(brush);
return pen;
}
QBrush createGraphBrush(const QSize& size, const QColor& color) {
QLinearGradient gradient(0, 10, 0, size.height() - 20);
gradient.setColorAt(0, color);
gradient.setColorAt(1, QColor(255, 255, 255, 0));
return QBrush(gradient);
}
Graph Initialization
void initializeGraphs(QCustomPlot* plot, const QStringList& coreNames) {
QVector<QColor> colors;
QVector<QCPGraph*> graphs;
for (int i = 0; i < coreNames.size(); ++i) {
QCPGraph* graph = plot->addGraph();
// Generate random color for each core
QColor coreColor(qrand() % 245 + 10, qrand() % 245 + 10, qrand() % 245 + 10);
colors.append(coreColor);
// Configure graph appearance
QPen pen;
pen.setColor(coreColor);
pen.setWidthF(2);
graph->setPen(pen);
graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black, 1.5), QBrush(Qt::white), 2));
graph->setBrush(createGraphBrush(plot->size(), coreColor));
graph->setName(coreNames[i]);
graph->setLineStyle(QCPGraph::lsLine);
// Add layer for proper rendering order
plot->addLayer(coreNames[i].toStdString().c_str(), plot->layer("main"), QCustomPlot::limAbove);
graph->setLayer(coreNames[i].toStdString().c_str());
graphs.append(graph);
}
plot->replot();
}
Data Update Mechanism
void updateTemperatureData(QCustomPlot* plot, qint64 timestamp, const QList<double>& temperatures) {
plot->xAxis->setRange(timestamp, 120, Qt::AlignRight);
for (int i = 0; i < temperatures.size() && i < plot->graphCount(); ++i) {
plot->graph(i)->addData(timestamp, temperatures[i]);
}
plot->replot();
}
Visualization Style 2: Detailed Temperature Display
This style adds additional information displays and axis tags for precise temperature readings.
Enhanced Graph Initialization
void initializeDetailedGraphs(QCustomPlot* plot, const QStringList& coreNames) {
QVector<QColor> colors;
QVector<QCPGraph*> graphs;
QVector<AxisTag*> axisTags;
for (int i = 0; i < coreNames.size(); ++i) {
QCPGraph* graph = plot->addGraph();
// Generate random color
QColor coreColor(qrand() % 245 + 10, qrand() % 245 + 10, qrand() % 245 + 10);
colors.append(coreColor);
// Configure graph
QPen pen;
pen.setColor(coreColor);
pen.setWidthF(2);
graph->setPen(pen);
graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black, 1.5), QBrush(Qt::white), 2));
graph->setBrush(createGraphBrush(plot->size(), coreColor));
graph->setName(coreNames[i]);
graph->setLineStyle(QCPGraph::lsLine);
// Add secondary axis for temperature display
QCPAxis* tempAxis = plot->axisRect()->addAxis(QCPAxis::atRight);
tempAxis->setRange(0, 100);
tempAxis->setTickLabels(false);
tempAxis->setPadding(55);
// Create axis tag for temperature display
AxisTag* tag = new AxisTag(tempAxis);
tag->setPen(pen);
axisTags.append(tag);
// Add layer
plot->addLayer(coreNames[i].toStdString().c_str(), plot->layer("main"), QCustomPlot::limAbove);
graph->setLayer(coreNames[i].toStdString().c_str());
graphs.append(graph);
}
plot->replot();
}
Enhanced Data Update
void updateDetailedTemperatureData(QCustomPlot* plot, qint64 timestamp, const QList<double>& temperatures) {
plot->xAxis->setRange(timestamp, 120, Qt::AlignRight);
for (int i = 0; i < temperatures.size() && i < plot->graphCount(); ++i) {
plot->graph(i)->addData(timestamp, temperatures[i]);
axisTags[i]->updatePosition(temperatures[i]);
axisTags[i]->setText(QString::number(temperatures[i], 'f', 2));
}
plot->replot();
}
Temperature Data Acquisition
To obtain CPU temperature data in Windows, you can use the Windows Management Instrumentation (WMI) API or specialized libraries that provide access to system sensors. The data acquisition should be performed in a separate thread to avoid blocking the UI, with results passed to the main thread for visualization.
This implementation provides a robust foundation for real-time CPU temperature monitoring with customizable visualization options, suitable for system monitoring applications or performance analysis tools.