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