QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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
23
24#include "qgsproject.h"
25#include "qgsapplication.h"
29#include "moc_qgsdiagramproperties.cpp"
30#include "qgsdiagramrenderer.h"
31#include "qgsfeatureiterator.h"
33#include "qgsmapcanvas.h"
35#include "qgslogger.h"
36#include "qgssettings.h"
38#include "qgsauxiliarystorage.h"
42#include "qgspainteffect.h"
43#include "qgslinesymbol.h"
44
45#include <QList>
46#include <QMessageBox>
47#include <QStyledItemDelegate>
48#include <QRandomGenerator>
49
50QgsExpressionContext QgsDiagramProperties::createExpressionContext() const
51{
52 QgsExpressionContext expContext;
53 if ( mMapCanvas )
54 {
55 expContext = mMapCanvas->createExpressionContext();
56 }
57 else
58 {
63 }
64 expContext << QgsExpressionContextUtils::layerScope( mLayer );
65
66 return expContext;
67}
68
70 : QgsPanelWidget( parent )
71 , mLayer( layer )
72 , mMapCanvas( canvas )
73{
74 if ( !layer )
75 {
76 return;
77 }
78
79 setupUi( this );
80 connect( mDiagramTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsDiagramProperties::mDiagramTypeComboBox_currentIndexChanged );
81 connect( mAddCategoryPushButton, &QPushButton::clicked, this, &QgsDiagramProperties::mAddCategoryPushButton_clicked );
82 connect( mAttributesTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsDiagramProperties::mAttributesTreeWidget_itemDoubleClicked );
83 connect( mFindMaximumValueButton, &QPushButton::clicked, this, &QgsDiagramProperties::mFindMaximumValueButton_clicked );
84 connect( mRemoveCategoryPushButton, &QPushButton::clicked, this, &QgsDiagramProperties::mRemoveCategoryPushButton_clicked );
85 connect( mDiagramAttributesTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsDiagramProperties::mDiagramAttributesTreeWidget_itemDoubleClicked );
86 connect( mDiagramStackedWidget, &QStackedWidget::currentChanged, this, &QgsDiagramProperties::mDiagramStackedWidget_currentChanged );
87
88 // get rid of annoying outer focus rect on Mac
89 mDiagramOptionsListWidget->setAttribute( Qt::WA_MacShowFocusRect, false );
90
91 const int iconSize = QgsGuiUtils::scaleIconSize( 20 );
92 mOptionsTab->setIconSize( QSize( iconSize, iconSize ) );
93 mDiagramOptionsListWidget->setIconSize( QSize( iconSize, iconSize ) );
94
95 mBarSpacingSpinBox->setClearValue( 0 );
97
98 mDiagramFontButton->setMode( QgsFontButton::ModeQFont );
99
100 mDiagramTypeComboBox->blockSignals( true );
101 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "pie-chart.svg" ) );
102 mDiagramTypeComboBox->addItem( icon, tr( "Pie Chart" ), QgsPieDiagram::DIAGRAM_NAME_PIE );
103 icon = QgsApplication::getThemeIcon( QStringLiteral( "text.svg" ) );
104 mDiagramTypeComboBox->addItem( icon, tr( "Text Diagram" ), QgsTextDiagram::DIAGRAM_NAME_TEXT );
105 icon = QgsApplication::getThemeIcon( QStringLiteral( "histogram.svg" ) );
106 mDiagramTypeComboBox->addItem( icon, tr( "Histogram" ), QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM );
107 icon = QgsApplication::getThemeIcon( QStringLiteral( "stacked-bar.svg" ) );
108 mDiagramTypeComboBox->addItem( icon, tr( "Stacked Bars" ), QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR );
109 mDiagramTypeComboBox->blockSignals( false );
110
111 mAxisLineStyleButton->setSymbolType( Qgis::SymbolType::Line );
112 mAxisLineStyleButton->setDialogTitle( tr( "Axis Line Symbol" ) );
113
114 mScaleRangeWidget->setMapCanvas( mMapCanvas );
115 mSizeFieldExpressionWidget->registerExpressionContextGenerator( this );
116
117 mBackgroundColorButton->setColorDialogTitle( tr( "Select Background Color" ) );
118 mBackgroundColorButton->setAllowOpacity( true );
119 mBackgroundColorButton->setContext( QStringLiteral( "symbology" ) );
120 mBackgroundColorButton->setShowNoColor( true );
121 mBackgroundColorButton->setNoColorString( tr( "Transparent Background" ) );
122 mDiagramPenColorButton->setColorDialogTitle( tr( "Select Pen Color" ) );
123 mDiagramPenColorButton->setAllowOpacity( true );
124 mDiagramPenColorButton->setContext( QStringLiteral( "symbology" ) );
125 mDiagramPenColorButton->setShowNoColor( true );
126 mDiagramPenColorButton->setNoColorString( tr( "Transparent Stroke" ) );
127
128 mMaxValueSpinBox->setShowClearButton( false );
129 mSizeSpinBox->setClearValue( 5 );
130
131 mDiagramAttributesTreeWidget->setItemDelegateForColumn( ColumnAttributeExpression, new EditBlockerDelegate( this ) );
132 mDiagramAttributesTreeWidget->setItemDelegateForColumn( ColumnColor, new QgsColorSwatchDelegate( this ) );
133
134 mDiagramAttributesTreeWidget->setColumnWidth( ColumnColor, Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6.6 );
135
136 connect( mFixedSizeRadio, &QRadioButton::toggled, this, &QgsDiagramProperties::scalingTypeChanged );
137 connect( mAttributeBasedScalingRadio, &QRadioButton::toggled, this, &QgsDiagramProperties::scalingTypeChanged );
138
141
142 const Qgis::GeometryType layerType = layer->geometryType();
143 if ( layerType == Qgis::GeometryType::Unknown || layerType == Qgis::GeometryType::Null )
144 {
145 mDiagramTypeComboBox->setEnabled( false );
146 mOptionsTab->setEnabled( false );
147 mDiagramFrame->setEnabled( false );
148 }
149
150 // set placement methods page based on geometry type
151
152 switch ( layerType )
153 {
155 stackedPlacement->setCurrentWidget( pagePoint );
156 mLinePlacementFrame->setVisible( false );
157 break;
159 stackedPlacement->setCurrentWidget( pageLine );
160 mLinePlacementFrame->setVisible( true );
161 break;
163 stackedPlacement->setCurrentWidget( pagePolygon );
164 mLinePlacementFrame->setVisible( false );
165 break;
168 break;
169 }
170
171 //insert placement options
172 // setup point placement button group
173 mPlacePointBtnGrp = new QButtonGroup( this );
174 mPlacePointBtnGrp->addButton( radAroundPoint );
175 mPlacePointBtnGrp->addButton( radOverPoint );
176 mPlacePointBtnGrp->setExclusive( true );
177 connect( mPlacePointBtnGrp, qOverload<QAbstractButton *>( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets );
178
179 // setup line placement button group
180 mPlaceLineBtnGrp = new QButtonGroup( this );
181 mPlaceLineBtnGrp->addButton( radAroundLine );
182 mPlaceLineBtnGrp->addButton( radOverLine );
183 mPlaceLineBtnGrp->setExclusive( true );
184 connect( mPlaceLineBtnGrp, qOverload<QAbstractButton *>( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets );
185
186 // setup polygon placement button group
187 mPlacePolygonBtnGrp = new QButtonGroup( this );
188 mPlacePolygonBtnGrp->addButton( radAroundCentroid );
189 mPlacePolygonBtnGrp->addButton( radOverCentroid );
190 mPlacePolygonBtnGrp->addButton( radPolygonPerimeter );
191 mPlacePolygonBtnGrp->addButton( radInsidePolygon );
192 mPlacePolygonBtnGrp->setExclusive( true );
193 connect( mPlacePolygonBtnGrp, qOverload<QAbstractButton *>( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets );
194
195 mLabelPlacementComboBox->addItem( tr( "Height" ), QgsDiagramSettings::Height );
196 mLabelPlacementComboBox->addItem( tr( "x-height" ), QgsDiagramSettings::XHeight );
197
198 mScaleDependencyComboBox->addItem( tr( "Area" ), true );
199 mScaleDependencyComboBox->addItem( tr( "Diameter" ), false );
200
201 mAngleOffsetComboBox->addItem( tr( "Top" ), 270 );
202 mAngleOffsetComboBox->addItem( tr( "Right" ), 0 );
203 mAngleOffsetComboBox->addItem( tr( "Bottom" ), 90 );
204 mAngleOffsetComboBox->addItem( tr( "Left" ), 180 );
205
206 mAngleDirectionComboBox->addItem( tr( "Clockwise" ), QgsDiagramSettings::Clockwise );
207 mAngleDirectionComboBox->addItem( tr( "Counter-clockwise" ), QgsDiagramSettings::Counterclockwise );
208
209 const QgsSettings settings;
210
211 // reset horiz stretch of left side of options splitter (set to 1 for previewing in Qt Designer)
212 QSizePolicy policy( mDiagramOptionsListFrame->sizePolicy() );
213 policy.setHorizontalStretch( 0 );
214 mDiagramOptionsListFrame->setSizePolicy( policy );
215 if ( !settings.contains( QStringLiteral( "/Windows/Diagrams/OptionsSplitState" ) ) )
216 {
217 // set left list widget width on initial showing
218 QList<int> splitsizes;
219 splitsizes << 115;
220 mDiagramOptionsSplitter->setSizes( splitsizes );
221 }
222
223 // restore dialog, splitters and current tab
224 mDiagramOptionsSplitter->restoreState( settings.value( QStringLiteral( "Windows/Diagrams/OptionsSplitState" ) ).toByteArray() );
225 mDiagramOptionsListWidget->setCurrentRow( settings.value( QStringLiteral( "Windows/Diagrams/Tab" ), 0 ).toInt() );
226
227 // set correct initial tab to match displayed setting page
228 whileBlocking( mOptionsTab )->setCurrentIndex( mDiagramStackedWidget->currentIndex() );
229 mOptionsTab->tabBar()->setUsesScrollButtons( true );
230
231 // field combo and expression button
232 mSizeFieldExpressionWidget->setLayer( mLayer );
233 QgsDistanceArea myDa;
235 myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
236 mSizeFieldExpressionWidget->setGeomCalculator( myDa );
237
238 //insert all attributes into the combo boxes
239 const QgsFields &layerFields = layer->fields();
240 for ( int idx = 0; idx < layerFields.count(); ++idx )
241 {
242 QTreeWidgetItem *newItem = new QTreeWidgetItem( mAttributesTreeWidget );
243 const QString name = QStringLiteral( "\"%1\"" ).arg( layerFields.at( idx ).name() );
244 newItem->setText( 0, name );
245 newItem->setData( 0, RoleAttributeExpression, name );
246 newItem->setFlags( newItem->flags() & ~Qt::ItemIsDropEnabled );
247 }
248
249 mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
250 mPaintEffect->setEnabled( false );
251
252 mOrientationLeftButton->setProperty( "direction", QgsDiagramSettings::Left );
253 mOrientationRightButton->setProperty( "direction", QgsDiagramSettings::Right );
254 mOrientationUpButton->setProperty( "direction", QgsDiagramSettings::Up );
255 mOrientationDownButton->setProperty( "direction", QgsDiagramSettings::Down );
256
257 // Labels to let users know some widgets are not present
258 // when editing sub diagrams in a stacked diagram.
259 mDlsLabel_1->hide();
260 mDlsLabel_2->hide();
261
262 insertDefaults();
263 mPaintEffectWidget->setPaintEffect( mPaintEffect.get() );
264
265 connect( mAddAttributeExpression, &QPushButton::clicked, this, &QgsDiagramProperties::showAddAttributeExpressionDialog );
266 registerDataDefinedButton( mBackgroundColorDDBtn, QgsDiagramLayerSettings::Property::BackgroundColor );
267 registerDataDefinedButton( mLineColorDDBtn, QgsDiagramLayerSettings::Property::StrokeColor );
268 registerDataDefinedButton( mLineWidthDDBtn, QgsDiagramLayerSettings::Property::StrokeWidth );
269 registerDataDefinedButton( mCoordXDDBtn, QgsDiagramLayerSettings::Property::PositionX );
270 registerDataDefinedButton( mCoordYDDBtn, QgsDiagramLayerSettings::Property::PositionY );
271 registerDataDefinedButton( mDistanceDDBtn, QgsDiagramLayerSettings::Property::Distance );
272 registerDataDefinedButton( mPriorityDDBtn, QgsDiagramLayerSettings::Property::Priority );
273 registerDataDefinedButton( mZOrderDDBtn, QgsDiagramLayerSettings::Property::ZIndex );
274 registerDataDefinedButton( mShowDiagramDDBtn, QgsDiagramLayerSettings::Property::Show );
275 registerDataDefinedButton( mAlwaysShowDDBtn, QgsDiagramLayerSettings::Property::AlwaysShow );
276 registerDataDefinedButton( mIsObstacleDDBtn, QgsDiagramLayerSettings::Property::IsObstacle );
277 registerDataDefinedButton( mStartAngleDDBtn, QgsDiagramLayerSettings::Property::StartAngle );
278
279 connect( mButtonSizeLegendSettings, &QPushButton::clicked, this, &QgsDiagramProperties::showSizeLegendDialog );
280
281 QList<QWidget *> widgets;
282 widgets << chkLineAbove;
283 widgets << chkLineBelow;
284 widgets << chkLineOn;
285 widgets << chkLineOrientationDependent;
286 widgets << mAngleDirectionComboBox;
287 widgets << mAngleOffsetComboBox;
288 widgets << mAttributeBasedScalingRadio;
289 widgets << mAxisLineStyleButton;
290 widgets << mBackgroundColorButton;
291 widgets << mBarSpacingSpinBox;
292 widgets << mBarSpacingUnitComboBox;
293 widgets << mBarWidthSpinBox;
294 widgets << mCheckBoxAttributeLegend;
295 widgets << mDiagramAttributesTreeWidget;
296 widgets << mDiagramDistanceSpinBox;
297 widgets << mDiagramFontButton;
298 widgets << mDiagramPenColorButton;
299 widgets << mDiagramSizeSpinBox;
300 widgets << mDiagramLineUnitComboBox;
301 widgets << mDiagramTypeComboBox;
302 widgets << mDiagramUnitComboBox;
303 widgets << mEnableDiagramCheckBox;
304 widgets << mFixedSizeRadio;
305 widgets << mIncreaseMinimumSizeSpinBox;
306 widgets << mIncreaseSmallDiagramsGroupBox;
307 widgets << mLabelPlacementComboBox;
308 widgets << mMaxValueSpinBox;
309 widgets << mPaintEffectWidget;
310 widgets << mPenWidthSpinBox;
311 widgets << mPrioritySlider;
312 widgets << mOpacityWidget;
313 widgets << mOrientationDownButton;
314 widgets << mOrientationLeftButton;
315 widgets << mOrientationRightButton;
316 widgets << mOrientationUpButton;
317 widgets << mScaleDependencyComboBox;
318 widgets << mScaleRangeWidget;
319 widgets << mScaleVisibilityGroupBox;
320 widgets << mShowAllCheckBox;
321 widgets << mShowAxisGroupBox;
322 widgets << mSizeFieldExpressionWidget;
323 widgets << mSizeSpinBox;
324 widgets << mZIndexSpinBox;
325 widgets << radAroundCentroid;
326 widgets << radAroundLine;
327 widgets << radAroundPoint;
328 widgets << radInsidePolygon;
329 widgets << radOverCentroid;
330 widgets << radOverLine;
331 widgets << radOverPoint;
332 widgets << radPolygonPerimeter;
333
334 connectValueChanged( widgets );
335}
336
338{
340 mOptionsTab->setVisible( dockMode );
341 mOptionsTab->setTabToolTip( 0, tr( "Attributes" ) );
342 mOptionsTab->setTabToolTip( 1, tr( "Rendering" ) );
343 mOptionsTab->setTabToolTip( 2, tr( "Size" ) );
344 mOptionsTab->setTabToolTip( 3, tr( "Placement" ) );
345 mOptionsTab->setTabToolTip( 4, tr( "Options" ) );
346 mOptionsTab->setTabToolTip( 5, tr( "Legend" ) );
347 mDiagramOptionsListFrame->setVisible( !dockMode );
348}
349
350void QgsDiagramProperties::setDiagramType( const QString diagramType )
351{
352 mDiagramType = diagramType;
353
354 mDiagramTypeComboBox->setVisible( false );
355 mDiagramTypeComboBox->blockSignals( true );
356 mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( mDiagramType ) );
357 mDiagramTypeComboBox->blockSignals( false );
358
359 //force a refresh of widget status to match diagram type
360 mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
361}
362
363void QgsDiagramProperties::insertDefaults()
364{
365 mFixedSizeRadio->setChecked( true );
366 mDiagramUnitComboBox->setUnit( Qgis::RenderUnit::Millimeters );
367 mDiagramLineUnitComboBox->setUnit( Qgis::RenderUnit::Millimeters );
368 mLabelPlacementComboBox->setCurrentIndex( mLabelPlacementComboBox->findText( tr( "x-height" ) ) );
369 mDiagramSizeSpinBox->setEnabled( true );
370 mDiagramSizeSpinBox->setValue( 15 );
371 mLinearScaleFrame->setEnabled( false );
372 mBarWidthSpinBox->setValue( 5 );
373 mScaleVisibilityGroupBox->setChecked( mLayer->hasScaleBasedVisibility() );
374 mScaleRangeWidget->setScaleRange( mLayer->minimumScale(), mLayer->maximumScale() );
375 mShowAllCheckBox->setChecked( true );
376 mCheckBoxAttributeLegend->setChecked( true );
377
378 switch ( mLayer->geometryType() )
379 {
381 radAroundPoint->setChecked( true );
382 break;
383
385 radAroundLine->setChecked( true );
386 chkLineAbove->setChecked( true );
387 chkLineBelow->setChecked( false );
388 chkLineOn->setChecked( false );
389 chkLineOrientationDependent->setChecked( false );
390 break;
391
393 radOverCentroid->setChecked( true );
394 mDiagramDistanceLabel->setEnabled( false );
395 mDiagramDistanceSpinBox->setEnabled( false );
396 mDistanceDDBtn->setEnabled( false );
397 break;
398
401 break;
402 }
403 mBackgroundColorButton->setColor( QColor( 255, 255, 255, 255 ) );
404 mDiagramPenColorButton->setColor( QColor( 0, 0, 0, 255 ) );
405 //force a refresh of widget status to match diagram type
406 mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
407}
408
410{
411 const QgsDiagramRenderer *renderer = mLayer->diagramRenderer();
413 {
414 const QgsStackedDiagramRenderer *stackedRenderer = static_cast<const QgsStackedDiagramRenderer *>( renderer );
415 if ( stackedRenderer->rendererCount() > 0 )
416 {
417 // If layer has a stacked diagram renderer, take its first sub
418 // renderer as the basis for the new single one being created
419 renderer = stackedRenderer->renderer( 0 );
420 }
421 }
422 syncToRenderer( renderer );
423
424 const QgsDiagramLayerSettings *layerDls = mLayer->diagramLayerSettings();
425 syncToSettings( layerDls );
426}
427
429{
430 mDiagramAttributesTreeWidget->clear();
431
432 if ( !dr ) //no diagram renderer yet, insert reasonable default
433 {
434 insertDefaults();
435 }
436 else // already a diagram renderer present
437 {
438 //single category renderer or interpolated one?
440 {
441 mFixedSizeRadio->setChecked( true );
442 }
443 else
444 {
445 mAttributeBasedScalingRadio->setChecked( true );
446 }
447 mDiagramSizeSpinBox->setEnabled( mFixedSizeRadio->isChecked() );
448 mLinearScaleFrame->setEnabled( mAttributeBasedScalingRadio->isChecked() );
449 mCheckBoxAttributeLegend->setChecked( dr->attributeLegend() );
450
451 // Assume single category or linearly interpolated diagram renderer for now.
452 const QList<QgsDiagramSettings> settingList = dr->diagramSettings();
453 if ( !settingList.isEmpty() )
454 {
455 setDiagramEnabled( settingList.at( 0 ).enabled );
456 mDiagramFontButton->setCurrentFont( settingList.at( 0 ).font );
457 const QSizeF size = settingList.at( 0 ).size;
458 mBackgroundColorButton->setColor( settingList.at( 0 ).backgroundColor );
459 mOpacityWidget->setOpacity( settingList.at( 0 ).opacity );
460 mDiagramPenColorButton->setColor( settingList.at( 0 ).penColor );
461 mPenWidthSpinBox->setValue( settingList.at( 0 ).penWidth );
462 mDiagramSizeSpinBox->setValue( ( size.width() + size.height() ) / 2.0 );
463 mScaleRangeWidget->setScaleRange( ( settingList.at( 0 ).minimumScale > 0 ? settingList.at( 0 ).minimumScale : mLayer->minimumScale() ), ( settingList.at( 0 ).maximumScale > 0 ? settingList.at( 0 ).maximumScale : mLayer->maximumScale() ) );
464 mScaleVisibilityGroupBox->setChecked( settingList.at( 0 ).scaleBasedVisibility );
465 mDiagramUnitComboBox->setUnit( settingList.at( 0 ).sizeType );
466 mDiagramUnitComboBox->setMapUnitScale( settingList.at( 0 ).sizeScale );
467 mDiagramLineUnitComboBox->setUnit( settingList.at( 0 ).lineSizeUnit );
468 mDiagramLineUnitComboBox->setMapUnitScale( settingList.at( 0 ).lineSizeScale );
469
470 if ( settingList.at( 0 ).labelPlacementMethod == QgsDiagramSettings::Height )
471 {
472 mLabelPlacementComboBox->setCurrentIndex( 0 );
473 }
474 else
475 {
476 mLabelPlacementComboBox->setCurrentIndex( 1 );
477 }
478
479 if ( settingList.at( 0 ).paintEffect() )
480 mPaintEffect.reset( settingList.at( 0 ).paintEffect()->clone() );
481
482 mAngleOffsetComboBox->setCurrentIndex( mAngleOffsetComboBox->findData( settingList.at( 0 ).rotationOffset ) );
483 mAngleDirectionComboBox->setCurrentIndex( mAngleDirectionComboBox->findData( settingList.at( 0 ).direction() ) );
484
485 switch ( settingList.at( 0 ).diagramOrientation )
486 {
488 mOrientationLeftButton->setChecked( true );
489 break;
490
492 mOrientationRightButton->setChecked( true );
493 break;
494
496 mOrientationUpButton->setChecked( true );
497 break;
498
500 mOrientationDownButton->setChecked( true );
501 break;
502 }
503
504 mBarWidthSpinBox->setValue( settingList.at( 0 ).barWidth );
505 mBarSpacingSpinBox->setValue( settingList.at( 0 ).spacing() );
506 mBarSpacingUnitComboBox->setUnit( settingList.at( 0 ).spacingUnit() );
507 mBarSpacingUnitComboBox->setMapUnitScale( settingList.at( 0 ).spacingMapUnitScale() );
508
509 mShowAxisGroupBox->setChecked( settingList.at( 0 ).showAxis() );
510 if ( settingList.at( 0 ).axisLineSymbol() )
511 mAxisLineStyleButton->setSymbol( settingList.at( 0 ).axisLineSymbol()->clone() );
512
513 mIncreaseSmallDiagramsGroupBox->setChecked( settingList.at( 0 ).minimumSize != 0 );
514 mIncreaseMinimumSizeSpinBox->setValue( settingList.at( 0 ).minimumSize );
515
516 if ( settingList.at( 0 ).scaleByArea )
517 {
518 mScaleDependencyComboBox->setCurrentIndex( 0 );
519 }
520 else
521 {
522 mScaleDependencyComboBox->setCurrentIndex( 1 );
523 }
524
525 const QList<QColor> categoryColors = settingList.at( 0 ).categoryColors;
526 const QList<QString> categoryAttributes = settingList.at( 0 ).categoryAttributes;
527 const QList<QString> categoryLabels = settingList.at( 0 ).categoryLabels;
528 QList<QString>::const_iterator catIt = categoryAttributes.constBegin();
529 QList<QColor>::const_iterator coIt = categoryColors.constBegin();
530 QList<QString>::const_iterator labIt = categoryLabels.constBegin();
531 for ( ; catIt != categoryAttributes.constEnd(); ++catIt, ++coIt, ++labIt )
532 {
533 QTreeWidgetItem *newItem = new QTreeWidgetItem( mDiagramAttributesTreeWidget );
534 newItem->setText( 0, *catIt );
535 newItem->setData( 0, RoleAttributeExpression, *catIt );
536 newItem->setFlags( newItem->flags() & ~Qt::ItemIsDropEnabled );
537 newItem->setData( ColumnColor, Qt::EditRole, *coIt );
538 newItem->setText( 2, *labIt );
539 newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
540 }
541 }
542
544 {
546 if ( lidr )
547 {
548 mDiagramSizeSpinBox->setEnabled( false );
549 mLinearScaleFrame->setEnabled( true );
550 mMaxValueSpinBox->setValue( lidr->upperValue() );
551 mSizeSpinBox->setValue( ( lidr->upperSize().width() + lidr->upperSize().height() ) / 2 );
553 {
554 mSizeFieldExpressionWidget->setField( lidr->classificationAttributeExpression() );
555 }
556 else
557 {
558 mSizeFieldExpressionWidget->setField( lidr->classificationField() );
559 }
560
561 mSizeLegend.reset( lidr->dataDefinedSizeLegend() ? new QgsDataDefinedSizeLegend( *lidr->dataDefinedSizeLegend() ) : nullptr );
562 }
563 }
564
565 if ( dr->diagram() )
566 {
567 mDiagramType = dr->diagram()->diagramName();
568
569 mDiagramTypeComboBox->blockSignals( true );
570 mDiagramTypeComboBox->setCurrentIndex( mDiagramTypeComboBox->findData( mDiagramType ) );
571 mDiagramTypeComboBox->blockSignals( false );
572 //force a refresh of widget status to match diagram type
573 mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
574 }
575 }
576 mPaintEffectWidget->setPaintEffect( mPaintEffect.get() );
577}
578
580{
581 if ( dls )
582 {
583 mDiagramDistanceSpinBox->setValue( dls->distance() );
584 mPrioritySlider->setValue( dls->priority() );
585 mZIndexSpinBox->setValue( dls->zIndex() );
586
587 switch ( dls->placement() )
588 {
590 radAroundPoint->setChecked( true );
591 radAroundCentroid->setChecked( true );
592 break;
593
595 radOverPoint->setChecked( true );
596 radOverCentroid->setChecked( true );
597 break;
598
600 radAroundLine->setChecked( true );
601 radPolygonPerimeter->setChecked( true );
602 break;
603
605 radOverLine->setChecked( true );
606 radInsidePolygon->setChecked( true );
607 break;
608
609 default:
610 break;
611 }
612
613 chkLineAbove->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::AboveLine );
614 chkLineBelow->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::BelowLine );
615 chkLineOn->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::OnLine );
617 chkLineOrientationDependent->setChecked( true );
619
620 mShowAllCheckBox->setChecked( dls->showAllDiagrams() );
621
622 mDataDefinedProperties = dls->dataDefinedProperties();
623 }
624}
625
627{
628 QgsSettings settings;
629 settings.setValue( QStringLiteral( "Windows/Diagrams/OptionsSplitState" ), mDiagramOptionsSplitter->saveState() );
630 settings.setValue( QStringLiteral( "Windows/Diagrams/Tab" ), mDiagramOptionsListWidget->currentRow() );
631}
632
633void QgsDiagramProperties::registerDataDefinedButton( QgsPropertyOverrideButton *button, QgsDiagramLayerSettings::Property key )
634{
635 button->init( static_cast<int>( key ), mDataDefinedProperties, QgsDiagramLayerSettings::propertyDefinitions(), mLayer, true );
636 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsDiagramProperties::updateProperty );
637 connect( button, &QgsPropertyOverrideButton::createAuxiliaryField, this, &QgsDiagramProperties::createAuxiliaryField );
639}
640
641void QgsDiagramProperties::updateProperty()
642{
643 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
645 mDataDefinedProperties.setProperty( key, button->toProperty() );
646 emit widgetChanged();
647}
648
650{
651 mDiagramType = mDiagramTypeComboBox->itemData( index ).toString();
652
653 if ( QgsTextDiagram::DIAGRAM_NAME_TEXT == mDiagramType )
654 {
655 mTextOptionsFrame->show();
656 mBackgroundColorLabel->show();
657 mBackgroundColorButton->show();
658 mBackgroundColorDDBtn->show();
659 mDiagramFontButton->show();
660 }
661 else
662 {
663 mTextOptionsFrame->hide();
664 mBackgroundColorLabel->hide();
665 mBackgroundColorButton->hide();
666 mBackgroundColorDDBtn->hide();
667 mDiagramFontButton->hide();
668 }
669
671 {
672 mBarWidthLabel->show();
673 mBarWidthSpinBox->show();
674 mBarSpacingLabel->show();
675 mBarSpacingSpinBox->show();
676 mBarSpacingUnitComboBox->show();
677 mBarOptionsFrame->show();
678 mShowAxisGroupBox->show();
680 mAttributeBasedScalingRadio->setChecked( true );
681 mFixedSizeRadio->setEnabled( QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR == mDiagramType );
682 mDiagramSizeSpinBox->setEnabled( QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR == mDiagramType );
683 mLinearlyScalingLabel->setText( tr( "Bar length: Scale linearly, so that the following value matches the specified bar length:" ) );
684 mSizeLabel->setText( tr( "Bar length" ) );
685 mFrameIncreaseSize->setVisible( false );
686 }
687 else
688 {
689 mBarWidthLabel->hide();
690 mBarWidthSpinBox->hide();
691 mBarSpacingLabel->hide();
692 mBarSpacingSpinBox->hide();
693 mBarSpacingUnitComboBox->hide();
694 mShowAxisGroupBox->hide();
695 mBarOptionsFrame->hide();
696 mLinearlyScalingLabel->setText( tr( "Scale linearly between 0 and the following attribute value / diagram size:" ) );
697 mSizeLabel->setText( tr( "Size" ) );
698 mAttributeBasedScalingRadio->setEnabled( true );
699 mFixedSizeRadio->setEnabled( true );
700 mDiagramSizeSpinBox->setEnabled( mFixedSizeRadio->isChecked() );
701 mFrameIncreaseSize->setVisible( true );
702 }
703
704 if ( QgsTextDiagram::DIAGRAM_NAME_TEXT == mDiagramType || QgsPieDiagram::DIAGRAM_NAME_PIE == mDiagramType )
705 {
706 mScaleDependencyComboBox->show();
707 mScaleDependencyLabel->show();
708 }
709 else
710 {
711 mScaleDependencyComboBox->hide();
712 mScaleDependencyLabel->hide();
713 }
714
715 if ( QgsPieDiagram::DIAGRAM_NAME_PIE == mDiagramType )
716 {
717 mAngleOffsetComboBox->show();
718 mAngleDirectionComboBox->show();
719 mAngleDirectionLabel->show();
720 mAngleOffsetLabel->show();
721 mStartAngleDDBtn->show();
722 }
723 else
724 {
725 mAngleOffsetComboBox->hide();
726 mAngleDirectionComboBox->hide();
727 mAngleDirectionLabel->hide();
728 mAngleOffsetLabel->hide();
729 mStartAngleDDBtn->hide();
730 }
731}
732
733QString QgsDiagramProperties::guessLegendText( const QString &expression )
734{
735 //trim unwanted characters from expression text for legend
736 QString text = expression.mid( expression.startsWith( '\"' ) ? 1 : 0 );
737 if ( text.endsWith( '\"' ) )
738 text.chop( 1 );
739 return text;
740}
741
742void QgsDiagramProperties::addAttribute( QTreeWidgetItem *item )
743{
744 QTreeWidgetItem *newItem = new QTreeWidgetItem( mDiagramAttributesTreeWidget );
745
746 newItem->setText( 0, item->text( 0 ) );
747 newItem->setText( 2, guessLegendText( item->text( 0 ) ) );
748 newItem->setData( 0, RoleAttributeExpression, item->data( 0, RoleAttributeExpression ) );
749 newItem->setFlags( ( newItem->flags() | Qt::ItemIsEditable ) & ~Qt::ItemIsDropEnabled );
750
751 //set initial color for diagram category
752 const int red = QRandomGenerator::global()->bounded( 1, 256 );
753 const int green = QRandomGenerator::global()->bounded( 1, 256 );
754 const int blue = QRandomGenerator::global()->bounded( 1, 256 );
755 const QColor randomColor( red, green, blue );
756 newItem->setData( ColumnColor, Qt::EditRole, randomColor );
757 mDiagramAttributesTreeWidget->addTopLevelItem( newItem );
758}
759
761{
762 const auto constSelectedItems = mAttributesTreeWidget->selectedItems();
763 for ( QTreeWidgetItem *attributeItem : constSelectedItems )
764 {
765 addAttribute( attributeItem );
766 }
767}
768
770{
771 Q_UNUSED( column )
772 addAttribute( item );
773}
774
776{
777 const auto constSelectedItems = mDiagramAttributesTreeWidget->selectedItems();
778 for ( QTreeWidgetItem *attributeItem : constSelectedItems )
779 {
780 delete attributeItem;
781 }
782}
783
785{
786 if ( !mLayer )
787 return;
788
789 float maxValue = 0.0;
790
791 bool isExpression;
792 const QString sizeFieldNameOrExp = mSizeFieldExpressionWidget->currentField( &isExpression );
793 if ( isExpression )
794 {
795 QgsExpression exp( sizeFieldNameOrExp );
796 QgsExpressionContext context;
801
802 exp.prepare( &context );
803 if ( !exp.hasEvalError() )
804 {
805 QgsFeature feature;
806 QgsFeatureIterator features = mLayer->getFeatures();
807 while ( features.nextFeature( *&feature ) )
808 {
809 context.setFeature( feature );
810 maxValue = std::max( maxValue, exp.evaluate( &context ).toFloat() );
811 }
812 }
813 else
814 {
815 QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
816 }
817 }
818 else
819 {
820 const int attributeNumber = mLayer->fields().lookupField( sizeFieldNameOrExp );
821 maxValue = mLayer->maximumValue( attributeNumber ).toFloat();
822 }
823
824 mMaxValueSpinBox->setValue( maxValue );
825}
826
828{
829 switch ( column )
830 {
831 case ColumnAttributeExpression:
832 {
833 const QString currentExpression = item->data( 0, RoleAttributeExpression ).toString();
834
835 const QString newExpression = showExpressionBuilder( currentExpression );
836 if ( !newExpression.isEmpty() )
837 {
838 item->setData( 0, Qt::DisplayRole, newExpression );
839 item->setData( 0, RoleAttributeExpression, newExpression );
840 }
841 break;
842 }
843
844 case ColumnColor:
845 break;
846
847 case ColumnLegendText:
848 break;
849 }
850}
851
852std::unique_ptr<QgsDiagram> QgsDiagramProperties::createDiagramObject()
853{
854 std::unique_ptr<QgsDiagram> diagram;
855
856 if ( mDiagramType == QgsTextDiagram::DIAGRAM_NAME_TEXT )
857 {
858 diagram = std::make_unique<QgsTextDiagram>();
859 }
860 else if ( mDiagramType == QgsPieDiagram::DIAGRAM_NAME_PIE )
861 {
862 diagram = std::make_unique<QgsPieDiagram>();
863 }
864 else if ( mDiagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR )
865 {
866 diagram = std::make_unique<QgsStackedBarDiagram>();
867 }
868 else // if ( diagramType == QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM )
869 {
870 diagram = std::make_unique<QgsHistogramDiagram>();
871 }
872 return diagram;
873}
874
875std::unique_ptr<QgsDiagramSettings> QgsDiagramProperties::createDiagramSettings()
876{
877 std::unique_ptr<QgsDiagramSettings> ds = std::make_unique<QgsDiagramSettings>();
878 ds->enabled = isDiagramEnabled();
879 ds->font = mDiagramFontButton->currentFont();
880 ds->opacity = mOpacityWidget->opacity();
881
882 QList<QColor> categoryColors;
883 QList<QString> categoryAttributes;
884 QList<QString> categoryLabels;
885 categoryColors.reserve( mDiagramAttributesTreeWidget->topLevelItemCount() );
886 categoryAttributes.reserve( mDiagramAttributesTreeWidget->topLevelItemCount() );
887 categoryLabels.reserve( mDiagramAttributesTreeWidget->topLevelItemCount() );
888 for ( int i = 0; i < mDiagramAttributesTreeWidget->topLevelItemCount(); ++i )
889 {
890 QColor color = mDiagramAttributesTreeWidget->topLevelItem( i )->data( ColumnColor, Qt::EditRole ).value<QColor>();
891 categoryColors.append( color );
892 categoryAttributes.append( mDiagramAttributesTreeWidget->topLevelItem( i )->data( 0, RoleAttributeExpression ).toString() );
893 categoryLabels.append( mDiagramAttributesTreeWidget->topLevelItem( i )->text( 2 ) );
894 }
895 ds->categoryColors = categoryColors;
896 ds->categoryAttributes = categoryAttributes;
897 ds->categoryLabels = categoryLabels;
898 ds->size = QSizeF( mDiagramSizeSpinBox->value(), mDiagramSizeSpinBox->value() );
899 ds->sizeType = mDiagramUnitComboBox->unit();
900 ds->sizeScale = mDiagramUnitComboBox->getMapUnitScale();
901 ds->lineSizeUnit = mDiagramLineUnitComboBox->unit();
902 ds->lineSizeScale = mDiagramLineUnitComboBox->getMapUnitScale();
903 ds->labelPlacementMethod = static_cast<QgsDiagramSettings::LabelPlacementMethod>( mLabelPlacementComboBox->currentData().toInt() );
904 ds->scaleByArea = ( mDiagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR ) ? false : mScaleDependencyComboBox->currentData().toBool();
905
906 if ( mIncreaseSmallDiagramsGroupBox->isChecked() )
907 {
908 ds->minimumSize = mIncreaseMinimumSizeSpinBox->value();
909 }
910 else
911 {
912 ds->minimumSize = 0;
913 }
914
915 ds->backgroundColor = mBackgroundColorButton->color();
916 ds->penColor = mDiagramPenColorButton->color();
917 ds->penWidth = mPenWidthSpinBox->value();
918 ds->minimumScale = mScaleRangeWidget->minimumScale();
919 ds->maximumScale = mScaleRangeWidget->maximumScale();
920 ds->scaleBasedVisibility = mScaleVisibilityGroupBox->isChecked();
921
922 // Diagram angle offset (pie)
923 ds->rotationOffset = mAngleOffsetComboBox->currentData().toInt();
924 ds->setDirection( static_cast<QgsDiagramSettings::Direction>( mAngleDirectionComboBox->currentData().toInt() ) );
925
926 // Diagram orientation (histogram)
927 ds->diagramOrientation = static_cast<QgsDiagramSettings::DiagramOrientation>( mOrientationButtonGroup->checkedButton()->property( "direction" ).toInt() );
928
929 ds->barWidth = mBarWidthSpinBox->value();
930
931 ds->setAxisLineSymbol( mAxisLineStyleButton->clonedSymbol<QgsLineSymbol>() );
932 ds->setShowAxis( mShowAxisGroupBox->isChecked() );
933
934 ds->setSpacing( mBarSpacingSpinBox->value() );
935 ds->setSpacingUnit( mBarSpacingUnitComboBox->unit() );
936 ds->setSpacingMapUnitScale( mBarSpacingUnitComboBox->getMapUnitScale() );
937
938 if ( mPaintEffect && ( !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) || mPaintEffect->enabled() ) )
939 ds->setPaintEffect( mPaintEffect->clone() );
940 else
941 ds->setPaintEffect( nullptr );
942
943 return ds;
944}
945
946std::unique_ptr<QgsDiagramRenderer> QgsDiagramProperties::createRenderer()
947{
948 std::unique_ptr<QgsDiagramSettings> ds = createDiagramSettings();
949
950 std::unique_ptr<QgsDiagramRenderer> renderer;
951 if ( mFixedSizeRadio->isChecked() )
952 {
953 std::unique_ptr<QgsSingleCategoryDiagramRenderer> dr = std::make_unique<QgsSingleCategoryDiagramRenderer>();
954 dr->setDiagramSettings( *ds );
955 renderer = std::move( dr );
956 }
957 else
958 {
959 std::unique_ptr<QgsLinearlyInterpolatedDiagramRenderer> dr = std::make_unique<QgsLinearlyInterpolatedDiagramRenderer>();
960 dr->setLowerValue( 0.0 );
961 dr->setLowerSize( QSizeF( 0.0, 0.0 ) );
962 dr->setUpperValue( mMaxValueSpinBox->value() );
963 dr->setUpperSize( QSizeF( mSizeSpinBox->value(), mSizeSpinBox->value() ) );
964
965 bool isExpression;
966 const QString sizeFieldNameOrExp = mSizeFieldExpressionWidget->currentField( &isExpression );
967 dr->setClassificationAttributeIsExpression( isExpression );
968 if ( isExpression )
969 {
970 dr->setClassificationAttributeExpression( sizeFieldNameOrExp );
971 }
972 else
973 {
974 dr->setClassificationField( sizeFieldNameOrExp );
975 }
976 dr->setDiagramSettings( *ds );
977
978 dr->setDataDefinedSizeLegend( mSizeLegend ? new QgsDataDefinedSizeLegend( *mSizeLegend ) : nullptr );
979
980 renderer = std::move( dr );
981 }
982
983 renderer->setAttributeLegend( mCheckBoxAttributeLegend->isChecked() );
984
985 std::unique_ptr<QgsDiagram> diagram = createDiagramObject();
986 renderer->setDiagram( diagram.release() );
987
988 return renderer;
989}
990
991QgsDiagramLayerSettings QgsDiagramProperties::createDiagramLayerSettings()
992{
994 dls.setDataDefinedProperties( mDataDefinedProperties );
995 dls.setDistance( mDiagramDistanceSpinBox->value() );
996 dls.setPriority( mPrioritySlider->value() );
997 dls.setZIndex( mZIndexSpinBox->value() );
998 dls.setShowAllDiagrams( mShowAllCheckBox->isChecked() );
999
1000 QWidget *curWdgt = stackedPlacement->currentWidget();
1001 if ( ( curWdgt == pagePoint && radAroundPoint->isChecked() )
1002 || ( curWdgt == pagePolygon && radAroundCentroid->isChecked() ) )
1003 {
1005 }
1006 else if ( ( curWdgt == pagePoint && radOverPoint->isChecked() )
1007 || ( curWdgt == pagePolygon && radOverCentroid->isChecked() ) )
1008 {
1010 }
1011 else if ( ( curWdgt == pageLine && radAroundLine->isChecked() )
1012 || ( curWdgt == pagePolygon && radPolygonPerimeter->isChecked() ) )
1013 {
1015 }
1016 else if ( ( curWdgt == pageLine && radOverLine->isChecked() )
1017 || ( curWdgt == pagePolygon && radInsidePolygon->isChecked() ) )
1018 {
1020 }
1021 else
1022 {
1023 qFatal( "Invalid settings" );
1024 }
1025
1027 if ( chkLineAbove->isChecked() )
1029 if ( chkLineBelow->isChecked() )
1031 if ( chkLineOn->isChecked() )
1033 if ( !chkLineOrientationDependent->isChecked() )
1035 dls.setLinePlacementFlags( flags );
1036
1037 return dls;
1038}
1039
1041{
1042 // Avoid this messageBox when in both dock and liveUpdate mode
1043 QgsSettings settings;
1044 if ( !dockMode() || !settings.value( QStringLiteral( "UI/autoApplyStyling" ), true ).toBool() )
1045 {
1046 if ( isDiagramEnabled() && 0 == mDiagramAttributesTreeWidget->topLevelItemCount() )
1047 {
1048 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." ) );
1049 }
1050 }
1051
1052 std::unique_ptr<QgsDiagramRenderer> renderer = createRenderer();
1053 mLayer->setDiagramRenderer( renderer.release() );
1054
1055 QgsDiagramLayerSettings dls = createDiagramLayerSettings();
1056 mLayer->setDiagramLayerSettings( dls );
1057
1058 // refresh
1059 QgsProject::instance()->setDirty( true );
1060 mLayer->triggerRepaint();
1061}
1062
1063QString QgsDiagramProperties::showExpressionBuilder( const QString &initialExpression )
1064{
1065 QgsExpressionContext context = createExpressionContext();
1066
1067 QgsExpressionBuilderDialog dlg( mLayer, initialExpression, this, QStringLiteral( "generic" ), context );
1068 dlg.setWindowTitle( tr( "Expression Based Attribute" ) );
1069
1070 QgsDistanceArea myDa;
1071 myDa.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
1072 myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
1073 dlg.setGeomCalculator( myDa );
1074
1075 if ( dlg.exec() == QDialog::Accepted )
1076 {
1077 return dlg.expressionText();
1078 }
1079 else
1080 {
1081 return QString();
1082 }
1083}
1084
1086{
1087 QString expression;
1088 QList<QTreeWidgetItem *> selections = mAttributesTreeWidget->selectedItems();
1089 if ( !selections.empty() )
1090 {
1091 expression = selections[0]->text( 0 );
1092 }
1093
1094 const QString newExpression = showExpressionBuilder( expression );
1095
1096 //Only add the expression if the user has entered some text.
1097 if ( !newExpression.isEmpty() )
1098 {
1099 QTreeWidgetItem *newItem = new QTreeWidgetItem( mDiagramAttributesTreeWidget );
1100
1101 newItem->setText( 0, newExpression );
1102 newItem->setText( 2, newExpression );
1103 newItem->setData( 0, RoleAttributeExpression, newExpression );
1104 newItem->setFlags( ( newItem->flags() | Qt::ItemIsEditable ) & ~Qt::ItemIsDropEnabled );
1105
1106 //set initial color for diagram category
1107 QRandomGenerator colorGenerator;
1108 const int red = colorGenerator.bounded( 1, 256 );
1109 const int green = colorGenerator.bounded( 1, 256 );
1110 const int blue = colorGenerator.bounded( 1, 256 );
1111
1112 const QColor randomColor( red, green, blue );
1113 newItem->setData( ColumnColor, Qt::EditRole, randomColor );
1114 mDiagramAttributesTreeWidget->addTopLevelItem( newItem );
1115 }
1116 activateWindow(); // set focus back parent
1117}
1118
1120{
1121 mDiagramOptionsListWidget->blockSignals( true );
1122 mDiagramOptionsListWidget->setCurrentRow( index );
1123 mDiagramOptionsListWidget->blockSignals( false );
1124}
1125
1127{
1128 QWidget *curWdgt = stackedPlacement->currentWidget();
1129
1130 if ( ( curWdgt == pagePoint && radAroundPoint->isChecked() )
1131 || ( curWdgt == pageLine && radAroundLine->isChecked() )
1132 || ( curWdgt == pagePolygon && radAroundCentroid->isChecked() ) )
1133 {
1134 mDiagramDistanceLabel->setEnabled( true );
1135 mDiagramDistanceSpinBox->setEnabled( true );
1136 mDistanceDDBtn->setEnabled( true );
1137 }
1138 else
1139 {
1140 mDiagramDistanceLabel->setEnabled( false );
1141 mDiagramDistanceSpinBox->setEnabled( false );
1142 mDistanceDDBtn->setEnabled( false );
1143 }
1144
1145 const bool linePlacementEnabled = mLayer->geometryType() == Qgis::GeometryType::Line && ( curWdgt == pageLine && radAroundLine->isChecked() );
1146 chkLineAbove->setEnabled( linePlacementEnabled );
1147 chkLineBelow->setEnabled( linePlacementEnabled );
1148 chkLineOn->setEnabled( linePlacementEnabled );
1149 chkLineOrientationDependent->setEnabled( linePlacementEnabled );
1150}
1151
1153{
1154 mButtonSizeLegendSettings->setEnabled( mAttributeBasedScalingRadio->isChecked() );
1155}
1156
1158{
1159 mAllowedToEditDls = allowed;
1160
1161 label_16->setVisible( allowed );
1162 mZIndexSpinBox->setVisible( allowed );
1163 mZOrderDDBtn->setVisible( allowed );
1164 mShowAllCheckBox->setVisible( allowed );
1165 mDlsLabel_1->setVisible( !allowed );
1166
1167 mCoordinatesGrpBox->setVisible( allowed );
1168 mLinePlacementFrame->setVisible( allowed );
1169 mObstaclesGrpBox->setVisible( allowed );
1170 mPlacementFrame->setVisible( allowed );
1171 mPriorityGrpBox->setVisible( allowed );
1172 stackedPlacement->setVisible( allowed );
1173 mDlsLabel_2->setVisible( !allowed );
1174}
1175
1177{
1178 return mAllowedToEditDls;
1179}
1180
1182{
1183 // prepare size transformer
1184 bool isExpression;
1185 const QString sizeFieldNameOrExp = mSizeFieldExpressionWidget->currentField( &isExpression );
1186 QgsProperty ddSize = isExpression ? QgsProperty::fromExpression( sizeFieldNameOrExp ) : QgsProperty::fromField( sizeFieldNameOrExp );
1187 const bool scaleByArea = mScaleDependencyComboBox->currentData().toBool();
1188 ddSize.setTransformer( new QgsSizeScaleTransformer( scaleByArea ? QgsSizeScaleTransformer::Area : QgsSizeScaleTransformer::Linear, 0.0, mMaxValueSpinBox->value(), 0.0, mSizeSpinBox->value() ) );
1189
1190 QgsDataDefinedSizeLegendWidget *panel = new QgsDataDefinedSizeLegendWidget( mSizeLegend.get(), ddSize, nullptr, mMapCanvas );
1191
1192 QDialog dlg;
1193 dlg.setLayout( new QVBoxLayout() );
1194 dlg.setWindowTitle( panel->panelTitle() );
1195 dlg.layout()->addWidget( panel );
1196 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
1197 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1198 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDiagramProperties::showHelp );
1199 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1200 dlg.layout()->addWidget( buttonBox );
1201 if ( dlg.exec() )
1202 mSizeLegend.reset( panel->dataDefinedSizeLegend() );
1203}
1204
1205void QgsDiagramProperties::showHelp()
1206{
1207 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#legend" ) );
1208}
1209
1210void QgsDiagramProperties::createAuxiliaryField()
1211{
1212 // try to create an auxiliary layer if not yet created
1213 if ( !mLayer->auxiliaryLayer() )
1214 {
1215 QgsNewAuxiliaryLayerDialog dlg( mLayer, this );
1216 dlg.exec();
1217 }
1218
1219 // return if still not exists
1220 if ( !mLayer->auxiliaryLayer() )
1221 return;
1222
1223 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
1225 const QgsPropertyDefinition def = QgsDiagramLayerSettings::propertyDefinitions()[static_cast<int>( key )];
1226
1227 // create property in auxiliary storage if necessary
1228 if ( !mLayer->auxiliaryLayer()->exists( def ) )
1229 mLayer->auxiliaryLayer()->addAuxiliaryField( def );
1230
1231 // update property with join field name from auxiliary storage
1232 QgsProperty property = button->toProperty();
1233 property.setField( QgsAuxiliaryLayer::nameFromProperty( def, true ) );
1234 property.setActive( true );
1235 button->updateFieldLists();
1236 button->setToProperty( property );
1237 mDataDefinedProperties.setProperty( key, button->toProperty() );
1238
1239 emit auxiliaryFieldCreated();
1240}
1241
1242void QgsDiagramProperties::connectValueChanged( const QList<QWidget *> &widgets )
1243{
1244 const auto constWidgets = widgets;
1245 for ( QWidget *widget : constWidgets )
1246 {
1247 if ( QgsSymbolButton *w = qobject_cast<QgsSymbolButton *>( widget ) )
1248 {
1250 }
1251 else if ( QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( widget ) )
1252 {
1253 connect( w, qOverload<const QString &>( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsDiagramProperties::widgetChanged );
1254 }
1255 else if ( QgsOpacityWidget *w = qobject_cast<QgsOpacityWidget *>( widget ) )
1256 {
1258 }
1259 else if ( QgsUnitSelectionWidget *w = qobject_cast<QgsUnitSelectionWidget *>( widget ) )
1260 {
1262 }
1263 else if ( QComboBox *w = qobject_cast<QComboBox *>( widget ) )
1264 {
1265 connect( w, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsDiagramProperties::widgetChanged );
1266 }
1267 else if ( QSpinBox *w = qobject_cast<QSpinBox *>( widget ) )
1268 {
1269 connect( w, qOverload<int>( &QSpinBox::valueChanged ), this, &QgsDiagramProperties::widgetChanged );
1270 }
1271 else if ( QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( widget ) )
1272 {
1273 connect( w, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsDiagramProperties::widgetChanged );
1274 }
1275 else if ( QgsColorButton *w = qobject_cast<QgsColorButton *>( widget ) )
1276 {
1278 }
1279 else if ( QCheckBox *w = qobject_cast<QCheckBox *>( widget ) )
1280 {
1281 connect( w, &QCheckBox::toggled, this, &QgsDiagramProperties::widgetChanged );
1282 }
1283 else if ( QRadioButton *w = qobject_cast<QRadioButton *>( widget ) )
1284 {
1285 connect( w, &QRadioButton::toggled, this, &QgsDiagramProperties::widgetChanged );
1286 }
1287 else if ( QSlider *w = qobject_cast<QSlider *>( widget ) )
1288 {
1289 connect( w, &QSlider::valueChanged, this, &QgsDiagramProperties::widgetChanged );
1290 }
1291 else if ( QGroupBox *w = qobject_cast<QGroupBox *>( widget ) )
1292 {
1293 connect( w, &QGroupBox::toggled, this, &QgsDiagramProperties::widgetChanged );
1294 }
1295 else if ( QTreeWidget *w = qobject_cast<QTreeWidget *>( widget ) )
1296 {
1297 connect( w, &QTreeWidget::itemChanged, this, &QgsDiagramProperties::widgetChanged );
1298 }
1299 else if ( QgsScaleRangeWidget *w = qobject_cast<QgsScaleRangeWidget *>( widget ) )
1300 {
1302 }
1303 else if ( QgsEffectStackCompactWidget *w = qobject_cast<QgsEffectStackCompactWidget *>( widget ) )
1304 {
1306 }
1307 else if ( QgsFontButton *w = qobject_cast<QgsFontButton *>( widget ) )
1308 {
1310 }
1311 else
1312 {
1313 QgsLogger::warning( QStringLiteral( "Could not create connection for widget %1" ).arg( widget->objectName() ) );
1314 }
1315 }
1316}
1317
1318void QgsDiagramProperties::setDiagramEnabled( bool enabled )
1319{
1320 mEnableDiagramCheckBox->setChecked( enabled );
1321}
1322
1323bool QgsDiagramProperties::isDiagramEnabled() const
1324{
1325 return mEnableDiagramCheckBox->isChecked();
1326}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:337
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
@ Millimeters
Millimeters.
@ Points
Points (e.g., for font sizes)
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ Line
Line symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:5775
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString nameFromProperty(const QgsPropertyDefinition &def, bool joined=false)
Returns the name of the auxiliary field for a property definition.
bool addAuxiliaryField(const QgsPropertyDefinition &definition)
Adds an auxiliary field for the given property.
bool exists(const QgsPropertyDefinition &definition) const
Returns true if the property is stored in the layer already, false otherwise.
A cross platform button subclass for selecting colors.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
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.
QFlags< LinePlacementFlag > LinePlacementFlags
bool showAllDiagrams() const
Returns whether the layer should show all diagrams, including overlapping diagrams.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the diagram's property collection, used for data defined overrides.
LinePlacementFlags linePlacementFlags() const
Returns the diagram placement flags.
Property
Data definable properties.
@ PositionX
X-coordinate data defined diagram position.
@ Distance
Distance to diagram from feature.
@ PositionY
Y-coordinate data defined diagram position.
@ Show
Whether to show the diagram.
@ Priority
Diagram priority (between 0 and 10)
@ ZIndex
Z-index for diagram ordering.
@ BackgroundColor
Diagram background color.
@ StartAngle
Angle offset for pie diagram.
@ IsObstacle
Whether diagram features act as obstacles for other diagrams/labels.
@ AlwaysShow
Whether the diagram should always be shown, even if it overlaps other diagrams/labels.
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.
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 syncToSettings(const QgsDiagramLayerSettings *dls)
Updates the widget to reflect the diagram layer settings.
void setDockMode(bool dockMode) override
Sets the widget in dock mode.
void mDiagramAttributesTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
bool isAllowedToEditDiagramLayerSettings() const
Returns whether this widget is allowed to edit diagram layer settings.
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 setDiagramType(const QString diagramType)
Defines the widget's diagram type and lets it know it should hide the type comboBox.
void addAttribute(QTreeWidgetItem *item)
Adds an attribute from the list of available attributes to the assigned attributes with a random colo...
void syncToRenderer(const QgsDiagramRenderer *dr)
Updates the widget to reflect the diagram renderer.
void mAttributesTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
void setAllowedToEditDiagramLayerSettings(bool allowed)
Sets whether the widget should show diagram layer settings.
void mDiagramStackedWidget_currentChanged(int index)
Evaluates and returns the diagram settings relating to a diagram for a specific feature.
virtual QString rendererName() const =0
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.
DiagramOrientation
Orientation of histogram.
Direction
Angular directions.
@ Counterclockwise
Counter-clockwise orientation.
@ Clockwise
Clockwise orientation.
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 small widget consisting of a checkbox for enabling/disabling an effect stack and a button for openi...
void changed()
Emitted when the paint effect properties change.
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)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
The QgsFieldExpressionWidget class creates a widget to choose fields and edit expressions It contains...
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
QString name
Definition qgsfield.h:62
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A button for customizing QgsTextFormat settings.
@ ModeQFont
Configure font settings for use with QFont objects.
void changed()
Emitted when the widget's text format settings are changed.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
static const QString DIAGRAM_NAME_HISTOGRAM
A line symbol type, for rendering LineString and MultiLineString geometries.
Alters the size of rendered diagrams using a linear scaling.
QString classificationField() const
Returns the field name used for interpolating the diagram size.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend.
static const QString DIAGRAM_RENDERER_NAME_LINEARLY_INTERPOLATED
static void warning(const QString &msg)
Goes to qWarning.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
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.
The QgsMapSettings class contains configuration for rendering of the map.
A dialog to create a new auxiliary layer.
A widget for setting an opacity value.
void opacityChanged(double opacity)
Emitted when the opacity is changed in the widget, where opacity ranges from 0.0 (transparent) to 1....
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.
QString panelTitle()
The title of the panel.
void widgetChanged()
Emitted when the widget state changes.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
bool dockMode()
Returns the dock mode state.
static const QString DIAGRAM_NAME_PIE
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
void setDirty(bool b=true)
Flag the project as dirty (modified).
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:45
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.
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.
A widget allowing entry of a range of map scales, e.g.
void rangeChanged(double min, double max)
Emitted when the scale range set in the widget is changed.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
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.
static const QString DIAGRAM_RENDERER_NAME_SINGLE_CATEGORY
QgsPropertyTransformer subclass for scaling a value into a size according to various scaling methods.
static const QString DIAGRAM_NAME_STACKED_BAR
Renders diagrams using mixed diagram render types.
static const QString DIAGRAM_RENDERER_NAME_STACKED
const QgsDiagramRenderer * renderer(const int index) const
Returns the renderer at the given index.
int rendererCount() const
Returns the number of sub renderers in the stacked diagram renderer.
A button for creating and modifying QgsSymbol settings.
void changed()
Emitted when the symbol's settings are changed.
static const QString DIAGRAM_NAME_TEXT
A widget displaying a combobox allowing the user to choose between various display units,...
void changed()
Emitted when the selected unit is changed, or the definition of the map unit scale is changed.
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.
const QgsDiagramLayerSettings * diagramLayerSettings() const
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
void setDiagramLayerSettings(const QgsDiagramLayerSettings &s)
void setDiagramRenderer(QgsDiagramRenderer *r)
Sets diagram rendering object (takes ownership)
const QgsDiagramRenderer * diagramRenderer() const
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5970
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39