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