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