From ad03c0f6a0dd066b79e0ada0d03fedae9c15d346 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 16 Apr 2021 16:11:09 +0200 Subject: [PATCH] Support more than one data point in a diagram --- examples/iot-dashboard/Diagram.cpp | 28 ++-- examples/iot-dashboard/Diagram.h | 9 +- examples/iot-dashboard/DiagramSkinlet.cpp | 120 +++++++++++++----- examples/iot-dashboard/Skin.cpp | 6 +- examples/iot-dashboard/UsageDiagram.cpp | 43 +++++-- examples/iot-dashboard/UsageDiagram.h | 2 + .../nodes/DiagramSegmentsNode.cpp | 2 +- .../iot-dashboard/nodes/DiagramSegmentsNode.h | 4 +- 8 files changed, 142 insertions(+), 72 deletions(-) diff --git a/examples/iot-dashboard/Diagram.cpp b/examples/iot-dashboard/Diagram.cpp index 4161165f..746dc179 100644 --- a/examples/iot-dashboard/Diagram.cpp +++ b/examples/iot-dashboard/Diagram.cpp @@ -9,17 +9,21 @@ class Diagram::PrivateData { } - QVector dataPoints; + QVector > dataPoints; int xGridLines = -1; qreal yMax = -1; Qsk::Position position = Qsk::Bottom; - Types types = Area; + QVector types; }; QSK_SUBCONTROL( Diagram, Chart ) QSK_SUBCONTROL( Diagram, Segments ) -QSK_SUBCONTROL( Diagram, ChartLine ) -QSK_SUBCONTROL( Diagram, ChartArea ) +QSK_SUBCONTROL( Diagram, ChartLine1 ) +QSK_SUBCONTROL( Diagram, ChartArea1 ) +QSK_SUBCONTROL( Diagram, ChartLine2 ) +QSK_SUBCONTROL( Diagram, ChartArea2 ) +QSK_SUBCONTROL( Diagram, ChartLine3 ) +QSK_SUBCONTROL( Diagram, ChartArea3 ) Diagram::Diagram( QQuickItem* parent ) : Inherited( parent ) @@ -31,24 +35,20 @@ Diagram::~Diagram() { } -QVector Diagram::dataPoints() const +QVector< QVector > Diagram::dataPoints() const { return m_data->dataPoints; } -void Diagram::setDataPoints( const QVector& dataPoints ) +void Diagram::addDataPoints( const QVector& dataPoints, const Types& types ) { - m_data->dataPoints = dataPoints; + m_data->dataPoints.append( dataPoints ); + m_data->types.append( types ); } -Diagram::Types Diagram::types() const +Diagram::Types Diagram::typesAt( uint pos ) const { - return m_data->types; -} - -void Diagram::setTypes( Types types ) -{ - m_data->types = types; + return m_data->types.at( pos ); } qreal Diagram::yMax() const diff --git a/examples/iot-dashboard/Diagram.h b/examples/iot-dashboard/Diagram.h index 2cb19f56..089f142a 100644 --- a/examples/iot-dashboard/Diagram.h +++ b/examples/iot-dashboard/Diagram.h @@ -12,7 +12,7 @@ class Diagram : public QskControl using Inherited = QskControl; public: - QSK_SUBCONTROLS( Chart, Segments, ChartLine, ChartArea ) + QSK_SUBCONTROLS( Chart, Segments, ChartLine1, ChartArea1, ChartLine2, ChartArea2, ChartLine3, ChartArea3 ) enum Type { @@ -25,11 +25,10 @@ class Diagram : public QskControl Diagram( QQuickItem* parent = nullptr ); ~Diagram() override; - QVector dataPoints() const; - void setDataPoints( const QVector& dataPoints ); + QVector< QVector > dataPoints() const; + void addDataPoints( const QVector& dataPoints, const Types& types ); - Types types() const; - void setTypes( Types types ); + Types typesAt( uint pos ) const; qreal yMax() const; void setYMax( qreal yMax ); diff --git a/examples/iot-dashboard/DiagramSkinlet.cpp b/examples/iot-dashboard/DiagramSkinlet.cpp index 0b604b97..8d453519 100644 --- a/examples/iot-dashboard/DiagramSkinlet.cpp +++ b/examples/iot-dashboard/DiagramSkinlet.cpp @@ -3,6 +3,39 @@ #include "nodes/DiagramDataNode.h" #include "nodes/DiagramSegmentsNode.h" +namespace +{ + QskAspect::Subcontrol areaForIndex( int i ) + { + switch( i ) + { + case 1: + return Diagram::ChartArea2; + + case 2: + return Diagram::ChartArea3; + + default: + return Diagram::ChartArea1; + } + } + + QskAspect::Subcontrol lineForIndex( int i ) + { + switch( i ) + { + case 1: + return Diagram::ChartLine2; + + case 2: + return Diagram::ChartLine3; + + default: + return Diagram::ChartLine1; + } + } +} + DiagramSkinlet::DiagramSkinlet( QskSkin* skin ) : QskSkinlet( skin ) { @@ -46,59 +79,78 @@ QSGNode* DiagramSkinlet::updateSubNode( return nullptr; } -QSGNode* DiagramSkinlet::updateChartNode( const Diagram* distribution, QSGNode* node ) const +QSGNode* DiagramSkinlet::updateChartNode( const Diagram* diagram, QSGNode* node ) const { if( node == nullptr ) { - node = new IdlChartNode; + node = new IdlChartNode; // ### rename } using Q = Diagram; - const QRectF rect = distribution->subControlRect( Q::Chart ); - const QVector dataPoints = distribution->dataPoints(); - const qreal yMax = distribution->yMax(); - const Qsk::Position position = distribution->chartPosition(); - int lineWidth = distribution->metric( Diagram::ChartLine | QskAspect::Size ); + const QRectF rect = diagram->subControlRect( Q::Chart ); + const qreal yMax = diagram->yMax(); + const Qsk::Position position = diagram->chartPosition(); QVector types = {Diagram::Line, Diagram::Area}; - int nodeIndex = 0; - - for( int i = 0; i < types.size(); ++i ) + for( int i = 0; i < diagram->dataPoints().size(); ++i ) { - if( distribution->types() & types.at( i ) ) + QSGNode* chartNode; + + if( node->childCount() > i ) { - IdlChartNode* lineNode; + chartNode = node->childAtIndex( i ); + } + else + { + chartNode = new QSGNode; + node->appendChildNode( chartNode ); + } - if( node->childCount() > nodeIndex ) + const QVector dataPoints = diagram->dataPoints().at( i ); + int nodeIndex = 0; + QskAspect::Subcontrol lineSubcontrol = lineForIndex( i ); + QskAspect::Subcontrol areaSubcontrol = areaForIndex( i ); + int lineWidth = diagram->metric( lineSubcontrol | QskAspect::Size ); + + for( int j = 0; j < types.size(); ++j ) + { + if( diagram->typesAt( i ) & types.at( j ) ) { - lineNode = static_cast( node->childAtIndex( nodeIndex ) ); - } - else - { - lineNode = new IdlChartNode; - node->appendChildNode( lineNode ); - } + IdlChartNode* dataPointNode; - const IdlChartNode::Type nodeType = ( types.at( i ) == Diagram::Line ) ? IdlChartNode::Line : IdlChartNode::Area; - const QColor color = ( types.at( i ) == Diagram::Line ) ? distribution->color( Q::ChartLine ) - : distribution->color( Q::ChartArea ); + if( chartNode->childCount() > nodeIndex ) + { + dataPointNode = static_cast( chartNode->childAtIndex( nodeIndex ) ); + } + else + { + dataPointNode = new IdlChartNode; + chartNode->appendChildNode( dataPointNode ); + } - lineNode->update( rect, nodeType, color, dataPoints, yMax, position, lineWidth ); - nodeIndex++; + const IdlChartNode::Type nodeType = ( types.at( j ) == Diagram::Line ) ? IdlChartNode::Line : IdlChartNode::Area; + const QColor color = ( types.at( j ) == Diagram::Line ) ? diagram->color( lineSubcontrol ) + : diagram->color( areaSubcontrol ); + + dataPointNode->update( rect, nodeType, color, dataPoints, yMax, position, lineWidth ); + nodeIndex++; + } + } + + while( nodeIndex < chartNode->childCount() ) + { + chartNode->removeChildNode( chartNode->childAtIndex( nodeIndex++ ) ); } } - while( nodeIndex < node->childCount() ) - { - node->removeChildNode( node->childAtIndex( nodeIndex++ ) ); - } + // ### also check for superfluous nodes here return node; } -QSGNode* DiagramSkinlet::updateSeparatorNode( const Diagram* discharge, QSGNode* node ) const +QSGNode* DiagramSkinlet::updateSeparatorNode( const Diagram* diagram, QSGNode* node ) const { - const int xGridLines = discharge->xGridLines(); + const int xGridLines = diagram->xGridLines(); if( xGridLines <= 0 ) { @@ -113,9 +165,9 @@ QSGNode* DiagramSkinlet::updateSeparatorNode( const Diagram* discharge, QSGNode* } using Q = Diagram; - const QRectF rect = discharge->subControlRect( Q::Chart ); - const QColor color = discharge->color( Q::Segments ); - const QVector dataPoints = discharge->dataPoints(); + const QRectF rect = diagram->subControlRect( Q::Chart ); + const QColor color = diagram->color( Q::Segments ); + const QVector< QVector > dataPoints = diagram->dataPoints(); separatorNode->update( rect, color, dataPoints, xGridLines ); diff --git a/examples/iot-dashboard/Skin.cpp b/examples/iot-dashboard/Skin.cpp index 82303823..c9b12884 100644 --- a/examples/iot-dashboard/Skin.cpp +++ b/examples/iot-dashboard/Skin.cpp @@ -156,8 +156,9 @@ void Skin::initHints( const Palette& palette ) // new diagram: - ed.setMetric( Diagram::ChartLine | QskAspect::Size, 2 ); - ed.setColor( Diagram::ChartArea, "#886776ff" ); + ed.setColor( Diagram::ChartArea1, "#886776ff" ); + ed.setColor( Diagram::ChartArea2, "#88ff3122" ); + ed.setColor( Diagram::ChartArea3, "#88ff7d34" ); // light intensity: @@ -178,7 +179,6 @@ void Skin::initHints( const Palette& palette ) ed.setGradient( RoundButton::Panel, palette.roundButton ); ed.setBoxBorderColors( WeekdayBox::Panel, palette.weekdayBox ); ed.setColor( QskTextLabel::Text, palette.text ); - ed.setColor( Diagram::ChartLine, Qt::transparent ); ed.setColor( WeekdayLabel::Text, palette.text ); ed.setColor( ShadowPositioner::Panel, palette.shadow ); } diff --git a/examples/iot-dashboard/UsageDiagram.cpp b/examples/iot-dashboard/UsageDiagram.cpp index 95ede3ca..cefcfda8 100644 --- a/examples/iot-dashboard/UsageDiagram.cpp +++ b/examples/iot-dashboard/UsageDiagram.cpp @@ -70,23 +70,33 @@ UsageDiagram::UsageDiagram( QQuickItem* parent ) setAutoAddChildren( false ); setAutoLayoutChildren( true ); - m_diagram->setTypes( Diagram::Line | Diagram::Area ); - m_diagram->setChartPosition( Qsk::Bottom ); int number = 100; - QVector dataPoints1; - dataPoints1.reserve( number ); - std::vector yValues1 = {40, 20, 30, 50, 30, 70, 80, 100, 90, 60}; - qreal t0 = yValues1[0]; - qreal step = 10; - boost::math::cubic_b_spline spline1( yValues1.data(), yValues1.size(), t0, step ); - - for( int x = 0; x < number; ++x ) + std::vector< std::vector > yValues = { - qreal y = spline1( x ); - dataPoints1.append( QPointF( x, y ) ); + {40, 20, 30, 50, 30, 70, 80, 100, 90, 60}, + {15, 70, 40, 60, 10, 90, 20, 40, 45, 50}, + {70, 40, 60, 10, 70, 20, 50, 20, 30, 40} + }; + + for( int i = 0; i < 3; ++i ) + { + auto y = yValues.at( i ); + + QVector dataPoints; + dataPoints.reserve( number ); + qreal t0 = y[0]; + qreal step = 10; + boost::math::cubic_b_spline spline( y.data(), y.size(), t0, step ); + + for( int x = 0; x < number; ++x ) + { + const qreal y = spline( x ); + dataPoints.append( QPointF( x, y ) ); + } + + m_diagram->addDataPoints( dataPoints, Diagram::Area ); } - m_diagram->setDataPoints( dataPoints1 ); m_diagram->setYMax( 100 ); addItem( m_diagram ); @@ -113,4 +123,11 @@ UsageDiagram::UsageDiagram( QQuickItem* parent ) m_captionBox->addItem( new CaptionItem( CaptionItem::Gas, this ) ); } +void UsageDiagram::updateLayout() +{ + auto weekdaysHeight = m_weekdays->preferredSize().height(); + m_diagram->setHeight( m_diagram->height() - weekdaysHeight ); + m_captionBox->setPosition( {0, 0} ); +} + #include "Diagram.moc" diff --git a/examples/iot-dashboard/UsageDiagram.h b/examples/iot-dashboard/UsageDiagram.h index b482f1aa..7920ccb6 100644 --- a/examples/iot-dashboard/UsageDiagram.h +++ b/examples/iot-dashboard/UsageDiagram.h @@ -130,6 +130,8 @@ class UsageDiagram : public Box UsageDiagram( QQuickItem* parent ); + void updateLayout() override; + QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override final { diff --git a/examples/iot-dashboard/nodes/DiagramSegmentsNode.cpp b/examples/iot-dashboard/nodes/DiagramSegmentsNode.cpp index af5f75d6..97982349 100644 --- a/examples/iot-dashboard/nodes/DiagramSegmentsNode.cpp +++ b/examples/iot-dashboard/nodes/DiagramSegmentsNode.cpp @@ -11,7 +11,7 @@ IdlChartSegmentsNode::IdlChartSegmentsNode() setMaterial( &m_material ); } -void IdlChartSegmentsNode::update( const QRectF& rect, const QColor& color, const QVector& dataPoints, int xGridLines ) +void IdlChartSegmentsNode::update( const QRectF& rect, const QColor& color, const QVector >& dataPoints, int xGridLines ) { Q_UNUSED( rect ); diff --git a/examples/iot-dashboard/nodes/DiagramSegmentsNode.h b/examples/iot-dashboard/nodes/DiagramSegmentsNode.h index 6faa9d4a..3f05eeb8 100644 --- a/examples/iot-dashboard/nodes/DiagramSegmentsNode.h +++ b/examples/iot-dashboard/nodes/DiagramSegmentsNode.h @@ -9,7 +9,7 @@ class IdlChartSegmentsNode : public QSGGeometryNode public: IdlChartSegmentsNode(); - void update( const QRectF& rect, const QColor& color, const QVector& dataPoints, int xGridLines ); + void update( const QRectF& rect, const QColor& color, const QVector< QVector >& dataPoints, int xGridLines ); private: QSGFlatColorMaterial m_material; @@ -17,6 +17,6 @@ class IdlChartSegmentsNode : public QSGGeometryNode QRectF m_rect; QColor m_color; - QVector m_dataPoints; + QVector< QVector > m_dataPoints; int m_xGridLines; };