QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsdiagramproperties.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdiagramproperties.cpp
3  Adjust the properties for diagrams
4  -------------------
5  begin : August 2012
6  copyright : (C) Matthias Kuhn
7  email : matthias at opengis dot ch
8 
9  ***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "diagram/qgspiediagram.h"
20 #include "diagram/qgstextdiagram.h"
22 
23 #include "qgsproject.h"
24 #include "qgsapplication.h"
27 #include "qgsdiagramproperties.h"
28 #include "qgsdiagramrenderer.h"
30 #include "qgsmessagebar.h"
32 #include "qgsvectordataprovider.h"
33 #include "qgsfeatureiterator.h"
34 #include "qgscolordialog.h"
35 #include "qgsguiutils.h"
37 #include "qgsstyle.h"
38 #include "qgsmapcanvas.h"
40 #include "qgslogger.h"
41 #include "qgssettings.h"
43 #include "qgsauxiliarystorage.h"
45 #include "qgspropertytransformer.h"
46 #include "qgspainteffectregistry.h"
47 #include "qgspainteffect.h"
48 #include "qgslinesymbol.h"
49 
50 #include <QList>
51 #include <QMessageBox>
52 #include <QStyledItemDelegate>
53 #include <QRandomGenerator>
54 
59 class EditBlockerDelegate: public QStyledItemDelegate
60 {
61  public:
62  EditBlockerDelegate( QObject *parent = nullptr )
63  : QStyledItemDelegate( parent )
64  {}
65 
66  QWidget *createEditor( QWidget *, const QStyleOptionViewItem &, const QModelIndex & ) const override
67  {
68  return nullptr;
69  }
70 };
71 
72 
73 QgsExpressionContext QgsDiagramProperties::createExpressionContext() const
74 {
75  QgsExpressionContext expContext;
81 
82  return expContext;
83 }
84 
86  : QWidget( parent )
87  , mMapCanvas( canvas )
88 {
89  mLayer = layer;
90  if ( !layer )
91  {
92  return;
93  }
94 
95  setupUi( this );
96  connect( mDiagramTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsDiagramProperties::mDiagramTypeComboBox_currentIndexChanged );
97  connect( mAddCategoryPushButton, &QPushButton::clicked, this, &QgsDiagramProperties::mAddCategoryPushButton_clicked );
98  connect( mAttributesTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsDiagramProperties::mAttributesTreeWidget_itemDoubleClicked );
99  connect( mFindMaximumValueButton, &QPushButton::clicked, this, &QgsDiagramProperties::mFindMaximumValueButton_clicked );
100  connect( mRemoveCategoryPushButton, &QPushButton::clicked, this, &QgsDiagramProperties::mRemoveCategoryPushButton_clicked );
101  connect( mDiagramAttributesTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsDiagramProperties::mDiagramAttributesTreeWidget_itemDoubleClicked );
102  connect( mEngineSettingsButton, &QPushButton::clicked, this, &QgsDiagramProperties::mEngineSettingsButton_clicked );
103  connect( mDiagramStackedWidget, &QStackedWidget::currentChanged, this, &QgsDiagramProperties::mDiagramStackedWidget_currentChanged );
104 
105  // get rid of annoying outer focus rect on Mac
106  mDiagramOptionsListWidget->setAttribute( Qt::WA_MacShowFocusRect, false );
107 
108  mBarSpacingSpinBox->setClearValue( 0 );
111 
112  mDiagramFontButton->setMode( QgsFontButton::ModeQFont );
113 
114  mDiagramTypeComboBox->blockSignals( true );
115  QPixmap pix = QgsApplication::getThemePixmap( QStringLiteral( "diagramNone" ) );
116  mDiagramTypeComboBox->addItem( pix, tr( "No Diagrams" ), "None" );
117  pix = QgsApplication::getThemePixmap( QStringLiteral( "pie-chart" ) );
118  mDiagramTypeComboBox->addItem( pix, tr( "Pie Chart" ), DIAGRAM_NAME_PIE );
119  pix = QgsApplication::getThemePixmap( QStringLiteral( "text" ) );
120  mDiagramTypeComboBox->addItem( pix, tr( "Text Diagram" ), DIAGRAM_NAME_TEXT );
121  pix = QgsApplication::getThemePixmap( QStringLiteral( "histogram" ) );
122  mDiagramTypeComboBox->addItem( pix, tr( "Histogram" ), DIAGRAM_NAME_HISTOGRAM );
123  pix = QgsApplication::getThemePixmap( QStringLiteral( "stacked-bar" ) );
124  mDiagramTypeComboBox->addItem( pix, tr( "Stacked Bars" ), DIAGRAM_NAME_STACKED );
125  mDiagramTypeComboBox->blockSignals( false );
126 
127  mAxisLineStyleButton->setSymbolType( Qgis::SymbolType::Line );
128  mAxisLineStyleButton->setDialogTitle( tr( "Axis Line Symbol" ) );
129 
130  mScaleRangeWidget->setMapCanvas( mMapCanvas );
131  mSizeFieldExpressionWidget->registerExpressionContextGenerator( this );
132 
133  mBackgroundColorButton->setColorDialogTitle( tr( "Select Background Color" ) );
134  mBackgroundColorButton->setAllowOpacity( true );
135  mBackgroundColorButton->setContext( QStringLiteral( "symbology" ) );
136  mBackgroundColorButton->setShowNoColor( true );
137  mBackgroundColorButton->setNoColorString( tr( "Transparent Background" ) );
138  mDiagramPenColorButton->setColorDialogTitle( tr( "Select Pen Color" ) );
139  mDiagramPenColorButton->setAllowOpacity( true );
140  mDiagramPenColorButton->setContext( QStringLiteral( "symbology" ) );
141  mDiagramPenColorButton->setShowNoColor( true );
142  mDiagramPenColorButton->setNoColorString( tr( "Transparent Stroke" ) );
143 
144  mMaxValueSpinBox->setShowClearButton( false );
145  mSizeSpinBox->setClearValue( 5 );
146 
147  mDiagramAttributesTreeWidget->setItemDelegateForColumn( ColumnAttributeExpression, new EditBlockerDelegate( this ) );
148  mDiagramAttributesTreeWidget->setItemDelegateForColumn( ColumnColor, new QgsColorSwatchDelegate( this ) );
149 
150  mDiagramAttributesTreeWidget->setColumnWidth( ColumnColor, Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6.6 );
151 
152  connect( mFixedSizeRadio, &QRadioButton::toggled, this, &QgsDiagramProperties::scalingTypeChanged );
153  connect( mAttributeBasedScalingRadio, &QRadioButton::toggled, this, &QgsDiagramProperties::scalingTypeChanged );
154 
159 
160  const QgsWkbTypes::GeometryType layerType = layer->geometryType();
161  if ( layerType == QgsWkbTypes::UnknownGeometry || layerType == QgsWkbTypes::NullGeometry )
162  {
163  mDiagramTypeComboBox->setEnabled( false );
164  mDiagramFrame->setEnabled( false );
165  }
166 
167  // set placement methods page based on geometry type
168 
169  switch ( layerType )
170  {
172  stackedPlacement->setCurrentWidget( pagePoint );
173  mLinePlacementFrame->setVisible( false );
174  break;
176  stackedPlacement->setCurrentWidget( pageLine );
177  mLinePlacementFrame->setVisible( true );
178  break;
180  stackedPlacement->setCurrentWidget( pagePolygon );
181  mLinePlacementFrame->setVisible( false );
182  break;
185  break;
186  }
187 
188  //insert placement options
189  // setup point placement button group
190  mPlacePointBtnGrp = new QButtonGroup( this );
191  mPlacePointBtnGrp->addButton( radAroundPoint );
192  mPlacePointBtnGrp->addButton( radOverPoint );
193  mPlacePointBtnGrp->setExclusive( true );
194  connect( mPlacePointBtnGrp, qOverload< QAbstractButton * >( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets );
195 
196  // setup line placement button group
197  mPlaceLineBtnGrp = new QButtonGroup( this );
198  mPlaceLineBtnGrp->addButton( radAroundLine );
199  mPlaceLineBtnGrp->addButton( radOverLine );
200  mPlaceLineBtnGrp->setExclusive( true );
201  connect( mPlaceLineBtnGrp, qOverload< QAbstractButton * >( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets );
202 
203  // setup polygon placement button group
204  mPlacePolygonBtnGrp = new QButtonGroup( this );
205  mPlacePolygonBtnGrp->addButton( radAroundCentroid );
206  mPlacePolygonBtnGrp->addButton( radOverCentroid );
207  mPlacePolygonBtnGrp->addButton( radPolygonPerimeter );
208  mPlacePolygonBtnGrp->addButton( radInsidePolygon );
209  mPlacePolygonBtnGrp->setExclusive( true );
210  connect( mPlacePolygonBtnGrp, qOverload< QAbstractButton * >( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets );
211 
212  mLabelPlacementComboBox->addItem( tr( "Height" ), QgsDiagramSettings::Height );
213  mLabelPlacementComboBox->addItem( tr( "x-height" ), QgsDiagramSettings::XHeight );
214 
215  mScaleDependencyComboBox->addItem( tr( "Area" ), true );
216  mScaleDependencyComboBox->addItem( tr( "Diameter" ), false );
217 
218  mAngleOffsetComboBox->addItem( tr( "Top" ), 270 );
219  mAngleOffsetComboBox->addItem( tr( "Right" ), 0 );
220  mAngleOffsetComboBox->addItem( tr( "Bottom" ), 90 );
221  mAngleOffsetComboBox->addItem( tr( "Left" ), 180 );
222 
223  mAngleDirectionComboBox->addItem( tr( "Clockwise" ), QgsDiagramSettings::Clockwise );
224  mAngleDirectionComboBox->addItem( tr( "Counter-clockwise" ), QgsDiagramSettings::Counterclockwise );
225 
226  const QgsSettings settings;
227 
228  // reset horiz stretch of left side of options splitter (set to 1 for previewing in Qt Designer)
229  QSizePolicy policy( mDiagramOptionsListFrame->sizePolicy() );
230  policy.setHorizontalStretch( 0 );
231  mDiagramOptionsListFrame->setSizePolicy( policy );
232  if ( !settings.contains( QStringLiteral( "/Windows/Diagrams/OptionsSplitState" ) ) )
233  {
234  // set left list widget width on initial showing
235  QList<int> splitsizes;
236  splitsizes << 115;
237  mDiagramOptionsSplitter->setSizes( splitsizes );
238  }
239 
240  // restore dialog, splitters and current tab
241  mDiagramOptionsSplitter->restoreState( settings.value( QStringLiteral( "Windows/Diagrams/OptionsSplitState" ) ).toByteArray() );
242  mDiagramOptionsListWidget->setCurrentRow( settings.value( QStringLiteral( "Windows/Diagrams/Tab" ), 0 ).toInt() );
243 
244  // field combo and expression button
245  mSizeFieldExpressionWidget->setLayer( mLayer );
246  QgsDistanceArea myDa;
247  myDa.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
248  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
249  mSizeFieldExpressionWidget->setGeomCalculator( myDa );
250 
251  //insert all attributes into the combo boxes
252  const QgsFields &layerFields = layer->fields();
253  for ( int idx = 0; idx < layerFields.count(); ++idx )
254  {
255  QTreeWidgetItem *newItem = new QTreeWidgetItem( mAttributesTreeWidget );
256  const QString name = QStringLiteral( "\"%1\"" ).arg( layerFields.at( idx ).name() );
257  newItem->setText( 0, name );
258  newItem->setData( 0, RoleAttributeExpression, name );
259  newItem->setFlags( newItem->flags() & ~Qt::ItemIsDropEnabled );
260  }
261 
262  mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
263  mPaintEffect->setEnabled( false );
264 
265  syncToLayer();
266 
267  connect( mAddAttributeExpression, &QPushButton::clicked, this, &QgsDiagramProperties::showAddAttributeExpressionDialog );
268  registerDataDefinedButton( mBackgroundColorDDBtn, QgsDiagramLayerSettings::BackgroundColor );
269  registerDataDefinedButton( mLineColorDDBtn, QgsDiagramLayerSettings::StrokeColor );
270  registerDataDefinedButton( mLineWidthDDBtn, QgsDiagramLayerSettings::StrokeWidth );
271  registerDataDefinedButton( mCoordXDDBtn, QgsDiagramLayerSettings::PositionX );
272  registerDataDefinedButton( mCoordYDDBtn, QgsDiagramLayerSettings::PositionY );
273  registerDataDefinedButton( mDistanceDDBtn, QgsDiagramLayerSettings::Distance );
274  registerDataDefinedButton( mPriorityDDBtn, QgsDiagramLayerSettings::Priority );
275  registerDataDefinedButton( mZOrderDDBtn, QgsDiagramLayerSettings::ZIndex );
276  registerDataDefinedButton( mShowDiagramDDBtn, QgsDiagramLayerSettings::Show );
277  registerDataDefinedButton( mAlwaysShowDDBtn, QgsDiagramLayerSettings::AlwaysShow );
278  registerDataDefinedButton( mIsObstacleDDBtn, QgsDiagramLayerSettings::IsObstacle );
279  registerDataDefinedButton( mStartAngleDDBtn, QgsDiagramLayerSettings::StartAngle );
280 
281  connect( mButtonSizeLegendSettings, &QPushButton::clicked, this, &QgsDiagramProperties::showSizeLegendDialog );
282 }
283 
285 {
286  mDiagramAttributesTreeWidget->clear();
287 
288  const QgsDiagramRenderer *dr = mLayer->diagramRenderer();
289  if ( !dr ) //no diagram renderer yet, insert reasonable default
290  {
291  mDiagramTypeComboBox->blockSignals( true );
292  mDiagramTypeComboBox->setCurrentIndex( 0 );
293  mDiagramTypeComboBox->blockSignals( false );
294  mFixedSizeRadio->setChecked( true );
295  mDiagramUnitComboBox->setUnit( QgsUnitTypes::RenderMillimeters );
296  mDiagramLineUnitComboBox->setUnit( QgsUnitTypes::RenderMillimeters );
297  mLabelPlacementComboBox->setCurrentIndex( mLabelPlacementComboBox->findText( tr( "x-height" ) ) );
298  mDiagramSizeSpinBox->setEnabled( true );
299  mDiagramSizeSpinBox->setValue( 15 );
300  mLinearScaleFrame->setEnabled( false );
301  mIncreaseMinimumSizeSpinBox->setEnabled( false );
302  mIncreaseMinimumSizeLabel->setEnabled( false );
303  mBarWidthSpinBox->setValue( 5 );
304  mScaleVisibilityGroupBox->setChecked( mLayer->hasScaleBasedVisibility() );
305  mScaleRangeWidget->setScaleRange( mLayer->minimumScale(), mLayer->maximumScale() );
306  mShowAllCheckBox->setChecked( true );
307  mCheckBoxAttributeLegend->setChecked( true );
308 
309  switch ( mLayer->geometryType() )
310  {
312  radAroundPoint->setChecked( true );
313  break;
314 
316  radAroundLine->setChecked( true );
317  chkLineAbove->setChecked( true );
318  chkLineBelow->setChecked( false );
319  chkLineOn->setChecked( false );
320  chkLineOrientationDependent->setChecked( false );
321  break;
322 
324  radOverCentroid->setChecked( true );
325  mDiagramDistanceLabel->setEnabled( false );
326  mDiagramDistanceSpinBox->setEnabled( false );
327  mDistanceDDBtn->setEnabled( false );
328  break;
329 
332  break;
333  }
334  mBackgroundColorButton->setColor( QColor( 255, 255, 255, 255 ) );
335  //force a refresh of widget status to match diagram type
336  mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
337 
338  }
339  else // already a diagram renderer present
340  {
341  //single category renderer or interpolated one?
342  if ( dr->rendererName() == QLatin1String( "SingleCategory" ) )
343  {
344  mFixedSizeRadio->setChecked( true );
345  }
346  else
347  {
348  mAttributeBasedScalingRadio->setChecked( true );
349  }
350  mDiagramSizeSpinBox->setEnabled( mFixedSizeRadio->isChecked() );
351  mLinearScaleFrame->setEnabled( mAttributeBasedScalingRadio->isChecked() );
352  mCheckBoxAttributeLegend->setChecked( dr->attributeLegend() );
353 
354  //assume single category or linearly interpolated diagram renderer for now
355  const QList<QgsDiagramSettings> settingList = dr->diagramSettings();
356  if ( !settingList.isEmpty() )
357  {
358  mDiagramFrame->setEnabled( settingList.at( 0 ).enabled );
359  mDiagramFontButton->setCurrentFont( settingList.at( 0 ).font );
360  const QSizeF size = settingList.at( 0 ).size;
361  mBackgroundColorButton->setColor( settingList.at( 0 ).backgroundColor );
362  mOpacityWidget->setOpacity( settingList.at( 0 ).opacity );
363  mDiagramPenColorButton->setColor( settingList.at( 0 ).penColor );
364  mPenWidthSpinBox->setValue( settingList.at( 0 ).penWidth );
365  mDiagramSizeSpinBox->setValue( ( size.width() + size.height() ) / 2.0 );
366  mScaleRangeWidget->setScaleRange( ( settingList.at( 0 ).minimumScale > 0 ? settingList.at( 0 ).minimumScale : mLayer->minimumScale() ),
367  ( settingList.at( 0 ).maximumScale > 0 ? settingList.at( 0 ).maximumScale : mLayer->maximumScale() ) );
368  mScaleVisibilityGroupBox->setChecked( settingList.at( 0 ).scaleBasedVisibility );
369  mDiagramUnitComboBox->setUnit( settingList.at( 0 ).sizeType );
370  mDiagramUnitComboBox->setMapUnitScale( settingList.at( 0 ).sizeScale );
371  mDiagramLineUnitComboBox->setUnit( settingList.at( 0 ).lineSizeUnit );
372  mDiagramLineUnitComboBox->setMapUnitScale( settingList.at( 0 ).lineSizeScale );
373 
374  if ( settingList.at( 0 ).labelPlacementMethod == QgsDiagramSettings::Height )
375  {
376  mLabelPlacementComboBox->setCurrentIndex( 0 );
377  }
378  else
379  {
380  mLabelPlacementComboBox->setCurrentIndex( 1 );
381  }
382 
383  if ( settingList.at( 0 ).paintEffect() )
384  mPaintEffect.reset( settingList.at( 0 ).paintEffect()->clone() );
385 
386  mAngleOffsetComboBox->setCurrentIndex( mAngleOffsetComboBox->findData( settingList.at( 0 ).rotationOffset ) );
387  mAngleDirectionComboBox->setCurrentIndex( mAngleDirectionComboBox->findData( settingList.at( 0 ).direction() ) );
388 
389  mOrientationLeftButton->setProperty( "direction", QgsDiagramSettings::Left );
390  mOrientationRightButton->setProperty( "direction", QgsDiagramSettings::Right );
391  mOrientationUpButton->setProperty( "direction", QgsDiagramSettings::Up );
392  mOrientationDownButton->setProperty( "direction", QgsDiagramSettings::Down );
393  switch ( settingList.at( 0 ).diagramOrientation )
394  {
396  mOrientationLeftButton->setChecked( true );
397  break;
398 
400  mOrientationRightButton->setChecked( true );
401  break;
402 
404  mOrientationUpButton->setChecked( true );
405  break;
406 
408  mOrientationDownButton->setChecked( true );
409  break;
410  }
411 
412  mBarWidthSpinBox->setValue( settingList.at( 0 ).barWidth );
413  mBarSpacingSpinBox->setValue( settingList.at( 0 ).spacing() );
414  mBarSpacingUnitComboBox->setUnit( settingList.at( 0 ).spacingUnit() );
415  mBarSpacingUnitComboBox->setMapUnitScale( settingList.at( 0 ).spacingMapUnitScale() );
416 
417  mShowAxisGroupBox->setChecked( settingList.at( 0 ).showAxis() );
418  if ( settingList.at( 0 ).axisLineSymbol() )
419  mAxisLineStyleButton->setSymbol( settingList.at( 0 ).axisLineSymbol()->clone() );
420 
421  mIncreaseSmallDiagramsCheck->setChecked( settingList.at( 0 ).minimumSize != 0 );
422  mIncreaseMinimumSizeSpinBox->setEnabled( mIncreaseSmallDiagramsCheck->isChecked() );
423  mIncreaseMinimumSizeLabel->setEnabled( mIncreaseSmallDiagramsCheck->isChecked() );
424 
425  mIncreaseMinimumSizeSpinBox->setValue( settingList.at( 0 ).minimumSize );
426 
427  if ( settingList.at( 0 ).scaleByArea )
428  {
429  mScaleDependencyComboBox->setCurrentIndex( 0 );
430  }
431  else
432  {
433  mScaleDependencyComboBox->setCurrentIndex( 1 );
434  }
435 
436  const QList< QColor > categoryColors = settingList.at( 0 ).categoryColors;
437  const QList< QString > categoryAttributes = settingList.at( 0 ).categoryAttributes;
438  const QList< QString > categoryLabels = settingList.at( 0 ).categoryLabels;
439  QList< QString >::const_iterator catIt = categoryAttributes.constBegin();
440  QList< QColor >::const_iterator coIt = categoryColors.constBegin();
441  QList< QString >::const_iterator labIt = categoryLabels.constBegin();
442  for ( ; catIt != categoryAttributes.constEnd(); ++catIt, ++coIt, ++labIt )
443  {
444  QTreeWidgetItem *newItem = new QTreeWidgetItem( mDiagramAttributesTreeWidget );
445  newItem->setText( 0, *catIt );
446  newItem->setData( 0, RoleAttributeExpression, *catIt );
447  newItem->setFlags( newItem->flags() & ~Qt::ItemIsDropEnabled );
448  QColor col( *coIt );
449  col.setAlpha( 255 );
450  newItem->setData( ColumnColor, Qt::EditRole, col );
451  newItem->setText( 2, *labIt );
452  newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
453  }
454  }
455 
456  if ( dr->rendererName() == QLatin1String( "LinearlyInterpolated" ) )
457  {
458  const QgsLinearlyInterpolatedDiagramRenderer *lidr = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer *>( dr );
459  if ( lidr )
460  {
461  mDiagramSizeSpinBox->setEnabled( false );
462  mLinearScaleFrame->setEnabled( true );
463  mMaxValueSpinBox->setValue( lidr->upperValue() );
464  mSizeSpinBox->setValue( ( lidr->upperSize().width() + lidr->upperSize().height() ) / 2 );
466  {
467  mSizeFieldExpressionWidget->setField( lidr->classificationAttributeExpression() );
468  }
469  else
470  {
471  mSizeFieldExpressionWidget->setField( lidr->classificationField() );
472  }
473 
474  mSizeLegend.reset( lidr->dataDefinedSizeLegend() ? new QgsDataDefinedSizeLegend( *lidr->dataDefinedSizeLegend() ) : nullptr );
475  }
476  }
477 
478  const QgsDiagramLayerSettings *dls = mLayer->diagramLayerSettings();
479  if ( dls )
480  {
481  mDiagramDistanceSpinBox->setValue( dls->distance() );
482  mPrioritySlider->setValue( dls->priority() );
483  mZIndexSpinBox->setValue( dls->zIndex() );
484 
485  switch ( dls->placement() )
486  {
488  radAroundPoint->setChecked( true );
489  radAroundCentroid->setChecked( true );
490  break;
491 
493  radOverPoint->setChecked( true );
494  radOverCentroid->setChecked( true );
495  break;
496 
498  radAroundLine->setChecked( true );
499  radPolygonPerimeter->setChecked( true );
500  break;
501 
503  radOverLine->setChecked( true );
504  radInsidePolygon->setChecked( true );
505  break;
506 
507  default:
508  break;
509  }
510 
511  chkLineAbove->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::AboveLine );
512  chkLineBelow->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::BelowLine );
513  chkLineOn->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::OnLine );
515  chkLineOrientationDependent->setChecked( true );
517 
518  mShowAllCheckBox->setChecked( dls->showAllDiagrams() );
519 
520  mDataDefinedProperties = dls->dataDefinedProperties();
521  }
522 
523  if ( dr->diagram() )
524  {
525  mDiagramType = dr->diagram()->diagramName();
526 
527  mDiagramTypeComboBox->blockSignals( true );
528  mDiagramTypeComboBox->setCurrentIndex( settingList.at( 0 ).enabled ? mDiagramTypeComboBox->findData( mDiagramType ) : 0 );
529  mDiagramTypeComboBox->blockSignals( false );
530  //force a refresh of widget status to match diagram type
531  mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
532  if ( mDiagramTypeComboBox->currentIndex() == -1 )
533  {
534  QMessageBox::warning( this, tr( "Diagram Properties" ),
535  tr( "The diagram type '%1' is unknown. A default type is selected for you." ).arg( mDiagramType ), QMessageBox::Ok );
536  mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( DIAGRAM_NAME_PIE ) );
537  }
538  }
539  }
540  mPaintEffectWidget->setPaintEffect( mPaintEffect.get() );
541 }
542 
544 {
545  QgsSettings settings;
546  settings.setValue( QStringLiteral( "Windows/Diagrams/OptionsSplitState" ), mDiagramOptionsSplitter->saveState() );
547  settings.setValue( QStringLiteral( "Windows/Diagrams/Tab" ), mDiagramOptionsListWidget->currentRow() );
548 }
549 
550 void QgsDiagramProperties::registerDataDefinedButton( QgsPropertyOverrideButton *button, QgsDiagramLayerSettings::Property key )
551 {
552  button->init( key, mDataDefinedProperties, QgsDiagramLayerSettings::propertyDefinitions(), mLayer, true );
553  connect( button, &QgsPropertyOverrideButton::changed, this, &QgsDiagramProperties::updateProperty );
554  connect( button, &QgsPropertyOverrideButton::createAuxiliaryField, this, &QgsDiagramProperties::createAuxiliaryField );
555  button->registerExpressionContextGenerator( this );
556 }
557 
558 void QgsDiagramProperties::updateProperty()
559 {
560  QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
562  mDataDefinedProperties.setProperty( key, button->toProperty() );
563 }
564 
566 {
567  if ( index == 0 )
568  {
569  mDiagramFrame->setEnabled( false );
570  }
571  else
572  {
573  mDiagramFrame->setEnabled( true );
574 
575  mDiagramType = mDiagramTypeComboBox->itemData( index ).toString();
576 
577  if ( DIAGRAM_NAME_TEXT == mDiagramType )
578  {
579  mTextOptionsFrame->show();
580  mBackgroundColorLabel->show();
581  mBackgroundColorButton->show();
582  mBackgroundColorDDBtn->show();
583  mDiagramFontButton->show();
584  }
585  else
586  {
587  mTextOptionsFrame->hide();
588  mBackgroundColorLabel->hide();
589  mBackgroundColorButton->hide();
590  mBackgroundColorDDBtn->hide();
591  mDiagramFontButton->hide();
592  }
593 
594  if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType || DIAGRAM_NAME_STACKED == mDiagramType )
595  {
596  mBarWidthLabel->show();
597  mBarWidthSpinBox->show();
598  mBarSpacingLabel->show();
599  mBarSpacingSpinBox->show();
600  mBarSpacingUnitComboBox->show();
601  mBarOptionsFrame->show();
602  mShowAxisGroupBox->show();
603  if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType )
604  mAttributeBasedScalingRadio->setChecked( true );
605  mFixedSizeRadio->setEnabled( DIAGRAM_NAME_STACKED == mDiagramType );
606  mDiagramSizeSpinBox->setEnabled( DIAGRAM_NAME_STACKED == mDiagramType );
607  mLinearlyScalingLabel->setText( tr( "Bar length: Scale linearly, so that the following value matches the specified bar length:" ) );
608  mSizeLabel->setText( tr( "Bar length" ) );
609  mFrameIncreaseSize->setVisible( false );
610  }
611  else
612  {
613  mBarWidthLabel->hide();
614  mBarWidthSpinBox->hide();
615  mBarSpacingLabel->hide();
616  mBarSpacingSpinBox->hide();
617  mBarSpacingUnitComboBox->hide();
618  mShowAxisGroupBox->hide();
619  mBarOptionsFrame->hide();
620  mLinearlyScalingLabel->setText( tr( "Scale linearly between 0 and the following attribute value / diagram size:" ) );
621  mSizeLabel->setText( tr( "Size" ) );
622  mAttributeBasedScalingRadio->setEnabled( true );
623  mFixedSizeRadio->setEnabled( true );
624  mDiagramSizeSpinBox->setEnabled( mFixedSizeRadio->isChecked() );
625  mFrameIncreaseSize->setVisible( true );
626  }
627 
628  if ( DIAGRAM_NAME_TEXT == mDiagramType || DIAGRAM_NAME_PIE == mDiagramType )
629  {
630  mScaleDependencyComboBox->show();
631  mScaleDependencyLabel->show();
632  }
633  else
634  {
635  mScaleDependencyComboBox->hide();
636  mScaleDependencyLabel->hide();
637  }
638 
639  if ( DIAGRAM_NAME_PIE == mDiagramType )
640  {
641  mAngleOffsetComboBox->show();
642  mAngleDirectionComboBox->show();
643  mAngleDirectionLabel->show();
644  mAngleOffsetLabel->show();
645  mStartAngleDDBtn->show();
646  }
647  else
648  {
649  mAngleOffsetComboBox->hide();
650  mAngleDirectionComboBox->hide();
651  mAngleDirectionLabel->hide();
652  mAngleOffsetLabel->hide();
653  mStartAngleDDBtn->hide();
654  }
655  }
656 }
657 QString QgsDiagramProperties::guessLegendText( const QString &expression )
658 {
659  //trim unwanted characters from expression text for legend
660  QString text = expression.mid( expression.startsWith( '\"' ) ? 1 : 0 );
661  if ( text.endsWith( '\"' ) )
662  text.chop( 1 );
663  return text;
664 }
665 
666 void QgsDiagramProperties::addAttribute( QTreeWidgetItem *item )
667 {
668  QTreeWidgetItem *newItem = new QTreeWidgetItem( mDiagramAttributesTreeWidget );
669 
670  newItem->setText( 0, item->text( 0 ) );
671  newItem->setText( 2, guessLegendText( item->text( 0 ) ) );
672  newItem->setData( 0, RoleAttributeExpression, item->data( 0, RoleAttributeExpression ) );
673  newItem->setFlags( ( newItem->flags() | Qt::ItemIsEditable ) & ~Qt::ItemIsDropEnabled );
674 
675  //set initial color for diagram category
676  QRandomGenerator colorGenerator;
677  const int red = colorGenerator.bounded( 1, 256 );
678  const int green = colorGenerator.bounded( 1, 256 );
679  const int blue = colorGenerator.bounded( 1, 256 );
680  const QColor randomColor( red, green, blue );
681  newItem->setData( ColumnColor, Qt::EditRole, randomColor );
682  mDiagramAttributesTreeWidget->addTopLevelItem( newItem );
683 }
684 
686 {
687  const auto constSelectedItems = mAttributesTreeWidget->selectedItems();
688  for ( QTreeWidgetItem *attributeItem : constSelectedItems )
689  {
690  addAttribute( attributeItem );
691  }
692 }
693 
694 void QgsDiagramProperties::mAttributesTreeWidget_itemDoubleClicked( QTreeWidgetItem *item, int column )
695 {
696  Q_UNUSED( column )
697  addAttribute( item );
698 }
699 
701 {
702  const auto constSelectedItems = mDiagramAttributesTreeWidget->selectedItems();
703  for ( QTreeWidgetItem *attributeItem : constSelectedItems )
704  {
705  delete attributeItem;
706  }
707 }
708 
710 {
711  if ( !mLayer )
712  return;
713 
714  float maxValue = 0.0;
715 
716  bool isExpression;
717  const QString sizeFieldNameOrExp = mSizeFieldExpressionWidget->currentField( &isExpression );
718  if ( isExpression )
719  {
720  QgsExpression exp( sizeFieldNameOrExp );
721  QgsExpressionContext context;
726 
727  exp.prepare( &context );
728  if ( !exp.hasEvalError() )
729  {
730  QgsFeature feature;
731  QgsFeatureIterator features = mLayer->getFeatures();
732  while ( features.nextFeature( *&feature ) )
733  {
734  context.setFeature( feature );
735  maxValue = std::max( maxValue, exp.evaluate( &context ).toFloat() );
736  }
737  }
738  else
739  {
740  QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
741  }
742  }
743  else
744  {
745  const int attributeNumber = mLayer->fields().lookupField( sizeFieldNameOrExp );
746  maxValue = mLayer->maximumValue( attributeNumber ).toFloat();
747  }
748 
749  mMaxValueSpinBox->setValue( maxValue );
750 }
751 
753 {
754  switch ( column )
755  {
756  case ColumnAttributeExpression:
757  {
758  const QString currentExpression = item->data( 0, RoleAttributeExpression ).toString();
759 
760  const QString newExpression = showExpressionBuilder( currentExpression );
761  if ( !newExpression.isEmpty() )
762  {
763  item->setData( 0, Qt::DisplayRole, newExpression );
764  item->setData( 0, RoleAttributeExpression, newExpression );
765  }
766  break;
767  }
768 
769  case ColumnColor:
770  break;
771 
772  case ColumnLegendText:
773  break;
774  }
775 }
776 
778 {
780  if ( panel && panel->dockMode() )
781  {
782  QgsLabelEngineConfigWidget *widget = new QgsLabelEngineConfigWidget( mMapCanvas );
784  panel->openPanel( widget );
785  }
786  else
787  {
788  QgsLabelEngineConfigDialog dialog( mMapCanvas, this );
789  dialog.exec();
790  // reactivate button's window
791  activateWindow();
792  }
793 }
794 
796 {
797  const int index = mDiagramTypeComboBox->currentIndex();
798  const bool diagramsEnabled = ( index != 0 );
799 
800  std::unique_ptr< QgsDiagram > diagram;
801 
802  if ( diagramsEnabled && 0 == mDiagramAttributesTreeWidget->topLevelItemCount() )
803  {
804  QMessageBox::warning( this, tr( "Diagrams: No attributes added." ),
805  tr( "You did not add any attributes to this diagram layer. Please specify the attributes to visualize on the diagrams or disable diagrams." ) );
806  }
807 
808  if ( mDiagramType == DIAGRAM_NAME_TEXT )
809  {
810  diagram = std::make_unique< QgsTextDiagram >();
811  }
812  else if ( mDiagramType == DIAGRAM_NAME_PIE )
813  {
814  diagram = std::make_unique< QgsPieDiagram >();
815  }
816  else if ( mDiagramType == DIAGRAM_NAME_STACKED )
817  {
818  diagram = std::make_unique< QgsStackedBarDiagram >();
819  }
820  else // if ( diagramType == DIAGRAM_NAME_HISTOGRAM )
821  {
822  diagram = std::make_unique< QgsHistogramDiagram >();
823  }
824 
826  ds.enabled = ( mDiagramTypeComboBox->currentIndex() != 0 );
827  ds.font = mDiagramFontButton->currentFont();
828  ds.opacity = mOpacityWidget->opacity();
829 
830  QList<QColor> categoryColors;
831  QList<QString> categoryAttributes;
832  QList<QString> categoryLabels;
833  categoryColors.reserve( mDiagramAttributesTreeWidget->topLevelItemCount() );
834  categoryAttributes.reserve( mDiagramAttributesTreeWidget->topLevelItemCount() );
835  categoryLabels.reserve( mDiagramAttributesTreeWidget->topLevelItemCount() );
836  for ( int i = 0; i < mDiagramAttributesTreeWidget->topLevelItemCount(); ++i )
837  {
838  QColor color = mDiagramAttributesTreeWidget->topLevelItem( i )->data( ColumnColor, Qt::EditRole ).value<QColor>();
839  color.setAlphaF( ds.opacity );
840  categoryColors.append( color );
841  categoryAttributes.append( mDiagramAttributesTreeWidget->topLevelItem( i )->data( 0, RoleAttributeExpression ).toString() );
842  categoryLabels.append( mDiagramAttributesTreeWidget->topLevelItem( i )->text( 2 ) );
843  }
844  ds.categoryColors = categoryColors;
845  ds.categoryAttributes = categoryAttributes;
846  ds.categoryLabels = categoryLabels;
847  ds.size = QSizeF( mDiagramSizeSpinBox->value(), mDiagramSizeSpinBox->value() );
848  ds.sizeType = mDiagramUnitComboBox->unit();
849  ds.sizeScale = mDiagramUnitComboBox->getMapUnitScale();
850  ds.lineSizeUnit = mDiagramLineUnitComboBox->unit();
851  ds.lineSizeScale = mDiagramLineUnitComboBox->getMapUnitScale();
852  ds.labelPlacementMethod = static_cast<QgsDiagramSettings::LabelPlacementMethod>( mLabelPlacementComboBox->currentData().toInt() );
853  ds.scaleByArea = ( mDiagramType == DIAGRAM_NAME_STACKED ) ? false : mScaleDependencyComboBox->currentData().toBool();
854 
855  if ( mIncreaseSmallDiagramsCheck->isChecked() )
856  {
857  ds.minimumSize = mIncreaseMinimumSizeSpinBox->value();
858  }
859  else
860  {
861  ds.minimumSize = 0;
862  }
863 
864  ds.backgroundColor = mBackgroundColorButton->color();
865  ds.penColor = mDiagramPenColorButton->color();
866  ds.penWidth = mPenWidthSpinBox->value();
867  ds.minimumScale = mScaleRangeWidget->minimumScale();
868  ds.maximumScale = mScaleRangeWidget->maximumScale();
869  ds.scaleBasedVisibility = mScaleVisibilityGroupBox->isChecked();
870 
871  // Diagram angle offset (pie)
872  ds.rotationOffset = mAngleOffsetComboBox->currentData().toInt();
873  ds.setDirection( static_cast< QgsDiagramSettings::Direction>( mAngleDirectionComboBox->currentData().toInt() ) );
874 
875  // Diagram orientation (histogram)
876  ds.diagramOrientation = static_cast<QgsDiagramSettings::DiagramOrientation>( mOrientationButtonGroup->checkedButton()->property( "direction" ).toInt() );
877 
878  ds.barWidth = mBarWidthSpinBox->value();
879 
880  ds.setAxisLineSymbol( mAxisLineStyleButton->clonedSymbol< QgsLineSymbol >() );
881  ds.setShowAxis( mShowAxisGroupBox->isChecked() );
882 
883  ds.setSpacing( mBarSpacingSpinBox->value() );
884  ds.setSpacingUnit( mBarSpacingUnitComboBox->unit() );
885  ds.setSpacingMapUnitScale( mBarSpacingUnitComboBox->getMapUnitScale() );
886 
887  if ( mPaintEffect && ( !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) || mPaintEffect->enabled() ) )
888  ds.setPaintEffect( mPaintEffect->clone() );
889  else
890  ds.setPaintEffect( nullptr );
891 
892  QgsDiagramRenderer *renderer = nullptr;
893  if ( mFixedSizeRadio->isChecked() )
894  {
896  dr->setDiagramSettings( ds );
897  renderer = dr;
898  }
899  else
900  {
902  dr->setLowerValue( 0.0 );
903  dr->setLowerSize( QSizeF( 0.0, 0.0 ) );
904  dr->setUpperValue( mMaxValueSpinBox->value() );
905  dr->setUpperSize( QSizeF( mSizeSpinBox->value(), mSizeSpinBox->value() ) );
906 
907  bool isExpression;
908  const QString sizeFieldNameOrExp = mSizeFieldExpressionWidget->currentField( &isExpression );
909  dr->setClassificationAttributeIsExpression( isExpression );
910  if ( isExpression )
911  {
912  dr->setClassificationAttributeExpression( sizeFieldNameOrExp );
913  }
914  else
915  {
916  dr->setClassificationField( sizeFieldNameOrExp );
917  }
918  dr->setDiagramSettings( ds );
919 
920  dr->setDataDefinedSizeLegend( mSizeLegend ? new QgsDataDefinedSizeLegend( *mSizeLegend ) : nullptr );
921 
922  renderer = dr;
923  }
924  renderer->setDiagram( diagram.release() );
925  renderer->setAttributeLegend( mCheckBoxAttributeLegend->isChecked() );
926  mLayer->setDiagramRenderer( renderer );
927 
929  dls.setDataDefinedProperties( mDataDefinedProperties );
930  dls.setDistance( mDiagramDistanceSpinBox->value() );
931  dls.setPriority( mPrioritySlider->value() );
932  dls.setZIndex( mZIndexSpinBox->value() );
933  dls.setShowAllDiagrams( mShowAllCheckBox->isChecked() );
934 
935  QWidget *curWdgt = stackedPlacement->currentWidget();
936  if ( ( curWdgt == pagePoint && radAroundPoint->isChecked() )
937  || ( curWdgt == pagePolygon && radAroundCentroid->isChecked() ) )
938  {
940  }
941  else if ( ( curWdgt == pagePoint && radOverPoint->isChecked() )
942  || ( curWdgt == pagePolygon && radOverCentroid->isChecked() ) )
943  {
945  }
946  else if ( ( curWdgt == pageLine && radAroundLine->isChecked() )
947  || ( curWdgt == pagePolygon && radPolygonPerimeter->isChecked() ) )
948  {
950  }
951  else if ( ( curWdgt == pageLine && radOverLine->isChecked() )
952  || ( curWdgt == pagePolygon && radInsidePolygon->isChecked() ) )
953  {
955  }
956  else
957  {
958  qFatal( "Invalid settings" );
959  }
960 
961  QgsDiagramLayerSettings::LinePlacementFlags flags = QgsDiagramLayerSettings::LinePlacementFlags();
962  if ( chkLineAbove->isChecked() )
964  if ( chkLineBelow->isChecked() )
966  if ( chkLineOn->isChecked() )
968  if ( ! chkLineOrientationDependent->isChecked() )
970  dls.setLinePlacementFlags( flags );
971 
972  mLayer->setDiagramLayerSettings( dls );
973 
974  // refresh
975  QgsProject::instance()->setDirty( true );
976  mLayer->triggerRepaint();
977 }
978 
979 QString QgsDiagramProperties::showExpressionBuilder( const QString &initialExpression )
980 {
981  QgsExpressionContext context;
987 
988  QgsExpressionBuilderDialog dlg( mLayer, initialExpression, this, QStringLiteral( "generic" ), context );
989  dlg.setWindowTitle( tr( "Expression Based Attribute" ) );
990 
991  QgsDistanceArea myDa;
992  myDa.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
993  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
994  dlg.setGeomCalculator( myDa );
995 
996  if ( dlg.exec() == QDialog::Accepted )
997  {
998  return dlg.expressionText();
999  }
1000  else
1001  {
1002  return QString();
1003  }
1004 }
1005 
1007 {
1008  QString expression;
1009  QList<QTreeWidgetItem *> selections = mAttributesTreeWidget->selectedItems();
1010  if ( !selections.empty() )
1011  {
1012  expression = selections[0]->text( 0 );
1013  }
1014 
1015  const QString newExpression = showExpressionBuilder( expression );
1016 
1017  //Only add the expression if the user has entered some text.
1018  if ( !newExpression.isEmpty() )
1019  {
1020  QTreeWidgetItem *newItem = new QTreeWidgetItem( mDiagramAttributesTreeWidget );
1021 
1022  newItem->setText( 0, newExpression );
1023  newItem->setText( 2, newExpression );
1024  newItem->setData( 0, RoleAttributeExpression, newExpression );
1025  newItem->setFlags( ( newItem->flags() | Qt::ItemIsEditable ) & ~Qt::ItemIsDropEnabled );
1026 
1027  //set initial color for diagram category
1028  QRandomGenerator colorGenerator;
1029  const int red = colorGenerator.bounded( 1, 256 );
1030  const int green = colorGenerator.bounded( 1, 256 );
1031  const int blue = colorGenerator.bounded( 1, 256 );
1032 
1033  const QColor randomColor( red, green, blue );
1034  newItem->setData( ColumnColor, Qt::EditRole, randomColor );
1035  mDiagramAttributesTreeWidget->addTopLevelItem( newItem );
1036  }
1037  activateWindow(); // set focus back parent
1038 }
1039 
1041 {
1042  mDiagramOptionsListWidget->blockSignals( true );
1043  mDiagramOptionsListWidget->setCurrentRow( index );
1044  mDiagramOptionsListWidget->blockSignals( false );
1045 }
1046 
1048 {
1049  QWidget *curWdgt = stackedPlacement->currentWidget();
1050 
1051  if ( ( curWdgt == pagePoint && radAroundPoint->isChecked() )
1052  || ( curWdgt == pageLine && radAroundLine->isChecked() )
1053  || ( curWdgt == pagePolygon && radAroundCentroid->isChecked() ) )
1054  {
1055  mDiagramDistanceLabel->setEnabled( true );
1056  mDiagramDistanceSpinBox->setEnabled( true );
1057  mDistanceDDBtn->setEnabled( true );
1058  }
1059  else
1060  {
1061  mDiagramDistanceLabel->setEnabled( false );
1062  mDiagramDistanceSpinBox->setEnabled( false );
1063  mDistanceDDBtn->setEnabled( false );
1064  }
1065 
1066  const bool linePlacementEnabled = mLayer->geometryType() == QgsWkbTypes::LineGeometry && ( curWdgt == pageLine && radAroundLine->isChecked() );
1067  chkLineAbove->setEnabled( linePlacementEnabled );
1068  chkLineBelow->setEnabled( linePlacementEnabled );
1069  chkLineOn->setEnabled( linePlacementEnabled );
1070  chkLineOrientationDependent->setEnabled( linePlacementEnabled );
1071 }
1072 
1074 {
1075  mButtonSizeLegendSettings->setEnabled( mAttributeBasedScalingRadio->isChecked() );
1076 }
1077 
1079 {
1080  // prepare size transformer
1081  bool isExpression;
1082  const QString sizeFieldNameOrExp = mSizeFieldExpressionWidget->currentField( &isExpression );
1083  QgsProperty ddSize = isExpression ? QgsProperty::fromExpression( sizeFieldNameOrExp ) : QgsProperty::fromField( sizeFieldNameOrExp );
1084  const bool scaleByArea = mScaleDependencyComboBox->currentData().toBool();
1086  0.0, mMaxValueSpinBox->value(), 0.0, mSizeSpinBox->value() ) );
1087 
1088  QgsDataDefinedSizeLegendWidget *panel = new QgsDataDefinedSizeLegendWidget( mSizeLegend.get(), ddSize, nullptr, mMapCanvas );
1089 
1090  QDialog dlg;
1091  dlg.setLayout( new QVBoxLayout() );
1092  dlg.setWindowTitle( panel->panelTitle() );
1093  dlg.layout()->addWidget( panel );
1094  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
1095  connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1096  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDiagramProperties::showHelp );
1097  connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1098  dlg.layout()->addWidget( buttonBox );
1099  if ( dlg.exec() )
1100  mSizeLegend.reset( panel->dataDefinedSizeLegend() );
1101 }
1102 
1103 void QgsDiagramProperties::showHelp()
1104 {
1105  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#legend" ) );
1106 }
1107 
1108 void QgsDiagramProperties::createAuxiliaryField()
1109 {
1110  // try to create an auxiliary layer if not yet created
1111  if ( !mLayer->auxiliaryLayer() )
1112  {
1113  QgsNewAuxiliaryLayerDialog dlg( mLayer, this );
1114  dlg.exec();
1115  }
1116 
1117  // return if still not exists
1118  if ( !mLayer->auxiliaryLayer() )
1119  return;
1120 
1121  QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
1124 
1125  // create property in auxiliary storage if necessary
1126  if ( !mLayer->auxiliaryLayer()->exists( def ) )
1127  mLayer->auxiliaryLayer()->addAuxiliaryField( def );
1128 
1129  // update property with join field name from auxiliary storage
1130  QgsProperty property = button->toProperty();
1131  property.setField( QgsAuxiliaryLayer::nameFromProperty( def, true ) );
1132  property.setActive( true );
1133  button->updateFieldLists();
1134  button->setToProperty( property );
1135  mDataDefinedProperties.setProperty( key, button->toProperty() );
1136 
1137  emit auxiliaryFieldCreated();
1138 }
EditBlockerDelegate(QObject *parent=nullptr)
QWidget * createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const override
@ Line
Line symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1052
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
bool addAuxiliaryField(const QgsPropertyDefinition &definition)
Adds an auxiliary field for the given property.
static QString nameFromProperty(const QgsPropertyDefinition &def, bool joined=false)
Returns the name of the auxiliary field for a property definition.
bool exists(const QgsPropertyDefinition &definition) const
Returns true if the property is stored in the layer already, false otherwise.
A delegate for showing a color swatch in a list.
Widget for configuration of appearance of legend for marker symbols with data-defined size.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be nullptr). Ownership is passed to the caller.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
Stores the settings for rendering of all diagrams for a layer.
void setZIndex(double index)
Sets the diagram z-index.
Placement placement() const
Returns the diagram placement.
Property
Data definable properties.
@ StartAngle
Angle offset for pie diagram.
@ Distance
Distance to diagram from feature.
@ IsObstacle
Whether diagram features act as obstacles for other diagrams/labels.
@ PositionX
X-coordinate data defined diagram position.
@ Priority
Diagram priority (between 0 and 10)
@ AlwaysShow
Whether the diagram should always be shown, even if it overlaps other diagrams/labels.
@ Show
Whether to show the diagram.
@ ZIndex
Z-index for diagram ordering.
@ BackgroundColor
Diagram background color.
@ PositionY
Y-coordinate data defined diagram position.
bool showAllDiagrams() const
Returns whether the layer should show all diagrams, including overlapping diagrams.
LinePlacementFlags linePlacementFlags() const
Returns the diagram placement flags.
void setShowAllDiagrams(bool showAllDiagrams)
Sets whether the layer should show all diagrams, including overlapping diagrams.
void setDistance(double distance)
Sets the distance between the diagram and the feature.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the diagram property definitions.
void setPriority(int value)
Sets the diagram priority.
int priority() const
Returns the diagram priority.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the diagram's property collection, used for data defined overrides.
void setPlacement(Placement value)
Sets the diagram placement.
void setLinePlacementFlags(LinePlacementFlags flags)
Sets the the diagram placement flags.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the diagram's property collection, used for data defined overrides.
double zIndex() const
Returns the diagram z-index.
double distance() const
Returns the distance between the diagram and the feature (in mm).
void mDiagramAttributesTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
void mDiagramTypeComboBox_currentIndexChanged(int index)
void syncToLayer()
Updates the widget to reflect the layer's current diagram settings.
QgsDiagramProperties(QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas)
void addAttribute(QTreeWidgetItem *item)
Adds an attribute from the list of available attributes to the assigned attributes with a random colo...
void mAttributesTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
void mDiagramStackedWidget_currentChanged(int index)
Evaluates and returns the diagram settings relating to a diagram for a specific feature.
virtual QString rendererName() const =0
void setAttributeLegend(bool enabled)
Sets whether the renderer will show legend items for diagram attributes.
QgsDiagram * diagram() const
virtual QList< QgsDiagramSettings > diagramSettings() const =0
Returns list with all diagram settings in the renderer.
bool attributeLegend() const
Returns true if renderer will show legend items for diagram attributes.
void setDiagram(QgsDiagram *d)
Stores the settings for rendering a single diagram.
QgsUnitTypes::RenderUnit sizeType
Diagram size unit.
void setDirection(Direction direction)
Sets the chart's angular direction.
DiagramOrientation
Orientation of histogram.
LabelPlacementMethod labelPlacementMethod
double opacity
Opacity, from 0 (transparent) to 1.0 (opaque)
QList< QString > categoryAttributes
QList< QString > categoryLabels
DiagramOrientation diagramOrientation
QgsMapUnitScale lineSizeScale
Line unit scale.
QList< QColor > categoryColors
double rotationOffset
Rotation offset, in degrees clockwise from horizontal.
QgsMapUnitScale sizeScale
Diagram size unit scale.
double minimumScale
The minimum map scale (i.e.
void setSpacing(double spacing)
Sets the spacing between diagram contents.
double maximumScale
The maximum map scale (i.e.
void setAxisLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol to use for rendering axis in diagrams.
QgsUnitTypes::RenderUnit lineSizeUnit
Line unit index.
void setSpacingMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the content spacing.
double minimumSize
Scale diagrams smaller than mMinimumSize to mMinimumSize.
Direction
Angular directions.
@ Counterclockwise
Counter-clockwise orientation.
@ Clockwise
Clockwise orientation.
void setShowAxis(bool showAxis)
Sets whether the diagram axis should be shown.
void setPaintEffect(QgsPaintEffect *effect)
Sets the paint effect to use while rendering diagrams.
void setSpacingUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the content spacing.
virtual QString diagramName() const =0
Gets a descriptive name for this diagram type.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
A generic dialog for building expression strings.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QString evalErrorString() const
Returns evaluation error.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QString name
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
@ ModeQFont
Configure font settings for use with QFont objects.
Definition: qgsfontbutton.h:62
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
Dialog for configuring the labeling engine.
Widget for configuring the labeling engine.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
void setDiagramSettings(const QgsDiagramSettings &s)
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend.
QString classificationField() const
Returns the field name used for interpolating the diagram size.
void setClassificationAttributeExpression(const QString &expression)
void setClassificationField(const QString &field)
Sets the field name used for interpolating the diagram size.
void setClassificationAttributeIsExpression(bool isExpression)
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
double minimumScale() const
Returns the minimum map scale (i.e.
double maximumScale() const
Returns the maximum map scale (i.e.
A dialog to create a new auxiliary layer.
static QgsPaintEffect * defaultStack()
Returns a new effect stack consisting of a sensible selection of default effects.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
Base class for any widget that can be shown as a inline panel.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
QString panelTitle()
The title of the panel.
void widgetChanged()
Emitted when the widget state changes.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
bool dockMode()
Returns the dock mode state.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:518
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
Definition for a property.
Definition: qgsproperty.h:48
A button for controlling property overrides which may apply to a widget.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void updateFieldLists()
Updates list of fields.
void changed()
Emitted when property definition changes.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout).
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
int propertyKey() const
Returns the property key linked to the button.
void setToProperty(const QgsProperty &property)
Sets the widget to reflect the current state of a QgsProperty.
void createAuxiliaryField()
Emitted when creating a new auxiliary field.
A store for object properties.
Definition: qgsproperty.h:232
void setTransformer(QgsPropertyTransformer *transformer)
Sets an optional transformer to use for manipulating the calculated values for the property.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void setField(const QString &field)
Sets the field name the property references.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Renders the diagrams for all features with the same settings.
void setDiagramSettings(const QgsDiagramSettings &s)
QgsPropertyTransformer subclass for scaling a value into a size according to various scaling methods.
QList< QgsUnitTypes::RenderUnit > RenderUnitList
List of render units.
Definition: qgsunittypes.h:240
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:176
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:171
@ RenderInches
Inches.
Definition: qgsunittypes.h:174
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
Represents a vector layer which manages a vector based data sets.
QVariant maximumValue(int index) const FINAL
Returns the maximum value for an attribute column or an invalid variant in case of error.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
const QgsDiagramLayerSettings * diagramLayerSettings() const
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
void setDiagramLayerSettings(const QgsDiagramLayerSettings &s)
const QgsDiagramRenderer * diagramRenderer() const
void setDiagramRenderer(QgsDiagramRenderer *r)
Sets diagram rendering object (takes ownership)
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
#define DIAGRAM_NAME_HISTOGRAM
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define DIAGRAM_NAME_PIE
Definition: qgspiediagram.h:18
#define DIAGRAM_NAME_STACKED
#define DIAGRAM_NAME_TEXT