[gui] Move Diagrams properties to a panel widget, showing tabs instead of a list when docked; harmonize stacked diagram configuration with rule-based labeling; allow QgsDiagramProperties to sync to renderers and diagram layer settings, in addition to syncing to layers; when editing a subdiagram of a stacked diagram, only show widgets for diagram layer settings if the subdiagram is the first one, for the rest, hide those widgets and show a note informing users; make sure stacked diagrams handle enabled and disabled subdiagrams (i.e., don't take into account disabled subdiagrams) and add a test for it; switching from single to stacked diagram: take the single diagram definition as the first stacked diagram; fix #58782 (calling twice the apply method for label rendering)

This commit is contained in:
Germán Carrillo 2024-09-17 23:34:25 -05:00
parent e855941a34
commit f81df2876c
22 changed files with 2325 additions and 1091 deletions

View File

@ -1017,30 +1017,24 @@ Writes stacked renderers state to a DOM element.
virtual QList< QgsLayerTreeModelLegendNode * > legendItems( QgsLayerTreeLayer *nodeLayer ) const /Factory/;
QList< QgsDiagramRenderer * > renderers() const;
QList< QgsDiagramRenderer * > renderers( bool sortByDiagramMode = false ) const;
%Docstring
Returns an ordered list with the renderers of the stacked renderer object.
If the stacked diagram orientation is vertical, the list is returned backwards.
@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).
@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
int rendererCount() const;
%Docstring
Returns the number of renderers that this stacked renderer is composed of.
%End
protected:

View File

@ -1017,30 +1017,24 @@ Writes stacked renderers state to a DOM element.
virtual QList< QgsLayerTreeModelLegendNode * > legendItems( QgsLayerTreeLayer *nodeLayer ) const /Factory/;
QList< QgsDiagramRenderer * > renderers() const;
QList< QgsDiagramRenderer * > renderers( bool sortByDiagramMode = false ) const;
%Docstring
Returns an ordered list with the renderers of the stacked renderer object.
If the stacked diagram orientation is vertical, the list is returned backwards.
@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).
@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
int rendererCount() const;
%Docstring
Returns the number of renderers that this stacked renderer is composed of.
%End
protected:

View File

@ -8049,35 +8049,8 @@ void QgisApp::diagramProperties()
return;
}
QDialog dlg;
dlg.setWindowTitle( tr( "Layer Diagram Properties" ) );
QgsStackedDiagramProperties *gui = new QgsStackedDiagramProperties( 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, &QgsStackedDiagramProperties::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()

View File

@ -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<QgsLabelingWidget *>( current ) )
{
widget->apply();
styleWasChanged = true;
undoName = QStringLiteral( "Label Change" );
}
if ( QgsMaskingWidget *widget = qobject_cast<QgsMaskingWidget *>( current ) )
{
widget->apply();
@ -373,8 +373,23 @@ void QgsLayerStylingWidget::apply()
styleWasChanged = true;
triggerRepaint = true;
}
else if ( QgsLabelingWidget *widget = qobject_cast<QgsLabelingWidget *>( current ) )
{
widget->apply();
styleWasChanged = true;
undoName = QStringLiteral( "Label Change" );
}
else if ( QgsDiagramWidget *widget = qobject_cast<QgsDiagramWidget *>( current ) )
{
widget->apply();
styleWasChanged = true;
undoName = QStringLiteral( "Diagram Change" );
}
else if ( QgsMapLayerConfigWidget *widget = qobject_cast<QgsMapLayerConfigWidget *>( 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<QgsDiagramWidget *>( current ) )
{
mDiagramWidget = widget;
}
}
mWidgetStack->clear();
@ -495,6 +514,11 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer()
{
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( 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;
}

View File

@ -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<const QgsMapLayerConfigWidgetFactory *> &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;

View File

@ -528,7 +528,7 @@ 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();
}
@ -845,16 +845,18 @@ QgsStackedDiagramRenderer *QgsStackedDiagramRenderer::clone() const
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 ( int i = 0; i < mDiagramRenderers.count(); i++ )
for ( const auto &subRenderer : std::as_const( mDiagramRenderers ) )
{
QSizeF size = mDiagramRenderers.at( i )->sizeMapUnits( feature, c );
QSizeF size = subRenderer->sizeMapUnits( feature, c );
if ( size.isValid() )
{
enabledDiagramCount++;
switch ( mSettings.stackedDiagramMode )
{
case QgsDiagramSettings::Horizontal:
@ -875,11 +877,11 @@ QSizeF QgsStackedDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const
switch ( mSettings.stackedDiagramMode )
{
case QgsDiagramSettings::Horizontal:
stackedSize.scale( stackedSize.width() + spacing * ( mDiagramRenderers.count() - 1 ), stackedSize.height(), Qt::IgnoreAspectRatio );
stackedSize.scale( stackedSize.width() + spacing * ( enabledDiagramCount - 1 ), stackedSize.height(), Qt::IgnoreAspectRatio );
break;
case QgsDiagramSettings::Vertical:
stackedSize.scale( stackedSize.width(), stackedSize.height() + spacing * ( mDiagramRenderers.count() - 1 ), Qt::IgnoreAspectRatio );
stackedSize.scale( stackedSize.width(), stackedSize.height() + spacing * ( enabledDiagramCount - 1 ), Qt::IgnoreAspectRatio );
break;
}
return stackedSize;
@ -893,11 +895,15 @@ void QgsStackedDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRen
}
QPointF newPos = pos; // Each subdiagram will have its own newPos
QList< QgsDiagramRenderer * > stackedRenderers = renderers();
for ( const auto &stackedRenderer : std::as_const( stackedRenderers ) )
// 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;
}
@ -905,7 +911,12 @@ void QgsStackedDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRen
QgsDiagramSettings s;
if ( !stackedRenderer->diagramSettings( feature, c, s ) )
{
return;
continue;
}
if ( !s.enabled )
{
continue;
}
if ( properties.hasActiveProperties() )
@ -963,26 +974,19 @@ QList<QString> QgsStackedDiagramRenderer::diagramAttributes() const
QList< QgsLayerTreeModelLegendNode * > QgsStackedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
{
QList< QgsLayerTreeModelLegendNode * > nodes;
for ( int i = 0; i < rendererCount(); i++ )
for ( const auto &renderer : std::as_const( mDiagramRenderers ) )
{
nodes << mDiagramRenderers.at( i )->legendItems( nodeLayer );
nodes << renderer->legendItems( nodeLayer );
}
return nodes;
}
QList< QgsDiagramRenderer * > QgsStackedDiagramRenderer::renderers() const
QList< QgsDiagramRenderer * > QgsStackedDiagramRenderer::renderers( bool sortByDiagramMode ) const
{
QList< QgsDiagramRenderer * > renderers;
if ( mSettings.stackedDiagramMode == QgsDiagramSettings::Horizontal )
{
for ( const auto &item : std::as_const( mDiagramRenderers ) )
{
renderers.append( item );
}
}
else
if ( sortByDiagramMode && mSettings.stackedDiagramMode == QgsDiagramSettings::Vertical )
{
// We draw vertical diagrams backwards, so
// we return the subrenderers in reverse order
@ -992,6 +996,10 @@ QList< QgsDiagramRenderer * > QgsStackedDiagramRenderer::renderers() const
renderers.append( *iter );
}
}
else
{
renderers = mDiagramRenderers;
}
return renderers;
}
@ -1013,11 +1021,6 @@ const QgsDiagramRenderer *QgsStackedDiagramRenderer::renderer( const int index )
return nullptr;
}
int QgsStackedDiagramRenderer::rendererCount() const
{
return mDiagramRenderers.count();
}
void QgsStackedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
{
const QDomElement categoryElem = elem.firstChildElement( QStringLiteral( "DiagramCategory" ) );
@ -1234,7 +1237,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;
@ -1243,6 +1246,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 );

View File

@ -1039,13 +1039,13 @@ class CORE_EXPORT QgsStackedDiagramRenderer : public QgsDiagramRenderer
/**
* Returns an ordered list with the renderers of the stacked renderer object.
* If the stacked diagram orientation is vertical, the list is returned backwards.
* @param sortByDiagramMode If true, the list is returned backwards for vertical orientation.
*/
QList< QgsDiagramRenderer * > renderers() const;
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
* @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).
*/
@ -1057,11 +1057,6 @@ class CORE_EXPORT QgsStackedDiagramRenderer : public QgsDiagramRenderer
*/
const QgsDiagramRenderer *renderer( const int index ) const;
/**
* Returns the number of renderers that this stacked renderer is composed of.
*/
int rendererCount() const;
protected:
bool diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const override;
QSizeF diagramSize( const QgsFeature &, const QgsRenderContext &c ) const override;

View File

@ -35,6 +35,7 @@ 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
@ -1497,6 +1498,7 @@ 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

View File

@ -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;
@ -80,6 +80,10 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
// 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,
@ -144,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 );
}
@ -224,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;
@ -250,6 +259,11 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
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();
//force a refresh of widget status to match diagram type
mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
@ -268,15 +282,99 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
registerDataDefinedButton( mStartAngleDDBtn, QgsDiagramLayerSettings::Property::StartAngle );
connect( mButtonSizeLegendSettings, &QPushButton::clicked, this, &QgsDiagramProperties::showSizeLegendDialog );
QList<QWidget *> 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 << mIncreaseSmallDiagramsCheck;
widgets << mLabelPlacementComboBox;
widgets << mMaxValueSpinBox;
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, SIGNAL( widgetChanged() ) );
}
void QgsDiagramProperties::syncToLayer( const QgsDiagramRenderer *dr )
void QgsDiagramProperties::setDockMode( bool dockMode )
{
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 );
}
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::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 )
{
dr = mLayer->diagramRenderer();
}
if ( !dr ) //no diagram renderer yet, insert reasonable default
{
@ -344,6 +442,7 @@ void QgsDiagramProperties::syncToLayer( const QgsDiagramRenderer *dr )
const QList<QgsDiagramSettings> 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;
@ -458,51 +557,6 @@ void QgsDiagramProperties::syncToLayer( const QgsDiagramRenderer *dr )
}
}
const QgsDiagramLayerSettings *dls = mLayer->diagramLayerSettings();
if ( dls )
{
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;
case QgsDiagramLayerSettings::OverPoint:
radOverPoint->setChecked( true );
radOverCentroid->setChecked( true );
break;
case QgsDiagramLayerSettings::Line:
radAroundLine->setChecked( true );
radPolygonPerimeter->setChecked( true );
break;
case QgsDiagramLayerSettings::Horizontal:
radOverLine->setChecked( true );
radInsidePolygon->setChecked( true );
break;
default:
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();
mShowAllCheckBox->setChecked( dls->showAllDiagrams() );
mDataDefinedProperties = dls->dataDefinedProperties();
}
if ( dr->diagram() )
{
mDiagramType = dr->diagram()->diagramName();
@ -523,6 +577,53 @@ void QgsDiagramProperties::syncToLayer( const QgsDiagramRenderer *dr )
mPaintEffectWidget->setPaintEffect( mPaintEffect.get() );
}
void QgsDiagramProperties::syncToSettings( const QgsDiagramLayerSettings *dls )
{
if ( dls )
{
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;
case QgsDiagramLayerSettings::OverPoint:
radOverPoint->setChecked( true );
radOverCentroid->setChecked( true );
break;
case QgsDiagramLayerSettings::Line:
radAroundLine->setChecked( true );
radPolygonPerimeter->setChecked( true );
break;
case QgsDiagramLayerSettings::Horizontal:
radOverLine->setChecked( true );
radInsidePolygon->setChecked( true );
break;
default:
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();
mShowAllCheckBox->setChecked( dls->showAllDiagrams() );
mDataDefinedProperties = dls->dataDefinedProperties();
}
}
QgsDiagramProperties::~QgsDiagramProperties()
{
QgsSettings settings;
@ -543,6 +644,7 @@ void QgsDiagramProperties::updateProperty()
QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
const QgsDiagramLayerSettings::Property key = static_cast< QgsDiagramLayerSettings::Property >( button->propertyKey() );
mDataDefinedProperties.setProperty( key, button->toProperty() );
emit widgetChanged();
}
void QgsDiagramProperties::mDiagramTypeComboBox_currentIndexChanged( int index )
@ -750,6 +852,29 @@ void QgsDiagramProperties::mDiagramAttributesTreeWidget_itemDoubleClicked( QTree
}
}
std::unique_ptr< QgsDiagram > QgsDiagramProperties::createDiagramObject()
{
std::unique_ptr< QgsDiagram > diagram;
if ( mDiagramType == DIAGRAM_NAME_TEXT )
{
diagram = std::make_unique< QgsTextDiagram >();
}
else if ( mDiagramType == DIAGRAM_NAME_PIE )
{
diagram = std::make_unique< QgsPieDiagram >();
}
else if ( mDiagramType == DIAGRAM_NAME_STACKED_BAR )
{
diagram = std::make_unique< QgsStackedBarDiagram >();
}
else // if ( diagramType == DIAGRAM_NAME_HISTOGRAM )
{
diagram = std::make_unique< QgsHistogramDiagram >();
}
return diagram;
}
std::unique_ptr<QgsDiagramSettings> QgsDiagramProperties::createDiagramSettings()
{
std::unique_ptr< QgsDiagramSettings > ds = std::make_unique< QgsDiagramSettings>();
@ -821,13 +946,15 @@ std::unique_ptr<QgsDiagramSettings> QgsDiagramProperties::createDiagramSettings(
return ds;
}
std::unique_ptr<QgsDiagramRenderer> QgsDiagramProperties::createRendererBaseInfo( const QgsDiagramSettings &ds )
std::unique_ptr<QgsDiagramRenderer> QgsDiagramProperties::createRenderer()
{
std::unique_ptr< QgsDiagramSettings > ds = createDiagramSettings();
std::unique_ptr< QgsDiagramRenderer > renderer;
if ( mFixedSizeRadio->isChecked() )
{
std::unique_ptr< QgsSingleCategoryDiagramRenderer > dr = std::make_unique< QgsSingleCategoryDiagramRenderer >();
dr->setDiagramSettings( ds );
dr->setDiagramSettings( *ds );
renderer = std::move( dr );
}
else
@ -849,7 +976,7 @@ std::unique_ptr<QgsDiagramRenderer> QgsDiagramProperties::createRendererBaseInfo
{
dr->setClassificationField( sizeFieldNameOrExp );
}
dr->setDiagramSettings( ds );
dr->setDiagramSettings( *ds );
dr->setDataDefinedSizeLegend( mSizeLegend ? new QgsDataDefinedSizeLegend( *mSizeLegend ) : nullptr );
@ -857,6 +984,10 @@ std::unique_ptr<QgsDiagramRenderer> QgsDiagramProperties::createRendererBaseInfo
}
renderer->setAttributeLegend( mCheckBoxAttributeLegend->isChecked() );
std::unique_ptr< QgsDiagram > diagram = createDiagramObject();
renderer->setDiagram( diagram.release() );
return renderer;
}
@ -914,34 +1045,18 @@ void QgsDiagramProperties::apply()
const int index = mDiagramTypeComboBox->currentIndex();
const bool diagramsEnabled = ( index != -1 );
std::unique_ptr< QgsDiagram > diagram;
if ( diagramsEnabled && 0 == mDiagramAttributesTreeWidget->topLevelItemCount() )
// Avoid this messageBox when in both dock and liveUpdate mode
QgsSettings settings;
if ( !dockMode() || !settings.value( QStringLiteral( "UI/autoApplyStyling" ), true ).toBool() )
{
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 ( 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 )
{
diagram = std::make_unique< QgsTextDiagram >();
}
else if ( mDiagramType == DIAGRAM_NAME_PIE )
{
diagram = std::make_unique< QgsPieDiagram >();
}
else if ( mDiagramType == DIAGRAM_NAME_STACKED_BAR )
{
diagram = std::make_unique< QgsStackedBarDiagram >();
}
else // if ( diagramType == DIAGRAM_NAME_HISTOGRAM )
{
diagram = std::make_unique< QgsHistogramDiagram >();
}
std::unique_ptr< QgsDiagramSettings > ds = createDiagramSettings();
std::unique_ptr< QgsDiagramRenderer > renderer = createRendererBaseInfo( *ds );
renderer->setDiagram( diagram.release() );
std::unique_ptr< QgsDiagramRenderer > renderer = createRenderer();
mLayer->setDiagramRenderer( renderer.release() );
QgsDiagramLayerSettings dls = createDiagramLayerSettings();
@ -1051,6 +1166,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
@ -1112,3 +1251,71 @@ void QgsDiagramProperties::createAuxiliaryField()
emit auxiliaryFieldCreated();
}
void QgsDiagramProperties::connectValueChanged( const QList<QWidget *> &widgets, const char *signal )
{
const auto constWidgets = widgets;
for ( QWidget *widget : constWidgets )
{
if ( QgsSymbolButton *w = qobject_cast<QgsSymbolButton *>( widget ) )
{
connect( w, SIGNAL( changed() ), this, signal );
}
else if ( QgsFieldExpressionWidget *w = qobject_cast< QgsFieldExpressionWidget *>( widget ) )
{
connect( w, SIGNAL( fieldChanged( QString ) ), this, signal );
}
else if ( QgsOpacityWidget *w = qobject_cast< QgsOpacityWidget *>( widget ) )
{
connect( w, SIGNAL( opacityChanged( double ) ), this, signal );
}
else if ( QgsUnitSelectionWidget *w = qobject_cast<QgsUnitSelectionWidget *>( widget ) )
{
connect( w, SIGNAL( changed() ), this, signal );
}
else if ( QComboBox *w = qobject_cast<QComboBox *>( widget ) )
{
connect( w, SIGNAL( currentIndexChanged( int ) ), this, signal );
}
else if ( QSpinBox *w = qobject_cast<QSpinBox *>( widget ) )
{
connect( w, SIGNAL( valueChanged( int ) ), this, signal );
}
else if ( QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( widget ) )
{
connect( w, SIGNAL( valueChanged( double ) ), this, signal );
}
else if ( QgsColorButton *w = qobject_cast<QgsColorButton *>( widget ) )
{
connect( w, SIGNAL( colorChanged( QColor ) ), this, signal );
}
else if ( QCheckBox *w = qobject_cast<QCheckBox *>( widget ) )
{
connect( w, SIGNAL( toggled( bool ) ), this, signal );
}
else if ( QRadioButton *w = qobject_cast<QRadioButton *>( widget ) )
{
connect( w, SIGNAL( toggled( bool ) ), this, signal );
}
else if ( QSlider *w = qobject_cast<QSlider *>( widget ) )
{
connect( w, SIGNAL( valueChanged( int ) ), this, signal );
}
else if ( QGroupBox *w = qobject_cast<QGroupBox *>( widget ) )
{
connect( w, SIGNAL( toggled( bool ) ), this, signal );
}
else if ( QTreeWidget *w = qobject_cast<QTreeWidget *>( widget ) )
{
connect( w, SIGNAL( itemChanged( QTreeWidgetItem *, int ) ), this, signal );
}
else if ( QgsScaleRangeWidget *w = qobject_cast<QgsScaleRangeWidget *>( widget ) )
{
connect( w, SIGNAL( rangeChanged( double, double ) ), this, signal );
}
else
{
QgsLogger::warning( QStringLiteral( "Could not create connection for widget %1" ).arg( widget->objectName() ) );
}
}
}

View File

@ -36,26 +36,68 @@ 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.
*
* \since QGIS 3.16
*/
void syncToLayer( const QgsDiagramRenderer *dr = nullptr );
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();
@ -118,8 +160,24 @@ 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.
* @param signal Signal to be triggered by each widget's change value signal.
*/
void connectValueChanged( const QList<QWidget *> &widgets, const char *signal );
/**
* 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.
*
@ -132,7 +190,7 @@ class GUI_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr
*
* \since QGIS 3.40
*/
std::unique_ptr<QgsDiagramRenderer> createRendererBaseInfo( const QgsDiagramSettings &ds );
std::unique_ptr<QgsDiagramRenderer> createRenderer();
/**
* Creates a QgsDiagramLayerSettings object from the GUI settings.
@ -142,6 +200,7 @@ class GUI_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr
QgsDiagramLayerSettings createDiagramLayerSettings();
friend class QgsStackedDiagramProperties;
friend class QgsStackedDiagramPropertiesDialog;
};

View File

@ -0,0 +1,220 @@
/***************************************************************************
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 "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<void ( QComboBox::* )( int )>( &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<QgsStackedDiagramProperties *>( mWidget )->apply();
break;
}
case ModePie:
case ModeText:
case ModeHistogram:
case ModeStackedBar:
{
// Delegate to single diagram's apply
static_cast<QgsDiagramProperties *>( 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() == QStringLiteral( "Stacked" ) )
{
mDiagramTypeComboBox->setCurrentIndex( ModeStacked );
}
else // Single diagram
{
const QString diagramName = dr->diagram()->diagramName();
if ( diagramName == DIAGRAM_NAME_PIE )
{
mDiagramTypeComboBox->setCurrentIndex( ModePie ) ;
}
else if ( diagramName == DIAGRAM_NAME_TEXT )
{
mDiagramTypeComboBox->setCurrentIndex( ModeText ) ;
}
else if ( diagramName == DIAGRAM_NAME_STACKED_BAR )
{
mDiagramTypeComboBox->setCurrentIndex( ModeStackedBar ) ;
}
else // diagramName == 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<QgsDiagramProperties *>( 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() );
if ( mode == ModePie )
singleWidget->setDiagramType( DIAGRAM_NAME_PIE );
else if ( mode == ModeText )
singleWidget->setDiagramType( DIAGRAM_NAME_TEXT );
else if ( mode == ModeHistogram )
singleWidget->setDiagramType( DIAGRAM_NAME_HISTOGRAM );
else if ( mode == ModeStackedBar )
singleWidget->setDiagramType( DIAGRAM_NAME_STACKED_BAR );
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();
}
}

View File

@ -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 <qgsmaplayerconfigwidget.h>
#include "ui_qgsdiagramwidget.h"
#include <QWidget>
/**
* \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

View File

@ -19,40 +19,37 @@
#include "diagram/qgspiediagram.h"
#include "diagram/qgstextdiagram.h"
#include "diagram/qgsstackedbardiagram.h"
#include "diagram/qgsstackeddiagram.h"
#include "qgsapplication.h"
#include "qgsgui.h"
#include "qgsdiagramproperties.h"
#include "qgslabelengineconfigdialog.h"
#include "qgsproject.h"
#include "qgsstackeddiagram.h"
#include "qgsstackeddiagramproperties.h"
#include "qgsvectorlayer.h"
#include "qgshelp.h"
QgsStackedDiagramProperties::QgsStackedDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas )
: QWidget{parent}
: QgsPanelWidget( parent )
, mLayer( layer )
, mMapCanvas( canvas )
{
mLayer = layer;
if ( !layer )
{
return;
}
setupUi( this );
connect( mDiagramTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsStackedDiagramProperties::mDiagramTypeComboBox_currentIndexChanged );
connect( mEngineSettingsButton, &QPushButton::clicked, this, &QgsStackedDiagramProperties::mEngineSettingsButton_clicked );
connect( mSubDiagramsView, &QAbstractItemView::doubleClicked, this, static_cast<void ( QgsStackedDiagramProperties::* )( const QModelIndex & )>( &QgsStackedDiagramProperties::editSubDiagram ) );
connect( mAddSubDiagramButton, &QPushButton::clicked, this, &QgsStackedDiagramProperties::addSubDiagram );
connect( mEditSubDiagramButton, &QAbstractButton::clicked, this, static_cast<void ( QgsStackedDiagramProperties::* )()>( &QgsStackedDiagramProperties::editSubDiagram ) );
connect( mRemoveSubDiagramButton, &QPushButton::clicked, this, &QgsStackedDiagramProperties::removeSubDiagram );
connect( mSubDiagramsTabWidget->tabBar(), &QTabBar::tabMoved, this, &QgsStackedDiagramProperties::mSubDiagramsTabWidget_tabMoved );
// Initialize stacked diagram controls
QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "diagramNone.svg" ) );
mDiagramTypeComboBox->addItem( icon, tr( "No Diagrams" ), "None" );
mDiagramTypeComboBox->addItem( tr( "Single diagram" ), QgsDiagramLayerSettings::Single );
mDiagramTypeComboBox->addItem( tr( "Stacked diagrams" ), QgsDiagramLayerSettings::Stacked );
mStackedDiagramModeComboBox->addItem( tr( "Horizontal" ), QgsDiagramSettings::Horizontal );
mStackedDiagramModeComboBox->addItem( tr( "Vertical" ), QgsDiagramSettings::Vertical );
mRemoveSubDiagramButton->setEnabled( false );
mStackedDiagramSpacingSpinBox->setClearValue( 0 );
mStackedDiagramSpacingUnitComboBox->setUnits( { Qgis::RenderUnit::Millimeters,
@ -62,37 +59,117 @@ QgsStackedDiagramProperties::QgsStackedDiagramProperties( QgsVectorLayer *layer,
Qgis::RenderUnit::Points,
Qgis::RenderUnit::Inches } );
// Add default subdiagram tab
addSubDiagram();
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 );
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()
{
QgsDiagramProperties *gui = new QgsDiagramProperties( mLayer, this, mMapCanvas );
gui->layout()->setContentsMargins( 6, 6, 6, 6 );
connect( gui, &QgsDiagramProperties::auxiliaryFieldCreated, this, &QgsStackedDiagramProperties::auxiliaryFieldCreated );
// 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 );
mSubDiagramsTabWidget->addTab( gui, tr( "Diagram %1" ).arg( mSubDiagramsTabWidget->count() + 1 ) );
QItemSelectionModel *sel = mSubDiagramsView->selectionModel();
const QModelIndex index = sel->currentIndex();
mRemoveSubDiagramButton->setEnabled( mSubDiagramsTabWidget->count() > 2 );
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()
{
if ( mSubDiagramsTabWidget->count() > 2 )
const QItemSelection sel = mSubDiagramsView->selectionModel()->selection();
const auto constSel = sel;
for ( const QItemSelectionRange &range : constSel )
{
const int index = mSubDiagramsTabWidget->currentIndex();
delete mSubDiagramsTabWidget->widget( index );
mRemoveSubDiagramButton->setEnabled( mSubDiagramsTabWidget->count() > 2 );
for ( int i = index; i < mSubDiagramsTabWidget->count(); i++ )
{
mSubDiagramsTabWidget->setTabText( i, tr( "Diagram %1" ).arg( i + 1 ) );
}
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()
@ -101,222 +178,372 @@ void QgsStackedDiagramProperties::syncToLayer()
if ( dr && dr->diagram() )
{
if ( dr->rendererName() == QStringLiteral( "Stacked" ) )
const QList<QgsDiagramSettings> settingList = dr->diagramSettings();
mStackedDiagramModeComboBox->setCurrentIndex( settingList.at( 0 ).stackedDiagramMode );
mStackedDiagramSpacingSpinBox->setValue( settingList.at( 0 ).stackedDiagramSpacing() );
mStackedDiagramSpacingUnitComboBox->setUnit( settingList.at( 0 ).stackedDiagramSpacingUnit() );
if ( dr->rendererName() == QLatin1String( "Stacked" ) )
{
mDiagramTypeComboBox->blockSignals( true );
mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( QgsDiagramLayerSettings::Stacked ) );
mDiagramTypeComboBox->blockSignals( false );
//force a refresh of widget status to match diagram type
mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
const QList<QgsDiagramSettings> settingList = dr->diagramSettings();
mStackedDiagramModeComboBox->setCurrentIndex( settingList.at( 0 ).stackedDiagramMode );
mStackedDiagramSpacingSpinBox->setValue( settingList.at( 0 ).stackedDiagramSpacing() );
mStackedDiagramSpacingUnitComboBox->setUnit( settingList.at( 0 ).stackedDiagramSpacingUnit() );
// Create/remove as many tabs as necessary
const QgsStackedDiagramRenderer *stackedDiagramRenderer = static_cast< const QgsStackedDiagramRenderer * >( dr );
const int rendererCount = stackedDiagramRenderer->rendererCount();
while ( mSubDiagramsTabWidget->count() < rendererCount )
const auto renderers = stackedDiagramRenderer->renderers();
for ( const auto &renderer : renderers )
{
addSubDiagram();
}
while ( mSubDiagramsTabWidget->count() > rendererCount )
{
mSubDiagramsTabWidget->setCurrentIndex( mSubDiagramsTabWidget->count() - 1 );
removeSubDiagram();
}
// Call subdiagrams' syncToLayer with the corresponding rendering object
for ( int i = 0; i < mSubDiagramsTabWidget->count(); i++ )
{
QgsDiagramProperties *diagramProperties = static_cast<QgsDiagramProperties *>( mSubDiagramsTabWidget->widget( i ) );
diagramProperties->syncToLayer( stackedDiagramRenderer->renderer( i ) );
appendSubDiagram( renderer );
}
}
else // Single diagram
else
{
mDiagramTypeComboBox->blockSignals( true );
mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( QgsDiagramLayerSettings::Single ) );
mDiagramTypeComboBox->blockSignals( false );
//force a refresh of widget status to match diagram type
mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
// Delegate to single diagram's syncToLayer
// If the diagram name is unknown, the single diagram will choose a default one (pie)
static_cast<QgsDiagramProperties *>( mSubDiagramsTabWidget->widget( 0 ) )->syncToLayer();
// Take this single renderer as the first stacked renderer
appendSubDiagram( dr->clone() );
}
}
else // No Diagram
{
mDiagramTypeComboBox->blockSignals( true );
mDiagramTypeComboBox->setCurrentIndex( 0 );
mDiagramTypeComboBox->blockSignals( false );
//force a refresh of widget status to match diagram type
mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
// Delegate to first diagram's syncToLayer
// It will add required reasonable defaults
static_cast<QgsDiagramProperties *>( mSubDiagramsTabWidget->widget( 0 ) )->syncToLayer();
}
}
void QgsStackedDiagramProperties::mSubDiagramsTabWidget_tabMoved( int from, int to )
{
Q_UNUSED( from )
Q_UNUSED( to )
for ( int i = 0; i < mSubDiagramsTabWidget->count(); i++ )
{
mSubDiagramsTabWidget->setTabText( i, tr( "Diagram %1" ).arg( i + 1 ) );
const QgsDiagramLayerSettings *dls = mLayer->diagramLayerSettings();
mModel->updateDiagramLayerSettings( *dls );
}
}
void QgsStackedDiagramProperties::apply()
{
if ( mDiagramTypeComboBox->currentData( Qt::UserRole ) == "None" )
std::unique_ptr< QgsDiagramSettings> ds = std::make_unique< QgsDiagramSettings >();
ds->stackedDiagramMode = static_cast<QgsDiagramSettings::StackedDiagramMode>( 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 )
{
std::unique_ptr< QgsDiagramRenderer > renderer;
mLayer->setDiagramRenderer( renderer.release() );
QgsDiagramLayerSettings dls;
mLayer->setDiagramLayerSettings( dls );
// refresh
QgsProject::instance()->setDirty( true );
mLayer->triggerRepaint();
}
else if ( mDiagramTypeComboBox->currentData( Qt::UserRole ) == QgsDiagramLayerSettings::Single )
{
static_cast<QgsDiagramProperties *>( mSubDiagramsTabWidget->widget( 0 ) )->apply();
}
else // Stacked diagram
{
// Create diagram settings for the StackedDiagram
std::unique_ptr< QgsDiagramSettings> ds = std::make_unique< QgsDiagramSettings >();
ds->stackedDiagramMode = static_cast<QgsDiagramSettings::StackedDiagramMode>( 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
for ( int i = 0; i < mSubDiagramsTabWidget->count(); i++ )
const QList< QgsDiagramSettings > ds1 = renderer->diagramSettings();
if ( !ds1.isEmpty() )
{
QgsDiagramProperties *diagramProperties = static_cast<QgsDiagramProperties *>( mSubDiagramsTabWidget->widget( i ) );
std::unique_ptr< QgsDiagramSettings > ds1 = diagramProperties->createDiagramSettings();
ds->categoryAttributes += ds1->categoryAttributes;
ds->categoryLabels += ds1->categoryLabels;
ds->categoryColors += ds1->categoryColors;
std::unique_ptr< QgsDiagramRenderer > dr1 = diagramProperties->createRendererBaseInfo( *ds1 );
std::unique_ptr< QgsDiagram > diagram;
if ( diagramProperties->mDiagramType == DIAGRAM_NAME_TEXT )
{
diagram = std::make_unique< QgsTextDiagram >();
}
else if ( diagramProperties->mDiagramType == DIAGRAM_NAME_PIE )
{
diagram = std::make_unique< QgsPieDiagram >();
}
else if ( diagramProperties->mDiagramType == DIAGRAM_NAME_STACKED_BAR )
{
diagram = std::make_unique< QgsStackedBarDiagram >();
}
else // DIAGRAM_NAME_HISTOGRAM
{
diagram = std::make_unique< QgsHistogramDiagram >();
}
dr1->setDiagram( diagram.release() );
dr->addRenderer( dr1.release() );
ds->categoryAttributes += ds1.at( 0 ).categoryAttributes;
ds->categoryLabels += ds1.at( 0 ).categoryLabels;
ds->categoryColors += ds1.at( 0 ).categoryColors;
}
dr->setDiagramSettings( *ds );
mLayer->setDiagramRenderer( dr );
// Create DiagramLayerSettings from first diagram
QgsDiagramProperties *firstDiagramProperties = static_cast< QgsDiagramProperties * >( mSubDiagramsTabWidget->widget( 0 ) );
QgsDiagramLayerSettings dls = firstDiagramProperties->createDiagramLayerSettings();
mLayer->setDiagramLayerSettings( dls );
// refresh
QgsProject::instance()->setDirty( true );
mLayer->triggerRepaint();
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();
}
void QgsStackedDiagramProperties::mDiagramTypeComboBox_currentIndexChanged( int index )
bool QgsStackedDiagramProperties::couldBeFirstSubDiagram( const QModelIndex &index ) const
{
if ( index == 0 ) // No diagram
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++ )
{
mDiagramsFrame->setEnabled( false );
mStackedDiagramSettingsFrame->hide();
// Hide tabs other than the first one
for ( int i = 0; i < mSubDiagramsTabWidget->count(); i++ )
const auto &renderer = renderers.at( i );
const QList< QgsDiagramSettings > ds = renderer->diagramSettings();
if ( !ds.isEmpty() && ds.at( 0 ).enabled )
{
if ( i < 1 )
continue;
mSubDiagramsTabWidget->setTabVisible( i, false );
}
}
else if ( index == 1 ) // Single
{
mDiagramsFrame->setEnabled( true );
mStackedDiagramSettingsFrame->hide();
// Hide tabs other than the first one
for ( int i = 0; i < mSubDiagramsTabWidget->count(); i++ )
{
if ( i < 1 )
continue;
mSubDiagramsTabWidget->setTabVisible( i, false );
}
}
else // Stacked
{
mDiagramsFrame->setEnabled( true );
mStackedDiagramSettingsFrame->show();
// Create the second tab or show all hidden tabs
if ( mSubDiagramsTabWidget->count() == 1 )
{
// Add second subdiagram tab
addSubDiagram();
}
else
{
for ( int i = 0; i < mSubDiagramsTabWidget->count(); i++ )
{
if ( i < 1 )
continue;
mSubDiagramsTabWidget->setTabVisible( i, true );
}
// 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::mEngineSettingsButton_clicked()
void QgsStackedDiagramProperties::subDiagramWidgetPanelAccepted( QgsPanelWidget *panel )
{
QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
if ( panel && panel->dockMode() )
QgsDiagramProperties *widget = qobject_cast<QgsDiagramProperties *>( 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<QgsPanelWidget *>( 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 )
{
QgsLabelEngineConfigWidget *widget = new QgsLabelEngineConfigWidget( mMapCanvas );
connect( widget, &QgsLabelEngineConfigWidget::widgetChanged, widget, &QgsLabelEngineConfigWidget::apply );
panel->openPanel( widget );
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 ( DIAGRAM_NAME_HISTOGRAM == dr->diagram()->diagramName() || 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::Alignment::Int>( Qt::AlignCenter ) : static_cast<Qt::Alignment::Int>( 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
{
QgsLabelEngineConfigDialog dialog( mMapCanvas, this );
dialog.exec();
// reactivate button's window
activateWindow();
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<QgsDiagramRenderer *> subRenderers;
subRenderers = mRenderers;
return subRenderers;
}
void QgsStackedDiagramPropertiesModel::updateDiagramLayerSettings( QgsDiagramLayerSettings dls )
{
mDiagramLayerSettings = dls;
}
QgsDiagramLayerSettings QgsStackedDiagramPropertiesModel::diagramLayerSettings() const
{
return mDiagramLayerSettings;
}

View File

@ -22,14 +22,71 @@
#define SIP_NO_FILE
#include "qgis_gui.h"
#include "qgsdiagramrenderer.h"
#include "ui_qgsstackeddiagrampropertiesbase.h"
#include <QWidget>
#include <QDialog>
#include <QDialogButtonBox>
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;
};
/**
@ -38,7 +95,7 @@ class QgsDiagramProperties;
*
* \since QGIS 3.40
*/
class GUI_EXPORT QgsStackedDiagramProperties : public QWidget, private Ui::QgsStackedDiagramPropertiesBase
class GUI_EXPORT QgsStackedDiagramProperties : public QgsPanelWidget, private Ui::QgsStackedDiagramPropertiesBase
{
Q_OBJECT
@ -55,27 +112,127 @@ class GUI_EXPORT QgsStackedDiagramProperties : public QWidget, private Ui::QgsSt
public slots:
void apply();
void mDiagramTypeComboBox_currentIndexChanged( int index );
void mSubDiagramsTabWidget_tabMoved( int from, int to );
void mEngineSettingsButton_clicked();
private slots:
/**
* Adds a diagram tab to the current QgsStackedDiagramProperties.
* Adds a diagram to the current QgsStackedDiagramProperties.
*/
void addSubDiagram();
/**
* Removes a diagram tab from the current QgsStackedDiagramProperties.
* Diagram tabs are removed only if the tab count is greater than 2.
* Tab texts are adjusted after tab removal, to keep sequential order.
* 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

View File

@ -26,7 +26,7 @@
#include "qgsapplication.h"
#include "qgsattributeactiondialog.h"
#include "qgsdatumtransformdialog.h"
#include "qgsstackeddiagramproperties.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 QgsStackedDiagramProperties( mLayer, mDiagramFrame, mCanvas );
diagramPropertiesDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
connect( diagramPropertiesDialog, &QgsStackedDiagramProperties::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 ) )

View File

@ -33,7 +33,7 @@ class QgsMapLayer;
class QgsAttributeActionDialog;
class QgsVectorLayer;
class QgsLabelingWidget;
class QgsStackedDiagramProperties;
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
QgsStackedDiagramProperties *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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsDiagramWidget</class>
<widget class="QWidget" name="QgsDiagramWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>842</width>
<height>472</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QComboBox" name="mDiagramTypeComboBox"/>
</item>
<item>
<widget class="QToolButton" name="mEngineSettingsButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Automated placement settings (applies to all layers)</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconAutoPlacementSettings.svg</normaloff>:/images/themes/default/mIconAutoPlacementSettings.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QStackedWidget" name="mStackedWidget">
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../images/images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -11,25 +11,106 @@
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<widget class="QTreeView" name="mSubDiagramsView">
<property name="selectionMode">
<enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerMinimumSectionSize">
<number>57</number>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="mDiagramTypeComboBox"/>
</item>
<item>
<widget class="QToolButton" name="mEngineSettingsButton">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="mAddSubDiagramButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Automated placement settings (apply to all layers)</string>
<string>Add subdiagram to the stacked diagram</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconAutoPlacementSettings.svg</normaloff>:/images/themes/default/mIconAutoPlacementSettings.svg</iconset>
<normaloff>:/images/themes/default/symbologyAdd.svg</normaloff>:/images/themes/default/symbologyAdd.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mEditSubDiagramButton">
<property name="toolTip">
<string>Edit subdiagram from the stacked diagram</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyEdit.svg</normaloff>:/images/themes/default/symbologyEdit.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mRemoveSubDiagramButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Remove subdiagram from the stacked diagram</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyRemove.svg</normaloff>:/images/themes/default/symbologyRemove.svg</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
@ -41,93 +122,6 @@
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="mAddSubDiagramButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Add subdiagram to the stacked diagram</string>
</property>
<property name="text">
<string>Add subdiagram</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyAdd.svg</normaloff>:/images/themes/default/symbologyAdd.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mRemoveSubDiagramButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Remove subdiagram from the stacked diagram</string>
</property>
<property name="text">
<string>Remove subdiagram</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyRemove.svg</normaloff>:/images/themes/default/symbologyRemove.svg</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="stackedDiagramModeLabel">
<property name="text">
<string>Stacked Diagram Mode</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mStackedDiagramModeComboBox"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
@ -175,42 +169,32 @@
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="mDiagramsFrame">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTabWidget" name="mSubDiagramsTabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
<property name="movable">
<bool>true</bool>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="stackedDiagramModeLabel">
<property name="text">
<string>Stacked diagram mode</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mStackedDiagramModeComboBox"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>

View File

@ -286,7 +286,8 @@ ACCEPTABLE_MISSING_DOCS = {
"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": ["mDiagramTypeComboBox_currentIndexChanged(int index)", "apply()", "mEngineSettingsButton_clicked()", "mSubDiagramsTabWidget_tabMoved(int from, int to)", "auxiliaryFieldCreated()", "QgsStackedDiagramProperties(QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas)"],
"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"],

View File

@ -191,6 +191,96 @@ class TestQgsStackedDiagram : public QgsTest
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<QColor>() << col1 << col2 << col3 << col4;
ds1.categoryAttributes = QList<QString>() << 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<QColor>() << col1 << col2 << col3 << col4;
ds2.categoryAttributes = QList<QString>() << 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 testVerticallyStackedHistograms()
{
// Histogram 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB