Conceptos Fundamentales de los Gráficos de Área
En el marco de trabajo Qt, los gráficos de área se construyen utilizando la clase QAreaSeries (o su equivalente AreaSeries en QML). Este tipo de visualización es ideal para resaltar la magnitud de un cambio a lo largo del tiempo, rellenando el espacio entre dos límites. Por defecto, el eje X actúa como límite inferior, mientras que una serie de líneas (QLineSeries) define el límite superior. Sin embargo, es posible definir ambos límites utilizando dos instancias de QLineSeries para crear áreas flotantes o apiladas.
Uso de QAreaSeries
La clase QAreaSeries se encarga de renderizar los datos cuantitativos rellenando la región comprendida entre las series de líneas superior e inferior. Dado que hereda conceptualmente de las series lineales, su configuración requiere al menos una serie de líneas para el borde superior. Si no se especifica un límite inferior, el área se extenderá hasta la base del gráfico.
Nota: Los términos "superior" e "inferior" son relativos. Si los valores de la serie inferior superan a los de la superior, el área se seguirá rellenando correctamente entre ambas trayectorias.
Inserción Eficiente de Datos
Para optimizar el rendimiento, especialmente con grandes volúmenes de datos, se recomienda evitar la inserción punto por punto. En su lugar, es preferible construir una lista de QPointF y agregarla de una sola vez. Además, en versiones modernas de Qt, se debe utilizar QRandomGenerator en lugar de la función obsoleta qrand().
#include <QLineSeries>
#include <QAreaSeries>
#include <QRandomGenerator>
#include <QVector>
#include <QPointF>
// Creación de las series de límites
QLineSeries *upperBoundary = new QLineSeries();
QLineSeries *lowerBoundary = new QLineSeries();
// Generación de datos en lote para el límite superior
QVector<QPointF> upperPoints;
for (int i = 0; i < 15; ++i) {
upperPoints.append(QPointF(i, QRandomGenerator::global()->bounded(20, 50)));
}
upperBoundary->append(upperPoints);
upperBoundary->setName("Límite Superior");
// Generación de datos en lote para el límite inferior
QVector<QPointF> lowerPoints;
for (int i = 0; i < 15; ++i) {
lowerPoints.append(QPointF(i, QRandomGenerator::global()->bounded(0, 20)));
}
lowerBoundary->append(lowerPoints);
lowerBoundary->setName("Límite Inferior");
// Configuración del área utilizando ambos límites
QAreaSeries *customArea = new QAreaSeries(upperBoundary, lowerBoundary);
customArea->setName("Zona de Operación");
// Añadir al gráfico (asumiendo que 'chart' es un puntero a QChart válido)
chart->addSeries(customArea);
chart->createDefaultAxes();
Arquitectura del Controlador de Gráficos
A continuación, se detalla la implementación de una clase controladora que gestiona la inicialización, configuración visual y actualización dinámica de un gráfico de área.
Configuración Inicial y Ejes
AreaPlotManager::AreaPlotManager(QWidget *parent)
: QWidget(parent),
chartView(new QChartView(this)),
mainChart(new QChart()),
primaryArea(nullptr),
secondaryArea(nullptr)
{
setupEnvironment();
}
void AreaPlotManager::setupEnvironment() {
// Inicialización de series
QLineSeries *topLine = new QLineSeries();
QLineSeries *midLine = new QLineSeries();
QLineSeries *bottomLine = new QLineSeries();
QVector<QPointF> topData, midData, bottomData;
for (int i = 0; i < 12; ++i) {
topData << QPointF(i, QRandomGenerator::global()->bounded(70, 100));
midData << QPointF(i, QRandomGenerator::global()->bounded(40, 70));
bottomData << QPointF(i, QRandomGenerator::global()->bounded(10, 40));
}
topLine->append(topData);
midLine->append(midData);
bottomLine->append(bottomData);
// Creación de áreas
primaryArea = new QAreaSeries(topLine, midLine);
primaryArea->setName("Rango Alto");
secondaryArea = new QAreaSeries(midLine, bottomLine);
secondaryArea->setName("Rango Bajo");
mainChart->addSeries(primaryArea);
mainChart->addSeries(secondaryArea);
mainChart->createDefaultAxes();
// Personalización del eje Y
QValueAxis *axisY = qobject_cast<QValueAxis *>(mainChart->axisY());
if (axisY) {
axisY->setRange(0, 110);
axisY->setTickCount(6);
axisY->setMinorTickCount(2);
axisY->setLabelFormat("%d");
axisY->setGridLineVisible(true);
axisY->setMinorGridLineVisible(true);
}
// Personalización del eje X
QValueAxis *axisX = qobject_cast<QValueAxis *>(mainChart->axisX());
if (axisX) {
axisX->setRange(0, 11);
axisX->setLabelFormat("%d s");
axisX->setGridLineVisible(true);
}
// Configuración de la vista
chartView->setChart(mainChart);
chartView->setRenderHint(QPainter::Antialiasing, true);
chartView->setRubberBand(QChartView::NoRubberBand);
// Leyenda y efectos
mainChart->legend()->setAlignment(Qt::AlignBottom);
mainChart->setDropShadowEnabled(true);
}
Gestión de Visibilidad y Estilos
El controlador permite modificar dinámicamente las propiedades visuales del gráfico, como la visibilidad de las series, los puntos de datos y las etiquetas.
void AreaPlotManager::toggleSeriesVisibility(int seriesIndex, bool isVisible) {
if (seriesIndex == 0 && primaryArea) {
primaryArea->setVisible(isVisible);
} else if (seriesIndex == 1 && secondaryArea) {
secondaryArea->setVisible(isVisible);
}
}
void AreaPlotManager::applyChartTheme(QChart::ChartTheme selectedTheme) {
mainChart->setTheme(selectedTheme);
}
void AreaPlotManager::configureAnimations(QChart::AnimationOptions options) {
mainChart->setAnimationOptions(options);
}
void AreaPlotManager::updateLegendAlignment(Qt::Alignment alignment) {
mainChart->legend()->setAlignment(alignment);
}
void AreaPlotManager::toggleDataPoints(bool showPoints) {
if (primaryArea) primaryArea->setPointsVisible(showPoints);
if (secondaryArea) secondaryArea->setPointsVisible(showPoints);
}
void AreaPlotManager::togglePointLabels(bool showLabels) {
QString format = showLabels ? "(@xPoint, @yPoint)" : "";
if (primaryArea) {
primaryArea->setPointLabelsFormat(format);
primaryArea->setPointLabelsVisible(showLabels);
}
if (secondaryArea) {
secondaryArea->setPointLabelsFormat(format);
secondaryArea->setPointLabelsVisible(showLabels);
}
}
void AreaPlotManager::enableAntialiasing(bool enable) {
chartView->setRenderHint(QPainter::Antialiasing, enable);
}
Regeneración de Datos
Para actualizar el gráfico con nuevos valores aleatorios, es necesario limpiar las series existentes y reconstruir los límites.
void AreaPlotManager::regenerateChartData() {
mainChart->removeAllSeries();
QLineSeries *newTop = new QLineSeries();
QLineSeries *newMid = new QLineSeries();
QLineSeries *newBottom = new QLineSeries();
auto generateRandomPoints = [](int count, int minVal, int maxVal) {
QVector<QPointF> points;
for (int i = 0; i < count; ++i) {
points << QPointF(i, QRandomGenerator::global()->bounded(minVal, maxVal));
}
return points;
};
newTop->append(generateRandomPoints(12, 70, 100));
newMid->append(generateRandomPoints(12, 40, 70));
newBottom->append(generateRandomPoints(12, 10, 40));
// Asignación de colores aleatorios para diferenciar las líneas
auto randomColor = []() {
return QColor(QRandomGenerator::global()->bounded(256),
QRandomGenerator::global()->bounded(256),
QRandomGenerator::global()->bounded(256));
};
newTop->setColor(randomColor());
newMid->setColor(randomColor());
newBottom->setColor(randomColor());
primaryArea = new QAreaSeries(newTop, newMid);
primaryArea->setName("Rango Alto");
secondaryArea = new QAreaSeries(newMid, newBottom);
secondaryArea->setName("Rango Bajo");
mainChart->addSeries(primaryArea);
mainChart->addSeries(secondaryArea);
// Recrear ejes para ajustar los nuevos rangos si es necesario
mainChart->createDefaultAxes();
}