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