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