diff --git a/python/PyQt6/core/auto_additions/qgsdiagramrenderer.py b/python/PyQt6/core/auto_additions/qgsdiagramrenderer.py index 610acc97d4a6..3b55d63a1210 100644 --- a/python/PyQt6/core/auto_additions/qgsdiagramrenderer.py +++ b/python/PyQt6/core/auto_additions/qgsdiagramrenderer.py @@ -64,6 +64,8 @@ """ # -- +QgsDiagramLayerSettings.Single = QgsDiagramLayerSettings.DiagramType.Single +QgsDiagramLayerSettings.Stacked = QgsDiagramLayerSettings.DiagramType.Stacked QgsDiagramSettings.Height = QgsDiagramSettings.LabelPlacementMethod.Height QgsDiagramSettings.XHeight = QgsDiagramSettings.LabelPlacementMethod.XHeight QgsDiagramSettings.Up = QgsDiagramSettings.DiagramOrientation.Up @@ -72,6 +74,8 @@ QgsDiagramSettings.Right = QgsDiagramSettings.DiagramOrientation.Right QgsDiagramSettings.Clockwise = QgsDiagramSettings.Direction.Clockwise QgsDiagramSettings.Counterclockwise = QgsDiagramSettings.Direction.Counterclockwise +QgsDiagramSettings.Horizontal = QgsDiagramSettings.StackedDiagramMode.Horizontal +QgsDiagramSettings.Vertical = QgsDiagramSettings.StackedDiagramMode.Vertical try: QgsDiagramSettings.__attribute_docs__ = {'sizeType': 'Diagram size unit', 'sizeScale': 'Diagram size unit scale', 'lineSizeUnit': 'Line unit index', 'lineSizeScale': 'Line unit scale', 'opacity': 'Opacity, from 0 (transparent) to 1.0 (opaque)', 'rotationOffset': 'Rotation offset, in degrees clockwise from horizontal.', 'maximumScale': 'The maximum map scale (i.e. most "zoomed in" scale) at which the diagrams will be visible.\nThe scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.\nA scale of 0 indicates no maximum scale visibility.\n\n.. seealso:: :py:func:`minimumScale`', 'minimumScale': 'The minimum map scale (i.e. most "zoomed out" scale) at which the diagrams will be visible.\nThe scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.\nA scale of 0 indicates no minimum scale visibility.\n\n.. seealso:: :py:func:`maximumScale`', 'minimumSize': 'Scale diagrams smaller than mMinimumSize to mMinimumSize'} except NameError: diff --git a/python/PyQt6/core/auto_additions/qgsstackeddiagram.py b/python/PyQt6/core/auto_additions/qgsstackeddiagram.py new file mode 100644 index 000000000000..4d3fc2a8efa2 --- /dev/null +++ b/python/PyQt6/core/auto_additions/qgsstackeddiagram.py @@ -0,0 +1,5 @@ +# The following has been generated automatically from src/core/diagram/qgsstackeddiagram.h +try: + QgsStackedDiagram.__group__ = ['diagram'] +except NameError: + pass diff --git a/python/PyQt6/core/auto_generated/diagram/qgsdiagram.sip.in b/python/PyQt6/core/auto_generated/diagram/qgsdiagram.sip.in index 330c103884d2..3198a5e9ef4d 100644 --- a/python/PyQt6/core/auto_generated/diagram/qgsdiagram.sip.in +++ b/python/PyQt6/core/auto_generated/diagram/qgsdiagram.sip.in @@ -27,8 +27,10 @@ Base class for all diagram types. sipType = sipType_QgsHistogramDiagram; else if ( sipCpp->diagramName() == QLatin1String( "Text" ) ) sipType = sipType_QgsTextDiagram; - else if ( sipCpp->diagramName() == QLatin1String( "Stacked" ) ) + else if ( sipCpp->diagramName() == QLatin1String( "StackedBar" ) ) sipType = sipType_QgsStackedBarDiagram; + else if ( sipCpp->diagramName() == QLatin1String( "Stacked" ) ) + sipType = sipType_QgsStackedDiagram; else sipType = NULL; %End @@ -107,7 +109,7 @@ Calculates a size to match the current settings and rendering context Calculates a length to match the current settings and rendering context :param l: The length to convert -:param s: Unused +:param s: The settings that specify the size type :param c: The rendering specifying the proper scale units for pixel conversion :return: The converted length for rendering diff --git a/python/PyQt6/core/auto_generated/diagram/qgshistogramdiagram.sip.in b/python/PyQt6/core/auto_generated/diagram/qgshistogramdiagram.sip.in index d42315b5d00a..c4988874aca9 100644 --- a/python/PyQt6/core/auto_generated/diagram/qgshistogramdiagram.sip.in +++ b/python/PyQt6/core/auto_generated/diagram/qgshistogramdiagram.sip.in @@ -10,7 +10,6 @@ - class QgsHistogramDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -21,6 +20,7 @@ A histogram style diagram. #include "qgshistogramdiagram.h" %End public: + QgsHistogramDiagram(); virtual QgsHistogramDiagram *clone() const /Factory/; diff --git a/python/PyQt6/core/auto_generated/diagram/qgspiediagram.sip.in b/python/PyQt6/core/auto_generated/diagram/qgspiediagram.sip.in index 65b97646ddc4..d954f42e2f11 100644 --- a/python/PyQt6/core/auto_generated/diagram/qgspiediagram.sip.in +++ b/python/PyQt6/core/auto_generated/diagram/qgspiediagram.sip.in @@ -8,7 +8,6 @@ - class QgsPieDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -19,6 +18,7 @@ A pie chart diagram. #include "qgspiediagram.h" %End public: + QgsPieDiagram(); virtual QgsPieDiagram *clone() const /Factory/; diff --git a/python/PyQt6/core/auto_generated/diagram/qgsstackedbardiagram.sip.in b/python/PyQt6/core/auto_generated/diagram/qgsstackedbardiagram.sip.in index 8991dbbba467..769f0fa99d69 100644 --- a/python/PyQt6/core/auto_generated/diagram/qgsstackedbardiagram.sip.in +++ b/python/PyQt6/core/auto_generated/diagram/qgsstackedbardiagram.sip.in @@ -10,7 +10,6 @@ - class QgsStackedBarDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -24,6 +23,7 @@ A stacked bar chart diagram. #include "qgsstackedbardiagram.h" %End public: + QgsStackedBarDiagram(); virtual QgsStackedBarDiagram *clone() const /Factory/; diff --git a/python/PyQt6/core/auto_generated/diagram/qgsstackeddiagram.sip.in b/python/PyQt6/core/auto_generated/diagram/qgsstackeddiagram.sip.in new file mode 100644 index 000000000000..4b7b96b62ce6 --- /dev/null +++ b/python/PyQt6/core/auto_generated/diagram/qgsstackeddiagram.sip.in @@ -0,0 +1,60 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/diagram/qgsstackeddiagram.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ + + + + + +class QgsStackedDiagram : QgsDiagram /NoDefaultCtors/ +{ +%Docstring(signature="appended") +A diagram composed of several subdiagrams, located side by side. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsstackeddiagram.h" +%End + public: + + QgsStackedDiagram(); + + virtual QgsStackedDiagram *clone() const /Factory/; + + + void subDiagramPosition( QPointF &newPos, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramSettings &subSettings ); +%Docstring +Calculates the position for the next subdiagram, updating the ``newPos`` object. + +:param newPos: out: position of the previous diagram +:param c: renderer context +:param s: stacked diagram settings +:param subSettings: previous subdiagram settings +%End + + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position ); + + + virtual QSizeF diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ); + + virtual QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ); + + virtual double legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const; + + virtual QString diagramName() const; + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/diagram/qgsstackeddiagram.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ diff --git a/python/PyQt6/core/auto_generated/diagram/qgstextdiagram.sip.in b/python/PyQt6/core/auto_generated/diagram/qgstextdiagram.sip.in index 5b099ee05c37..091c648888b1 100644 --- a/python/PyQt6/core/auto_generated/diagram/qgstextdiagram.sip.in +++ b/python/PyQt6/core/auto_generated/diagram/qgstextdiagram.sip.in @@ -8,7 +8,6 @@ - class QgsTextDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -19,6 +18,7 @@ A text based diagram. #include "qgstextdiagram.h" %End public: + enum Shape /BaseType=IntEnum/ { Circle, diff --git a/python/PyQt6/core/auto_generated/qgsdiagramrenderer.sip.in b/python/PyQt6/core/auto_generated/qgsdiagramrenderer.sip.in index 5c778d5ee80e..8ef0bdf1c523 100644 --- a/python/PyQt6/core/auto_generated/qgsdiagramrenderer.sip.in +++ b/python/PyQt6/core/auto_generated/qgsdiagramrenderer.sip.in @@ -63,6 +63,12 @@ Stores the settings for rendering of all diagrams for a layer. StartAngle, }; + enum DiagramType /BaseType=IntEnum/ + { + Single, + Stacked + }; + static const QgsPropertiesDefinition &propertyDefinitions(); %Docstring Returns the diagram property definitions. @@ -321,6 +327,12 @@ Stores the settings for rendering a single diagram. Counterclockwise, }; + enum StackedDiagramMode /BaseType=IntEnum/ + { + Horizontal, + Vertical + }; + QgsDiagramSettings(); ~QgsDiagramSettings(); QgsDiagramSettings( const QgsDiagramSettings &other ); @@ -346,6 +358,7 @@ Stores the settings for rendering a single diagram. double penWidth; LabelPlacementMethod labelPlacementMethod; DiagramOrientation diagramOrientation; + StackedDiagramMode stackedDiagramMode; double barWidth; double opacity; @@ -442,6 +455,88 @@ Returns the map unit scale for the content spacing. .. seealso:: :py:func:`spacingUnit` .. versionadded:: 3.12 +%End + + double stackedDiagramSpacing() const; +%Docstring +Returns the spacing between subdiagrams in a stacked diagram. + +Spacing units can be retrieved by calling :py:func:`~QgsDiagramSettings.stackedDiagramSpacingUnit`. + +.. seealso:: :py:func:`setStackedDiagramSpacing` + +.. seealso:: :py:func:`stackedDiagramSpacingUnit` + +.. seealso:: :py:func:`stackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + void setStackedDiagramSpacing( double spacing ); +%Docstring +Sets the ``spacing`` between subdiagrams in a stacked diagram. + +Spacing units are set via :py:func:`~QgsDiagramSettings.setStackedDiagramSpacingUnit`. + +.. seealso:: :py:func:`stackedDiagramSpacing` + +.. seealso:: :py:func:`setStackedDiagramSpacingUnit` + +.. seealso:: :py:func:`setStackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + void setStackedDiagramSpacingUnit( Qgis::RenderUnit unit ); +%Docstring +Sets the ``unit`` for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`stackedDiagramSpacingUnit` + +.. seealso:: :py:func:`setStackedDiagramSpacing` + +.. seealso:: :py:func:`setStackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + Qgis::RenderUnit stackedDiagramSpacingUnit() const; +%Docstring +Returns the units for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`setStackedDiagramSpacingUnit` + +.. seealso:: :py:func:`stackedDiagramSpacing` + +.. seealso:: :py:func:`stackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + void setStackedDiagramSpacingMapUnitScale( const QgsMapUnitScale &scale ); +%Docstring +Sets the map unit ``scale`` for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`stackedDiagramSpacingMapUnitScale` + +.. seealso:: :py:func:`setStackedDiagramSpacing` + +.. seealso:: :py:func:`setStackedDiagramSpacingUnit` + +.. versionadded:: 3.40 +%End + + const QgsMapUnitScale &stackedDiagramSpacingMapUnitScale() const; +%Docstring +Returns the map unit scale for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`setStackedDiagramSpacingMapUnitScale` ; + +.. seealso:: :py:func:`stackedDiagramSpacing` + +.. seealso:: :py:func:`stackedDiagramSpacingUnit` + +.. versionadded:: 3.40 %End Direction direction() const; @@ -585,6 +680,8 @@ Evaluates and returns the diagram settings relating to a diagram for a specific sipType = sipType_QgsSingleCategoryDiagramRenderer; else if ( sipCpp->rendererName() == QLatin1String( "LinearlyInterpolated" ) ) sipType = sipType_QgsLinearlyInterpolatedDiagramRenderer; + else if ( sipCpp->rendererName() == QLatin1String( "Stacked" ) ) + sipType = sipType_QgsStackedDiagramRenderer; else sipType = NULL; %End @@ -617,7 +714,7 @@ Returns the set of any fields required for diagram rendering :param context: expression context the diagrams will be drawn using %End - void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; %Docstring Renders the diagram for a specified feature at a specific position in the passed render context. %End @@ -683,9 +780,12 @@ Returns diagram settings for a feature (or ``False`` if the diagram for the feat :param s: out: diagram settings for the feature %End - virtual QSizeF diagramSize( const QgsFeature &features, const QgsRenderContext &c ) const = 0; + virtual QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c ) const = 0; %Docstring Returns size of the diagram (in painter units) or an invalid size in case of error + +:param feature: the feature +:param c: render context %End void convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const; @@ -714,6 +814,7 @@ Writes internal QgsDiagramRenderer diagram state to a DOM element. %End + }; class QgsSingleCategoryDiagramRenderer : QgsDiagramRenderer @@ -851,6 +952,100 @@ Returns configuration of appearance of legend. Will return ``None`` if no config }; +class QgsStackedDiagramRenderer : QgsDiagramRenderer +{ +%Docstring(signature="appended") +Renders diagrams using mixed diagram render types. The size of +the rendered diagram is given by a combination of subrenderers. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsdiagramrenderer.h" +%End + public: + QgsStackedDiagramRenderer(); + + virtual QgsStackedDiagramRenderer *clone() const /Factory/; + + + virtual QSizeF sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const; +%Docstring +Returns size of the diagram for a feature in map units. Returns an invalid QSizeF in case of error +%End + + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; +%Docstring +Renders the diagram for a specified feature at a specific position in the +passed render context, taking all renderers and their own diagrams into account. +Diagram rendering is delegated to renderer's diagram. +%End + + virtual QList diagramSettings() const; + +%Docstring +Returns list with all diagram settings in the renderer +%End + + void setDiagramSettings( const QgsDiagramSettings &s ); + + virtual QList diagramAttributes() const; + + + virtual QString rendererName() const; + + virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ); + + virtual void writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const; + + + void _readXmlSubRenderers( const QDomElement &elem, const QgsReadWriteContext &context ); +%Docstring +Reads stacked renderers state from a DOM element. + +.. seealso:: _writeXmlSubRenderers +%End + + void _writeXmlSubRenderers( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const; +%Docstring +Writes stacked renderers state to a DOM element. + +.. seealso:: _readXmlSubRenderers +%End + + virtual QList< QgsLayerTreeModelLegendNode * > legendItems( QgsLayerTreeLayer *nodeLayer ) const /Factory/; + + + QList< QgsDiagramRenderer * > renderers( bool sortByDiagramMode = false ) const; +%Docstring +Returns an ordered list with the renderers of the stacked renderer object. +@param sortByDiagramMode If true, the list is returned backwards for vertical orientation. +%End + + void addRenderer( QgsDiagramRenderer *renderer ); +%Docstring +Adds a renderer to the stacked renderer object. +@param renderer diagram renderer to be added to the stacked renderer +Renderers added first will render their diagrams first, i.e., more to +the left (horizontal mode) or more to the top (vertical mode). +%End + + const QgsDiagramRenderer *renderer( const int index ) const; +%Docstring +Returns the renderer at the given ``index``. +@param index index of the disired renderer in the stacked renderer +%End + + protected: + virtual bool diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const; + + virtual QSizeF diagramSize( const QgsFeature &, const QgsRenderContext &c ) const; + + +}; + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/PyQt6/core/core_auto.sip b/python/PyQt6/core/core_auto.sip index 5c3229dca24e..2b3c4986e795 100644 --- a/python/PyQt6/core/core_auto.sip +++ b/python/PyQt6/core/core_auto.sip @@ -280,6 +280,7 @@ %Include auto_generated/diagram/qgshistogramdiagram.sip %Include auto_generated/diagram/qgspiediagram.sip %Include auto_generated/diagram/qgsstackedbardiagram.sip +%Include auto_generated/diagram/qgsstackeddiagram.sip %Include auto_generated/diagram/qgstextdiagram.sip %Include auto_generated/dxf/qgsdxfexport.sip %Include auto_generated/editform/qgseditformconfig.sip diff --git a/python/PyQt6/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in b/python/PyQt6/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in index d7145cc753c0..46fb62c9231e 100644 --- a/python/PyQt6/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in +++ b/python/PyQt6/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in @@ -113,14 +113,13 @@ and block the user. protected: - void connectValueChanged( const QList &widgets, const char *slot ); + void connectValueChanged( const QList &widgets ); %Docstring -Connect the given slot to the value changed event for the set of widgets -Each widget is checked for type and the common type of signal is connected -to the slot. +Connect the value changed event for the set of widgets to +widgetChanged signal. Each widget is checked for type and +the common type of signal is connected to widgetChanged. :param widgets: The list of widgets to check. -:param slot: The slot to connect to the signals. %End virtual void keyPressEvent( QKeyEvent *event ); diff --git a/python/core/auto_additions/qgsstackeddiagram.py b/python/core/auto_additions/qgsstackeddiagram.py new file mode 100644 index 000000000000..4d3fc2a8efa2 --- /dev/null +++ b/python/core/auto_additions/qgsstackeddiagram.py @@ -0,0 +1,5 @@ +# The following has been generated automatically from src/core/diagram/qgsstackeddiagram.h +try: + QgsStackedDiagram.__group__ = ['diagram'] +except NameError: + pass diff --git a/python/core/auto_generated/diagram/qgsdiagram.sip.in b/python/core/auto_generated/diagram/qgsdiagram.sip.in index 330c103884d2..3198a5e9ef4d 100644 --- a/python/core/auto_generated/diagram/qgsdiagram.sip.in +++ b/python/core/auto_generated/diagram/qgsdiagram.sip.in @@ -27,8 +27,10 @@ Base class for all diagram types. sipType = sipType_QgsHistogramDiagram; else if ( sipCpp->diagramName() == QLatin1String( "Text" ) ) sipType = sipType_QgsTextDiagram; - else if ( sipCpp->diagramName() == QLatin1String( "Stacked" ) ) + else if ( sipCpp->diagramName() == QLatin1String( "StackedBar" ) ) sipType = sipType_QgsStackedBarDiagram; + else if ( sipCpp->diagramName() == QLatin1String( "Stacked" ) ) + sipType = sipType_QgsStackedDiagram; else sipType = NULL; %End @@ -107,7 +109,7 @@ Calculates a size to match the current settings and rendering context Calculates a length to match the current settings and rendering context :param l: The length to convert -:param s: Unused +:param s: The settings that specify the size type :param c: The rendering specifying the proper scale units for pixel conversion :return: The converted length for rendering diff --git a/python/core/auto_generated/diagram/qgshistogramdiagram.sip.in b/python/core/auto_generated/diagram/qgshistogramdiagram.sip.in index d42315b5d00a..c4988874aca9 100644 --- a/python/core/auto_generated/diagram/qgshistogramdiagram.sip.in +++ b/python/core/auto_generated/diagram/qgshistogramdiagram.sip.in @@ -10,7 +10,6 @@ - class QgsHistogramDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -21,6 +20,7 @@ A histogram style diagram. #include "qgshistogramdiagram.h" %End public: + QgsHistogramDiagram(); virtual QgsHistogramDiagram *clone() const /Factory/; diff --git a/python/core/auto_generated/diagram/qgspiediagram.sip.in b/python/core/auto_generated/diagram/qgspiediagram.sip.in index 65b97646ddc4..d954f42e2f11 100644 --- a/python/core/auto_generated/diagram/qgspiediagram.sip.in +++ b/python/core/auto_generated/diagram/qgspiediagram.sip.in @@ -8,7 +8,6 @@ - class QgsPieDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -19,6 +18,7 @@ A pie chart diagram. #include "qgspiediagram.h" %End public: + QgsPieDiagram(); virtual QgsPieDiagram *clone() const /Factory/; diff --git a/python/core/auto_generated/diagram/qgsstackedbardiagram.sip.in b/python/core/auto_generated/diagram/qgsstackedbardiagram.sip.in index 8991dbbba467..769f0fa99d69 100644 --- a/python/core/auto_generated/diagram/qgsstackedbardiagram.sip.in +++ b/python/core/auto_generated/diagram/qgsstackedbardiagram.sip.in @@ -10,7 +10,6 @@ - class QgsStackedBarDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -24,6 +23,7 @@ A stacked bar chart diagram. #include "qgsstackedbardiagram.h" %End public: + QgsStackedBarDiagram(); virtual QgsStackedBarDiagram *clone() const /Factory/; diff --git a/python/core/auto_generated/diagram/qgsstackeddiagram.sip.in b/python/core/auto_generated/diagram/qgsstackeddiagram.sip.in new file mode 100644 index 000000000000..4b7b96b62ce6 --- /dev/null +++ b/python/core/auto_generated/diagram/qgsstackeddiagram.sip.in @@ -0,0 +1,60 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/diagram/qgsstackeddiagram.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ + + + + + +class QgsStackedDiagram : QgsDiagram /NoDefaultCtors/ +{ +%Docstring(signature="appended") +A diagram composed of several subdiagrams, located side by side. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsstackeddiagram.h" +%End + public: + + QgsStackedDiagram(); + + virtual QgsStackedDiagram *clone() const /Factory/; + + + void subDiagramPosition( QPointF &newPos, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramSettings &subSettings ); +%Docstring +Calculates the position for the next subdiagram, updating the ``newPos`` object. + +:param newPos: out: position of the previous diagram +:param c: renderer context +:param s: stacked diagram settings +:param subSettings: previous subdiagram settings +%End + + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position ); + + + virtual QSizeF diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ); + + virtual QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ); + + virtual double legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const; + + virtual QString diagramName() const; + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/diagram/qgsstackeddiagram.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ diff --git a/python/core/auto_generated/diagram/qgstextdiagram.sip.in b/python/core/auto_generated/diagram/qgstextdiagram.sip.in index efa526fce5f1..86a1753d868f 100644 --- a/python/core/auto_generated/diagram/qgstextdiagram.sip.in +++ b/python/core/auto_generated/diagram/qgstextdiagram.sip.in @@ -8,7 +8,6 @@ - class QgsTextDiagram: QgsDiagram /NoDefaultCtors/ { %Docstring(signature="appended") @@ -19,6 +18,7 @@ A text based diagram. #include "qgstextdiagram.h" %End public: + enum Shape { Circle, diff --git a/python/core/auto_generated/qgsdiagramrenderer.sip.in b/python/core/auto_generated/qgsdiagramrenderer.sip.in index 5593f48e459e..6f3ec3ebd753 100644 --- a/python/core/auto_generated/qgsdiagramrenderer.sip.in +++ b/python/core/auto_generated/qgsdiagramrenderer.sip.in @@ -63,6 +63,12 @@ Stores the settings for rendering of all diagrams for a layer. StartAngle, }; + enum DiagramType + { + Single, + Stacked + }; + static const QgsPropertiesDefinition &propertyDefinitions(); %Docstring Returns the diagram property definitions. @@ -321,6 +327,12 @@ Stores the settings for rendering a single diagram. Counterclockwise, }; + enum StackedDiagramMode + { + Horizontal, + Vertical + }; + QgsDiagramSettings(); ~QgsDiagramSettings(); QgsDiagramSettings( const QgsDiagramSettings &other ); @@ -346,6 +358,7 @@ Stores the settings for rendering a single diagram. double penWidth; LabelPlacementMethod labelPlacementMethod; DiagramOrientation diagramOrientation; + StackedDiagramMode stackedDiagramMode; double barWidth; double opacity; @@ -442,6 +455,88 @@ Returns the map unit scale for the content spacing. .. seealso:: :py:func:`spacingUnit` .. versionadded:: 3.12 +%End + + double stackedDiagramSpacing() const; +%Docstring +Returns the spacing between subdiagrams in a stacked diagram. + +Spacing units can be retrieved by calling :py:func:`~QgsDiagramSettings.stackedDiagramSpacingUnit`. + +.. seealso:: :py:func:`setStackedDiagramSpacing` + +.. seealso:: :py:func:`stackedDiagramSpacingUnit` + +.. seealso:: :py:func:`stackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + void setStackedDiagramSpacing( double spacing ); +%Docstring +Sets the ``spacing`` between subdiagrams in a stacked diagram. + +Spacing units are set via :py:func:`~QgsDiagramSettings.setStackedDiagramSpacingUnit`. + +.. seealso:: :py:func:`stackedDiagramSpacing` + +.. seealso:: :py:func:`setStackedDiagramSpacingUnit` + +.. seealso:: :py:func:`setStackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + void setStackedDiagramSpacingUnit( Qgis::RenderUnit unit ); +%Docstring +Sets the ``unit`` for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`stackedDiagramSpacingUnit` + +.. seealso:: :py:func:`setStackedDiagramSpacing` + +.. seealso:: :py:func:`setStackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + Qgis::RenderUnit stackedDiagramSpacingUnit() const; +%Docstring +Returns the units for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`setStackedDiagramSpacingUnit` + +.. seealso:: :py:func:`stackedDiagramSpacing` + +.. seealso:: :py:func:`stackedDiagramSpacingMapUnitScale` + +.. versionadded:: 3.40 +%End + + void setStackedDiagramSpacingMapUnitScale( const QgsMapUnitScale &scale ); +%Docstring +Sets the map unit ``scale`` for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`stackedDiagramSpacingMapUnitScale` + +.. seealso:: :py:func:`setStackedDiagramSpacing` + +.. seealso:: :py:func:`setStackedDiagramSpacingUnit` + +.. versionadded:: 3.40 +%End + + const QgsMapUnitScale &stackedDiagramSpacingMapUnitScale() const; +%Docstring +Returns the map unit scale for the spacing between subdiagrams in a stacked diagram. + +.. seealso:: :py:func:`setStackedDiagramSpacingMapUnitScale` ; + +.. seealso:: :py:func:`stackedDiagramSpacing` + +.. seealso:: :py:func:`stackedDiagramSpacingUnit` + +.. versionadded:: 3.40 %End Direction direction() const; @@ -585,6 +680,8 @@ Evaluates and returns the diagram settings relating to a diagram for a specific sipType = sipType_QgsSingleCategoryDiagramRenderer; else if ( sipCpp->rendererName() == QLatin1String( "LinearlyInterpolated" ) ) sipType = sipType_QgsLinearlyInterpolatedDiagramRenderer; + else if ( sipCpp->rendererName() == QLatin1String( "Stacked" ) ) + sipType = sipType_QgsStackedDiagramRenderer; else sipType = NULL; %End @@ -617,7 +714,7 @@ Returns the set of any fields required for diagram rendering :param context: expression context the diagrams will be drawn using %End - void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; %Docstring Renders the diagram for a specified feature at a specific position in the passed render context. %End @@ -683,9 +780,12 @@ Returns diagram settings for a feature (or ``False`` if the diagram for the feat :param s: out: diagram settings for the feature %End - virtual QSizeF diagramSize( const QgsFeature &features, const QgsRenderContext &c ) const = 0; + virtual QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c ) const = 0; %Docstring Returns size of the diagram (in painter units) or an invalid size in case of error + +:param feature: the feature +:param c: render context %End void convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const; @@ -714,6 +814,7 @@ Writes internal QgsDiagramRenderer diagram state to a DOM element. %End + }; class QgsSingleCategoryDiagramRenderer : QgsDiagramRenderer @@ -851,6 +952,100 @@ Returns configuration of appearance of legend. Will return ``None`` if no config }; +class QgsStackedDiagramRenderer : QgsDiagramRenderer +{ +%Docstring(signature="appended") +Renders diagrams using mixed diagram render types. The size of +the rendered diagram is given by a combination of subrenderers. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsdiagramrenderer.h" +%End + public: + QgsStackedDiagramRenderer(); + + virtual QgsStackedDiagramRenderer *clone() const /Factory/; + + + virtual QSizeF sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const; +%Docstring +Returns size of the diagram for a feature in map units. Returns an invalid QSizeF in case of error +%End + + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; +%Docstring +Renders the diagram for a specified feature at a specific position in the +passed render context, taking all renderers and their own diagrams into account. +Diagram rendering is delegated to renderer's diagram. +%End + + virtual QList diagramSettings() const; + +%Docstring +Returns list with all diagram settings in the renderer +%End + + void setDiagramSettings( const QgsDiagramSettings &s ); + + virtual QList diagramAttributes() const; + + + virtual QString rendererName() const; + + virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ); + + virtual void writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const; + + + void _readXmlSubRenderers( const QDomElement &elem, const QgsReadWriteContext &context ); +%Docstring +Reads stacked renderers state from a DOM element. + +.. seealso:: _writeXmlSubRenderers +%End + + void _writeXmlSubRenderers( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const; +%Docstring +Writes stacked renderers state to a DOM element. + +.. seealso:: _readXmlSubRenderers +%End + + virtual QList< QgsLayerTreeModelLegendNode * > legendItems( QgsLayerTreeLayer *nodeLayer ) const /Factory/; + + + QList< QgsDiagramRenderer * > renderers( bool sortByDiagramMode = false ) const; +%Docstring +Returns an ordered list with the renderers of the stacked renderer object. +@param sortByDiagramMode If true, the list is returned backwards for vertical orientation. +%End + + void addRenderer( QgsDiagramRenderer *renderer ); +%Docstring +Adds a renderer to the stacked renderer object. +@param renderer diagram renderer to be added to the stacked renderer +Renderers added first will render their diagrams first, i.e., more to +the left (horizontal mode) or more to the top (vertical mode). +%End + + const QgsDiagramRenderer *renderer( const int index ) const; +%Docstring +Returns the renderer at the given ``index``. +@param index index of the disired renderer in the stacked renderer +%End + + protected: + virtual bool diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const; + + virtual QSizeF diagramSize( const QgsFeature &, const QgsRenderContext &c ) const; + + +}; + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 5c3229dca24e..2b3c4986e795 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -280,6 +280,7 @@ %Include auto_generated/diagram/qgshistogramdiagram.sip %Include auto_generated/diagram/qgspiediagram.sip %Include auto_generated/diagram/qgsstackedbardiagram.sip +%Include auto_generated/diagram/qgsstackeddiagram.sip %Include auto_generated/diagram/qgstextdiagram.sip %Include auto_generated/dxf/qgsdxfexport.sip %Include auto_generated/editform/qgseditformconfig.sip diff --git a/python/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in b/python/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in index d7145cc753c0..46fb62c9231e 100644 --- a/python/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in +++ b/python/gui/auto_generated/symbology/qgsrendererpropertiesdialog.sip.in @@ -113,14 +113,13 @@ and block the user. protected: - void connectValueChanged( const QList &widgets, const char *slot ); + void connectValueChanged( const QList &widgets ); %Docstring -Connect the given slot to the value changed event for the set of widgets -Each widget is checked for type and the common type of signal is connected -to the slot. +Connect the value changed event for the set of widgets to +widgetChanged signal. Each widget is checked for type and +the common type of signal is connected to widgetChanged. :param widgets: The list of widgets to check. -:param slot: The slot to connect to the signals. %End virtual void keyPressEvent( QKeyEvent *event ); diff --git a/scripts/old_sipify.pl b/scripts/old_sipify.pl index c27182427c9b..492e4ae0c9c6 100755 --- a/scripts/old_sipify.pl +++ b/scripts/old_sipify.pl @@ -182,9 +182,11 @@ "QgsDataSourceUri::SslMode", "QgsDiagramLayerSettings::LinePlacementFlag", "QgsDiagramLayerSettings::Placement", + "QgsDiagramLayerSettings::DiagramType", "QgsDiagramSettings::DiagramOrientation", "QgsDiagramSettings::Direction", "QgsDiagramSettings::LabelPlacementMethod", + "QgsDiagramSettings::StackedDiagramMode", "QgsDoubleSpinBox::ClearValueMode", "QgsDualView::FeatureListBrowsingAction", "QgsDualView::ViewMode", diff --git a/scripts/sipify.py b/scripts/sipify.py index f2bdafd6889e..9367fd5bc199 100755 --- a/scripts/sipify.py +++ b/scripts/sipify.py @@ -230,9 +230,11 @@ def current_fully_qualified_struct_name(self) -> str: "QgsDataSourceUri::SslMode", "QgsDiagramLayerSettings::LinePlacementFlag", "QgsDiagramLayerSettings::Placement", + "QgsDiagramLayerSettings::DiagramType", "QgsDiagramSettings::DiagramOrientation", "QgsDiagramSettings::Direction", "QgsDiagramSettings::LabelPlacementMethod", + "QgsDiagramSettings::StackedDiagramMode", "QgsDoubleSpinBox::ClearValueMode", "QgsDualView::FeatureListBrowsingAction", "QgsDualView::ViewMode", diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 1b48777fc1c4..e584b303f47e 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -269,7 +269,6 @@ #include "qgsgpstoolbar.h" #include "qgsgpscanvasbridge.h" #include "qgsguivectorlayertools.h" -#include "qgsdiagramproperties.h" #include "qgslayerdefinition.h" #include "qgslayertree.h" #include "qgslayertreefiltersettings.h" @@ -384,6 +383,7 @@ #include "qgselevationshadingrenderersettingswidget.h" #include "qgsshortcutsmanager.h" #include "qgssnappingwidget.h" +#include "qgsstackeddiagramproperties.h" #include "qgsstatisticalsummarydockwidget.h" #include "qgsstatusbar.h" #include "qgsstatusbarcoordinateswidget.h" @@ -8049,35 +8049,8 @@ void QgisApp::diagramProperties() return; } - QDialog dlg; - dlg.setWindowTitle( tr( "Layer Diagram Properties" ) ); - QgsDiagramProperties *gui = new QgsDiagramProperties( vlayer, &dlg, mMapCanvas ); - gui->layout()->setContentsMargins( 0, 0, 0, 0 ); - QVBoxLayout *layout = new QVBoxLayout( &dlg ); - layout->addWidget( gui ); - - QDialogButtonBox *buttonBox = new QDialogButtonBox( - QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply, - Qt::Horizontal, &dlg ); - layout->addWidget( buttonBox ); - - dlg.setLayout( layout ); - - connect( buttonBox->button( QDialogButtonBox::Ok ), &QAbstractButton::clicked, - &dlg, &QDialog::accept ); - connect( buttonBox->button( QDialogButtonBox::Cancel ), &QAbstractButton::clicked, - &dlg, &QDialog::reject ); - connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, - gui, &QgsDiagramProperties::apply ); - connect( buttonBox->button( QDialogButtonBox::Help ), &QAbstractButton::clicked, gui, [ = ] - { - QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#diagrams-properties" ) ); - } ); - - if ( dlg.exec() ) - gui->apply(); - - activateDeactivateLayerRelatedActions( vlayer ); + mapStyleDock( true ); + mMapStyleWidget->setCurrentPage( QgsLayerStylingWidget::VectorDiagram ); } void QgisApp::createAnnotationLayer() diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index c2bd2c5b914e..7dc8bd6ef2ea 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -138,7 +138,7 @@ class QgsDataItem; class QgsTileScaleWidget; class QgsLabelingWidget; class QgsLayerStylingWidget; -class QgsDiagramProperties; +class QgsStackedDiagramProperties; class QgsLocatorWidget; class QgsNominatimGeocoder; class QgsDataSourceManagerDialog; diff --git a/src/app/qgslayerstylingwidget.cpp b/src/app/qgslayerstylingwidget.cpp index ed18fd191873..c0fd515a574d 100644 --- a/src/app/qgslayerstylingwidget.cpp +++ b/src/app/qgslayerstylingwidget.cpp @@ -23,6 +23,7 @@ #include "qgsapplication.h" #include "qgslabelingwidget.h" #include "qgsmaskingwidget.h" +#include "qgsdiagramwidget.h" #include "qgslayerstylingwidget.h" #include "qgsrastertransparencywidget.h" #include "qgsrendererpropertiesdialog.h" @@ -217,6 +218,11 @@ void QgsLayerStylingWidget::setLayer( QgsMapLayer *layer ) symbol3DItem->setToolTip( tr( "3D View" ) ); mOptionsListWidget->addItem( symbol3DItem ); #endif + + QListWidgetItem *diagramItem = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/diagram.svg" ) ), QString() ); + diagramItem->setData( Qt::UserRole, VectorDiagram ); + diagramItem->setToolTip( tr( "Diagrams" ) ); + mOptionsListWidget->addItem( diagramItem ); break; } case Qgis::LayerType::Raster: @@ -334,12 +340,6 @@ void QgsLayerStylingWidget::apply() bool styleWasChanged = false; bool triggerRepaint = false; // whether the change needs the layer to be repainted - if ( QgsLabelingWidget *widget = qobject_cast( current ) ) - { - widget->apply(); - styleWasChanged = true; - undoName = QStringLiteral( "Label Change" ); - } if ( QgsMaskingWidget *widget = qobject_cast( current ) ) { widget->apply(); @@ -373,8 +373,23 @@ void QgsLayerStylingWidget::apply() styleWasChanged = true; triggerRepaint = true; } + else if ( QgsLabelingWidget *widget = qobject_cast( current ) ) + { + widget->apply(); + styleWasChanged = true; + undoName = QStringLiteral( "Label Change" ); + } + else if ( QgsDiagramWidget *widget = qobject_cast( current ) ) + { + widget->apply(); + styleWasChanged = true; + undoName = QStringLiteral( "Diagram Change" ); + } else if ( QgsMapLayerConfigWidget *widget = qobject_cast( current ) ) { + // Warning: All classes inheriting from QgsMapLayerConfigWidget + // should come in the current if block, before this else-if + // clause, to avoid duplicate calls to apply()! widget->apply(); styleWasChanged = true; triggerRepaint = widget->shouldTriggerLayerRepaint(); @@ -463,6 +478,10 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() mMesh3DWidget = widget; } #endif + else if ( QgsDiagramWidget *widget = qobject_cast( current ) ) + { + mDiagramWidget = widget; + } } mWidgetStack->clear(); @@ -495,6 +514,11 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() { QgsVectorLayer *vlayer = qobject_cast( mCurrentLayer ); +#ifdef HAVE_3D + const int tabShift = 1; // To move subsequent tabs +#else + const int tabShift = 0; +#endif switch ( row ) { case 0: // Style @@ -550,6 +574,15 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() break; } #endif + case 3 + tabShift: // Diagrams + { + mDiagramWidget = new QgsDiagramWidget( vlayer, mMapCanvas, mWidgetStack ); + mDiagramWidget->setDockMode( true ); + connect( mDiagramWidget, &QgsDiagramWidget::widgetChanged, this, &QgsLayerStylingWidget::autoApply ); + mDiagramWidget->syncToOwnLayer(); + mWidgetStack->setMainPanel( mDiagramWidget ); + break; + } default: break; } diff --git a/src/app/qgslayerstylingwidget.h b/src/app/qgslayerstylingwidget.h index 32f06ffa1f21..832f37846814 100644 --- a/src/app/qgslayerstylingwidget.h +++ b/src/app/qgslayerstylingwidget.h @@ -34,6 +34,7 @@ class QgsLabelingWidget; class QgsMaskingWidget; +class QgsDiagramWidget; class QgsMapLayer; class QgsMapCanvas; class QgsRendererPropertiesDialog; @@ -101,6 +102,7 @@ class APP_EXPORT QgsLayerStylingWidget : public QWidget, private Ui::QgsLayerSty History, Symbology3D, RasterAttributeTables, //!< Raster attribute tables, since QGIS 3.30 + VectorDiagram, //!< Vector diagram, since QGIS 3.40 }; QgsLayerStylingWidget( QgsMapCanvas *canvas, QgsMessageBar *messageBar, const QList &pages, QWidget *parent = nullptr ); @@ -175,6 +177,7 @@ class APP_EXPORT QgsLayerStylingWidget : public QWidget, private Ui::QgsLayerSty QgsVectorLayer3DRendererWidget *mVector3DWidget = nullptr; QgsMeshLayer3DRendererWidget *mMesh3DWidget = nullptr; #endif + QgsDiagramWidget *mDiagramWidget = nullptr; QgsRendererRasterPropertiesWidget *mRasterStyleWidget = nullptr; QgsRasterAttributeTableWidget *mRasterAttributeTableWidget = nullptr; QgsPanelWidget *mRasterAttributeTableDisabledWidget = nullptr; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6a4f2d498f2c..2476ad6e2603 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -141,6 +141,7 @@ set(QGIS_CORE_SRCS diagram/qgshistogramdiagram.cpp diagram/qgspiediagram.cpp diagram/qgsstackedbardiagram.cpp + diagram/qgsstackeddiagram.cpp diagram/qgstextdiagram.cpp editform/qgseditformconfig.cpp @@ -1414,6 +1415,7 @@ set(QGIS_CORE_HDRS diagram/qgshistogramdiagram.h diagram/qgspiediagram.h diagram/qgsstackedbardiagram.h + diagram/qgsstackeddiagram.h diagram/qgstextdiagram.h dxf/qgsdxfexport.h diff --git a/src/core/diagram/qgsdiagram.h b/src/core/diagram/qgsdiagram.h index b2c54871340a..8d5f3dd57c2d 100644 --- a/src/core/diagram/qgsdiagram.h +++ b/src/core/diagram/qgsdiagram.h @@ -48,8 +48,10 @@ class CORE_EXPORT QgsDiagram SIP_NODEFAULTCTORS sipType = sipType_QgsHistogramDiagram; else if ( sipCpp->diagramName() == QLatin1String( "Text" ) ) sipType = sipType_QgsTextDiagram; - else if ( sipCpp->diagramName() == QLatin1String( "Stacked" ) ) + else if ( sipCpp->diagramName() == QLatin1String( "StackedBar" ) ) sipType = sipType_QgsStackedBarDiagram; + else if ( sipCpp->diagramName() == QLatin1String( "Stacked" ) ) + sipType = sipType_QgsStackedDiagram; else sipType = NULL; SIP_END @@ -117,7 +119,7 @@ class CORE_EXPORT QgsDiagram SIP_NODEFAULTCTORS /** * Calculates a length to match the current settings and rendering context * \param l The length to convert - * \param s Unused + * \param s The settings that specify the size type * \param c The rendering specifying the proper scale units for pixel conversion * * \returns The converted length for rendering diff --git a/src/core/diagram/qgshistogramdiagram.cpp b/src/core/diagram/qgshistogramdiagram.cpp index e7aedb5855df..80b0802dc093 100644 --- a/src/core/diagram/qgshistogramdiagram.cpp +++ b/src/core/diagram/qgshistogramdiagram.cpp @@ -21,6 +21,8 @@ #include +const QString QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM = QStringLiteral( "Histogram" ); + QgsHistogramDiagram::QgsHistogramDiagram() { mCategoryBrush.setStyle( Qt::SolidPattern ); @@ -112,12 +114,11 @@ double QgsHistogramDiagram::legendSize( double value, const QgsDiagramSettings & QString QgsHistogramDiagram::diagramName() const { - return DIAGRAM_NAME_HISTOGRAM; + return QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM; } QSizeF QgsHistogramDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) { - Q_UNUSED( c ) QSizeF size; if ( attributes.isEmpty() ) @@ -208,7 +209,6 @@ void QgsHistogramDiagram::renderDiagram( const QgsFeature &feature, QgsRenderCon baseY -= maxBleed; } - mPen.setColor( s.penColor ); setPenWidth( mPen, s, c ); p->setPen( mPen ); diff --git a/src/core/diagram/qgshistogramdiagram.h b/src/core/diagram/qgshistogramdiagram.h index 2a5fd3f033d4..cb661f4992d1 100644 --- a/src/core/diagram/qgshistogramdiagram.h +++ b/src/core/diagram/qgshistogramdiagram.h @@ -16,8 +16,6 @@ #ifndef QGSHISTOGRAMDIAGRAM_H #define QGSHISTOGRAMDIAGRAM_H -#define DIAGRAM_NAME_HISTOGRAM "Histogram" - #include "qgis_core.h" #include "qgis.h" #include "qgsdiagram.h" @@ -40,6 +38,8 @@ class QgsRenderContext; class CORE_EXPORT QgsHistogramDiagram: public QgsDiagram SIP_NODEFAULTCTORS { public: + static const QString DIAGRAM_NAME_HISTOGRAM SIP_SKIP; + QgsHistogramDiagram(); QgsHistogramDiagram *clone() const override SIP_FACTORY; diff --git a/src/core/diagram/qgspiediagram.cpp b/src/core/diagram/qgspiediagram.cpp index 9586431feacf..c96089354fdc 100644 --- a/src/core/diagram/qgspiediagram.cpp +++ b/src/core/diagram/qgspiediagram.cpp @@ -19,6 +19,7 @@ #include +const QString QgsPieDiagram::DIAGRAM_NAME_PIE = QStringLiteral( "Pie" ); QgsPieDiagram::QgsPieDiagram() { @@ -69,7 +70,7 @@ double QgsPieDiagram::legendSize( double value, const QgsDiagramSettings &s, con QString QgsPieDiagram::diagramName() const { - return DIAGRAM_NAME_PIE; + return QgsPieDiagram::DIAGRAM_NAME_PIE; } QSizeF QgsPieDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) diff --git a/src/core/diagram/qgspiediagram.h b/src/core/diagram/qgspiediagram.h index 629add6ed1b7..b955723edae9 100644 --- a/src/core/diagram/qgspiediagram.h +++ b/src/core/diagram/qgspiediagram.h @@ -15,8 +15,6 @@ #ifndef QGSPIEDIAGRAM_H #define QGSPIEDIAGRAM_H -#define DIAGRAM_NAME_PIE "Pie" - #include "qgis_core.h" #include "qgis.h" #include "qgsdiagram.h" @@ -38,6 +36,8 @@ class QgsRenderContext; class CORE_EXPORT QgsPieDiagram: public QgsDiagram SIP_NODEFAULTCTORS { public: + static const QString DIAGRAM_NAME_PIE SIP_SKIP; + QgsPieDiagram(); QgsPieDiagram *clone() const override SIP_FACTORY; diff --git a/src/core/diagram/qgsstackedbardiagram.cpp b/src/core/diagram/qgsstackedbardiagram.cpp index c33bac3e9728..5b8acd864db2 100644 --- a/src/core/diagram/qgsstackedbardiagram.cpp +++ b/src/core/diagram/qgsstackedbardiagram.cpp @@ -22,6 +22,8 @@ #include +const QString QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR = QStringLiteral( "StackedBar" ); + QgsStackedBarDiagram::QgsStackedBarDiagram() { mCategoryBrush.setStyle( Qt::SolidPattern ); @@ -117,7 +119,7 @@ double QgsStackedBarDiagram::legendSize( double value, const QgsDiagramSettings QString QgsStackedBarDiagram::diagramName() const { - return DIAGRAM_NAME_STACKED; + return QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR; } QSizeF QgsStackedBarDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) diff --git a/src/core/diagram/qgsstackedbardiagram.h b/src/core/diagram/qgsstackedbardiagram.h index 53d0a1d3e60f..ba5bd284f85d 100644 --- a/src/core/diagram/qgsstackedbardiagram.h +++ b/src/core/diagram/qgsstackedbardiagram.h @@ -16,8 +16,6 @@ #ifndef QGSSTACKEDBARDIAGRAM_H #define QGSSTACKEDBARDIAGRAM_H -#define DIAGRAM_NAME_STACKED "Stacked" - #include "qgis_core.h" #include "qgis.h" #include "qgsdiagram.h" @@ -43,6 +41,8 @@ class QgsRenderContext; class CORE_EXPORT QgsStackedBarDiagram: public QgsDiagram SIP_NODEFAULTCTORS { public: + static const QString DIAGRAM_NAME_STACKED_BAR SIP_SKIP; + QgsStackedBarDiagram(); QgsStackedBarDiagram *clone() const override SIP_FACTORY; diff --git a/src/core/diagram/qgsstackeddiagram.cpp b/src/core/diagram/qgsstackeddiagram.cpp new file mode 100644 index 000000000000..fb64b61bc1df --- /dev/null +++ b/src/core/diagram/qgsstackeddiagram.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + qgsstackediagram.cpp + --------------------- + begin : June 2024 + copyright : (C) 2024 by Germán Carrillo + email : german at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgsstackeddiagram.h" +#include "qgsdiagramrenderer.h" +#include "qgsrendercontext.h" + +const QString QgsStackedDiagram::DIAGRAM_NAME_STACKED = QStringLiteral( "Stacked" ); + +QgsStackedDiagram::QgsStackedDiagram() +{ +} + +QgsStackedDiagram *QgsStackedDiagram::clone() const +{ + return new QgsStackedDiagram( *this ); +} + +void QgsStackedDiagram::subDiagramPosition( QPointF &newPos, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramSettings &subSettings ) +{ + QSizeF size = sizePainterUnits( subSettings.size, subSettings, c ); + const double spacing = c.convertToPainterUnits( s.stackedDiagramSpacing(), s.stackedDiagramSpacingUnit(), s.stackedDiagramSpacingMapUnitScale() ); + + if ( s.stackedDiagramMode == QgsDiagramSettings::Horizontal ) + { + newPos += QPointF( size.width() + spacing, 0 ); + } + else + { + newPos -= QPointF( 0, size.height() + spacing ); + } +} + +QSizeF QgsStackedDiagram::diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) +{ + Q_UNUSED( feature ) + Q_UNUSED( c ) + Q_UNUSED( s ) + Q_UNUSED( is ) + return QSize( 0, 0 ); +} + +double QgsStackedDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const +{ + Q_UNUSED( value ) + Q_UNUSED( s ) + Q_UNUSED( is ) + return 0; +} + +QString QgsStackedDiagram::diagramName() const +{ + return QgsStackedDiagram::DIAGRAM_NAME_STACKED; +} + +QSizeF QgsStackedDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) +{ + Q_UNUSED( attributes ) + Q_UNUSED( c ) + Q_UNUSED( s ) + return QSizeF( 0, 0 ); +} + +void QgsStackedDiagram::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position ) +{ + Q_UNUSED( feature ) + Q_UNUSED( c ) + Q_UNUSED( s ) + Q_UNUSED( position ) +} diff --git a/src/core/diagram/qgsstackeddiagram.h b/src/core/diagram/qgsstackeddiagram.h new file mode 100644 index 000000000000..a29607c04432 --- /dev/null +++ b/src/core/diagram/qgsstackeddiagram.h @@ -0,0 +1,61 @@ +/*************************************************************************** + qgsstackeddiagram.h + --------------------- + begin : June 2024 + copyright : (C) 2024 by Germán Carrillo + email : german at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSTACKEDDIAGRAM_H +#define QGSSTACKEDDIAGRAM_H + +#include "qgsdiagram.h" + +class QgsFeature; +class QPointF; +//class QgsDiagramSettings; +class QgsDiagramInterpolationSettings; +class QgsRenderContext; + + +/** + * \ingroup core + * \class QgsStackedDiagram + * \brief A diagram composed of several subdiagrams, located side by side. + * + * \since QGIS 3.40 + */ +class CORE_EXPORT QgsStackedDiagram : public QgsDiagram SIP_NODEFAULTCTORS +{ + public: + static const QString DIAGRAM_NAME_STACKED SIP_SKIP; + + QgsStackedDiagram(); + + QgsStackedDiagram *clone() const override SIP_FACTORY; + + /** + * Calculates the position for the next subdiagram, updating the \a newPos object. + * \param newPos out: position of the previous diagram + * \param c renderer context + * \param s stacked diagram settings + * \param subSettings previous subdiagram settings + */ + void subDiagramPosition( QPointF &newPos, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramSettings &subSettings ); + + void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position ) override; + + QSizeF diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) override; + QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) override; + double legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const override; + QString diagramName() const override; +}; + +#endif // QGSSTACKEDDIAGRAM_H diff --git a/src/core/diagram/qgstextdiagram.cpp b/src/core/diagram/qgstextdiagram.cpp index ec3d613692c4..021283c05bc5 100644 --- a/src/core/diagram/qgstextdiagram.cpp +++ b/src/core/diagram/qgstextdiagram.cpp @@ -19,6 +19,8 @@ #include +const QString QgsTextDiagram::DIAGRAM_NAME_TEXT = QStringLiteral( "Text" ); + QgsTextDiagram::QgsTextDiagram() { mPen.setWidthF( 2.0 ); @@ -68,7 +70,7 @@ double QgsTextDiagram::legendSize( double value, const QgsDiagramSettings &s, co QString QgsTextDiagram::diagramName() const { - return DIAGRAM_NAME_TEXT; + return QgsTextDiagram::DIAGRAM_NAME_TEXT; } QSizeF QgsTextDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) diff --git a/src/core/diagram/qgstextdiagram.h b/src/core/diagram/qgstextdiagram.h index a6dca952aee1..694b9ba92b51 100644 --- a/src/core/diagram/qgstextdiagram.h +++ b/src/core/diagram/qgstextdiagram.h @@ -15,8 +15,6 @@ #ifndef QGSTEXTDIAGRAM_H #define QGSTEXTDIAGRAM_H -#define DIAGRAM_NAME_TEXT "Text" - #include "qgis_core.h" #include "qgis.h" #include "qgsdiagram.h" @@ -38,6 +36,8 @@ class QgsRenderContext; class CORE_EXPORT QgsTextDiagram: public QgsDiagram SIP_NODEFAULTCTORS { public: + static const QString DIAGRAM_NAME_TEXT SIP_SKIP; + enum Shape { Circle = 0, diff --git a/src/core/qgsdiagramrenderer.cpp b/src/core/qgsdiagramrenderer.cpp index cfe5d5ce8199..0468516f0f04 100644 --- a/src/core/qgsdiagramrenderer.cpp +++ b/src/core/qgsdiagramrenderer.cpp @@ -20,6 +20,7 @@ #include "diagram/qgspiediagram.h" #include "diagram/qgshistogramdiagram.h" #include "diagram/qgsstackedbardiagram.h" +#include "diagram/qgsstackeddiagram.h" #include "qgsrendercontext.h" #include "qgslayertreemodellegendnode.h" #include "qgsfontutils.h" @@ -232,6 +233,10 @@ void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteCon mSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "spacingUnit" ) ) ); mSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "spacingUnitScale" ) ) ); + mStackedDiagramSpacing = elem.attribute( QStringLiteral( "stackedDiagramSpacing" ) ).toDouble(); + mStackedDiagramSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "stackedDiagramSpacingUnit" ) ) ); + mStackedDiagramSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "stackedDiagramSpacingUnitScale" ) ) ); + //label placement method if ( elem.attribute( QStringLiteral( "labelPlacementMethod" ) ) == QLatin1String( "Height" ) ) { @@ -260,6 +265,16 @@ void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteCon diagramOrientation = Up; } + // stacked mode + if ( elem.attribute( QStringLiteral( "stackedDiagramMode" ) ) == QLatin1String( "Horizontal" ) ) + { + stackedDiagramMode = Horizontal; + } + else if ( elem.attribute( QStringLiteral( "stackedDiagramMode" ) ) == QLatin1String( "Vertical" ) ) + { + stackedDiagramMode = Vertical; + } + // scale dependency if ( elem.attribute( QStringLiteral( "scaleDependency" ) ) == QLatin1String( "Diameter" ) ) { @@ -362,6 +377,9 @@ void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc, categoryElem.setAttribute( QStringLiteral( "spacing" ), QString::number( mSpacing ) ); categoryElem.setAttribute( QStringLiteral( "spacingUnit" ), QgsUnitTypes::encodeUnit( mSpacingUnit ) ); categoryElem.setAttribute( QStringLiteral( "spacingUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSpacingMapUnitScale ) ); + categoryElem.setAttribute( QStringLiteral( "stackedDiagramSpacing" ), QString::number( mStackedDiagramSpacing ) ); + categoryElem.setAttribute( QStringLiteral( "stackedDiagramSpacingUnit" ), QgsUnitTypes::encodeUnit( mStackedDiagramSpacingUnit ) ); + categoryElem.setAttribute( QStringLiteral( "stackedDiagramSpacingUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStackedDiagramSpacingMapUnitScale ) ); categoryElem.setAttribute( QStringLiteral( "direction" ), QString::number( mDirection ) ); //diagram size unit type and scale @@ -411,6 +429,18 @@ void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc, break; } + // stacked mode + switch ( stackedDiagramMode ) + { + case Horizontal: + categoryElem.setAttribute( QStringLiteral( "stackedDiagramMode" ), QStringLiteral( "Horizontal" ) ); + break; + + case Vertical: + categoryElem.setAttribute( QStringLiteral( "stackedDiagramMode" ), QStringLiteral( "Vertical" ) ); + break; + } + categoryElem.setAttribute( QStringLiteral( "barWidth" ), QString::number( barWidth ) ); categoryElem.setAttribute( QStringLiteral( "minimumSize" ), QString::number( minimumSize ) ); categoryElem.setAttribute( QStringLiteral( "rotationOffset" ), QString::number( rotationOffset ) ); @@ -498,11 +528,31 @@ void QgsDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderCont QSizeF QgsDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const { QgsDiagramSettings s; - if ( !diagramSettings( feature, c, s ) ) + if ( !diagramSettings( feature, c, s ) || !s.enabled ) { return QSizeF(); } + if ( s.scaleBasedVisibility ) + { + // Note: scale might be a non-round number, so compare with qgsDoubleNear + const double rendererScale = c.rendererScale(); + + // maxScale is inclusive ( (< && !=) --> < --> no size ) + double maxScale = s.maximumScale; + if ( maxScale > 0 && ( rendererScale < maxScale && !qgsDoubleNear( rendererScale, maxScale, 1E-8 ) ) ) + { + return QSizeF(); + } + + // minScale is exclusive ( >= --> no size) + double minScale = s.minimumScale; + if ( minScale > 0 && ( rendererScale > minScale || qgsDoubleNear( rendererScale, minScale, 1E-8 ) ) ) + { + return QSizeF(); + } + } + QSizeF size = diagramSize( feature, c ); if ( size.isValid() ) { @@ -563,22 +613,26 @@ void QgsDiagramRenderer::_readXml( const QDomElement &elem, const QgsReadWriteCo Q_UNUSED( context ) mDiagram.reset(); const QString diagramType = elem.attribute( QStringLiteral( "diagramType" ) ); - if ( diagramType == QLatin1String( "Pie" ) ) + if ( diagramType == QgsPieDiagram::DIAGRAM_NAME_PIE ) { mDiagram.reset( new QgsPieDiagram() ); } - else if ( diagramType == QLatin1String( "Text" ) ) + else if ( diagramType == QgsTextDiagram::DIAGRAM_NAME_TEXT ) { mDiagram.reset( new QgsTextDiagram() ); } - else if ( diagramType == QLatin1String( "Histogram" ) ) + else if ( diagramType == QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM ) { mDiagram.reset( new QgsHistogramDiagram() ); } - else if ( diagramType == QLatin1String( "Stacked" ) ) + else if ( diagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ) { mDiagram.reset( new QgsStackedBarDiagram() ); } + else if ( diagramType == QgsStackedDiagram::DIAGRAM_NAME_STACKED ) + { + mDiagram.reset( new QgsStackedDiagram() ); + } else { // unknown diagram type -- default to histograms @@ -643,7 +697,6 @@ void QgsSingleCategoryDiagramRenderer::writeXml( QDomElement &layerElem, QDomDoc layerElem.appendChild( rendererElem ); } - QgsLinearlyInterpolatedDiagramRenderer::QgsLinearlyInterpolatedDiagramRenderer() { mInterpolationSettings.classificationAttributeIsExpression = false; @@ -804,6 +857,273 @@ void QgsLinearlyInterpolatedDiagramRenderer::writeXml( QDomElement &layerElem, Q layerElem.appendChild( rendererElem ); } +QgsStackedDiagramRenderer *QgsStackedDiagramRenderer::clone() const +{ + return new QgsStackedDiagramRenderer( *this ); +} + +QSizeF QgsStackedDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const +{ + QSizeF stackedSize( 0, 0 ); + int enabledDiagramCount = 0; // We'll add spacing only for enabled subDiagrams + + // Iterate renderers. For each renderer, get the diagram + // size for the feature and add it to the total size + // accounting for stacked diagram defined spacing + for ( const auto &subRenderer : std::as_const( mDiagramRenderers ) ) + { + QSizeF size = subRenderer->sizeMapUnits( feature, c ); + + if ( size.isValid() ) + { + enabledDiagramCount++; + switch ( mSettings.stackedDiagramMode ) + { + case QgsDiagramSettings::Horizontal: + stackedSize.setWidth( stackedSize.width() + size.width() ); + stackedSize.setHeight( std::max( stackedSize.height(), size.height() ) ); + break; + + case QgsDiagramSettings::Vertical: + stackedSize.setWidth( std::max( stackedSize.width(), size.width() ) ); + stackedSize.setHeight( stackedSize.height() + size.height() ); + break; + } + } + } + + if ( stackedSize.isValid() ) + { + const double spacing = c.convertToMapUnits( mSettings.stackedDiagramSpacing(), mSettings.stackedDiagramSpacingUnit(), mSettings.stackedDiagramSpacingMapUnitScale() ); + + switch ( mSettings.stackedDiagramMode ) + { + case QgsDiagramSettings::Horizontal: + stackedSize.scale( stackedSize.width() + spacing * ( enabledDiagramCount - 1 ), stackedSize.height(), Qt::IgnoreAspectRatio ); + break; + + case QgsDiagramSettings::Vertical: + stackedSize.scale( stackedSize.width(), stackedSize.height() + spacing * ( enabledDiagramCount - 1 ), Qt::IgnoreAspectRatio ); + break; + } + } + return stackedSize; +} + +void QgsStackedDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const +{ + if ( !mDiagram ) + { + return; + } + + QPointF newPos = pos; // Each subdiagram will have its own newPos + + // Get subrenderers sorted by mode (vertical diagrams are returned backwards) + const QList< QgsDiagramRenderer * > stackedRenderers = renderers( true ); + + for ( const auto &stackedRenderer : stackedRenderers ) + { + if ( stackedRenderer->rendererName() == QStringLiteral( "Stacked" ) ) + { + // Nested stacked diagrams will use this recursion + stackedRenderer->renderDiagram( feature, c, newPos, properties ); + continue; + } + + QgsDiagramSettings s; + if ( !stackedRenderer->diagramSettings( feature, c, s ) ) + { + continue; + } + + if ( !s.enabled ) + { + continue; + } + + if ( s.scaleBasedVisibility ) + { + // Note: scale might be a non-round number, so compare with qgsDoubleNear + const double rendererScale = c.rendererScale(); + + // maxScale is inclusive ( (< && !=) --> < --> no diagram ) + double maxScale = s.maximumScale; + if ( maxScale > 0 && ( rendererScale < maxScale && !qgsDoubleNear( rendererScale, maxScale, 1E-8 ) ) ) + { + continue; + } + + // minScale is exclusive ( >= --> no diagram) + double minScale = s.minimumScale; + if ( minScale > 0 && ( rendererScale > minScale || qgsDoubleNear( rendererScale, minScale, 1E-8 ) ) ) + { + continue; + } + } + + if ( properties.hasActiveProperties() ) + { + c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.backgroundColor ) ); + s.backgroundColor = properties.valueAsColor( QgsDiagramLayerSettings::Property::BackgroundColor, c.expressionContext(), s.backgroundColor ); + c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.penColor ) ); + s.penColor = properties.valueAsColor( QgsDiagramLayerSettings::Property::StrokeColor, c.expressionContext(), s.penColor ); + c.expressionContext().setOriginalValueVariable( s.penWidth ); + s.penWidth = properties.valueAsDouble( QgsDiagramLayerSettings::Property::StrokeWidth, c.expressionContext(), s.penWidth ); + c.expressionContext().setOriginalValueVariable( s.rotationOffset ); + s.rotationOffset = properties.valueAsDouble( QgsDiagramLayerSettings::Property::StartAngle, c.expressionContext(), s.rotationOffset ); + } + + QgsPaintEffect *effect = s.paintEffect(); + std::unique_ptr< QgsEffectPainter > effectPainter; + if ( effect && effect->enabled() ) + { + effectPainter = std::make_unique< QgsEffectPainter >( c, effect ); + } + + stackedRenderer->diagram()->renderDiagram( feature, c, s, newPos ); + QgsStackedDiagram *stackedDiagram = dynamic_cast< QgsStackedDiagram *>( mDiagram.get() ); + stackedDiagram->subDiagramPosition( newPos, c, mSettings, s ); + } +} + +bool QgsStackedDiagramRenderer::diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const +{ + Q_UNUSED( feature ) + Q_UNUSED( c ) + Q_UNUSED( s ) + return false; +} + +QSizeF QgsStackedDiagramRenderer::diagramSize( const QgsFeature &feature, const QgsRenderContext &c ) const +{ + Q_UNUSED( feature ) + Q_UNUSED( c ) + return QSizeF( 0, 0 ); +} + +QList QgsStackedDiagramRenderer::diagramSettings() const +{ + QList settingsList; + settingsList.push_back( mSettings ); + return settingsList; +} + +QList QgsStackedDiagramRenderer::diagramAttributes() const +{ + return mSettings.categoryAttributes; +} + +QList< QgsLayerTreeModelLegendNode * > QgsStackedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const +{ + QList< QgsLayerTreeModelLegendNode * > nodes; + for ( const auto &renderer : std::as_const( mDiagramRenderers ) ) + { + nodes << renderer->legendItems( nodeLayer ); + } + + return nodes; +} + +QList< QgsDiagramRenderer * > QgsStackedDiagramRenderer::renderers( bool sortByDiagramMode ) const +{ + QList< QgsDiagramRenderer * > renderers = mDiagramRenderers; + + if ( sortByDiagramMode && mSettings.stackedDiagramMode == QgsDiagramSettings::Vertical ) + { + // We draw vertical diagrams backwards, so + // we return the subrenderers in reverse order + std::reverse( renderers.begin(), renderers.end() ); + } + return renderers; +} + +void QgsStackedDiagramRenderer::addRenderer( QgsDiagramRenderer *renderer ) +{ + if ( renderer ) + { + mDiagramRenderers.append( renderer ); + } +} + +const QgsDiagramRenderer *QgsStackedDiagramRenderer::renderer( const int index ) const +{ + if ( index >= 0 && index < mDiagramRenderers.count() ) + { + return mDiagramRenderers.at( index ); + } + + return nullptr; +} + +void QgsStackedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context ) +{ + const QDomElement categoryElem = elem.firstChildElement( QStringLiteral( "DiagramCategory" ) ); + if ( categoryElem.isNull() ) + { + return; + } + + mSettings.readXml( categoryElem, context ); + _readXml( elem, context ); + _readXmlSubRenderers( elem, context ); +} + +void QgsStackedDiagramRenderer::_readXmlSubRenderers( const QDomElement &elem, const QgsReadWriteContext &context ) +{ + const QDomElement subRenderersElem = elem.firstChildElement( QStringLiteral( "DiagramRenderers" ) ); + + if ( !subRenderersElem.isNull() ) + { + const QDomNodeList childRendererList = subRenderersElem.childNodes(); + + for ( int i = 0; i < childRendererList.size(); i++ ) + { + const QDomElement subRendererElem = childRendererList.at( i ).toElement(); + + if ( subRendererElem.nodeName() == QStringLiteral( "SingleCategoryDiagramRenderer" ) ) + { + std::unique_ptr< QgsSingleCategoryDiagramRenderer > singleCatDiagramRenderer = std::make_unique< QgsSingleCategoryDiagramRenderer >(); + singleCatDiagramRenderer->readXml( subRendererElem, context ); + addRenderer( singleCatDiagramRenderer.release() ); + } + else if ( subRendererElem.nodeName() == QStringLiteral( "LinearlyInterpolatedDiagramRenderer" ) ) + { + std::unique_ptr< QgsLinearlyInterpolatedDiagramRenderer > linearDiagramRenderer = std::make_unique< QgsLinearlyInterpolatedDiagramRenderer >(); + linearDiagramRenderer->readXml( subRendererElem, context ); + addRenderer( linearDiagramRenderer.release() ); + } + else if ( subRendererElem.nodeName() == QStringLiteral( "StackedDiagramRenderer" ) ) + { + std::unique_ptr< QgsStackedDiagramRenderer > stackedDiagramRenderer = std::make_unique< QgsStackedDiagramRenderer >(); + stackedDiagramRenderer->readXml( subRendererElem, context ); + addRenderer( stackedDiagramRenderer.release() ); + } + } + } +} + +void QgsStackedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const +{ + QDomElement rendererElem = doc.createElement( QStringLiteral( "StackedDiagramRenderer" ) ); + mSettings.writeXml( rendererElem, doc, context ); + _writeXml( rendererElem, doc, context ); + _writeXmlSubRenderers( rendererElem, doc, context ); + layerElem.appendChild( rendererElem ); +} + +void QgsStackedDiagramRenderer::_writeXmlSubRenderers( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const +{ + QDomElement renderersElem = doc.createElement( QStringLiteral( "DiagramRenderers" ) ); + + // Iterate sub renderers and write their settings to a DOM object + for ( int i = 0; i < mDiagramRenderers.count(); i++ ) + { + mDiagramRenderers.at( i )->writeXml( renderersElem, doc, context ); + } + rendererElem.appendChild( renderersElem ); +} + QList< QgsLayerTreeModelLegendNode * > QgsDiagramSettings::legendItems( QgsLayerTreeLayer *nodeLayer ) const { QList< QgsLayerTreeModelLegendNode * > list; @@ -872,6 +1192,7 @@ QgsDiagramSettings::QgsDiagramSettings( const QgsDiagramSettings &other ) , penWidth( other.penWidth ) , labelPlacementMethod( other.labelPlacementMethod ) , diagramOrientation( other.diagramOrientation ) + , stackedDiagramMode( other.stackedDiagramMode ) , barWidth( other.barWidth ) , opacity( other.opacity ) , scaleByArea( other.scaleByArea ) @@ -883,6 +1204,9 @@ QgsDiagramSettings::QgsDiagramSettings( const QgsDiagramSettings &other ) , mSpacing( other.mSpacing ) , mSpacingUnit( other.mSpacingUnit ) , mSpacingMapUnitScale( other.mSpacingMapUnitScale ) + , mStackedDiagramSpacing( other.mStackedDiagramSpacing ) + , mStackedDiagramSpacingUnit( other.mStackedDiagramSpacingUnit ) + , mStackedDiagramSpacingMapUnitScale( other.mStackedDiagramSpacingMapUnitScale ) , mDirection( other.mDirection ) , mShowAxis( other.mShowAxis ) , mAxisLineSymbol( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr ) @@ -908,6 +1232,7 @@ QgsDiagramSettings &QgsDiagramSettings::operator=( const QgsDiagramSettings &oth penWidth = other.penWidth; labelPlacementMethod = other.labelPlacementMethod; diagramOrientation = other.diagramOrientation; + stackedDiagramMode = other.stackedDiagramMode; barWidth = other.barWidth; opacity = other.opacity; scaleByArea = other.scaleByArea; @@ -919,6 +1244,9 @@ QgsDiagramSettings &QgsDiagramSettings::operator=( const QgsDiagramSettings &oth mSpacing = other.mSpacing; mSpacingUnit = other.mSpacingUnit; mSpacingMapUnitScale = other.mSpacingMapUnitScale; + mStackedDiagramSpacing = other.mStackedDiagramSpacing; + mStackedDiagramSpacingUnit = other.mStackedDiagramSpacingUnit; + mStackedDiagramSpacingMapUnitScale = other.mStackedDiagramSpacingMapUnitScale; mDirection = other.mDirection; mAxisLineSymbol.reset( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr ); mShowAxis = other.mShowAxis; @@ -944,7 +1272,7 @@ QList< QgsLayerTreeModelLegendNode * > QgsDiagramRenderer::legendItems( QgsLayer QList< QgsLayerTreeModelLegendNode * > QgsSingleCategoryDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const { QList< QgsLayerTreeModelLegendNode * > nodes; - if ( mShowAttributeLegend ) + if ( mShowAttributeLegend && mSettings.enabled ) nodes = mSettings.legendItems( nodeLayer ); return nodes; @@ -953,6 +1281,11 @@ QList< QgsLayerTreeModelLegendNode * > QgsSingleCategoryDiagramRenderer::legendI QList< QgsLayerTreeModelLegendNode * > QgsLinearlyInterpolatedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const { QList< QgsLayerTreeModelLegendNode * > nodes; + if ( !mSettings.enabled ) + { + return nodes; + } + if ( mShowAttributeLegend ) nodes = mSettings.legendItems( nodeLayer ); diff --git a/src/core/qgsdiagramrenderer.h b/src/core/qgsdiagramrenderer.h index 04538e57c235..6f9bdbb04dba 100644 --- a/src/core/qgsdiagramrenderer.h +++ b/src/core/qgsdiagramrenderer.h @@ -103,6 +103,16 @@ class CORE_EXPORT QgsDiagramLayerSettings }; // *INDENT-ON* + /** + * Diagram type + * \since QGIS 3.40 + */ + enum DiagramType + { + Single, + Stacked + }; + /** * Returns the diagram property definitions. */ @@ -378,6 +388,16 @@ class CORE_EXPORT QgsDiagramSettings Counterclockwise, //!< Counter-clockwise orientation }; + /** + * Orientation of the stacked diagrams + * \since QGIS 3.40 + */ + enum StackedDiagramMode + { + Horizontal, + Vertical + }; + QgsDiagramSettings(); ~QgsDiagramSettings(); QgsDiagramSettings( const QgsDiagramSettings &other ); @@ -416,6 +436,7 @@ class CORE_EXPORT QgsDiagramSettings double penWidth = 0.0; LabelPlacementMethod labelPlacementMethod = QgsDiagramSettings::Height; DiagramOrientation diagramOrientation = QgsDiagramSettings::Up; + StackedDiagramMode stackedDiagramMode = QgsDiagramSettings::Horizontal; double barWidth = 5.0; //! Opacity, from 0 (transparent) to 1.0 (opaque) @@ -514,6 +535,72 @@ class CORE_EXPORT QgsDiagramSettings */ const QgsMapUnitScale &spacingMapUnitScale() const { return mSpacingMapUnitScale; } + /** + * Returns the spacing between subdiagrams in a stacked diagram. + * + * Spacing units can be retrieved by calling stackedDiagramSpacingUnit(). + * + * \see setStackedDiagramSpacing() + * \see stackedDiagramSpacingUnit() + * \see stackedDiagramSpacingMapUnitScale() + * + * \since QGIS 3.40 + */ + double stackedDiagramSpacing() const { return mStackedDiagramSpacing; } + + /** + * Sets the \a spacing between subdiagrams in a stacked diagram. + * + * Spacing units are set via setStackedDiagramSpacingUnit(). + * + * \see stackedDiagramSpacing() + * \see setStackedDiagramSpacingUnit() + * \see setStackedDiagramSpacingMapUnitScale() + * + * \since QGIS 3.40 + */ + void setStackedDiagramSpacing( double spacing ) { mStackedDiagramSpacing = spacing; } + + /** + * Sets the \a unit for the spacing between subdiagrams in a stacked diagram. + * \see stackedDiagramSpacingUnit() + * \see setStackedDiagramSpacing() + * \see setStackedDiagramSpacingMapUnitScale() + * + * \since QGIS 3.40 + */ + void setStackedDiagramSpacingUnit( Qgis::RenderUnit unit ) { mStackedDiagramSpacingUnit = unit; } + + /** + * Returns the units for the spacing between subdiagrams in a stacked diagram. + * \see setStackedDiagramSpacingUnit() + * \see stackedDiagramSpacing() + * \see stackedDiagramSpacingMapUnitScale() + * + * \since QGIS 3.40 + */ + Qgis::RenderUnit stackedDiagramSpacingUnit() const { return mStackedDiagramSpacingUnit; } + + /** + * Sets the map unit \a scale for the spacing between subdiagrams in a stacked diagram. + * \see stackedDiagramSpacingMapUnitScale() + * \see setStackedDiagramSpacing() + * \see setStackedDiagramSpacingUnit() + * + * \since QGIS 3.40 + */ + void setStackedDiagramSpacingMapUnitScale( const QgsMapUnitScale &scale ) { mStackedDiagramSpacingMapUnitScale = scale; } + + /** + * Returns the map unit scale for the spacing between subdiagrams in a stacked diagram. + * \see setStackedDiagramSpacingMapUnitScale(); + * \see stackedDiagramSpacing() + * \see stackedDiagramSpacingUnit() + * + * \since QGIS 3.40 + */ + const QgsMapUnitScale &stackedDiagramSpacingMapUnitScale() const { return mStackedDiagramSpacingMapUnitScale; } + /** * Returns the chart's angular direction. * @@ -608,6 +695,11 @@ class CORE_EXPORT QgsDiagramSettings double mSpacing = 0; Qgis::RenderUnit mSpacingUnit = Qgis::RenderUnit::Millimeters; QgsMapUnitScale mSpacingMapUnitScale; + + double mStackedDiagramSpacing = 0; + Qgis::RenderUnit mStackedDiagramSpacingUnit = Qgis::RenderUnit::Millimeters; + QgsMapUnitScale mStackedDiagramSpacingMapUnitScale; + Direction mDirection = Counterclockwise; bool mShowAxis = false; @@ -652,6 +744,8 @@ class CORE_EXPORT QgsDiagramRenderer sipType = sipType_QgsSingleCategoryDiagramRenderer; else if ( sipCpp->rendererName() == QLatin1String( "LinearlyInterpolated" ) ) sipType = sipType_QgsLinearlyInterpolatedDiagramRenderer; + else if ( sipCpp->rendererName() == QLatin1String( "Stacked" ) ) + sipType = sipType_QgsStackedDiagramRenderer; else sipType = NULL; SIP_END @@ -684,7 +778,7 @@ class CORE_EXPORT QgsDiagramRenderer /** * Renders the diagram for a specified feature at a specific position in the passed render context. */ - void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const; void setDiagram( QgsDiagram *d SIP_TRANSFER ); QgsDiagram *diagram() const { return mDiagram.get(); } @@ -737,8 +831,12 @@ class CORE_EXPORT QgsDiagramRenderer */ virtual bool diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const = 0; - //! Returns size of the diagram (in painter units) or an invalid size in case of error - virtual QSizeF diagramSize( const QgsFeature &features, const QgsRenderContext &c ) const = 0; + /** + * Returns size of the diagram (in painter units) or an invalid size in case of error + * \param feature the feature + * \param c render context + */ + virtual QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c ) const = 0; //! Converts size from mm to map units void convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const; @@ -765,6 +863,8 @@ class CORE_EXPORT QgsDiagramRenderer //! Whether to show an attribute legend for the diagrams bool mShowAttributeLegend = true; + + friend class QgsStackedDiagramRenderer; }; /** @@ -886,4 +986,85 @@ class CORE_EXPORT QgsLinearlyInterpolatedDiagramRenderer : public QgsDiagramRend QgsDataDefinedSizeLegend *mDataDefinedSizeLegend = nullptr; }; +/** + * \ingroup core + * \class QgsStackedDiagramRenderer + * Renders diagrams using mixed diagram render types. The size of + * the rendered diagram is given by a combination of subrenderers. + * + * \since QGIS 3.40 + */ +class CORE_EXPORT QgsStackedDiagramRenderer : public QgsDiagramRenderer +{ + public: + QgsStackedDiagramRenderer() = default; + + QgsStackedDiagramRenderer *clone() const override SIP_FACTORY; + + //! Returns size of the diagram for a feature in map units. Returns an invalid QSizeF in case of error + virtual QSizeF sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const override; + + /** + * Renders the diagram for a specified feature at a specific position in the + * passed render context, taking all renderers and their own diagrams into account. + * Diagram rendering is delegated to renderer's diagram. + */ + virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties = QgsPropertyCollection() ) const override; + + //! Returns list with all diagram settings in the renderer + QList diagramSettings() const override; + + void setDiagramSettings( const QgsDiagramSettings &s ) { mSettings = s; } + + QList diagramAttributes() const override; + + QString rendererName() const override { return QStringLiteral( "Stacked" ); } + + void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override; + void writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const override; + + /** + * Reads stacked renderers state from a DOM element. + * \see _writeXmlSubRenderers() + */ + void _readXmlSubRenderers( const QDomElement &elem, const QgsReadWriteContext &context ); + + /** + * Writes stacked renderers state to a DOM element. + * \see _readXmlSubRenderers() + */ + void _writeXmlSubRenderers( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const; + + QList< QgsLayerTreeModelLegendNode * > legendItems( QgsLayerTreeLayer *nodeLayer ) const override SIP_FACTORY; + + /** + * Returns an ordered list with the renderers of the stacked renderer object. + * @param sortByDiagramMode If true, the list is returned backwards for vertical orientation. + */ + QList< QgsDiagramRenderer * > renderers( bool sortByDiagramMode = false ) const; + + /** + * Adds a renderer to the stacked renderer object. + * @param renderer diagram renderer to be added to the stacked renderer + * Renderers added first will render their diagrams first, i.e., more to + * the left (horizontal mode) or more to the top (vertical mode). + */ + void addRenderer( QgsDiagramRenderer *renderer ); + + /** + * Returns the renderer at the given \a index. + * @param index index of the disired renderer in the stacked renderer + */ + const QgsDiagramRenderer *renderer( const int index ) const; + + protected: + bool diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const override; + QSizeF diagramSize( const QgsFeature &, const QgsRenderContext &c ) const override; + + private: + QgsDiagramSettings mSettings; + QList< QgsDiagramRenderer * > mDiagramRenderers; +}; + + #endif // QGSDIAGRAMRENDERER_H diff --git a/src/core/vector/qgsvectorlayer.cpp b/src/core/vector/qgsvectorlayer.cpp index e37ffe6a8483..7810f5273e64 100644 --- a/src/core/vector/qgsvectorlayer.cpp +++ b/src/core/vector/qgsvectorlayer.cpp @@ -2926,6 +2926,12 @@ bool QgsVectorLayer::readStyle( const QDomNode &node, QString &errorMessage, mDiagramRenderer = new QgsLinearlyInterpolatedDiagramRenderer(); mDiagramRenderer->readXml( linearDiagramElem, context ); } + QDomElement stackedDiagramElem = node.firstChildElement( QStringLiteral( "StackedDiagramRenderer" ) ); + if ( !stackedDiagramElem.isNull() ) + { + mDiagramRenderer = new QgsStackedDiagramRenderer(); + mDiagramRenderer->readXml( stackedDiagramElem, context ); + } if ( mDiagramRenderer ) { diff --git a/src/core/vector/qgsvectorlayerdiagramprovider.cpp b/src/core/vector/qgsvectorlayerdiagramprovider.cpp index dc54a47bb7d8..a23d2c11ff58 100644 --- a/src/core/vector/qgsvectorlayerdiagramprovider.cpp +++ b/src/core/vector/qgsvectorlayerdiagramprovider.cpp @@ -194,14 +194,19 @@ QgsLabelFeature *QgsVectorLayerDiagramProvider::registerDiagram( const QgsFeatur QList settingList = dr->diagramSettings(); if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility ) { + // Note: scale might be a non-round number, so compare with qgsDoubleNear + const double rendererScale = context.rendererScale(); + + // maxScale is inclusive ( (< && !=) --> < --> no diagram ) double maxScale = settingList.at( 0 ).maximumScale; - if ( maxScale > 0 && context.rendererScale() < maxScale ) + if ( maxScale > 0 && ( rendererScale < maxScale && !qgsDoubleNear( rendererScale, maxScale, 1E-8 ) ) ) { return nullptr; } + // minScale is exclusive ( >= --> no diagram) double minScale = settingList.at( 0 ).minimumScale; - if ( minScale > 0 && context.rendererScale() > minScale ) + if ( minScale > 0 && ( rendererScale > minScale || qgsDoubleNear( rendererScale, minScale, 1E-8 ) ) ) { return nullptr; } diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 017f978a5906..ddaeede527be 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -35,9 +35,11 @@ set(QGIS_GUI_SRCS vector/qgsattributesforminitcode.cpp vector/qgsattributesformproperties.cpp vector/qgsdiagramproperties.cpp + vector/qgsdiagramwidget.cpp vector/qgsfieldcalculator.cpp vector/qgsjoindialog.cpp vector/qgssourcefieldsproperties.cpp + vector/qgsstackeddiagramproperties.cpp vector/qgsvectorlayerlegendwidget.cpp vector/qgsvectorlayerproperties.cpp vector/qgswmsdimensiondialog.cpp @@ -1496,9 +1498,11 @@ set(QGIS_GUI_HDRS vector/qgsattributesforminitcode.h vector/qgsattributesformproperties.h vector/qgsdiagramproperties.h + vector/qgsdiagramwidget.h vector/qgsfieldcalculator.h vector/qgsjoindialog.h vector/qgssourcefieldsproperties.h + vector/qgsstackeddiagramproperties.h vector/qgsvectorlayerlegendwidget.h vector/qgsvectorlayerproperties.h vector/qgswmsdimensiondialog.h diff --git a/src/gui/qgstextformatwidget.cpp b/src/gui/qgstextformatwidget.cpp index d18c1e9225f1..1eb0d1da83ee 100644 --- a/src/gui/qgstextformatwidget.cpp +++ b/src/gui/qgstextformatwidget.cpp @@ -509,7 +509,7 @@ void QgsTextFormatWidget::initWidget() << mTabDistanceUnitWidget << mTabStopDistanceSpin; - connectValueChanged( widgets, SLOT( updatePreview() ) ); + connectValueChanged( widgets ); connect( mQuadrantBtnGrp, qOverload< QAbstractButton * >( &QButtonGroup::buttonClicked ), this, &QgsTextFormatWidget::updatePreview ); @@ -659,70 +659,70 @@ void QgsTextFormatWidget::setDockMode( bool enabled ) mDockMode = enabled; } -void QgsTextFormatWidget::connectValueChanged( const QList &widgets, const char *slot ) +void QgsTextFormatWidget::connectValueChanged( const QList &widgets ) { const auto constWidgets = widgets; for ( QWidget *widget : constWidgets ) { if ( QgsSymbolButton *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( changed() ), this, slot ); + connect( w, &QgsSymbolButton::changed, this, &QgsTextFormatWidget::updatePreview ); } else if ( QgsFieldExpressionWidget *w = qobject_cast< QgsFieldExpressionWidget *>( widget ) ) { - connect( w, SIGNAL( fieldChanged( QString ) ), this, slot ); + connect( w, qOverload< const QString & >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsTextFormatWidget::updatePreview ); } else if ( QgsOpacityWidget *w = qobject_cast< QgsOpacityWidget *>( widget ) ) { - connect( w, SIGNAL( opacityChanged( double ) ), this, slot ); + connect( w, &QgsOpacityWidget::opacityChanged, this, &QgsTextFormatWidget::updatePreview ); } else if ( QgsScaleWidget *w = qobject_cast< QgsScaleWidget *>( widget ) ) { - connect( w, SIGNAL( scaleChanged( double ) ), this, slot ); + connect( w, &QgsScaleWidget::scaleChanged, this, &QgsTextFormatWidget::updatePreview ); } else if ( QgsUnitSelectionWidget *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( changed() ), this, slot ); + connect( w, &QgsUnitSelectionWidget::changed, this, &QgsTextFormatWidget::updatePreview ); } else if ( QComboBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( currentIndexChanged( int ) ), this, slot ); + connect( w, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::updatePreview ); } else if ( QSpinBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( valueChanged( int ) ), this, slot ); + connect( w, qOverload< int >( &QSpinBox::valueChanged ), this, &QgsTextFormatWidget::updatePreview ); } else if ( QDoubleSpinBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( valueChanged( double ) ), this, slot ); + connect( w, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, &QgsTextFormatWidget::updatePreview ); } else if ( QgsColorButton *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( colorChanged( QColor ) ), this, slot ); + connect( w, &QgsColorButton::colorChanged, this, &QgsTextFormatWidget::updatePreview ); } else if ( QCheckBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( toggled( bool ) ), this, slot ); + connect( w, &QCheckBox::toggled, this, &QgsTextFormatWidget::updatePreview ); } else if ( QRadioButton *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( toggled( bool ) ), this, slot ); + connect( w, &QRadioButton::toggled, this, &QgsTextFormatWidget::updatePreview ); } else if ( QLineEdit *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( textEdited( QString ) ), this, slot ); + connect( w, &QLineEdit::textEdited, this, &QgsTextFormatWidget::updatePreview ); } else if ( QSlider *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( valueChanged( int ) ), this, slot ); + connect( w, &QSlider::valueChanged, this, &QgsTextFormatWidget::updatePreview ); } else if ( QGroupBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( toggled( bool ) ), this, slot ); + connect( w, &QGroupBox::toggled, this, &QgsTextFormatWidget::updatePreview ); } else if ( QgsCodeEditorExpression *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( textChanged() ), this, slot ); + connect( w, &QgsCodeEditorExpression::textChanged, this, &QgsTextFormatWidget::updatePreview ); } else { diff --git a/src/gui/qgstextformatwidget.h b/src/gui/qgstextformatwidget.h index 3ab65be5a570..897b68945177 100644 --- a/src/gui/qgstextformatwidget.h +++ b/src/gui/qgstextformatwidget.h @@ -259,7 +259,7 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte void populateFontCapitalsComboBox(); void populateFontStyleComboBox(); void updateFont( const QFont &font ); - void connectValueChanged( const QList &widgets, const char *slot ); + void connectValueChanged( const QList &widgets ); void registerDataDefinedButton( QgsPropertyOverrideButton *button, QgsPalLayerSettings::Property key ); diff --git a/src/gui/symbology/qgsrendererpropertiesdialog.cpp b/src/gui/symbology/qgsrendererpropertiesdialog.cpp index 69d00fad8253..3ec33caf9b68 100644 --- a/src/gui/symbology/qgsrendererpropertiesdialog.cpp +++ b/src/gui/symbology/qgsrendererpropertiesdialog.cpp @@ -133,54 +133,54 @@ QgsRendererPropertiesDialog::QgsRendererPropertiesDialog( QgsVectorLayer *layer, << mFeatureBlendComboBox << mEffectWidget; - connectValueChanged( widgets, SIGNAL( widgetChanged() ) ); + connectValueChanged( widgets ); connect( mEffectWidget, &QgsPanelWidget::showPanel, this, &QgsRendererPropertiesDialog::openPanel ); } -void QgsRendererPropertiesDialog::connectValueChanged( const QList &widgets, const char *slot ) +void QgsRendererPropertiesDialog::connectValueChanged( const QList &widgets ) { for ( QWidget *widget : widgets ) { if ( QgsPropertyOverrideButton *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( changed ), this, slot ); + connect( w, &QgsPropertyOverrideButton::changed, this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QgsFieldExpressionWidget *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( fieldChanged( QString ) ), this, slot ); + connect( w, qOverload< const QString & >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QgsOpacityWidget *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( opacityChanged( double ) ), this, slot ); + connect( w, &QgsOpacityWidget::opacityChanged, this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QComboBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( currentIndexChanged( int ) ), this, slot ); + connect( w, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QSpinBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( valueChanged( int ) ), this, slot ); + connect( w, qOverload< int >( &QSpinBox::valueChanged ), this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QDoubleSpinBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( valueChanged( double ) ), this, slot ); + connect( w, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QgsColorButton *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( colorChanged( QColor ) ), this, slot ); + connect( w, &QgsColorButton::colorChanged, this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QCheckBox *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( toggled( bool ) ), this, slot ); + connect( w, &QCheckBox::toggled, this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QLineEdit *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( textEdited( QString ) ), this, slot ); - connect( w, SIGNAL( textChanged( QString ) ), this, slot ); + connect( w, &QLineEdit::textEdited, this, &QgsRendererPropertiesDialog::widgetChanged ); + connect( w, &QLineEdit::textChanged, this, &QgsRendererPropertiesDialog::widgetChanged ); } else if ( QgsEffectStackCompactWidget *w = qobject_cast( widget ) ) { - connect( w, SIGNAL( changed() ), this, slot ); + connect( w, &QgsEffectStackCompactWidget::changed, this, &QgsRendererPropertiesDialog::widgetChanged ); } } } diff --git a/src/gui/symbology/qgsrendererpropertiesdialog.h b/src/gui/symbology/qgsrendererpropertiesdialog.h index 86ac19d55e7b..fc61531212f5 100644 --- a/src/gui/symbology/qgsrendererpropertiesdialog.h +++ b/src/gui/symbology/qgsrendererpropertiesdialog.h @@ -135,14 +135,13 @@ class GUI_EXPORT QgsRendererPropertiesDialog : public QDialog, private Ui::QgsRe protected: /** - * Connect the given slot to the value changed event for the set of widgets - * Each widget is checked for type and the common type of signal is connected - * to the slot. + * Connect the value changed event for the set of widgets to + * widgetChanged signal. Each widget is checked for type and + * the common type of signal is connected to widgetChanged. * * \param widgets The list of widgets to check. - * \param slot The slot to connect to the signals. */ - void connectValueChanged( const QList &widgets, const char *slot ); + void connectValueChanged( const QList &widgets ); // Reimplements dialog keyPress event so we can ignore it void keyPressEvent( QKeyEvent *event ) override; diff --git a/src/gui/vector/qgsdiagramproperties.cpp b/src/gui/vector/qgsdiagramproperties.cpp index 039d1e55b69e..a89a9523fe05 100644 --- a/src/gui/vector/qgsdiagramproperties.cpp +++ b/src/gui/vector/qgsdiagramproperties.cpp @@ -19,6 +19,7 @@ #include "diagram/qgspiediagram.h" #include "diagram/qgstextdiagram.h" #include "diagram/qgsstackedbardiagram.h" +#include "diagram/qgsstackeddiagram.h" #include "qgsproject.h" #include "qgsapplication.h" @@ -26,7 +27,6 @@ #include "qgsdatadefinedsizelegendwidget.h" #include "qgsdiagramproperties.h" #include "qgsdiagramrenderer.h" -#include "qgslabelengineconfigdialog.h" #include "qgsfeatureiterator.h" #include "qgssymbolselectordialog.h" #include "qgsmapcanvas.h" @@ -59,10 +59,10 @@ QgsExpressionContext QgsDiagramProperties::createExpressionContext() const } QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas ) - : QWidget( parent ) + : QgsPanelWidget( parent ) + , mLayer( layer ) , mMapCanvas( canvas ) { - mLayer = layer; if ( !layer ) { return; @@ -75,12 +75,15 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare connect( mFindMaximumValueButton, &QPushButton::clicked, this, &QgsDiagramProperties::mFindMaximumValueButton_clicked ); connect( mRemoveCategoryPushButton, &QPushButton::clicked, this, &QgsDiagramProperties::mRemoveCategoryPushButton_clicked ); connect( mDiagramAttributesTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsDiagramProperties::mDiagramAttributesTreeWidget_itemDoubleClicked ); - connect( mEngineSettingsButton, &QPushButton::clicked, this, &QgsDiagramProperties::mEngineSettingsButton_clicked ); connect( mDiagramStackedWidget, &QStackedWidget::currentChanged, this, &QgsDiagramProperties::mDiagramStackedWidget_currentChanged ); // get rid of annoying outer focus rect on Mac mDiagramOptionsListWidget->setAttribute( Qt::WA_MacShowFocusRect, false ); + const int iconSize = QgsGuiUtils::scaleIconSize( 20 ); + mOptionsTab->setIconSize( QSize( iconSize, iconSize ) ); + mDiagramOptionsListWidget->setIconSize( QSize( iconSize, iconSize ) ) ; + mBarSpacingSpinBox->setClearValue( 0 ); mBarSpacingUnitComboBox->setUnits( { Qgis::RenderUnit::Millimeters, Qgis::RenderUnit::MetersInMapUnits, @@ -92,16 +95,14 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare mDiagramFontButton->setMode( QgsFontButton::ModeQFont ); mDiagramTypeComboBox->blockSignals( true ); - QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "diagramNone.svg" ) ); - mDiagramTypeComboBox->addItem( icon, tr( "No Diagrams" ), "None" ); - icon = QgsApplication::getThemeIcon( QStringLiteral( "pie-chart.svg" ) ); - mDiagramTypeComboBox->addItem( icon, tr( "Pie Chart" ), DIAGRAM_NAME_PIE ); + QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "pie-chart.svg" ) ); + mDiagramTypeComboBox->addItem( icon, tr( "Pie Chart" ), QgsPieDiagram::DIAGRAM_NAME_PIE ); icon = QgsApplication::getThemeIcon( QStringLiteral( "text.svg" ) ); - mDiagramTypeComboBox->addItem( icon, tr( "Text Diagram" ), DIAGRAM_NAME_TEXT ); + mDiagramTypeComboBox->addItem( icon, tr( "Text Diagram" ), QgsTextDiagram::DIAGRAM_NAME_TEXT ); icon = QgsApplication::getThemeIcon( QStringLiteral( "histogram.svg" ) ); - mDiagramTypeComboBox->addItem( icon, tr( "Histogram" ), DIAGRAM_NAME_HISTOGRAM ); + mDiagramTypeComboBox->addItem( icon, tr( "Histogram" ), QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM ); icon = QgsApplication::getThemeIcon( QStringLiteral( "stacked-bar.svg" ) ); - mDiagramTypeComboBox->addItem( icon, tr( "Stacked Bars" ), DIAGRAM_NAME_STACKED ); + mDiagramTypeComboBox->addItem( icon, tr( "Stacked Bars" ), QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ); mDiagramTypeComboBox->blockSignals( false ); mAxisLineStyleButton->setSymbolType( Qgis::SymbolType::Line ); @@ -147,6 +148,7 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare if ( layerType == Qgis::GeometryType::Unknown || layerType == Qgis::GeometryType::Null ) { mDiagramTypeComboBox->setEnabled( false ); + mOptionsTab->setEnabled( false ); mDiagramFrame->setEnabled( false ); } @@ -227,6 +229,10 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare mDiagramOptionsSplitter->restoreState( settings.value( QStringLiteral( "Windows/Diagrams/OptionsSplitState" ) ).toByteArray() ); mDiagramOptionsListWidget->setCurrentRow( settings.value( QStringLiteral( "Windows/Diagrams/Tab" ), 0 ).toInt() ); + // set correct initial tab to match displayed setting page + whileBlocking( mOptionsTab )->setCurrentIndex( mDiagramStackedWidget->currentIndex() ); + mOptionsTab->tabBar()->setUsesScrollButtons( true ); + // field combo and expression button mSizeFieldExpressionWidget->setLayer( mLayer ); QgsDistanceArea myDa; @@ -248,7 +254,18 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() ); mPaintEffect->setEnabled( false ); - syncToLayer(); + mOrientationLeftButton->setProperty( "direction", QgsDiagramSettings::Left ); + mOrientationRightButton->setProperty( "direction", QgsDiagramSettings::Right ); + mOrientationUpButton->setProperty( "direction", QgsDiagramSettings::Up ); + mOrientationDownButton->setProperty( "direction", QgsDiagramSettings::Down ); + + // Labels to let users know some widgets are not present + // when editing sub diagrams in a stacked diagram. + mDlsLabel_1->hide(); + mDlsLabel_2->hide(); + + insertDefaults(); + mPaintEffectWidget->setPaintEffect( mPaintEffect.get() ); connect( mAddAttributeExpression, &QPushButton::clicked, this, &QgsDiagramProperties::showAddAttributeExpressionDialog ); registerDataDefinedButton( mBackgroundColorDDBtn, QgsDiagramLayerSettings::Property::BackgroundColor ); @@ -265,62 +282,150 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare registerDataDefinedButton( mStartAngleDDBtn, QgsDiagramLayerSettings::Property::StartAngle ); connect( mButtonSizeLegendSettings, &QPushButton::clicked, this, &QgsDiagramProperties::showSizeLegendDialog ); + + QList widgets; + widgets << chkLineAbove; + widgets << chkLineBelow; + widgets << chkLineOn; + widgets << chkLineOrientationDependent; + widgets << mAngleDirectionComboBox; + widgets << mAngleOffsetComboBox; + widgets << mAttributeBasedScalingRadio; + widgets << mAxisLineStyleButton; + widgets << mBackgroundColorButton; + widgets << mBarSpacingSpinBox; + widgets << mBarSpacingUnitComboBox; + widgets << mBarWidthSpinBox; + widgets << mCheckBoxAttributeLegend; + widgets << mDiagramAttributesTreeWidget; + widgets << mDiagramDistanceSpinBox; + widgets << mDiagramFontButton; + widgets << mDiagramPenColorButton; + widgets << mDiagramSizeSpinBox; + widgets << mDiagramLineUnitComboBox; + widgets << mDiagramTypeComboBox; + widgets << mDiagramUnitComboBox; + widgets << mFixedSizeRadio; + widgets << mIncreaseMinimumSizeSpinBox; + widgets << mIncreaseSmallDiagramsGroupBox; + widgets << mLabelPlacementComboBox; + widgets << mMaxValueSpinBox; + widgets << mPaintEffectWidget; + widgets << mPenWidthSpinBox; + widgets << mPrioritySlider; + widgets << mOpacityWidget; + widgets << mOrientationDownButton; + widgets << mOrientationLeftButton; + widgets << mOrientationRightButton; + widgets << mOrientationUpButton; + widgets << mScaleDependencyComboBox; + widgets << mScaleRangeWidget; + widgets << mScaleVisibilityGroupBox; + widgets << mShowAllCheckBox; + widgets << mShowAxisGroupBox; + widgets << mSizeFieldExpressionWidget; + widgets << mSizeSpinBox; + widgets << mZIndexSpinBox; + widgets << radAroundCentroid; + widgets << radAroundLine; + widgets << radAroundPoint; + widgets << radInsidePolygon; + widgets << radOverCentroid; + widgets << radOverLine; + widgets << radOverPoint; + widgets << radPolygonPerimeter; + + connectValueChanged( widgets ); } -void QgsDiagramProperties::syncToLayer() +void QgsDiagramProperties::setDockMode( bool dockMode ) { - mDiagramAttributesTreeWidget->clear(); + QgsPanelWidget::setDockMode( dockMode ); + mOptionsTab->setVisible( dockMode ); + mOptionsTab->setTabToolTip( 0, tr( "Attributes" ) ); + mOptionsTab->setTabToolTip( 1, tr( "Rendering" ) ); + mOptionsTab->setTabToolTip( 2, tr( "Size" ) ); + mOptionsTab->setTabToolTip( 3, tr( "Placement" ) ); + mOptionsTab->setTabToolTip( 4, tr( "Options" ) ); + mOptionsTab->setTabToolTip( 5, tr( "Legend" ) ); + mDiagramOptionsListFrame->setVisible( !dockMode ); +} - const QgsDiagramRenderer *dr = mLayer->diagramRenderer(); - if ( !dr ) //no diagram renderer yet, insert reasonable default +void QgsDiagramProperties::setDiagramType( const QString diagramType ) +{ + mDiagramType = diagramType; + + mDiagramTypeComboBox->setVisible( false ); + mDiagramTypeComboBox->blockSignals( true ); + mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( mDiagramType ) ); + mDiagramTypeComboBox->blockSignals( false ); + + //force a refresh of widget status to match diagram type + mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() ); +} + +void QgsDiagramProperties::insertDefaults() +{ + mFixedSizeRadio->setChecked( true ); + mDiagramUnitComboBox->setUnit( Qgis::RenderUnit::Millimeters ); + mDiagramLineUnitComboBox->setUnit( Qgis::RenderUnit::Millimeters ); + mLabelPlacementComboBox->setCurrentIndex( mLabelPlacementComboBox->findText( tr( "x-height" ) ) ); + mDiagramSizeSpinBox->setEnabled( true ); + mDiagramSizeSpinBox->setValue( 15 ); + mLinearScaleFrame->setEnabled( false ); + mBarWidthSpinBox->setValue( 5 ); + mScaleVisibilityGroupBox->setChecked( mLayer->hasScaleBasedVisibility() ); + mScaleRangeWidget->setScaleRange( mLayer->minimumScale(), mLayer->maximumScale() ); + mShowAllCheckBox->setChecked( true ); + mCheckBoxAttributeLegend->setChecked( true ); + + switch ( mLayer->geometryType() ) { - mDiagramTypeComboBox->blockSignals( true ); - mDiagramTypeComboBox->setCurrentIndex( 0 ); - mDiagramTypeComboBox->blockSignals( false ); - mFixedSizeRadio->setChecked( true ); - mDiagramUnitComboBox->setUnit( Qgis::RenderUnit::Millimeters ); - mDiagramLineUnitComboBox->setUnit( Qgis::RenderUnit::Millimeters ); - mLabelPlacementComboBox->setCurrentIndex( mLabelPlacementComboBox->findText( tr( "x-height" ) ) ); - mDiagramSizeSpinBox->setEnabled( true ); - mDiagramSizeSpinBox->setValue( 15 ); - mLinearScaleFrame->setEnabled( false ); - mIncreaseMinimumSizeSpinBox->setEnabled( false ); - mIncreaseMinimumSizeLabel->setEnabled( false ); - mBarWidthSpinBox->setValue( 5 ); - mScaleVisibilityGroupBox->setChecked( mLayer->hasScaleBasedVisibility() ); - mScaleRangeWidget->setScaleRange( mLayer->minimumScale(), mLayer->maximumScale() ); - mShowAllCheckBox->setChecked( true ); - mCheckBoxAttributeLegend->setChecked( true ); - - switch ( mLayer->geometryType() ) - { - case Qgis::GeometryType::Point: - radAroundPoint->setChecked( true ); - break; + case Qgis::GeometryType::Point: + radAroundPoint->setChecked( true ); + break; - case Qgis::GeometryType::Line: - radAroundLine->setChecked( true ); - chkLineAbove->setChecked( true ); - chkLineBelow->setChecked( false ); - chkLineOn->setChecked( false ); - chkLineOrientationDependent->setChecked( false ); - break; + case Qgis::GeometryType::Line: + radAroundLine->setChecked( true ); + chkLineAbove->setChecked( true ); + chkLineBelow->setChecked( false ); + chkLineOn->setChecked( false ); + chkLineOrientationDependent->setChecked( false ); + break; - case Qgis::GeometryType::Polygon: - radOverCentroid->setChecked( true ); - mDiagramDistanceLabel->setEnabled( false ); - mDiagramDistanceSpinBox->setEnabled( false ); - mDistanceDDBtn->setEnabled( false ); - break; + case Qgis::GeometryType::Polygon: + radOverCentroid->setChecked( true ); + mDiagramDistanceLabel->setEnabled( false ); + mDiagramDistanceSpinBox->setEnabled( false ); + mDistanceDDBtn->setEnabled( false ); + break; - case Qgis::GeometryType::Unknown: - case Qgis::GeometryType::Null: - break; - } - mBackgroundColorButton->setColor( QColor( 255, 255, 255, 255 ) ); - //force a refresh of widget status to match diagram type - mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() ); + case Qgis::GeometryType::Unknown: + case Qgis::GeometryType::Null: + break; + } + mBackgroundColorButton->setColor( QColor( 255, 255, 255, 255 ) ); + mDiagramPenColorButton->setColor( QColor( 0, 0, 0, 255 ) ); + //force a refresh of widget status to match diagram type + mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() ); +} +void QgsDiagramProperties::syncToLayer() +{ + const QgsDiagramRenderer *renderer = mLayer->diagramRenderer(); + syncToRenderer( renderer ); + + const QgsDiagramLayerSettings *layerDls = mLayer->diagramLayerSettings(); + syncToSettings( layerDls ); +} + +void QgsDiagramProperties::syncToRenderer( const QgsDiagramRenderer *dr ) +{ + mDiagramAttributesTreeWidget->clear(); + + if ( !dr ) //no diagram renderer yet, insert reasonable default + { + insertDefaults(); } else // already a diagram renderer present { @@ -337,10 +442,11 @@ void QgsDiagramProperties::syncToLayer() mLinearScaleFrame->setEnabled( mAttributeBasedScalingRadio->isChecked() ); mCheckBoxAttributeLegend->setChecked( dr->attributeLegend() ); - //assume single category or linearly interpolated diagram renderer for now + // Assume single category or linearly interpolated diagram renderer for now. const QList settingList = dr->diagramSettings(); if ( !settingList.isEmpty() ) { + mOptionsTab->setEnabled( settingList.at( 0 ).enabled ); mDiagramFrame->setEnabled( settingList.at( 0 ).enabled ); mDiagramFontButton->setCurrentFont( settingList.at( 0 ).font ); const QSizeF size = settingList.at( 0 ).size; @@ -372,10 +478,6 @@ void QgsDiagramProperties::syncToLayer() mAngleOffsetComboBox->setCurrentIndex( mAngleOffsetComboBox->findData( settingList.at( 0 ).rotationOffset ) ); mAngleDirectionComboBox->setCurrentIndex( mAngleDirectionComboBox->findData( settingList.at( 0 ).direction() ) ); - mOrientationLeftButton->setProperty( "direction", QgsDiagramSettings::Left ); - mOrientationRightButton->setProperty( "direction", QgsDiagramSettings::Right ); - mOrientationUpButton->setProperty( "direction", QgsDiagramSettings::Up ); - mOrientationDownButton->setProperty( "direction", QgsDiagramSettings::Down ); switch ( settingList.at( 0 ).diagramOrientation ) { case QgsDiagramSettings::Left: @@ -404,10 +506,7 @@ void QgsDiagramProperties::syncToLayer() if ( settingList.at( 0 ).axisLineSymbol() ) mAxisLineStyleButton->setSymbol( settingList.at( 0 ).axisLineSymbol()->clone() ); - mIncreaseSmallDiagramsCheck->setChecked( settingList.at( 0 ).minimumSize != 0 ); - mIncreaseMinimumSizeSpinBox->setEnabled( mIncreaseSmallDiagramsCheck->isChecked() ); - mIncreaseMinimumSizeLabel->setEnabled( mIncreaseSmallDiagramsCheck->isChecked() ); - + mIncreaseSmallDiagramsGroupBox->setChecked( settingList.at( 0 ).minimumSize != 0 ); mIncreaseMinimumSizeSpinBox->setValue( settingList.at( 0 ).minimumSize ); if ( settingList.at( 0 ).scaleByArea ) @@ -459,69 +558,65 @@ void QgsDiagramProperties::syncToLayer() } } - const QgsDiagramLayerSettings *dls = mLayer->diagramLayerSettings(); - if ( dls ) + if ( dr->diagram() ) { - mDiagramDistanceSpinBox->setValue( dls->distance() ); - mPrioritySlider->setValue( dls->priority() ); - mZIndexSpinBox->setValue( dls->zIndex() ); - - switch ( dls->placement() ) - { - case QgsDiagramLayerSettings::AroundPoint: - radAroundPoint->setChecked( true ); - radAroundCentroid->setChecked( true ); - break; + mDiagramType = dr->diagram()->diagramName(); - case QgsDiagramLayerSettings::OverPoint: - radOverPoint->setChecked( true ); - radOverCentroid->setChecked( true ); - break; + mDiagramTypeComboBox->blockSignals( true ); + mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( mDiagramType ) ); + mDiagramTypeComboBox->blockSignals( false ); + //force a refresh of widget status to match diagram type + mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() ); + } + } + mPaintEffectWidget->setPaintEffect( mPaintEffect.get() ); +} - case QgsDiagramLayerSettings::Line: - radAroundLine->setChecked( true ); - radPolygonPerimeter->setChecked( true ); - break; +void QgsDiagramProperties::syncToSettings( const QgsDiagramLayerSettings *dls ) +{ + if ( dls ) + { + mDiagramDistanceSpinBox->setValue( dls->distance() ); + mPrioritySlider->setValue( dls->priority() ); + mZIndexSpinBox->setValue( dls->zIndex() ); - case QgsDiagramLayerSettings::Horizontal: - radOverLine->setChecked( true ); - radInsidePolygon->setChecked( true ); - break; + switch ( dls->placement() ) + { + case QgsDiagramLayerSettings::AroundPoint: + radAroundPoint->setChecked( true ); + radAroundCentroid->setChecked( true ); + break; - default: - break; - } + case QgsDiagramLayerSettings::OverPoint: + radOverPoint->setChecked( true ); + radOverCentroid->setChecked( true ); + break; - chkLineAbove->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::AboveLine ); - chkLineBelow->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::BelowLine ); - chkLineOn->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::OnLine ); - if ( !( dls->linePlacementFlags() & QgsDiagramLayerSettings::MapOrientation ) ) - chkLineOrientationDependent->setChecked( true ); - updatePlacementWidgets(); + case QgsDiagramLayerSettings::Line: + radAroundLine->setChecked( true ); + radPolygonPerimeter->setChecked( true ); + break; - mShowAllCheckBox->setChecked( dls->showAllDiagrams() ); + case QgsDiagramLayerSettings::Horizontal: + radOverLine->setChecked( true ); + radInsidePolygon->setChecked( true ); + break; - mDataDefinedProperties = dls->dataDefinedProperties(); + default: + break; } - if ( dr->diagram() ) - { - mDiagramType = dr->diagram()->diagramName(); + chkLineAbove->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::AboveLine ); + chkLineBelow->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::BelowLine ); + chkLineOn->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::OnLine ); + if ( !( dls->linePlacementFlags() & QgsDiagramLayerSettings::MapOrientation ) ) + chkLineOrientationDependent->setChecked( true ); + updatePlacementWidgets(); - mDiagramTypeComboBox->blockSignals( true ); - mDiagramTypeComboBox->setCurrentIndex( settingList.at( 0 ).enabled ? mDiagramTypeComboBox->findData( mDiagramType ) : 0 ); - mDiagramTypeComboBox->blockSignals( false ); - //force a refresh of widget status to match diagram type - mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() ); - if ( mDiagramTypeComboBox->currentIndex() == -1 ) - { - QMessageBox::warning( this, tr( "Diagram Properties" ), - tr( "The diagram type '%1' is unknown. A default type is selected for you." ).arg( mDiagramType ), QMessageBox::Ok ); - mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( DIAGRAM_NAME_PIE ) ); - } - } + mShowAllCheckBox->setChecked( dls->showAllDiagrams() ); + + mDataDefinedProperties = dls->dataDefinedProperties(); } - mPaintEffectWidget->setPaintEffect( mPaintEffect.get() ); } QgsDiagramProperties::~QgsDiagramProperties() @@ -544,100 +639,95 @@ void QgsDiagramProperties::updateProperty() QgsPropertyOverrideButton *button = qobject_cast( sender() ); const QgsDiagramLayerSettings::Property key = static_cast< QgsDiagramLayerSettings::Property >( button->propertyKey() ); mDataDefinedProperties.setProperty( key, button->toProperty() ); + emit widgetChanged(); } void QgsDiagramProperties::mDiagramTypeComboBox_currentIndexChanged( int index ) { - if ( index == 0 ) + mDiagramFrame->setEnabled( true ); + + mDiagramType = mDiagramTypeComboBox->itemData( index ).toString(); + + if ( QgsTextDiagram::DIAGRAM_NAME_TEXT == mDiagramType ) { - mDiagramFrame->setEnabled( false ); + mTextOptionsFrame->show(); + mBackgroundColorLabel->show(); + mBackgroundColorButton->show(); + mBackgroundColorDDBtn->show(); + mDiagramFontButton->show(); } else { - mDiagramFrame->setEnabled( true ); - - mDiagramType = mDiagramTypeComboBox->itemData( index ).toString(); - - if ( DIAGRAM_NAME_TEXT == mDiagramType ) - { - mTextOptionsFrame->show(); - mBackgroundColorLabel->show(); - mBackgroundColorButton->show(); - mBackgroundColorDDBtn->show(); - mDiagramFontButton->show(); - } - else - { - mTextOptionsFrame->hide(); - mBackgroundColorLabel->hide(); - mBackgroundColorButton->hide(); - mBackgroundColorDDBtn->hide(); - mDiagramFontButton->hide(); - } + mTextOptionsFrame->hide(); + mBackgroundColorLabel->hide(); + mBackgroundColorButton->hide(); + mBackgroundColorDDBtn->hide(); + mDiagramFontButton->hide(); + } - if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType || DIAGRAM_NAME_STACKED == mDiagramType ) - { - mBarWidthLabel->show(); - mBarWidthSpinBox->show(); - mBarSpacingLabel->show(); - mBarSpacingSpinBox->show(); - mBarSpacingUnitComboBox->show(); - mBarOptionsFrame->show(); - mShowAxisGroupBox->show(); - if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType ) - mAttributeBasedScalingRadio->setChecked( true ); - mFixedSizeRadio->setEnabled( DIAGRAM_NAME_STACKED == mDiagramType ); - mDiagramSizeSpinBox->setEnabled( DIAGRAM_NAME_STACKED == mDiagramType ); - mLinearlyScalingLabel->setText( tr( "Bar length: Scale linearly, so that the following value matches the specified bar length:" ) ); - mSizeLabel->setText( tr( "Bar length" ) ); - mFrameIncreaseSize->setVisible( false ); - } - else - { - mBarWidthLabel->hide(); - mBarWidthSpinBox->hide(); - mBarSpacingLabel->hide(); - mBarSpacingSpinBox->hide(); - mBarSpacingUnitComboBox->hide(); - mShowAxisGroupBox->hide(); - mBarOptionsFrame->hide(); - mLinearlyScalingLabel->setText( tr( "Scale linearly between 0 and the following attribute value / diagram size:" ) ); - mSizeLabel->setText( tr( "Size" ) ); - mAttributeBasedScalingRadio->setEnabled( true ); - mFixedSizeRadio->setEnabled( true ); - mDiagramSizeSpinBox->setEnabled( mFixedSizeRadio->isChecked() ); - mFrameIncreaseSize->setVisible( true ); - } + if ( QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM == mDiagramType || QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR == mDiagramType ) + { + mBarWidthLabel->show(); + mBarWidthSpinBox->show(); + mBarSpacingLabel->show(); + mBarSpacingSpinBox->show(); + mBarSpacingUnitComboBox->show(); + mBarOptionsFrame->show(); + mShowAxisGroupBox->show(); + if ( QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM == mDiagramType ) + mAttributeBasedScalingRadio->setChecked( true ); + mFixedSizeRadio->setEnabled( QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR == mDiagramType ); + mDiagramSizeSpinBox->setEnabled( QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR == mDiagramType ); + mLinearlyScalingLabel->setText( tr( "Bar length: Scale linearly, so that the following value matches the specified bar length:" ) ); + mSizeLabel->setText( tr( "Bar length" ) ); + mFrameIncreaseSize->setVisible( false ); + } + else + { + mBarWidthLabel->hide(); + mBarWidthSpinBox->hide(); + mBarSpacingLabel->hide(); + mBarSpacingSpinBox->hide(); + mBarSpacingUnitComboBox->hide(); + mShowAxisGroupBox->hide(); + mBarOptionsFrame->hide(); + mLinearlyScalingLabel->setText( tr( "Scale linearly between 0 and the following attribute value / diagram size:" ) ); + mSizeLabel->setText( tr( "Size" ) ); + mAttributeBasedScalingRadio->setEnabled( true ); + mFixedSizeRadio->setEnabled( true ); + mDiagramSizeSpinBox->setEnabled( mFixedSizeRadio->isChecked() ); + mFrameIncreaseSize->setVisible( true ); + } - if ( DIAGRAM_NAME_TEXT == mDiagramType || DIAGRAM_NAME_PIE == mDiagramType ) - { - mScaleDependencyComboBox->show(); - mScaleDependencyLabel->show(); - } - else - { - mScaleDependencyComboBox->hide(); - mScaleDependencyLabel->hide(); - } + if ( QgsTextDiagram::DIAGRAM_NAME_TEXT == mDiagramType || QgsPieDiagram::DIAGRAM_NAME_PIE == mDiagramType ) + { + mScaleDependencyComboBox->show(); + mScaleDependencyLabel->show(); + } + else + { + mScaleDependencyComboBox->hide(); + mScaleDependencyLabel->hide(); + } - if ( DIAGRAM_NAME_PIE == mDiagramType ) - { - mAngleOffsetComboBox->show(); - mAngleDirectionComboBox->show(); - mAngleDirectionLabel->show(); - mAngleOffsetLabel->show(); - mStartAngleDDBtn->show(); - } - else - { - mAngleOffsetComboBox->hide(); - mAngleDirectionComboBox->hide(); - mAngleDirectionLabel->hide(); - mAngleOffsetLabel->hide(); - mStartAngleDDBtn->hide(); - } + if ( QgsPieDiagram::DIAGRAM_NAME_PIE == mDiagramType ) + { + mAngleOffsetComboBox->show(); + mAngleDirectionComboBox->show(); + mAngleDirectionLabel->show(); + mAngleOffsetLabel->show(); + mStartAngleDDBtn->show(); + } + else + { + mAngleOffsetComboBox->hide(); + mAngleDirectionComboBox->hide(); + mAngleDirectionLabel->hide(); + mAngleOffsetLabel->hide(); + mStartAngleDDBtn->hide(); } } + QString QgsDiagramProperties::guessLegendText( const QString &expression ) { //trim unwanted characters from expression text for legend @@ -757,58 +847,35 @@ void QgsDiagramProperties::mDiagramAttributesTreeWidget_itemDoubleClicked( QTree } } -void QgsDiagramProperties::mEngineSettingsButton_clicked() +std::unique_ptr< QgsDiagram > QgsDiagramProperties::createDiagramObject() { - QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); - if ( panel && panel->dockMode() ) - { - QgsLabelEngineConfigWidget *widget = new QgsLabelEngineConfigWidget( mMapCanvas ); - connect( widget, &QgsLabelEngineConfigWidget::widgetChanged, widget, &QgsLabelEngineConfigWidget::apply ); - panel->openPanel( widget ); - } - else - { - QgsLabelEngineConfigDialog dialog( mMapCanvas, this ); - dialog.exec(); - // reactivate button's window - activateWindow(); - } -} - -void QgsDiagramProperties::apply() -{ - const int index = mDiagramTypeComboBox->currentIndex(); - const bool diagramsEnabled = ( index != 0 ); - std::unique_ptr< QgsDiagram > diagram; - if ( diagramsEnabled && 0 == mDiagramAttributesTreeWidget->topLevelItemCount() ) - { - QMessageBox::warning( this, tr( "Diagrams: No attributes added." ), - tr( "You did not add any attributes to this diagram layer. Please specify the attributes to visualize on the diagrams or disable diagrams." ) ); - } - - if ( mDiagramType == DIAGRAM_NAME_TEXT ) + if ( mDiagramType == QgsTextDiagram::DIAGRAM_NAME_TEXT ) { diagram = std::make_unique< QgsTextDiagram >(); } - else if ( mDiagramType == DIAGRAM_NAME_PIE ) + else if ( mDiagramType == QgsPieDiagram::DIAGRAM_NAME_PIE ) { diagram = std::make_unique< QgsPieDiagram >(); } - else if ( mDiagramType == DIAGRAM_NAME_STACKED ) + else if ( mDiagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ) { diagram = std::make_unique< QgsStackedBarDiagram >(); } - else // if ( diagramType == DIAGRAM_NAME_HISTOGRAM ) + else // if ( diagramType == QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM ) { diagram = std::make_unique< QgsHistogramDiagram >(); } + return diagram; +} - QgsDiagramSettings ds; - ds.enabled = ( mDiagramTypeComboBox->currentIndex() != 0 ); - ds.font = mDiagramFontButton->currentFont(); - ds.opacity = mOpacityWidget->opacity(); +std::unique_ptr QgsDiagramProperties::createDiagramSettings() +{ + std::unique_ptr< QgsDiagramSettings > ds = std::make_unique< QgsDiagramSettings>(); + ds->enabled = ( mDiagramTypeComboBox->currentIndex() != -1 ); + ds->font = mDiagramFontButton->currentFont(); + ds->opacity = mOpacityWidget->opacity(); QList categoryColors; QList categoryAttributes; @@ -823,64 +890,71 @@ void QgsDiagramProperties::apply() categoryAttributes.append( mDiagramAttributesTreeWidget->topLevelItem( i )->data( 0, RoleAttributeExpression ).toString() ); categoryLabels.append( mDiagramAttributesTreeWidget->topLevelItem( i )->text( 2 ) ); } - ds.categoryColors = categoryColors; - ds.categoryAttributes = categoryAttributes; - ds.categoryLabels = categoryLabels; - ds.size = QSizeF( mDiagramSizeSpinBox->value(), mDiagramSizeSpinBox->value() ); - ds.sizeType = mDiagramUnitComboBox->unit(); - ds.sizeScale = mDiagramUnitComboBox->getMapUnitScale(); - ds.lineSizeUnit = mDiagramLineUnitComboBox->unit(); - ds.lineSizeScale = mDiagramLineUnitComboBox->getMapUnitScale(); - ds.labelPlacementMethod = static_cast( mLabelPlacementComboBox->currentData().toInt() ); - ds.scaleByArea = ( mDiagramType == DIAGRAM_NAME_STACKED ) ? false : mScaleDependencyComboBox->currentData().toBool(); - - if ( mIncreaseSmallDiagramsCheck->isChecked() ) + ds->categoryColors = categoryColors; + ds->categoryAttributes = categoryAttributes; + ds->categoryLabels = categoryLabels; + ds->size = QSizeF( mDiagramSizeSpinBox->value(), mDiagramSizeSpinBox->value() ); + ds->sizeType = mDiagramUnitComboBox->unit(); + ds->sizeScale = mDiagramUnitComboBox->getMapUnitScale(); + ds->lineSizeUnit = mDiagramLineUnitComboBox->unit(); + ds->lineSizeScale = mDiagramLineUnitComboBox->getMapUnitScale(); + ds->labelPlacementMethod = static_cast( mLabelPlacementComboBox->currentData().toInt() ); + ds->scaleByArea = ( mDiagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ) ? false : mScaleDependencyComboBox->currentData().toBool(); + + if ( mIncreaseSmallDiagramsGroupBox->isChecked() ) { - ds.minimumSize = mIncreaseMinimumSizeSpinBox->value(); + ds->minimumSize = mIncreaseMinimumSizeSpinBox->value(); } else { - ds.minimumSize = 0; + ds->minimumSize = 0; } - ds.backgroundColor = mBackgroundColorButton->color(); - ds.penColor = mDiagramPenColorButton->color(); - ds.penWidth = mPenWidthSpinBox->value(); - ds.minimumScale = mScaleRangeWidget->minimumScale(); - ds.maximumScale = mScaleRangeWidget->maximumScale(); - ds.scaleBasedVisibility = mScaleVisibilityGroupBox->isChecked(); + ds->backgroundColor = mBackgroundColorButton->color(); + ds->penColor = mDiagramPenColorButton->color(); + ds->penWidth = mPenWidthSpinBox->value(); + ds->minimumScale = mScaleRangeWidget->minimumScale(); + ds->maximumScale = mScaleRangeWidget->maximumScale(); + ds->scaleBasedVisibility = mScaleVisibilityGroupBox->isChecked(); // Diagram angle offset (pie) - ds.rotationOffset = mAngleOffsetComboBox->currentData().toInt(); - ds.setDirection( static_cast< QgsDiagramSettings::Direction>( mAngleDirectionComboBox->currentData().toInt() ) ); + ds->rotationOffset = mAngleOffsetComboBox->currentData().toInt(); + ds->setDirection( static_cast< QgsDiagramSettings::Direction>( mAngleDirectionComboBox->currentData().toInt() ) ); // Diagram orientation (histogram) - ds.diagramOrientation = static_cast( mOrientationButtonGroup->checkedButton()->property( "direction" ).toInt() ); + ds->diagramOrientation = static_cast( mOrientationButtonGroup->checkedButton()->property( "direction" ).toInt() ); - ds.barWidth = mBarWidthSpinBox->value(); + ds->barWidth = mBarWidthSpinBox->value(); - ds.setAxisLineSymbol( mAxisLineStyleButton->clonedSymbol< QgsLineSymbol >() ); - ds.setShowAxis( mShowAxisGroupBox->isChecked() ); + ds->setAxisLineSymbol( mAxisLineStyleButton->clonedSymbol< QgsLineSymbol >() ); + ds->setShowAxis( mShowAxisGroupBox->isChecked() ); - ds.setSpacing( mBarSpacingSpinBox->value() ); - ds.setSpacingUnit( mBarSpacingUnitComboBox->unit() ); - ds.setSpacingMapUnitScale( mBarSpacingUnitComboBox->getMapUnitScale() ); + ds->setSpacing( mBarSpacingSpinBox->value() ); + ds->setSpacingUnit( mBarSpacingUnitComboBox->unit() ); + ds->setSpacingMapUnitScale( mBarSpacingUnitComboBox->getMapUnitScale() ); if ( mPaintEffect && ( !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) || mPaintEffect->enabled() ) ) - ds.setPaintEffect( mPaintEffect->clone() ); + ds->setPaintEffect( mPaintEffect->clone() ); else - ds.setPaintEffect( nullptr ); + ds->setPaintEffect( nullptr ); - QgsDiagramRenderer *renderer = nullptr; + return ds; +} + +std::unique_ptr QgsDiagramProperties::createRenderer() +{ + std::unique_ptr< QgsDiagramSettings > ds = createDiagramSettings(); + + std::unique_ptr< QgsDiagramRenderer > renderer; if ( mFixedSizeRadio->isChecked() ) { - QgsSingleCategoryDiagramRenderer *dr = new QgsSingleCategoryDiagramRenderer(); - dr->setDiagramSettings( ds ); - renderer = dr; + std::unique_ptr< QgsSingleCategoryDiagramRenderer > dr = std::make_unique< QgsSingleCategoryDiagramRenderer >(); + dr->setDiagramSettings( *ds ); + renderer = std::move( dr ); } else { - QgsLinearlyInterpolatedDiagramRenderer *dr = new QgsLinearlyInterpolatedDiagramRenderer(); + std::unique_ptr< QgsLinearlyInterpolatedDiagramRenderer > dr = std::make_unique< QgsLinearlyInterpolatedDiagramRenderer >(); dr->setLowerValue( 0.0 ); dr->setLowerSize( QSizeF( 0.0, 0.0 ) ); dr->setUpperValue( mMaxValueSpinBox->value() ); @@ -897,16 +971,23 @@ void QgsDiagramProperties::apply() { dr->setClassificationField( sizeFieldNameOrExp ); } - dr->setDiagramSettings( ds ); + dr->setDiagramSettings( *ds ); dr->setDataDefinedSizeLegend( mSizeLegend ? new QgsDataDefinedSizeLegend( *mSizeLegend ) : nullptr ); - renderer = dr; + renderer = std::move( dr ); } - renderer->setDiagram( diagram.release() ); + renderer->setAttributeLegend( mCheckBoxAttributeLegend->isChecked() ); - mLayer->setDiagramRenderer( renderer ); + std::unique_ptr< QgsDiagram > diagram = createDiagramObject(); + renderer->setDiagram( diagram.release() ); + + return renderer; +} + +QgsDiagramLayerSettings QgsDiagramProperties::createDiagramLayerSettings() +{ QgsDiagramLayerSettings dls; dls.setDataDefinedProperties( mDataDefinedProperties ); dls.setDistance( mDiagramDistanceSpinBox->value() ); @@ -951,6 +1032,29 @@ void QgsDiagramProperties::apply() flags |= QgsDiagramLayerSettings::MapOrientation; dls.setLinePlacementFlags( flags ); + return dls; +} + +void QgsDiagramProperties::apply() +{ + const int index = mDiagramTypeComboBox->currentIndex(); + const bool diagramsEnabled = ( index != -1 ); + + // Avoid this messageBox when in both dock and liveUpdate mode + QgsSettings settings; + if ( !dockMode() || !settings.value( QStringLiteral( "UI/autoApplyStyling" ), true ).toBool() ) + { + if ( diagramsEnabled && 0 == mDiagramAttributesTreeWidget->topLevelItemCount() ) + { + QMessageBox::warning( this, tr( "Diagrams: No attributes added." ), + tr( "You did not add any attributes to this diagram layer. Please specify the attributes to visualize on the diagrams or disable diagrams." ) ); + } + } + + std::unique_ptr< QgsDiagramRenderer > renderer = createRenderer(); + mLayer->setDiagramRenderer( renderer.release() ); + + QgsDiagramLayerSettings dls = createDiagramLayerSettings(); mLayer->setDiagramLayerSettings( dls ); // refresh @@ -1057,6 +1161,30 @@ void QgsDiagramProperties::scalingTypeChanged() mButtonSizeLegendSettings->setEnabled( mAttributeBasedScalingRadio->isChecked() ); } +void QgsDiagramProperties::setAllowedToEditDiagramLayerSettings( bool allowed ) +{ + mAllowedToEditDls = allowed; + + label_16->setVisible( allowed ); + mZIndexSpinBox->setVisible( allowed ); + mZOrderDDBtn->setVisible( allowed ); + mShowAllCheckBox->setVisible( allowed ); + mDlsLabel_1->setVisible( !allowed ); + + mCoordinatesGrpBox->setVisible( allowed ); + mLinePlacementFrame->setVisible( allowed ); + mObstaclesGrpBox->setVisible( allowed ); + mPlacementFrame->setVisible( allowed ); + mPriorityGrpBox->setVisible( allowed ); + stackedPlacement->setVisible( allowed ); + mDlsLabel_2->setVisible( !allowed ); +} + +bool QgsDiagramProperties::isAllowedToEditDiagramLayerSettings() const +{ + return mAllowedToEditDls; +} + void QgsDiagramProperties::showSizeLegendDialog() { // prepare size transformer @@ -1118,3 +1246,79 @@ void QgsDiagramProperties::createAuxiliaryField() emit auxiliaryFieldCreated(); } + +void QgsDiagramProperties::connectValueChanged( const QList &widgets ) +{ + const auto constWidgets = widgets; + for ( QWidget *widget : constWidgets ) + { + if ( QgsSymbolButton *w = qobject_cast( widget ) ) + { + connect( w, &QgsSymbolButton::changed, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsFieldExpressionWidget *w = qobject_cast< QgsFieldExpressionWidget *>( widget ) ) + { + connect( w, qOverload< const QString & >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsOpacityWidget *w = qobject_cast< QgsOpacityWidget *>( widget ) ) + { + connect( w, &QgsOpacityWidget::opacityChanged, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsUnitSelectionWidget *w = qobject_cast( widget ) ) + { + connect( w, &QgsUnitSelectionWidget::changed, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QComboBox *w = qobject_cast( widget ) ) + { + connect( w, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QSpinBox *w = qobject_cast( widget ) ) + { + connect( w, qOverload< int >( &QSpinBox::valueChanged ), this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QDoubleSpinBox *w = qobject_cast( widget ) ) + { + connect( w, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsColorButton *w = qobject_cast( widget ) ) + { + connect( w, &QgsColorButton::colorChanged, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QCheckBox *w = qobject_cast( widget ) ) + { + connect( w, &QCheckBox::toggled, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QRadioButton *w = qobject_cast( widget ) ) + { + connect( w, &QRadioButton::toggled, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QSlider *w = qobject_cast( widget ) ) + { + connect( w, &QSlider::valueChanged, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QGroupBox *w = qobject_cast( widget ) ) + { + connect( w, &QGroupBox::toggled, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QTreeWidget *w = qobject_cast( widget ) ) + { + connect( w, &QTreeWidget::itemChanged, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsScaleRangeWidget *w = qobject_cast( widget ) ) + { + connect( w, &QgsScaleRangeWidget::rangeChanged, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsEffectStackCompactWidget *w = qobject_cast( widget ) ) + { + connect( w, &QgsEffectStackCompactWidget::changed, this, &QgsDiagramProperties::widgetChanged ); + } + else if ( QgsFontButton *w = qobject_cast( widget ) ) + { + connect( w, &QgsFontButton::changed, this, &QgsDiagramProperties::widgetChanged ); + } + else + { + QgsLogger::warning( QStringLiteral( "Could not create connection for widget %1" ).arg( widget->objectName() ) ); + } + } +} diff --git a/src/gui/vector/qgsdiagramproperties.h b/src/gui/vector/qgsdiagramproperties.h index cd704903444e..1fe56b1ade81 100644 --- a/src/gui/vector/qgsdiagramproperties.h +++ b/src/gui/vector/qgsdiagramproperties.h @@ -36,13 +36,16 @@ class QgsMapCanvas; /** * \ingroup gui * \class QgsDiagramProperties + * + * \note This class is not a part of public API */ -class GUI_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPropertiesBase, private QgsExpressionContextGenerator +class GUI_EXPORT QgsDiagramProperties : public QgsPanelWidget, private Ui::QgsDiagramPropertiesBase, private QgsExpressionContextGenerator { Q_OBJECT public: QgsDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas ); + ~QgsDiagramProperties() override; /** * Updates the widget to reflect the layer's current diagram settings. @@ -51,11 +54,50 @@ class GUI_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr */ void syncToLayer(); - ~QgsDiagramProperties() override; + /** + * Updates the widget to reflect the diagram renderer. + * \param dr Diagram renderer where settings are taken from. + * + * \since QGIS 3.40 + */ + void syncToRenderer( const QgsDiagramRenderer *dr ); + + /** + * Updates the widget to reflect the diagram layer settings. + * \param dls Diagram Layer Settings to update the widget. + * + * \since QGIS 3.40 + */ + void syncToSettings( const QgsDiagramLayerSettings *dls ); //! Adds an attribute from the list of available attributes to the assigned attributes with a random color. void addAttribute( QTreeWidgetItem *item ); + /** + * Sets the widget in dock mode. + * \param dockMode TRUE for dock mode. + */ + void setDockMode( bool dockMode ) override; + + /** + * Defines the widget's diagram type and lets it know it should hide the type comboBox. + * @param diagramType Type of diagram to be set + */ + void setDiagramType( const QString diagramType ); + + /** + * Sets whether the widget should show diagram layer settings. + * Used by stacked diagrams, which disable editing of DLS for sub diagrams + * other than the first one. + * @param allowed Whether this widget should be allowed to edit diagram layer settings. + */ + void setAllowedToEditDiagramLayerSettings( bool allowed ); + + /** + * Returns whether this widget is allowed to edit diagram layer settings. + */ + bool isAllowedToEditDiagramLayerSettings() const; + signals: void auxiliaryFieldCreated(); @@ -68,7 +110,6 @@ class GUI_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr void mFindMaximumValueButton_clicked(); void mRemoveCategoryPushButton_clicked(); void mDiagramAttributesTreeWidget_itemDoubleClicked( QTreeWidgetItem *item, int column ); - void mEngineSettingsButton_clicked(); void showAddAttributeExpressionDialog(); void mDiagramStackedWidget_currentChanged( int index ); void updatePlacementWidgets(); @@ -119,7 +160,51 @@ class GUI_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr QgsExpressionContext createExpressionContext() const override; + bool mAllowedToEditDls = true; + void registerDataDefinedButton( QgsPropertyOverrideButton *button, QgsDiagramLayerSettings::Property key ); + + /** + * Convenience function to chain widgets' change value signal to another signal. + * @param widgets List of widgets. + */ + void connectValueChanged( const QList &widgets ); + + /** + * Creates a QgsDiagram object from the GUI settings. + * + * \since QGIS 3.40 + */ + std::unique_ptr< QgsDiagram > createDiagramObject(); + + /** + * Creates a QgsDiagramSettings object from the GUI settings. + * + * \since QGIS 3.40 + */ + std::unique_ptr createDiagramSettings(); + + /** + * Creates a QgsDiagramRenderer object from the GUI settings. + * + * \since QGIS 3.40 + */ + std::unique_ptr createRenderer(); + + /** + * Creates a QgsDiagramLayerSettings object from the GUI settings. + * + * \since QGIS 3.40 + */ + QgsDiagramLayerSettings createDiagramLayerSettings(); + + /** + * Insert reasonable defaults to have an initial diagram widget status. + */ + void insertDefaults(); + + friend class QgsStackedDiagramProperties; + friend class QgsStackedDiagramPropertiesDialog; }; diff --git a/src/gui/vector/qgsdiagramwidget.cpp b/src/gui/vector/qgsdiagramwidget.cpp new file mode 100644 index 000000000000..4cb0bc72ff03 --- /dev/null +++ b/src/gui/vector/qgsdiagramwidget.cpp @@ -0,0 +1,224 @@ +/*************************************************************************** + qgsdiagramwidget.h + Container widget for diagram layers + ------------------- + begin : September 2024 + copyright : (C) Germán Carrillo + email : german at opengis dot ch + + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "diagram/qgshistogramdiagram.h" +#include "diagram/qgspiediagram.h" +#include "diagram/qgstextdiagram.h" +#include "diagram/qgsstackedbardiagram.h" +#include "diagram/qgsstackeddiagram.h" + +#include "qgsdiagramwidget.h" +#include "qgsvectorlayer.h" +#include "qgsapplication.h" +#include "qgsguiutils.h" +#include "qgslabelengineconfigdialog.h" +#include "qgsdiagramproperties.h" +#include "qgsstackeddiagramproperties.h" + + +QgsDiagramWidget::QgsDiagramWidget( QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent ) + : QgsMapLayerConfigWidget( layer, canvas, parent ) + , mLayer( layer ) + , mCanvas( canvas ) +{ + if ( !layer ) + { + return; + } + + setupUi( this ); + + // Initialize stacked diagram controls + mDiagramTypeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "diagramNone.svg" ) ), tr( "No Diagrams" ), ModeNone ); + mDiagramTypeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "pie-chart.svg" ) ), tr( "Pie Chart" ), ModePie ); + mDiagramTypeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "text.svg" ) ), tr( "Text Diagram" ), ModeText ); + mDiagramTypeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "histogram.svg" ) ), tr( "Histogram" ), ModeHistogram ); + mDiagramTypeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "stacked-bar.svg" ) ), tr( "Stacked Bars" ), ModeStackedBar ); + mDiagramTypeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "diagramNone.svg" ) ), tr( "Stacked Diagram" ), ModeStacked ); + + connect( mEngineSettingsButton, &QAbstractButton::clicked, this, &QgsDiagramWidget::showEngineConfigDialog ); + + connect( mDiagramTypeComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsDiagramWidget::mDiagramTypeComboBox_currentIndexChanged ); + + const int iconSize16 = QgsGuiUtils::scaleIconSize( 16 ); + mEngineSettingsButton->setIconSize( QSize( iconSize16, iconSize16 ) ); +} + +void QgsDiagramWidget::apply() +{ + const Mode mode = static_cast< Mode >( mDiagramTypeComboBox->currentData().toInt() ); + + switch ( mode ) + { + case ModeStacked: + { + // Delegate to stacked diagram's apply + static_cast( mWidget )->apply(); + break; + } + case ModePie: + case ModeText: + case ModeHistogram: + case ModeStackedBar: + { + // Delegate to single diagram's apply + static_cast( mWidget )->apply(); + break; + } + case ModeNone: + { + mLayer->setDiagramRenderer( nullptr ); + + QgsDiagramLayerSettings dls; + mLayer->setDiagramLayerSettings( dls ); + + // refresh + QgsProject::instance()->setDirty( true ); + mLayer->triggerRepaint(); + } + } +} + +void QgsDiagramWidget::syncToOwnLayer() +{ + if ( !mLayer ) + { + return; + } + + whileBlocking( mDiagramTypeComboBox )->setCurrentIndex( -1 ); + + const QgsDiagramRenderer *dr = mLayer->diagramRenderer(); + + // pick the right mode from the layer + if ( dr && dr->diagram() ) + { + if ( dr->rendererName() == QgsStackedDiagram::DIAGRAM_NAME_STACKED ) + { + mDiagramTypeComboBox->setCurrentIndex( ModeStacked ); + } + else // Single diagram + { + const QString diagramName = dr->diagram()->diagramName(); + if ( diagramName == QgsPieDiagram::DIAGRAM_NAME_PIE ) + { + mDiagramTypeComboBox->setCurrentIndex( ModePie ) ; + } + else if ( diagramName == QgsTextDiagram::DIAGRAM_NAME_TEXT ) + { + mDiagramTypeComboBox->setCurrentIndex( ModeText ) ; + } + else if ( diagramName == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ) + { + mDiagramTypeComboBox->setCurrentIndex( ModeStackedBar ) ; + } + else // diagramName == QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM + { + // Play safe and set to histogram by default if the diagram name is unknown + mDiagramTypeComboBox->setCurrentIndex( ModeHistogram ); + } + // TODO: if we get a stacked diagram, take the first subdiagram, + // Set its diagram type and sync to its settings + + // Delegate to single diagram's syncToLayer + static_cast( mWidget )->syncToLayer(); + } + } + else // No Diagram + { + mDiagramTypeComboBox->setCurrentIndex( ModeNone ); + } +} + +void QgsDiagramWidget::mDiagramTypeComboBox_currentIndexChanged( int index ) +{ + if ( mWidget ) + mStackedWidget->removeWidget( mWidget ); + + delete mWidget; + mWidget = nullptr; + + if ( index < 0 ) + return; + + const Mode mode = static_cast< Mode >( mDiagramTypeComboBox->currentData().toInt() ); + + switch ( mode ) + { + case ModePie: + case ModeText: + case ModeHistogram: + case ModeStackedBar: + { + QgsDiagramProperties *singleWidget = new QgsDiagramProperties( mLayer, this, mMapCanvas ); + singleWidget->layout()->setContentsMargins( 0, 0, 0, 0 ); + singleWidget->setDockMode( dockMode() ); + singleWidget->syncToLayer(); + + if ( mode == ModePie ) + singleWidget->setDiagramType( QgsPieDiagram::DIAGRAM_NAME_PIE ); + else if ( mode == ModeText ) + singleWidget->setDiagramType( QgsTextDiagram::DIAGRAM_NAME_TEXT ); + else if ( mode == ModeHistogram ) + singleWidget->setDiagramType( QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM ); + else if ( mode == ModeStackedBar ) + singleWidget->setDiagramType( QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ); + + connect( singleWidget, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel ); + connect( singleWidget, &QgsDiagramProperties::widgetChanged, this, &QgsDiagramWidget::widgetChanged ); + connect( singleWidget, &QgsDiagramProperties::auxiliaryFieldCreated, this, &QgsDiagramWidget::auxiliaryFieldCreated ); + + mWidget = singleWidget; + mStackedWidget->addWidget( mWidget ); + mStackedWidget->setCurrentWidget( mWidget ); + break; + } + case ModeStacked: + { + QgsStackedDiagramProperties *stackedWidget = new QgsStackedDiagramProperties( mLayer, this, mCanvas ); + stackedWidget->setDockMode( dockMode() ); + connect( stackedWidget, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel ); + connect( stackedWidget, &QgsStackedDiagramProperties::widgetChanged, this, &QgsDiagramWidget::widgetChanged ); + + mWidget = stackedWidget; + mStackedWidget->addWidget( mWidget ); + mStackedWidget->setCurrentWidget( mWidget ); + break; + } + case ModeNone: + break; + } + emit widgetChanged(); +} + +void QgsDiagramWidget::showEngineConfigDialog() +{ + QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); + if ( panel && panel->dockMode() ) + { + QgsLabelEngineConfigWidget *widget = new QgsLabelEngineConfigWidget( mCanvas ); + connect( widget, &QgsLabelEngineConfigWidget::widgetChanged, widget, &QgsLabelEngineConfigWidget::apply ); + panel->openPanel( widget ); + } + else + { + QgsLabelEngineConfigDialog dialog( mCanvas, this ); + dialog.exec(); + // reactivate button's window + activateWindow(); + } +} diff --git a/src/gui/vector/qgsdiagramwidget.h b/src/gui/vector/qgsdiagramwidget.h new file mode 100644 index 000000000000..eb5f666b639a --- /dev/null +++ b/src/gui/vector/qgsdiagramwidget.h @@ -0,0 +1,78 @@ +/*************************************************************************** + qgsdiagramwidget.h + Container widget for diagram layers + ------------------- + begin : September 2024 + copyright : (C) Germán Carrillo + email : german at opengis dot ch + + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSDIAGRAMWIDGET_H +#define QGSDIAGRAMWIDGET_H + +// We don't want to expose this in the public API +#define SIP_NO_FILE + +#include +#include "ui_qgsdiagramwidget.h" + +#include + +/** + * \ingroup gui + * \class QgsDiagramWidget + * + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsDiagramWidget : public QgsMapLayerConfigWidget, private Ui::QgsDiagramWidget +{ + Q_OBJECT + + public: + //! constructor + QgsDiagramWidget( QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent = nullptr ); + + /** + * Updates the widget to reflect the layer's current diagram settings. + */ + void syncToOwnLayer(); + + public slots: + //! Saves the labeling configuration and immediately updates the map canvas to reflect the changes + void apply() override; + + signals: + //! Emitted when an auxiliary field is created + void auxiliaryFieldCreated(); + + private slots: + void mDiagramTypeComboBox_currentIndexChanged( int index ); + void showEngineConfigDialog(); + + private: + + enum Mode + { + ModeNone, + ModePie, + ModeText, + ModeHistogram, + ModeStackedBar, + ModeStacked + }; + + QgsVectorLayer *mLayer = nullptr; + QgsMapCanvas *mCanvas = nullptr; + + QWidget *mWidget = nullptr; +}; + +#endif // QGSDIAGRAMWIDGET_H diff --git a/src/gui/vector/qgsstackeddiagramproperties.cpp b/src/gui/vector/qgsstackeddiagramproperties.cpp new file mode 100644 index 000000000000..217af193efe3 --- /dev/null +++ b/src/gui/vector/qgsstackeddiagramproperties.cpp @@ -0,0 +1,549 @@ +/*************************************************************************** + qgsstackeddiagramproperties.h + Properties for stacked diagram layers + ------------------- + begin : August 2024 + copyright : (C) Germán Carrillo + email : german at opengis dot ch + + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "diagram/qgshistogramdiagram.h" +#include "diagram/qgspiediagram.h" +#include "diagram/qgstextdiagram.h" +#include "diagram/qgsstackedbardiagram.h" +#include "diagram/qgsstackeddiagram.h" + +#include "qgsgui.h" +#include "qgsdiagramproperties.h" +#include "qgslabelengineconfigdialog.h" +#include "qgsproject.h" +#include "qgsstackeddiagramproperties.h" +#include "qgsvectorlayer.h" +#include "qgshelp.h" + +QgsStackedDiagramProperties::QgsStackedDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas ) + : QgsPanelWidget( parent ) + , mLayer( layer ) + , mMapCanvas( canvas ) +{ + if ( !layer ) + { + return; + } + + setupUi( this ); + connect( mSubDiagramsView, &QAbstractItemView::doubleClicked, this, static_cast( &QgsStackedDiagramProperties::editSubDiagram ) ); + + connect( mAddSubDiagramButton, &QPushButton::clicked, this, &QgsStackedDiagramProperties::addSubDiagram ); + connect( mEditSubDiagramButton, &QAbstractButton::clicked, this, static_cast( &QgsStackedDiagramProperties::editSubDiagram ) ); + connect( mRemoveSubDiagramButton, &QPushButton::clicked, this, &QgsStackedDiagramProperties::removeSubDiagram ); + + // Initialize stacked diagram controls + mStackedDiagramModeComboBox->addItem( tr( "Horizontal" ), QgsDiagramSettings::Horizontal ); + mStackedDiagramModeComboBox->addItem( tr( "Vertical" ), QgsDiagramSettings::Vertical ); + + mStackedDiagramSpacingSpinBox->setClearValue( 0 ); + mStackedDiagramSpacingUnitComboBox->setUnits( { Qgis::RenderUnit::Millimeters, + Qgis::RenderUnit::MetersInMapUnits, + Qgis::RenderUnit::MapUnits, + Qgis::RenderUnit::Pixels, + Qgis::RenderUnit::Points, + Qgis::RenderUnit::Inches } ); + + connect( mStackedDiagramModeComboBox, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsStackedDiagramProperties::widgetChanged ); + connect( mStackedDiagramSpacingSpinBox, qOverload< double >( &QgsDoubleSpinBox::valueChanged ), this, &QgsStackedDiagramProperties::widgetChanged ); + connect( mStackedDiagramSpacingUnitComboBox, &QgsUnitSelectionWidget::changed, this, &QgsStackedDiagramProperties::widgetChanged ); + + mModel = new QgsStackedDiagramPropertiesModel(); + mSubDiagramsView->setModel( mModel ); + mSubDiagramsView->resizeColumnToContents( 0 ); + + connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsStackedDiagramProperties::widgetChanged ); + connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsStackedDiagramProperties::widgetChanged ); + connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsStackedDiagramProperties::widgetChanged ); + + syncToLayer(); +} + +void QgsStackedDiagramProperties::addSubDiagram() +{ + // Create a single category renderer by default + std::unique_ptr< QgsDiagramRenderer > renderer; + std::unique_ptr< QgsSingleCategoryDiagramRenderer > dr = std::make_unique< QgsSingleCategoryDiagramRenderer >(); + renderer = std::move( dr ); + + QItemSelectionModel *sel = mSubDiagramsView->selectionModel(); + const QModelIndex index = sel->currentIndex(); + + if ( index.isValid() ) + { + // add after this subDiagram + const QModelIndex currentIndex = mSubDiagramsView->selectionModel()->currentIndex(); + mModel->insertSubDiagram( currentIndex.row() + 1, renderer.release() ); + const QModelIndex newIndex = mModel->index( currentIndex.row() + 1, 0 ); + mSubDiagramsView->selectionModel()->setCurrentIndex( newIndex, QItemSelectionModel::ClearAndSelect ); + } + else + { + // append to root + appendSubDiagram( renderer.release() ); + } + editSubDiagram(); +} + +void QgsStackedDiagramProperties::appendSubDiagram( QgsDiagramRenderer *dr ) +{ + const int rows = mModel->rowCount(); + mModel->insertSubDiagram( rows, dr ); + const QModelIndex newIndex = mModel->index( rows, 0 ); + mSubDiagramsView->selectionModel()->setCurrentIndex( newIndex, QItemSelectionModel::ClearAndSelect ); +} + +void QgsStackedDiagramProperties::editSubDiagram() +{ + editSubDiagram( mSubDiagramsView->selectionModel()->currentIndex() ); +} + +void QgsStackedDiagramProperties::editSubDiagram( const QModelIndex &index ) +{ + if ( !index.isValid() ) + return; + + QgsDiagramRenderer *renderer = mModel->subDiagramForIndex( index ); + QgsDiagramLayerSettings dls = mModel->diagramLayerSettings(); + QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); + + if ( panel && panel->dockMode() ) + { + QgsDiagramProperties *widget = new QgsDiagramProperties( mLayer, this, mMapCanvas ); + widget->setPanelTitle( tr( "Edit Sub Diagram" ) ); + widget->layout()->setContentsMargins( 0, 0, 0, 0 ); + widget->syncToRenderer( renderer ); + widget->syncToSettings( &dls ); + if ( !couldBeFirstSubDiagram( index ) ) + { + widget->setAllowedToEditDiagramLayerSettings( false ); + } + + connect( widget, &QgsDiagramProperties::auxiliaryFieldCreated, this, &QgsStackedDiagramProperties::auxiliaryFieldCreated ); + connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsStackedDiagramProperties::subDiagramWidgetPanelAccepted ); + connect( widget, &QgsDiagramProperties::widgetChanged, this, &QgsStackedDiagramProperties::liveUpdateSubDiagramFromPanel ); + openPanel( widget ); + return; + } + + QgsStackedDiagramPropertiesDialog dlg( mLayer, this, mMapCanvas ); + dlg.syncToRenderer( renderer ); + dlg.syncToSettings( &dls ); + if ( !couldBeFirstSubDiagram( index ) ) + { + dlg.setAllowedToEditDiagramLayerSettings( false ); + } + + if ( dlg.exec() ) + { + const QModelIndex index = mSubDiagramsView->selectionModel()->currentIndex(); + if ( dlg.isAllowedToEditDiagramLayerSettings() ) + mModel->updateDiagramLayerSettings( dlg.diagramLayerSettings() ); + + // This call will emit dataChanged, which in turns triggers widgetChanged() + mModel->updateSubDiagram( index, dlg.renderer() ); + } +} + +void QgsStackedDiagramProperties::removeSubDiagram() +{ + const QItemSelection sel = mSubDiagramsView->selectionModel()->selection(); + const auto constSel = sel; + for ( const QItemSelectionRange &range : constSel ) + { + if ( range.isValid() ) + mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() ); + } + // make sure that the selection is gone + mSubDiagramsView->selectionModel()->clear(); +} + +void QgsStackedDiagramProperties::syncToLayer() +{ + const QgsDiagramRenderer *dr = mLayer->diagramRenderer(); + + if ( dr && dr->diagram() ) + { + const QList settingList = dr->diagramSettings(); + mStackedDiagramModeComboBox->setCurrentIndex( settingList.at( 0 ).stackedDiagramMode ); + mStackedDiagramSpacingSpinBox->setValue( settingList.at( 0 ).stackedDiagramSpacing() ); + mStackedDiagramSpacingUnitComboBox->setUnit( settingList.at( 0 ).stackedDiagramSpacingUnit() ); + + if ( dr->rendererName() == QgsStackedDiagram::DIAGRAM_NAME_STACKED ) + { + const QgsStackedDiagramRenderer *stackedDiagramRenderer = static_cast< const QgsStackedDiagramRenderer * >( dr ); + const auto renderers = stackedDiagramRenderer->renderers(); + for ( const auto &renderer : renderers ) + { + appendSubDiagram( renderer ); + } + } + else + { + // Take this single renderer as the first stacked renderer + appendSubDiagram( dr->clone() ); + } + + const QgsDiagramLayerSettings *dls = mLayer->diagramLayerSettings(); + mModel->updateDiagramLayerSettings( *dls ); + } +} + +void QgsStackedDiagramProperties::apply() +{ + std::unique_ptr< QgsDiagramSettings> ds = std::make_unique< QgsDiagramSettings >(); + ds->stackedDiagramMode = static_cast( mStackedDiagramModeComboBox->currentData().toInt() ); + ds->setStackedDiagramSpacingUnit( mStackedDiagramSpacingUnitComboBox->unit() ); + ds->setStackedDiagramSpacing( mStackedDiagramSpacingSpinBox->value() ); + + // Create diagram renderer for the StackedDiagram + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + + // Get DiagramSettings from each subdiagram + const QList< QgsDiagramRenderer *> renderers = mModel->subRenderers(); + for ( const auto &renderer : renderers ) + { + const QList< QgsDiagramSettings > ds1 = renderer->diagramSettings(); + if ( !ds1.isEmpty() ) + { + ds->categoryAttributes += ds1.at( 0 ).categoryAttributes; + ds->categoryLabels += ds1.at( 0 ).categoryLabels; + ds->categoryColors += ds1.at( 0 ).categoryColors; + } + dr->addRenderer( renderer ); + } + + dr->setDiagramSettings( *ds ); + mLayer->setDiagramRenderer( dr ); + + // Get DiagramLayerSettings from the model + QgsDiagramLayerSettings dls = mModel->diagramLayerSettings(); + mLayer->setDiagramLayerSettings( dls ); + + // refresh + QgsProject::instance()->setDirty( true ); + mLayer->triggerRepaint(); +} + +bool QgsStackedDiagramProperties::couldBeFirstSubDiagram( const QModelIndex &index ) const +{ + if ( !index.isValid() ) + return false; + + if ( mModel->rowCount() == 1 ) + return true; + + // Is there any enabled subdiagram before our index.row()? + // If so, ours cannot be the first diagram. + const QList< QgsDiagramRenderer * > renderers = mModel->subRenderers(); + + for ( int i = 0; i < index.row(); i++ ) + { + const auto &renderer = renderers.at( i ); + const QList< QgsDiagramSettings > ds = renderer->diagramSettings(); + if ( !ds.isEmpty() && ds.at( 0 ).enabled ) + { + // First enabled subdiagram found, and we know our row is after. + // Therefore, return false to disallow showing DLS settings for it. + return false; + } + } + // Either our row is the first subdiagram enabled or it's disabled, + // but there are no enabled ones before. So, ours could be the first + // enabled one after being edited. + // Therefore, we should allow DLS settings on its corresponding widget. + return true; +} + +void QgsStackedDiagramProperties::subDiagramWidgetPanelAccepted( QgsPanelWidget *panel ) +{ + QgsDiagramProperties *widget = qobject_cast( panel ); + + std::unique_ptr< QgsDiagramRenderer > renderer = widget->createRenderer(); + + const QModelIndex index = mSubDiagramsView->selectionModel()->currentIndex(); + if ( widget->isAllowedToEditDiagramLayerSettings() ) + mModel->updateDiagramLayerSettings( widget->createDiagramLayerSettings() ); + + mModel->updateSubDiagram( index, renderer.release() ); +} + +void QgsStackedDiagramProperties::liveUpdateSubDiagramFromPanel() +{ + subDiagramWidgetPanelAccepted( qobject_cast( sender() ) ); +} + +//// + +#include "qgsvscrollarea.h" + +QgsStackedDiagramPropertiesDialog::QgsStackedDiagramPropertiesDialog( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *mapCanvas ) + : QDialog( parent ) +{ + +#ifdef Q_OS_MAC + setWindowModality( Qt::WindowModal ); +#endif + + QVBoxLayout *layout = new QVBoxLayout( this ); + QgsVScrollArea *scrollArea = new QgsVScrollArea( this ); + scrollArea->setFrameShape( QFrame::NoFrame ); + layout->addWidget( scrollArea ); + + buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok ); + mPropsWidget = new QgsDiagramProperties( layer, this, mapCanvas ); + mPropsWidget->setDockMode( false ); + + scrollArea->setWidget( mPropsWidget ); + layout->addWidget( buttonBox ); + this->setWindowTitle( "Edit Sub Diagram" ); + QgsGui::enableAutoGeometryRestore( this ); + + connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsStackedDiagramPropertiesDialog::accept ); + connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsStackedDiagramPropertiesDialog::showHelp ); +} + +void QgsStackedDiagramPropertiesDialog::syncToRenderer( const QgsDiagramRenderer *dr ) const +{ + mPropsWidget->syncToRenderer( dr ); +} + +void QgsStackedDiagramPropertiesDialog::syncToSettings( const QgsDiagramLayerSettings *dls ) const +{ + mPropsWidget->syncToSettings( dls ); +} + +void QgsStackedDiagramPropertiesDialog::accept() +{ + // Get renderer and diagram layer settings from widget + mRenderer = mPropsWidget->createRenderer(); + mDiagramLayerSettings = mPropsWidget->createDiagramLayerSettings(); + QDialog::accept(); +} + +QgsDiagramRenderer *QgsStackedDiagramPropertiesDialog::renderer() +{ + return mRenderer.release(); +} + +QgsDiagramLayerSettings QgsStackedDiagramPropertiesDialog::diagramLayerSettings() const +{ + return mDiagramLayerSettings; +} + +void QgsStackedDiagramPropertiesDialog::setAllowedToEditDiagramLayerSettings( bool allowed ) const +{ + mPropsWidget->setAllowedToEditDiagramLayerSettings( allowed ); +} + +bool QgsStackedDiagramPropertiesDialog::isAllowedToEditDiagramLayerSettings() const +{ + return mPropsWidget->isAllowedToEditDiagramLayerSettings(); +} + +void QgsStackedDiagramPropertiesDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#diagrams-properties" ) ); +} + +//// + +QgsStackedDiagramPropertiesModel::QgsStackedDiagramPropertiesModel( QObject *parent ) + : QAbstractTableModel( parent ) +{ +} + +Qt::ItemFlags QgsStackedDiagramPropertiesModel::flags( const QModelIndex &index ) const +{ + const Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags ); + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | checkable; +} + +QVariant QgsStackedDiagramPropertiesModel::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid() ) + return QVariant(); + + QgsDiagramRenderer *dr = subDiagramForIndex( index ); + + if ( role == Qt::DisplayRole || role == Qt::ToolTipRole ) + { + switch ( index.column() ) + { + case 1: + return ( !dr || !dr->diagram() ) ? tr( "(no diagram)" ) : dr->diagram()->diagramName(); + case 2: + if ( !dr ) + { + return tr( "(no renderer)" ); + } + else + { + if ( dr->rendererName() == QLatin1String( "SingleCategory" ) ) + return tr( "Fixed" ); + else if ( dr->rendererName() == QLatin1String( "LinearlyInterpolated" ) ) + return tr( "Scaled" ); + else + return tr( "Unknown" ); + } + case 3: + if ( dr && dr->diagram() && !dr->diagramSettings().isEmpty() ) + { + if ( QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM == dr->diagram()->diagramName() || QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR == dr->diagram()->diagramName() ) + { + switch ( dr->diagramSettings().at( 0 ).diagramOrientation ) + { + case QgsDiagramSettings::Left: + return tr( "Left" ); + case QgsDiagramSettings::Right: + return tr( "Right" ); + case QgsDiagramSettings::Up: + return tr( "Up" ); + case QgsDiagramSettings::Down: + return tr( "Down" ); + } + } + } + return QVariant(); + case 0: + default: + return QVariant(); + } + } + else if ( role == Qt::TextAlignmentRole ) + { + return index.column() == 0 ? static_cast( Qt::AlignCenter ) : static_cast( Qt::AlignLeft ); + } + else if ( role == Qt::CheckStateRole ) + { + if ( index.column() != 0 ) + return QVariant(); + + return ( dr && !dr->diagramSettings().isEmpty() && dr->diagramSettings().at( 0 ).enabled ) ? Qt::Checked : Qt::Unchecked; + } + else + { + return QVariant(); + } +} + +QVariant QgsStackedDiagramPropertiesModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 4 ) + { + QStringList lst; + lst << tr( "Enabled" ) << tr( "Diagram type" ) << tr( "Size" ) << tr( "Orientation" ); + return lst[section]; + } + + return QVariant(); +} + +int QgsStackedDiagramPropertiesModel::rowCount( const QModelIndex & ) const +{ + return mRenderers.size(); +} + +int QgsStackedDiagramPropertiesModel::columnCount( const QModelIndex & ) const +{ + return 4; +} + +bool QgsStackedDiagramPropertiesModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( !index.isValid() ) + return false; + + QgsDiagramRenderer *dr = subDiagramForIndex( index ); + + if ( role == Qt::CheckStateRole ) + { + if ( dr && !dr->diagramSettings().isEmpty() ) + { + QgsDiagramSettings ds = dr->diagramSettings().at( 0 ); + ds.enabled = ( value.toInt() == Qt::Checked ); + + if ( dr->rendererName() == QLatin1String( "SingleCategory" ) ) + { + QgsSingleCategoryDiagramRenderer *dsr = static_cast< QgsSingleCategoryDiagramRenderer * >( dr ); + dsr->setDiagramSettings( ds ); + } + else + { + QgsLinearlyInterpolatedDiagramRenderer *dlir = static_cast< QgsLinearlyInterpolatedDiagramRenderer * >( dr ); + dlir->setDiagramSettings( ds ); + } + + emit dataChanged( index, index ); + return true; + } + } + return false; +} + +bool QgsStackedDiagramPropertiesModel::removeRows( int row, int count, const QModelIndex &parent ) +{ + if ( row < 0 || row >= mRenderers.size() ) + return false; + + beginRemoveRows( parent, row, row + count - 1 ); + while ( count-- ) + mRenderers.removeAt( row ); + endRemoveRows(); + + return true; +} + +QgsDiagramRenderer *QgsStackedDiagramPropertiesModel::subDiagramForIndex( const QModelIndex &index ) const +{ + if ( index.isValid() ) + return mRenderers.at( index.row() ); + return nullptr; +} + +void QgsStackedDiagramPropertiesModel::insertSubDiagram( const int index, QgsDiagramRenderer *newSubDiagram ) +{ + beginInsertRows( QModelIndex(), index, index ); + mRenderers.insert( index, newSubDiagram ); + endInsertRows(); +} + +void QgsStackedDiagramPropertiesModel::updateSubDiagram( const QModelIndex &index, QgsDiagramRenderer *dr ) +{ + mRenderers.replace( index.row(), dr ); + emit dataChanged( index, index ); +} + +QList< QgsDiagramRenderer *> QgsStackedDiagramPropertiesModel::subRenderers() const +{ + QList subRenderers; + subRenderers = mRenderers; + return subRenderers; +} + +void QgsStackedDiagramPropertiesModel::updateDiagramLayerSettings( QgsDiagramLayerSettings dls ) +{ + mDiagramLayerSettings = dls; +} + +QgsDiagramLayerSettings QgsStackedDiagramPropertiesModel::diagramLayerSettings() const +{ + return mDiagramLayerSettings; +} diff --git a/src/gui/vector/qgsstackeddiagramproperties.h b/src/gui/vector/qgsstackeddiagramproperties.h new file mode 100644 index 000000000000..d80339150bb6 --- /dev/null +++ b/src/gui/vector/qgsstackeddiagramproperties.h @@ -0,0 +1,238 @@ +/*************************************************************************** + qgsstackeddiagramproperties.h + Properties for stacked diagram layers + ------------------- + begin : August 2024 + copyright : (C) Germán Carrillo + email : german at opengis dot ch + + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSTACKEDDIAGRAMPROPERTIES_H +#define QGSSTACKEDDIAGRAMPROPERTIES_H + +// We don't want to expose this in the public API +#define SIP_NO_FILE + +#include "qgis_gui.h" +#include "qgsdiagramrenderer.h" +#include "ui_qgsstackeddiagrampropertiesbase.h" + +#include +#include +#include + +class QgsVectorLayer; +class QgsMapCanvas; +class QgsDiagramProperties; +class QgsDiagramRenderer; + + +/** + * \ingroup gui + * \brief Model for sub diagrams in a stacked diagram view. + * + * \note This class is not a part of public API + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsStackedDiagramPropertiesModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + //! constructor + QgsStackedDiagramPropertiesModel( QObject *parent = nullptr ); + + Qt::ItemFlags flags( const QModelIndex &index ) const override; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + QVariant headerData( int section, Qt::Orientation orientation, + int role = Qt::DisplayRole ) const override; + int rowCount( const QModelIndex & = QModelIndex() ) const override; + int columnCount( const QModelIndex & = QModelIndex() ) const override; + + // editing support + bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; + bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() ) override; + + // new methods + + //! Returns the diagram renderer at the specified index + QgsDiagramRenderer *subDiagramForIndex( const QModelIndex &index ) const; + + //! Inserts a new diagram at the specified position + void insertSubDiagram( const int index, QgsDiagramRenderer *newSubDiagram ); + //! Replaces the diagram located at \a index by \a dr + void updateSubDiagram( const QModelIndex &index, QgsDiagramRenderer *dr ); + + //! Returns the list of diagram renderers from the model + QList< QgsDiagramRenderer *> subRenderers() const; + + //! Returns the diagram layer settings from the model + QgsDiagramLayerSettings diagramLayerSettings() const; + + /** + * Sets the diagram layer settings for the model. + * @param dls DiagramLayerSettings to be set. + */ + void updateDiagramLayerSettings( QgsDiagramLayerSettings dls ); + + protected: + QList< QgsDiagramRenderer *> mRenderers; + QgsDiagramLayerSettings mDiagramLayerSettings; +}; + + +/** + * \ingroup gui + * \class QgsStackedDiagramProperties + * + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsStackedDiagramProperties : public QgsPanelWidget, private Ui::QgsStackedDiagramPropertiesBase +{ + Q_OBJECT + + public: + explicit QgsStackedDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas ); + + /** + * Updates the widget to reflect the layer's current diagram settings. + */ + void syncToLayer(); + + signals: + void auxiliaryFieldCreated(); + + public slots: + void apply(); + + private slots: + + /** + * Adds a diagram to the current QgsStackedDiagramProperties. + */ + void addSubDiagram(); + + /** + * Appends a diagram to the current QgsStackedDiagramProperties. + * @param dr Diagram renderer to be appended. + */ + void appendSubDiagram( QgsDiagramRenderer *dr ); + + /** + * Edits the properties of the current diagram. + */ + void editSubDiagram(); + + /** + * Edits the properties of a diagram located at a given \a index. + * @param index Model index where the diagram is located. + */ + void editSubDiagram( const QModelIndex &index ); + + /** + * Removes a diagram from the current QgsStackedDiagramProperties. + */ + void removeSubDiagram(); + + private: + QgsVectorLayer *mLayer = nullptr; + QgsMapCanvas *mMapCanvas = nullptr; + + QgsStackedDiagramPropertiesModel *mModel = nullptr; + + /** + * Determines whether the subdiagram in the given \a index may be + * the first sub diagram in the stacked diagram. This includes the + * first enabled sub diagram, as well as disabled sub diagrams that, + * after being edited, can become the first enabled one. + * @param index Model index where the sub diagram is located. + */ + bool couldBeFirstSubDiagram( const QModelIndex &index ) const; + + private slots: + void subDiagramWidgetPanelAccepted( QgsPanelWidget *panel ); + void liveUpdateSubDiagramFromPanel(); +}; + +/** + * \ingroup gui + * \class QgsStackedDiagramPropertiesDialog + * \brief Dialog for editing sub diagrams + * + * \note This class is not a part of public API + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsStackedDiagramPropertiesDialog : public QDialog +{ + Q_OBJECT + + public: + + /** + * Constructor for QgsStackedDiagramPropertiesDialog + * \param layer source vector layer + * \param parent parent widget + * \param mapCanvas map canvas + */ + QgsStackedDiagramPropertiesDialog( QgsVectorLayer *layer, QWidget *parent = nullptr, QgsMapCanvas *mapCanvas = nullptr ); + + /** + * Delegates to the diagram properties widget to sync with the given renderer. + * @param dr Diagram Renderer to be used for the sync. + */ + void syncToRenderer( const QgsDiagramRenderer *dr ) const; + + /** + * Delegates to the diagram properties widget to sync with the given diagram layer settings. + * @param dls Diagram Layer Settings to be used for the sync. + */ + void syncToSettings( const QgsDiagramLayerSettings *dls ) const; + + /** + * Gets a renderer object built from the diagram properties widget. + */ + QgsDiagramRenderer *renderer(); + + /** + * Gets diagram layer settings built from the diagram properties widget. + */ + QgsDiagramLayerSettings diagramLayerSettings() const; + + /** + * Delegates to the main widget to set whether the widget should show + * diagram layer settings to be edited. + * @param allowed Whether the main widget should be allowed to edit diagram layer settings. + */ + void setAllowedToEditDiagramLayerSettings( bool allowed ) const; + + /** + * Returns whether the main widget is allowed to edit diagram layer settings. + */ + bool isAllowedToEditDiagramLayerSettings() const; + + public slots: + + /** + * Applies changes from the widget to the internal renderer and diagram layer settings. + */ + void accept() override; + + private slots: + void showHelp(); + + private: + QgsDiagramProperties *mPropsWidget = nullptr; + std::unique_ptr< QgsDiagramRenderer > mRenderer; + QgsDiagramLayerSettings mDiagramLayerSettings; + QDialogButtonBox *buttonBox = nullptr; +}; + +#endif // QGSSTACKEDDIAGRAMPROPERTIES_H diff --git a/src/gui/vector/qgsvectorlayerproperties.cpp b/src/gui/vector/qgsvectorlayerproperties.cpp index 2133b0083dcc..ead248999eaa 100644 --- a/src/gui/vector/qgsvectorlayerproperties.cpp +++ b/src/gui/vector/qgsvectorlayerproperties.cpp @@ -26,7 +26,7 @@ #include "qgsapplication.h" #include "qgsattributeactiondialog.h" #include "qgsdatumtransformdialog.h" -#include "qgsdiagramproperties.h" +#include "qgsdiagramwidget.h" #include "qgssourcefieldsproperties.h" #include "qgsattributesformproperties.h" #include "qgslabelingwidget.h" @@ -280,6 +280,15 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( mSelectionSymbolButton->setEnabled( false ); mRadioDefaultSelectionColor->setChecked( true ); + // Diagram tab, before the syncToLayer + QVBoxLayout *diagLayout = new QVBoxLayout( mDiagramFrame ); + diagLayout->setContentsMargins( 0, 0, 0, 0 ); + diagramPropertiesDialog = new QgsDiagramWidget( mLayer, mCanvas, mDiagramFrame ); + diagramPropertiesDialog->layout()->setContentsMargins( 0, 0, 0, 0 ); + connect( diagramPropertiesDialog, &QgsDiagramWidget::auxiliaryFieldCreated, this, [ = ] { updateAuxiliaryStoragePage(); } ); + diagLayout->addWidget( diagramPropertiesDialog ); + mDiagramFrame->setLayout( diagLayout ); + syncToLayer(); if ( mLayer->dataProvider() ) @@ -333,14 +342,6 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( mOldJoins = mLayer->vectorJoins(); - QVBoxLayout *diagLayout = new QVBoxLayout( mDiagramFrame ); - diagLayout->setContentsMargins( 0, 0, 0, 0 ); - diagramPropertiesDialog = new QgsDiagramProperties( mLayer, mDiagramFrame, mCanvas ); - diagramPropertiesDialog->layout()->setContentsMargins( 0, 0, 0, 0 ); - connect( diagramPropertiesDialog, &QgsDiagramProperties::auxiliaryFieldCreated, this, [ = ] { updateAuxiliaryStoragePage(); } ); - diagLayout->addWidget( diagramPropertiesDialog ); - mDiagramFrame->setLayout( diagLayout ); - // Legend tab mLegendWidget->setMapCanvas( mCanvas ); mLegendWidget->setLayer( mLayer ); @@ -728,7 +729,7 @@ void QgsVectorLayerProperties::syncToLayer() updateVariableEditor(); if ( diagramPropertiesDialog ) - diagramPropertiesDialog->syncToLayer(); + diagramPropertiesDialog->syncToOwnLayer(); // sync all plugin dialogs for ( QgsMapLayerConfigWidget *page : std::as_const( mConfigWidgets ) ) diff --git a/src/gui/vector/qgsvectorlayerproperties.h b/src/gui/vector/qgsvectorlayerproperties.h index f3644cc73d63..70cf23d77c04 100644 --- a/src/gui/vector/qgsvectorlayerproperties.h +++ b/src/gui/vector/qgsvectorlayerproperties.h @@ -33,7 +33,7 @@ class QgsMapLayer; class QgsAttributeActionDialog; class QgsVectorLayer; class QgsLabelingWidget; -class QgsDiagramProperties; +class QgsDiagramWidget; class QgsSourceFieldsProperties; class QgsAttributesFormProperties; class QgsRendererPropertiesDialog; @@ -172,7 +172,7 @@ class GUI_EXPORT QgsVectorLayerProperties : public QgsLayerPropertiesDialog, pri //! Actions dialog. If apply is pressed, the actions are stored for later use QgsAttributeActionDialog *mActionDialog = nullptr; //! Diagram dialog. If apply is pressed, options are applied to vector's diagrams - QgsDiagramProperties *diagramPropertiesDialog = nullptr; + QgsDiagramWidget *diagramPropertiesDialog = nullptr; //! SourceFields dialog. If apply is pressed, options are applied to vector's diagrams QgsSourceFieldsProperties *mSourceFieldsPropertiesDialog = nullptr; //! AttributesForm dialog. If apply is pressed, options are applied to vector's diagrams @@ -181,8 +181,6 @@ class GUI_EXPORT QgsVectorLayerProperties : public QgsLayerPropertiesDialog, pri //! List of joins of a layer at the time of creation of the dialog. Used to return joins to previous state if dialog is canceled QList< QgsVectorLayerJoinInfo > mOldJoins; - void initDiagramTab(); - //! Adds a new join to mJoinTreeWidget void addJoinToTreeWidget( const QgsVectorLayerJoinInfo &join, int insertIndex = -1 ); diff --git a/src/ui/qgsdiagrampropertiesbase.ui b/src/ui/qgsdiagrampropertiesbase.ui index 2ee3da44edcc..f9ae3638ebde 100644 --- a/src/ui/qgsdiagrampropertiesbase.ui +++ b/src/ui/qgsdiagrampropertiesbase.ui @@ -10,7 +10,7 @@ 491 - + 0 @@ -48,30 +48,86 @@ - - - - true - - - Automated placement settings (apply to all layers) - - - - :/images/themes/default/mIconAutoPlacementSettings.svg:/images/themes/default/mIconAutoPlacementSettings.svg - - - + + + + 0 + + + + 20 + 20 + + + + true + + + + + :/images/themes/default/propertyicons/symbology.svg:/images/themes/default/propertyicons/symbology.svg + + + + + + + + + :/images/themes/default/propertyicons/render.svg:/images/themes/default/propertyicons/render.svg + + + + + + + + + :/images/themes/default/propertyicons/transparency.svg:/images/themes/default/propertyicons/transparency.svg + + + + + + + + + :/images/themes/default/propertyicons/labelplacement.svg:/images/themes/default/propertyicons/labelplacement.svg + + + + + + + + + :/images/themes/default/propertyicons/action.svg:/images/themes/default/propertyicons/action.svg + + + + + + + + + :/images/themes/default/legend.svg:/images/themes/default/legend.svg + + + + + + + - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised @@ -92,7 +148,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal false @@ -111,10 +167,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised @@ -218,10 +274,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised @@ -265,7 +321,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -275,11 +331,11 @@ 0 0 - 642 - 421 + 646 + 373 - + 0 @@ -303,8 +359,14 @@ + + + 0 + 0 + + - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection 0 @@ -331,11 +393,11 @@ - + - Qt::Vertical + Qt::Orientation::Vertical @@ -402,7 +464,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -425,6 +487,12 @@ + + + 0 + 0 + + Drag and drop to reorder @@ -432,13 +500,13 @@ true - QAbstractItemView::InternalMove + QAbstractItemView::DragDropMode::InternalMove - Qt::TargetMoveAction + Qt::DropAction::TargetMoveAction - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection 0 @@ -507,7 +575,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -517,11 +585,11 @@ 0 0 - 630 - 535 + 632 + 671 - + 0 @@ -605,7 +673,7 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus @@ -710,7 +778,7 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus @@ -770,7 +838,7 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus @@ -853,154 +921,172 @@ labelrenderinggroup - - - - - + + + + + + 0 + 0 + + + + Controls how diagrams are drawn on top of each other. Diagrams with a higher z-index are drawn above diagrams and labels with a lower z-index. + + + -9999999.000000000000000 + + + 9999999.000000000000000 + + + + + + + Diagram z-index + + + + + + + Scale dependent visibility + + + true + + + + 9 + + + 9 + + + + + + + + + + + + + + + + + + Always show all diagrams, even when they overlap with each other or other map labels + + + Show all diagrams + + + true + + + + + + + + + true - - - - - + 0 0 - Controls how diagrams are drawn on top of each other. Diagrams with a higher z-index are drawn above diagrams and labels with a lower z-index. - - - -9999999.000000000000000 + Controls whether specific diagrams should be shown - - 9999999.000000000000000 + + Show diagram - - + + - Diagram z-index + - - - - - - true - - - - 0 - 0 - - - - Controls whether specific diagrams should be shown - - - Show diagram - - - - - - - - - - - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - Controls whether specific diagrams should always be rendered, even when they overlap other diagrams or map labels - - - Always show - - - - - - - - - - - - - - Qt::Horizontal - - - - 195 - 20 - - - - - - - - - - Scale dependent visibility - - - true + + + + Qt::Orientation::Vertical - - - 9 - - - 9 - - - - - - - + + + + + 0 + 0 + + - Always show all diagrams, even when they overlap with each other or other map labels + Controls whether specific diagrams should always be rendered, even when they overlap other diagrams or map labels - Show all diagrams + Always show - - true + + + + + + + + + + Qt::Orientation::Horizontal + + + + 195 + 20 + + + + + + + + + + true + + + + color: rgb(220, 125, 0); + + + z-index and show-all-diagrams settings can be configured in the first enabled sub diagram of the stacked diagram. + + + true + + + + - Qt::Vertical + Qt::Orientation::Vertical @@ -1043,7 +1129,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -1053,11 +1139,11 @@ 0 0 - 642 - 421 + 646 + 373 - + 0 @@ -1109,14 +1195,14 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus - Qt::Vertical + Qt::Orientation::Vertical @@ -1129,10 +1215,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -1155,6 +1241,9 @@ true + + 0 + @@ -1179,10 +1268,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised @@ -1198,36 +1287,44 @@ 0 - - + + Increase size of small diagrams - - - - - - Minimum size - - - - - - - - 0 - 0 - - - - 6 - - - 100000.000000000000000 + + true - - 0.200000000000000 + + true + + + + + Minimum size + + + + + + + + 0 + 0 + + + + 6 + + + 100000.000000000000000 + + + 0.200000000000000 + + + + @@ -1344,7 +1441,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -1354,11 +1451,11 @@ 0 0 - 642 - 421 + 286 + 481 - + 0 @@ -1371,320 +1468,19 @@ 0 - - - - Coordinates - - - - - - - 0 - 0 - - - - X - - - - - - - - - - - - - - - 0 - 0 - - - - Y - - - - - - - - - - - - - - Qt::Horizontal - - - - 0 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - + + + + + 0 + 0 + - - - - - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - Above line - - - true - - - - - - - Below line - - - - - - - - 0 - 0 - - - - On line - - - - - - - Line orientation dependent position - - - - - - - - - - - - Priority - - - labelplacementgroup - - - - 8 - - - 8 - - - - - Low - - - - - - - 10 - - - Qt::Horizontal - - - false - - - false - - - QSlider::TicksBelow - - - 1 - - - - - - - High - - - - - - - - - - - - - - - - - Obstacles - - - labelplacementgroup - - - - 8 - - - 8 - - - - - Discourage diagrams and labels from covering features - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - QFrame::NoFrame - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Distance - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Sunken + QFrame::Shadow::Sunken 0 @@ -1729,7 +1525,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1787,7 +1583,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1839,7 +1635,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1882,6 +1678,337 @@ + + + + + true + + + + color: rgb(220, 125, 0); + + + Placement settings can be configured in the first enabled sub diagram of the stacked diagram. + + + true + + + + + + + QFrame::Shape::NoFrame + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Distance + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + Above line + + + true + + + + + + + Below line + + + + + + + + 0 + 0 + + + + On line + + + + + + + Line orientation dependent position + + + + + + + + + + + + Coordinates + + + + + + + 0 + 0 + + + + X + + + + + + + + + + + + + + + 0 + 0 + + + + Y + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 0 + 20 + + + + + + + + + + + Priority + + + labelplacementgroup + + + + 8 + + + 8 + + + + + Low + + + + + + + 10 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::TicksBelow + + + 1 + + + + + + + High + + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Obstacles + + + labelplacementgroup + + + + 8 + + + 8 + + + + + + 0 + 0 + + + + Discourage diagrams and labels from covering features + + + true + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Expanding + + + + 40 + 20 + + + + + + + @@ -1915,7 +2042,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -1925,11 +2052,11 @@ 0 0 - 642 - 421 + 106 + 165 - + 0 @@ -1945,10 +2072,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -2019,10 +2146,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -2078,7 +2205,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2094,7 +2221,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -2134,7 +2261,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -2144,11 +2271,11 @@ 0 0 - 341 - 81 + 336 + 80 - + @@ -2171,7 +2298,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2186,7 +2313,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -2214,18 +2341,6 @@ - - QgsScrollArea - QScrollArea -
qgsscrollarea.h
- 1 -
- - QgsCollapsibleGroupBox - QGroupBox -
qgscollapsiblegroupbox.h
- 1 -
QgsDoubleSpinBox QDoubleSpinBox @@ -2244,31 +2359,38 @@ 1 - QgsPropertyOverrideButton - QToolButton -
qgspropertyoverridebutton.h
+ QgsScrollArea + QScrollArea +
qgsscrollarea.h
+ 1
- QgsFieldExpressionWidget + QgsScaleRangeWidget QWidget -
qgsfieldexpressionwidget.h
- 1 +
qgsscalerangewidget.h
- QgsOpacityWidget + QgsEffectStackCompactWidget QWidget -
qgsopacitywidget.h
+
qgseffectstackpropertieswidget.h
1
- QgsFontButton + QgsPropertyOverrideButton QToolButton -
qgsfontbutton.h
+
qgspropertyoverridebutton.h
- QgsScaleRangeWidget + QgsCollapsibleGroupBox + QGroupBox +
qgscollapsiblegroupbox.h
+ 1 +
+ + QgsFieldExpressionWidget QWidget -
qgsscalerangewidget.h
+
qgsfieldexpressionwidget.h
+ 1
QgsSymbolButton @@ -2276,15 +2398,19 @@
qgssymbolbutton.h
- QgsEffectStackCompactWidget + QgsOpacityWidget QWidget -
qgseffectstackpropertieswidget.h
+
qgsopacitywidget.h
1
+ + QgsFontButton + QToolButton +
qgsfontbutton.h
+
mDiagramTypeComboBox - mEngineSettingsButton mDiagramOptionsListWidget scrollArea mAttributesTreeWidget @@ -2325,7 +2451,7 @@ mFindMaximumValueButton mSizeSpinBox mScaleDependencyComboBox - mIncreaseSmallDiagramsCheck + mIncreaseSmallDiagramsGroupBox mIncreaseMinimumSizeSpinBox scrollArea_6 radAroundPoint @@ -2409,34 +2535,18 @@ - mIncreaseSmallDiagramsCheck - toggled(bool) - mIncreaseMinimumSizeSpinBox - setEnabled(bool) - - - 248 - 341 - - - 589 - 342 - - - - - mIncreaseSmallDiagramsCheck - toggled(bool) - mIncreaseMinimumSizeLabel - setEnabled(bool) + mOptionsTab + currentChanged(int) + mDiagramStackedWidget + setCurrentIndex(int) - 248 - 341 + 343 + 56 - 476 - 342 + 364 + 286 diff --git a/src/ui/qgsdiagramwidget.ui b/src/ui/qgsdiagramwidget.ui new file mode 100644 index 000000000000..b6f803546756 --- /dev/null +++ b/src/ui/qgsdiagramwidget.ui @@ -0,0 +1,59 @@ + + + QgsDiagramWidget + + + + 0 + 0 + 842 + 472 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + true + + + Automated placement settings (applies to all layers) + + + + :/images/themes/default/mIconAutoPlacementSettings.svg:/images/themes/default/mIconAutoPlacementSettings.svg + + + + + + + + + + + + + + + + + + diff --git a/src/ui/qgsstackeddiagrampropertiesbase.ui b/src/ui/qgsstackeddiagrampropertiesbase.ui new file mode 100644 index 000000000000..6c1fa9553c5f --- /dev/null +++ b/src/ui/qgsstackeddiagrampropertiesbase.ui @@ -0,0 +1,224 @@ + + + QgsStackedDiagramPropertiesBase + + + + 0 + 0 + 842 + 472 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::SelectionMode::SingleSelection + + + false + + + false + + + 57 + + + 110 + + + + + + + + + + 0 + 0 + + + + Add subdiagram to the stacked diagram + + + + + + + :/images/themes/default/symbologyAdd.svg:/images/themes/default/symbologyAdd.svg + + + + + + + Edit subdiagram from the stacked diagram + + + + + + + :/images/themes/default/symbologyEdit.svg:/images/themes/default/symbologyEdit.svg + + + + + + + + 0 + 0 + + + + Remove subdiagram from the stacked diagram + + + + + + + :/images/themes/default/symbologyRemove.svg:/images/themes/default/symbologyRemove.svg + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Expanding + + + + 40 + 20 + + + + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + + Diagram spacing + + + + + + + + 70 + 0 + + + + + + + + + 120 + 0 + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Stacked diagram mode + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
+ + QgsUnitSelectionWidget + QWidget +
qgsunitselectionwidget.h
+ 1 +
+
+ + + + +
diff --git a/tests/code_layout/acceptable_missing_doc.py b/tests/code_layout/acceptable_missing_doc.py index 5b026a010e2e..c4c96cdae63d 100644 --- a/tests/code_layout/acceptable_missing_doc.py +++ b/tests/code_layout/acceptable_missing_doc.py @@ -135,6 +135,7 @@ "pal::PointSet": ["getCentroid(double &px, double &py, bool forceInside=false) const", "PointSet(double x, double y)", "invalidateGeos() const", "getGeosType() const", "PointSet(int nbPoints, double *x, double *y)", "getNumPoints() const", "deleteCoords()", "preparedGeom() const", "PointSet(const PointSet &ps)", "createGeosGeom() const"], "QgsSimpleFillSymbolLayer": ["createFromSld(QDomElement &element)", "setBrushStyle(Qt::BrushStyle style)", "setStrokeWidth(double strokeWidth)", "strokeWidth() const", "brushStyle() const", "strokeWidthMapUnitScale() const", "strokeStyle() const", "setPenJoinStyle(Qt::PenJoinStyle style)", "QgsSimpleFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, Qt::BrushStyle style=DEFAULT_SIMPLEFILL_STYLE, const QColor &strokeColor=DEFAULT_SIMPLEFILL_BORDERCOLOR, Qt::PenStyle strokeStyle=DEFAULT_SIMPLEFILL_BORDERSTYLE, double strokeWidth=DEFAULT_SIMPLEFILL_BORDERWIDTH, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEFILL_JOINSTYLE)", "setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)", "penJoinStyle() const", "setStrokeStyle(Qt::PenStyle strokeStyle)"], "QgsSingleCategoryDiagramRenderer": ["setDiagramSettings(const QgsDiagramSettings &s)"], + "QgsStackedDiagramRenderer": ["setDiagramSettings(const QgsDiagramSettings &s)"], "QgsPointDisplacementRendererWidget": ["QgsPointDisplacementRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)", "create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)"], "QgsSingleBandGrayRendererWidget": ["create(QgsRasterLayer *layer, const QgsRectangle &extent)", "QgsSingleBandGrayRendererWidget(QgsRasterLayer *layer, const QgsRectangle &extent=QgsRectangle())"], "QgsLayerTreeModel": ["nodeWillRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)", "disconnectFromLayers(QgsLayerTreeGroup *parentGroup)", "connectToLayers(QgsLayerTreeGroup *parentGroup)", "nodeLayerWillBeUnloaded()", "legendInvalidateMapBasedData()", "nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)", "legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const", "legendCleanup()", "Flag", "iconGroup()", "disconnectFromRootNode()", "layerNeedsUpdate()", "addLegendToLayer(QgsLayerTreeLayer *nodeL)", "nodeVisibilityChanged(QgsLayerTreeNode *node)", "layerLegendChanged()", "legendRootRowCount(QgsLayerTreeLayer *nL) const", "removeLegendFromLayer(QgsLayerTreeLayer *nodeLayer)", "legendNodeRowCount(QgsLayerTreeModelLegendNode *node) const", "legendNodeDataChanged()", "connectToRootNode()", "legendNodeFlags(QgsLayerTreeModelLegendNode *node) const", "indexOfParentLayerTreeNode(QgsLayerTreeNode *parentNode) const", "nodeLayerLoaded()", "nodeWillAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)", "nodeRemovedChildren()", "legendRootIndex(int row, int column, QgsLayerTreeLayer *nL) const", "connectToLayer(QgsLayerTreeLayer *nodeLayer)", "legendIconEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const", "nodeAddedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)", "legendNodeIndex(int row, int column, QgsLayerTreeModelLegendNode *node) const", "invalidateLegendMapBasedData()", "disconnectFromLayer(QgsLayerTreeLayer *nodeLayer)", "legendEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const", "legendParent(QgsLayerTreeModelLegendNode *legendNode) const"], @@ -285,6 +286,8 @@ "QgsEffectStack": ["QgsEffectStack(const QgsEffectStack &other)"], "QgsRelationReferenceWidget": ["setRelation(const QgsRelation &relation, bool allowNullValue)", "CanvasExtent", "setOpenFormButtonVisible(bool openFormButtonVisible)", "QgsRelationReferenceWidget(QWidget *parent)", "setReadOnlySelector(bool readOnly)", "setRelationEditable(bool editable)", "init()", "setAllowMapIdentification(bool allowMapIdentification)", "setEmbedForm(bool display)"], "QgsDiagramProperties": ["mAttributesTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)", "scalingTypeChanged()", "mAddCategoryPushButton_clicked()", "mEngineSettingsButton_clicked()", "apply()", "mRemoveCategoryPushButton_clicked()", "showSizeLegendDialog()", "QgsDiagramProperties(QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas)", "mDiagramAttributesTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)", "mDiagramStackedWidget_currentChanged(int index)", "mDiagramTypeComboBox_currentIndexChanged(int index)", "mFindMaximumValueButton_clicked()", "showAddAttributeExpressionDialog()", "auxiliaryFieldCreated()", "updatePlacementWidgets()"], + "QgsStackedDiagramProperties": ["apply()", "auxiliaryFieldCreated()", "QgsStackedDiagramProperties(QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas)"], + "QgsDiagramWidget": ["mDiagramTypeComboBox_currentIndexChanged(int index)", "showEngineConfigDialog()"], "QgsFeatureListView": ["repaintRequested()", "repaintRequested(const QModelIndexList &indexes)"], "QgsGradientFillSymbolLayerWidget": ["setGradientSpread(int index)", "setColor(const QColor &color)", "setCoordinateMode(int index)", "setColor2(const QColor &color)", "setGradientType(int index)"], "QgsFillSymbol": ["setAngle(double angle) const"], diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index 2c5b9e379869..81b8326760a7 100644 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -181,6 +181,7 @@ set(TESTS testqgsspatialindexkdbush.cpp testqgssqliteexpressioncompiler.cpp testqgssqliteutils.cpp + testqgsstackeddiagram.cpp testqgsrelation.cpp testqgsstatisticalsummary.cpp testqgsstoredexpressionmanager.cpp diff --git a/tests/src/core/testqgsstackeddiagram.cpp b/tests/src/core/testqgsstackeddiagram.cpp new file mode 100644 index 000000000000..add44c566bec --- /dev/null +++ b/tests/src/core/testqgsstackeddiagram.cpp @@ -0,0 +1,1372 @@ +/*************************************************************************** + testqgsstackeddiagram.cpp + -------------------------------------- + Date : Jun 25 2024 + Copyright : (C) 2024 by Germán Carrillo + Email : german at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgstest.h" +#include "diagram/qgspiediagram.h" +#include "diagram/qgstextdiagram.h" +#include "diagram/qgsstackedbardiagram.h" +#include "diagram/qgshistogramdiagram.h" +#include "diagram/qgsstackeddiagram.h" +#include "qgsdiagramrenderer.h" +#include "qgsmaplayer.h" +#include "qgsvectorlayer.h" +#include "qgsapplication.h" +#include "qgssinglesymbolrenderer.h" +#include "qgsproject.h" +#include "qgsmarkersymbol.h" + +#include + +/** + * \ingroup UnitTests + * Unit tests for stacked diagrams + * (Based on TestQgsDiagram) + */ +class TestQgsStackedDiagram : public QgsTest +{ + Q_OBJECT + + public: + TestQgsStackedDiagram() : QgsTest( QStringLiteral( "Stacked Diagram Tests" ), QStringLiteral( "stackeddiagrams" ) ) {} + + private: + bool mTestHasError = false ; + QgsMapSettings *mMapSettings = nullptr; + QgsVectorLayer *mPointsLayer = nullptr; + QString mTestDataDir; + + private slots: + // will be called before the first testfunction is executed. + void initTestCase() + { + mTestHasError = false; + QgsApplication::init(); + QgsApplication::initQgis(); + QgsApplication::showSettings(); + + mMapSettings = new QgsMapSettings(); + + //create some objects that will be used in all tests... + + const QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt + mTestDataDir = myDataDir + '/'; + + // + //create a point layer that will be used in all tests... + // + const QString myPointsFileName = mTestDataDir + "stacked_diagrams.gpkg|layername=centroids"; + mPointsLayer = new QgsVectorLayer( myPointsFileName, + QStringLiteral( "population" ), QStringLiteral( "ogr" ) ); + + //Add points to diagrams, so that it's easier to also verify diagram positioning + QVariantMap symbolProps { { QStringLiteral( "color" ), QStringLiteral( "0,0,0,0" ) } }; + QgsMarkerSymbol *symbol = QgsMarkerSymbol::createSimple( symbolProps ); + QgsSingleSymbolRenderer *symbolRenderer = new QgsSingleSymbolRenderer( symbol ); + mPointsLayer->setRenderer( symbolRenderer ); + + // Create map composition to draw on + QgsProject::instance()->addMapLayer( mPointsLayer ); + mMapSettings->setLayers( QList() << mPointsLayer ); + } + + // will be called after the last testfunction was executed. + void cleanupTestCase() + { + delete mMapSettings; + QgsProject::instance()->removeAllMapLayers(); + + QgsApplication::exitQgis(); + } + + // will be called before each testfunction is executed + void init() + { + mPointsLayer->setDiagramRenderer( nullptr ); + const QgsDiagramLayerSettings dls; + mPointsLayer->setDiagramLayerSettings( dls ); + } + + // will be called after every testfunction. + void cleanup() + { + } + + void testStackedHistograms() + { + // Histogram 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Left; + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Right; + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Pixels ); + ds.setStackedDiagramSpacing( 0 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedhistograms", "stackedhistograms", *mMapSettings, 200, 15 ); + } + + void testDisabledSubDiagram() + { + // Histogram 1 (disabled) + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Left; + ds1.enabled = false; + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Right; + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Pixels ); + ds.setStackedDiagramSpacing( 0 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "disabledsubdiagram", "disabledsubdiagram", *mMapSettings, 200, 15 ); + } + + void testScaledDependentVisibilitySubDiagram() + { + // Histogram 1 (disabled) + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Left; + ds1.scaleBasedVisibility = true; + ds1.maximumScale = 0; + ds1.minimumScale = 50000; + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Right; + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Pixels ); + ds.setStackedDiagramSpacing( 0 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "scaledependentvisibilitysubdiagram", "scaledependentvisibilitysubdiagram", *mMapSettings, 200, 15 ); + } + + void testVerticallyStackedHistograms() + { + // Histogram 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Up; + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Down; + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Vertical; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Pixels ); + ds.setStackedDiagramSpacing( 0 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "verticallystackedhistograms", "verticallystackedhistograms", *mMapSettings, 200, 15 ); + } + + void testVerticallyStackedHistogramsWithSpacing() + { + // Histogram 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Up; + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Down; + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Vertical; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 8 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "verticallystackedhistogramswithspacing", "verticallystackedhistogramswithspacing", *mMapSettings, 200, 15 ); + } + + void testStackedHistogramsWithSpacing() + { + // Histogram 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Left; + ds1.setSpacing( 8 ); + ds1.setSpacingUnit( Qgis::RenderUnit::Points ); + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Right; + ds2.setSpacing( 8 ); + ds2.setSpacingUnit( Qgis::RenderUnit::Points ); + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 8 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedhistogramswithspacing", "stackedhistogramswithspacing", *mMapSettings, 200, 15 ); + } + + void testStackedHistogramsWithSpacing2() + { + // Histogram 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.scaleByArea = true; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.rotationOffset = 0; + ds1.diagramOrientation = QgsDiagramSettings::Left; + ds1.setSpacing( 0 ); + ds1.setSpacingUnit( Qgis::RenderUnit::Points ); + + QgsLinearlyInterpolatedDiagramRenderer *dr1 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr1->setDiagram( new QgsHistogramDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->setLowerValue( 0.0 ); + dr1->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr1->setUpperValue( 15000 ); + dr1->setUpperSize( QSizeF( 20, 20 ) ); + //dr1->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Right; + ds2.setSpacing( 0 ); + ds2.setSpacingUnit( Qgis::RenderUnit::Points ); + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + //dr2->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 8 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedhistogramswithspacing2", "stackedhistogramswithspacing2", *mMapSettings, 200, 15 ); + } + + void testMenHistogram() + { + QgsDiagramSettings ds; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds.categoryColors = QList() << col1 << col2 << col3 << col4; + ds.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds.minimumScale = -1; + ds.maximumScale = -1; + ds.minimumSize = 0; + ds.penColor = Qt::black; + ds.penWidth = .5; + ds.scaleByArea = true; + ds.sizeType = Qgis::RenderUnit::Millimeters; + ds.size = QSizeF( 3, 3 ); + ds.rotationOffset = 0; + ds.diagramOrientation = QgsDiagramSettings::Left; + + QgsLinearlyInterpolatedDiagramRenderer *dr = new QgsLinearlyInterpolatedDiagramRenderer(); + dr->setLowerValue( 0.0 ); + dr->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr->setUpperValue( 15000 ); + dr->setUpperSize( QSizeF( 20, 20 ) ); + dr->setClassificationField( QStringLiteral( "max( \"maennlich_6_17\", \"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_unter_6\" )" ) ); //#spellok + dr->setDiagram( new QgsHistogramDiagram() ); + dr->setDiagramSettings( ds ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedmenhistogram", "stackedmenhistogram", *mMapSettings, 200, 15 ); + } + + void testWomenHistogram() + { + QgsDiagramSettings ds; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds.categoryColors = QList() << col1 << col2 << col3 << col4; + ds.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds.minimumScale = -1; + ds.maximumScale = -1; + ds.minimumSize = 0; + ds.penColor = Qt::black; + ds.penWidth = .5; + ds.scaleByArea = true; + ds.sizeType = Qgis::RenderUnit::Millimeters; + ds.size = QSizeF( 3, 3 ); + ds.rotationOffset = 0; + ds.diagramOrientation = QgsDiagramSettings::Right; + + QgsLinearlyInterpolatedDiagramRenderer *dr = new QgsLinearlyInterpolatedDiagramRenderer(); + dr->setLowerValue( 0.0 ); + dr->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr->setUpperValue( 15000 ); + dr->setUpperSize( QSizeF( 20, 20 ) ); + dr->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + dr->setDiagram( new QgsHistogramDiagram() ); + dr->setDiagramSettings( ds ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedwomenhistogram", "stackedwomenhistogram", *mMapSettings, 200, 15 ); + } + + void testStackedPiesVertical() + { + // Pie 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.size = QSizeF( 10, 10 ); + ds1.rotationOffset = 270; + ds1.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr1 = new QgsSingleCategoryDiagramRenderer(); + dr1->setDiagram( new QgsPieDiagram() ); + dr1->setDiagramSettings( ds1 ); + + // Pie 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.size = QSizeF( 10, 10 ); + ds2.rotationOffset = 270; + ds2.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr2 = new QgsSingleCategoryDiagramRenderer(); + dr2->setDiagram( new QgsPieDiagram() ); + dr2->setDiagramSettings( ds2 ); + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Vertical; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedpies", "stackedpies", *mMapSettings, 200, 15 ); + } + + void testStackedPiesVerticalWithSpacing() + { + // Pie 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.size = QSizeF( 10, 10 ); + ds1.rotationOffset = 270; + ds1.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr1 = new QgsSingleCategoryDiagramRenderer(); + dr1->setDiagram( new QgsPieDiagram() ); + dr1->setDiagramSettings( ds1 ); + + // Pie 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.size = QSizeF( 10, 10 ); + ds2.rotationOffset = 270; + ds2.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr2 = new QgsSingleCategoryDiagramRenderer(); + dr2->setDiagram( new QgsPieDiagram() ); + dr2->setDiagramSettings( ds2 ); + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Vertical; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 8 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedpiesverticalwithspacing", "stackedpiesverticalwithspacing", *mMapSettings, 200, 15 ); + } + + void testStackedPiesHorizontalWithSpacing() + { + // Pie 1 + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.size = QSizeF( 10, 10 ); + ds1.rotationOffset = 270; + ds1.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr1 = new QgsSingleCategoryDiagramRenderer(); + dr1->setDiagram( new QgsPieDiagram() ); + dr1->setDiagramSettings( ds1 ); + + // Pie 2 + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.size = QSizeF( 10, 10 ); + ds2.rotationOffset = 270; + ds2.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr2 = new QgsSingleCategoryDiagramRenderer(); + dr2->setDiagram( new QgsPieDiagram() ); + dr2->setDiagramSettings( ds2 ); + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 8 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedpieshorizontalwithspacing", "stackedpieshorizontalwithspacing", *mMapSettings, 200, 15 ); + } + + void testMenPie() + { + QgsDiagramSettings ds; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds.categoryColors = QList() << col1 << col2 << col3 << col4; + ds.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds.minimumScale = -1; + ds.maximumScale = -1; + ds.minimumSize = 0; + ds.penColor = Qt::black; + ds.penWidth = .5; + ds.sizeType = Qgis::RenderUnit::Millimeters; + ds.size = QSizeF( 10, 10 ); + ds.rotationOffset = 270; + ds.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr = new QgsSingleCategoryDiagramRenderer(); + dr->setDiagram( new QgsPieDiagram() ); + dr->setDiagramSettings( ds ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedmenpie", "stackedmenpie", *mMapSettings, 200, 15 ); + } + + void testWomenPie() + { + QgsDiagramSettings ds; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds.categoryColors = QList() << col1 << col2 << col3 << col4; + ds.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds.minimumScale = -1; + ds.maximumScale = -1; + ds.minimumSize = 0; + ds.penColor = Qt::black; + ds.penWidth = .5; + ds.sizeType = Qgis::RenderUnit::Millimeters; + ds.size = QSizeF( 10, 10 ); + ds.rotationOffset = 270; + ds.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr = new QgsSingleCategoryDiagramRenderer(); + dr->setDiagram( new QgsPieDiagram() ); + dr->setDiagramSettings( ds ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedwomenpie", "stackedwomenpie", *mMapSettings, 200, 15 ); + } + + void testStackedPieHistogram() + { + // Pie + QgsDiagramSettings ds1; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds1.categoryColors = QList() << col1 << col2 << col3 << col4; + ds1.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds1.minimumScale = -1; + ds1.maximumScale = -1; + ds1.minimumSize = 0; + ds1.penColor = Qt::black; + ds1.penWidth = .5; + ds1.sizeType = Qgis::RenderUnit::Millimeters; + ds1.size = QSizeF( 10, 10 ); + ds1.rotationOffset = 270; + ds1.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr1 = new QgsSingleCategoryDiagramRenderer(); + dr1->setDiagram( new QgsPieDiagram() ); + dr1->setDiagramSettings( ds1 ); + + // Histogram + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.scaleByArea = true; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.rotationOffset = 0; + ds2.diagramOrientation = QgsDiagramSettings::Right; + + QgsLinearlyInterpolatedDiagramRenderer *dr2 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr2->setLowerValue( 0.0 ); + dr2->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr2->setUpperValue( 15000 ); + dr2->setUpperSize( QSizeF( 20, 20 ) ); + dr2->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + dr2->setDiagram( new QgsHistogramDiagram() ); + dr2->setDiagramSettings( ds2 ); + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds.categoryAttributes = ds1.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 8 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackedpiehistogram", "stackedpiehistogram", *mMapSettings, 200, 15 ); + } + + void testStackedDiagramsNested() + { + // Nested stacked histograms (just because we can :)) + // 1 vertically stacked diagram: + // Above: + // + Horizontally stacked diagram + // + 2 histograms + // Below: + // + 1 pie + + // Histogram 1 + QgsDiagramSettings ds11; + QColor col1 = Qt::blue; + QColor col2 = Qt::red; + QColor col3 = Qt::yellow; + QColor col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds11.categoryColors = QList() << col1 << col2 << col3 << col4; + ds11.categoryAttributes = QList() << QStringLiteral( "\"maennlich_ab_65\"" ) << QStringLiteral( "\"maennlich_18_64\"" ) << QStringLiteral( "\"maennlich_6_17\"" ) << QStringLiteral( "\"maennlich_unter_6\"" ); //#spellok + ds11.minimumScale = -1; + ds11.maximumScale = -1; + ds11.minimumSize = 0; + ds11.penColor = Qt::black; + ds11.penWidth = .5; + ds11.scaleByArea = true; + ds11.sizeType = Qgis::RenderUnit::Millimeters; + ds11.barWidth = 3; + ds11.rotationOffset = 0; + ds11.diagramOrientation = QgsDiagramSettings::Left; + + QgsLinearlyInterpolatedDiagramRenderer *dr11 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr11->setDiagram( new QgsHistogramDiagram() ); + dr11->setDiagramSettings( ds11 ); + dr11->setLowerValue( 0.0 ); + dr11->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr11->setUpperValue( 15000 ); + dr11->setUpperSize( QSizeF( 20, 20 ) ); + //dr11->setClassificationField( QStringLiteral( "max(\"maennlich_18_64\", \"maennlich_ab_65\", \"maennlich_6_17\", \"maennlich_unter_6\")" ) ); //#spellok + + // Histogram 2 + QgsDiagramSettings ds12; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds12.categoryColors = QList() << col1 << col2 << col3 << col4; + ds12.categoryAttributes = QList() << QStringLiteral( "\"weiblich_ab_65\"" ) << QStringLiteral( "\"weiblich_18_64\"" ) << QStringLiteral( "\"weiblich_6_17\"" ) << QStringLiteral( "\"weiblich_unter_6\"" ); //#spellok + ds12.minimumScale = -1; + ds12.maximumScale = -1; + ds12.minimumSize = 0; + ds12.penColor = Qt::black; + ds12.penWidth = .5; + ds12.scaleByArea = true; + ds12.barWidth = 3; + ds12.sizeType = Qgis::RenderUnit::Millimeters; + ds12.rotationOffset = 0; + ds12.diagramOrientation = QgsDiagramSettings::Right; + + QgsLinearlyInterpolatedDiagramRenderer *dr12 = new QgsLinearlyInterpolatedDiagramRenderer(); + dr12->setDiagram( new QgsHistogramDiagram() ); + dr12->setDiagramSettings( ds12 ); + dr12->setLowerValue( 0.0 ); + dr12->setLowerSize( QSizeF( 0.0, 0.0 ) ); + dr12->setUpperValue( 15000 ); + dr12->setUpperSize( QSizeF( 20, 20 ) ); + //dr12->setClassificationField( QStringLiteral( "max(\"weiblich_unter_6\", \"weiblich_6_17\", \"weiblich_18_64\", \"weiblich_ab_65\")" ) ); //#spellok + + QgsDiagramSettings ds1; + ds1.stackedDiagramMode = QgsDiagramSettings::Horizontal; + ds1.categoryAttributes = ds11.categoryAttributes + ds12.categoryAttributes; + ds1.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Pixels ); + ds1.setStackedDiagramSpacing( 0 ); + + QgsStackedDiagramRenderer *dr1 = new QgsStackedDiagramRenderer(); + dr1->setDiagram( new QgsStackedDiagram() ); + dr1->setDiagramSettings( ds1 ); + dr1->addRenderer( dr11 ); + dr1->addRenderer( dr12 ); + + // Pie + QgsDiagramSettings ds2; + col1 = Qt::blue; + col2 = Qt::red; + col3 = Qt::yellow; + col4 = Qt::green; + col1.setAlphaF( 0.5 ); + col2.setAlphaF( 0.5 ); + col3.setAlphaF( 0.5 ); + col4.setAlphaF( 0.5 ); + ds2.categoryColors = QList() << col1 << col2 << col3 << col4; + ds2.categoryAttributes = QList() << QStringLiteral( "\"gesamt_ab_65\"" ) << QStringLiteral( "\"gesamt_18_64\"" ) << QStringLiteral( "\"gesamt_6_17\"" ) << QStringLiteral( "\"gesamt_unter_6\"" ); //#spellok + ds2.minimumScale = -1; + ds2.maximumScale = -1; + ds2.minimumSize = 0; + ds2.penColor = Qt::black; + ds2.penWidth = .5; + ds2.sizeType = Qgis::RenderUnit::Millimeters; + ds2.size = QSizeF( 10, 10 ); + ds2.rotationOffset = 270; + ds2.setDirection( QgsDiagramSettings::Counterclockwise ); + + QgsSingleCategoryDiagramRenderer *dr2 = new QgsSingleCategoryDiagramRenderer(); + dr2->setDiagram( new QgsPieDiagram() ); + dr2->setDiagramSettings( ds2 ); + + QgsDiagramSettings ds; + ds.stackedDiagramMode = QgsDiagramSettings::Vertical; + ds.categoryAttributes = ds11.categoryAttributes + ds12.categoryAttributes + ds2.categoryAttributes; + ds.setStackedDiagramSpacingUnit( Qgis::RenderUnit::Points ); + ds.setStackedDiagramSpacing( 4 ); + + QgsStackedDiagramRenderer *dr = new QgsStackedDiagramRenderer(); + dr->setDiagram( new QgsStackedDiagram() ); + dr->setDiagramSettings( ds ); + dr->addRenderer( dr1 ); + dr->addRenderer( dr2 ); + mPointsLayer->setDiagramRenderer( dr ); + + QgsDiagramLayerSettings dls = QgsDiagramLayerSettings(); + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + dls.setShowAllDiagrams( true ); + mPointsLayer->setDiagramLayerSettings( dls ); + + const QgsRectangle extent( 9.7, 53.5, 9.95, 53.6 ); + mMapSettings->setExtent( extent ); + mMapSettings->setFlag( Qgis::MapSettingsFlag::ForceVectorOutput ); + mMapSettings->setOutputDpi( 96 ); + QGSVERIFYRENDERMAPSETTINGSCHECK( "stackeddiagramsnested", "stackeddiagramsnested", *mMapSettings, 200, 15 ); + } + +}; + + +QGSTEST_MAIN( TestQgsStackedDiagram ) +#include "testqgsstackeddiagram.moc" diff --git a/tests/testdata/control_images/stackeddiagrams/expected_disabledsubdiagram/expected_disabledsubdiagram.png b/tests/testdata/control_images/stackeddiagrams/expected_disabledsubdiagram/expected_disabledsubdiagram.png new file mode 100644 index 000000000000..cf388a653596 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_disabledsubdiagram/expected_disabledsubdiagram.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_scaledependentvisibilitysubdiagram/expected_scaledependentvisibilitysubdiagram.png b/tests/testdata/control_images/stackeddiagrams/expected_scaledependentvisibilitysubdiagram/expected_scaledependentvisibilitysubdiagram.png new file mode 100644 index 000000000000..cf388a653596 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_scaledependentvisibilitysubdiagram/expected_scaledependentvisibilitysubdiagram.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackeddiagramsnested/expected_stackeddiagramsnested.png b/tests/testdata/control_images/stackeddiagrams/expected_stackeddiagramsnested/expected_stackeddiagramsnested.png new file mode 100644 index 000000000000..3d089da21a46 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackeddiagramsnested/expected_stackeddiagramsnested.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedhistograms/expected_stackedhistograms.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedhistograms/expected_stackedhistograms.png new file mode 100644 index 000000000000..04b86c69bd0f Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedhistograms/expected_stackedhistograms.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedhistogramswithspacing/expected_stackedhistogramswithspacing.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedhistogramswithspacing/expected_stackedhistogramswithspacing.png new file mode 100644 index 000000000000..62e5f789d5f7 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedhistogramswithspacing/expected_stackedhistogramswithspacing.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedhistogramswithspacing2/expected_stackedhistogramswithspacing2.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedhistogramswithspacing2/expected_stackedhistogramswithspacing2.png new file mode 100644 index 000000000000..962b9a33f186 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedhistogramswithspacing2/expected_stackedhistogramswithspacing2.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedmenhistogram/expected_stackedmenhistogram.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedmenhistogram/expected_stackedmenhistogram.png new file mode 100644 index 000000000000..e8be61171de2 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedmenhistogram/expected_stackedmenhistogram.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedmenpie/expected_stackedmenpie.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedmenpie/expected_stackedmenpie.png new file mode 100644 index 000000000000..763891a996ff Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedmenpie/expected_stackedmenpie.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedpiehistogram/expected_stackedpiehistogram.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedpiehistogram/expected_stackedpiehistogram.png new file mode 100644 index 000000000000..1939a1bc513e Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedpiehistogram/expected_stackedpiehistogram.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedpies/expected_stackedpies.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedpies/expected_stackedpies.png new file mode 100644 index 000000000000..3307b5d44f95 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedpies/expected_stackedpies.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedpieshorizontalwithspacing/expected_stackedpieshorizontalwithspacing.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedpieshorizontalwithspacing/expected_stackedpieshorizontalwithspacing.png new file mode 100644 index 000000000000..e38b1a1de1d7 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedpieshorizontalwithspacing/expected_stackedpieshorizontalwithspacing.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedpiesverticalwithspacing/expected_stackedpiesverticalwithspacing.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedpiesverticalwithspacing/expected_stackedpiesverticalwithspacing.png new file mode 100644 index 000000000000..44e1e13349d2 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedpiesverticalwithspacing/expected_stackedpiesverticalwithspacing.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedwomenhistogram/expected_stackedwomenhistogram.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedwomenhistogram/expected_stackedwomenhistogram.png new file mode 100644 index 000000000000..cf388a653596 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedwomenhistogram/expected_stackedwomenhistogram.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_stackedwomenpie/expected_stackedwomenpie.png b/tests/testdata/control_images/stackeddiagrams/expected_stackedwomenpie/expected_stackedwomenpie.png new file mode 100644 index 000000000000..d8b19ef851b5 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_stackedwomenpie/expected_stackedwomenpie.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_verticallystackedhistograms/expected_verticallystackedhistograms.png b/tests/testdata/control_images/stackeddiagrams/expected_verticallystackedhistograms/expected_verticallystackedhistograms.png new file mode 100644 index 000000000000..8a2206651157 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_verticallystackedhistograms/expected_verticallystackedhistograms.png differ diff --git a/tests/testdata/control_images/stackeddiagrams/expected_verticallystackedhistogramswithspacing/expected_verticallystackedhistogramswithspacing.png b/tests/testdata/control_images/stackeddiagrams/expected_verticallystackedhistogramswithspacing/expected_verticallystackedhistogramswithspacing.png new file mode 100644 index 000000000000..ce316fd0dc22 Binary files /dev/null and b/tests/testdata/control_images/stackeddiagrams/expected_verticallystackedhistogramswithspacing/expected_verticallystackedhistogramswithspacing.png differ diff --git a/tests/testdata/stacked_diagrams.gpkg b/tests/testdata/stacked_diagrams.gpkg new file mode 100644 index 000000000000..1a79aaa648f2 Binary files /dev/null and b/tests/testdata/stacked_diagrams.gpkg differ