QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgslabelinggui.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslabelinggui.cpp
3  Smart labeling for vector layers
4  -------------------
5  begin : June 2009
6  copyright : (C) Martin Dobias
7  email : wonder dot sk at gmail dot com
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 
18 #include "qgslabelinggui.h"
19 #include "qgsvectorlayer.h"
20 #include "qgsmapcanvas.h"
21 #include "qgsvectorlayerlabeling.h"
22 #include "qgsproject.h"
23 #include "qgsauxiliarystorage.h"
27 #include "qgshelp.h"
28 #include "qgsstylesavedialog.h"
29 #include "qgscallout.h"
30 #include "qgsapplication.h"
31 #include "qgscalloutsregistry.h"
35 
36 #include <mutex>
37 
38 #include <QButtonGroup>
39 #include <QMessageBox>
40 
42 
43 QgsExpressionContext QgsLabelingGui::createExpressionContext() const
44 {
45  QgsExpressionContext expContext;
49  if ( mCanvas )
50  expContext << QgsExpressionContextUtils::mapSettingsScope( mCanvas->mapSettings() );
51 
52  if ( mLayer )
53  expContext << QgsExpressionContextUtils::layerScope( mLayer );
54 
56 
57  //TODO - show actual value
58  expContext.setOriginalValueVariable( QVariant() );
60 
61  return expContext;
62 }
63 
64 static bool _initCalloutWidgetFunction( const QString &name, QgsCalloutWidgetFunc f )
65 {
67 
68  QgsCalloutAbstractMetadata *abstractMetadata = registry->calloutMetadata( name );
69  if ( !abstractMetadata )
70  {
71  QgsDebugMsg( QStringLiteral( "Failed to find callout entry in registry: %1" ).arg( name ) );
72  return false;
73  }
74  QgsCalloutMetadata *metadata = dynamic_cast<QgsCalloutMetadata *>( abstractMetadata );
75  if ( !metadata )
76  {
77  QgsDebugMsg( QStringLiteral( "Failed to cast callout's metadata: " ) .arg( name ) );
78  return false;
79  }
80  metadata->setWidgetFunction( f );
81  return true;
82 }
83 
84 void QgsLabelingGui::initCalloutWidgets()
85 {
86  _initCalloutWidgetFunction( QStringLiteral( "simple" ), QgsSimpleLineCalloutWidget::create );
87  _initCalloutWidgetFunction( QStringLiteral( "manhattan" ), QgsManhattanLineCalloutWidget::create );
88  _initCalloutWidgetFunction( QStringLiteral( "curved" ), QgsCurvedLineCalloutWidget::create );
89  _initCalloutWidgetFunction( QStringLiteral( "balloon" ), QgsBalloonCalloutWidget::create );
90 }
91 
92 void QgsLabelingGui::updateCalloutWidget( QgsCallout *callout )
93 {
94  if ( !callout )
95  {
96  mCalloutStackedWidget->setCurrentWidget( pageDummy );
97  return;
98  }
99 
100  if ( mCalloutStackedWidget->currentWidget() != pageDummy )
101  {
102  // stop updating from the original widget
103  if ( QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
104  disconnect( pew, &QgsCalloutWidget::changed, this, &QgsLabelingGui::updatePreview );
105  }
106 
108  if ( QgsCalloutAbstractMetadata *am = registry->calloutMetadata( callout->type() ) )
109  {
110  if ( QgsCalloutWidget *w = am->createCalloutWidget( mLayer ) )
111  {
112 
113  QgsWkbTypes::GeometryType geometryType = mGeomType;
114  if ( mGeometryGeneratorGroupBox->isChecked() )
115  geometryType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
116  else if ( mLayer )
117  geometryType = mLayer->geometryType();
118  w->setGeometryType( geometryType );
119  w->setCallout( callout );
120 
121  w->setContext( context() );
122  mCalloutStackedWidget->addWidget( w );
123  mCalloutStackedWidget->setCurrentWidget( w );
124  // start receiving updates from widget
125  connect( w, &QgsCalloutWidget::changed, this, &QgsLabelingGui::updatePreview );
126  return;
127  }
128  }
129  // When anything is not right
130  mCalloutStackedWidget->setCurrentWidget( pageDummy );
131 }
132 
133 void QgsLabelingGui::showObstacleSettings()
134 {
135  QgsExpressionContext context = createExpressionContext();
136 
137  QgsSymbolWidgetContext symbolContext;
138  symbolContext.setExpressionContext( &context );
139  symbolContext.setMapCanvas( mMapCanvas );
140 
141  QgsLabelObstacleSettingsWidget *widget = new QgsLabelObstacleSettingsWidget( nullptr, mLayer );
142  widget->setDataDefinedProperties( mDataDefinedProperties );
143  widget->setSettings( mObstacleSettings );
144  widget->setGeometryType( mLayer ? mLayer->geometryType() : QgsWkbTypes::UnknownGeometry );
145  widget->setContext( symbolContext );
146 
147  auto applySettings = [ = ]
148  {
149  mObstacleSettings = widget->settings();
150  const QgsPropertyCollection obstacleDataDefinedProperties = widget->dataDefinedProperties();
151  widget->updateDataDefinedProperties( mDataDefinedProperties );
152  emit widgetChanged();
153  };
154 
156  if ( panel && panel->dockMode() )
157  {
158  connect( widget, &QgsLabelSettingsWidgetBase::changed, this, [ = ]
159  {
160  applySettings();
161  } );
162  panel->openPanel( widget );
163  }
164  else
165  {
166  QgsLabelSettingsWidgetDialog dialog( widget, this );
167 
168  dialog.buttonBox()->addButton( QDialogButtonBox::Help );
169  connect( dialog.buttonBox(), &QDialogButtonBox::helpRequested, this, [ = ]
170  {
171  QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html#obstacles" ) );
172  } );
173 
174  if ( dialog.exec() )
175  {
176  applySettings();
177  }
178  // reactivate button's window
179  activateWindow();
180  }
181 }
182 
183 void QgsLabelingGui::showLineAnchorSettings()
184 {
185  QgsExpressionContext context = createExpressionContext();
186 
187  QgsSymbolWidgetContext symbolContext;
188  symbolContext.setExpressionContext( &context );
189  symbolContext.setMapCanvas( mMapCanvas );
190 
191  QgsLabelLineAnchorWidget *widget = new QgsLabelLineAnchorWidget( nullptr, mLayer );
192  widget->setDataDefinedProperties( mDataDefinedProperties );
193  widget->setSettings( mLineSettings );
194  widget->setGeometryType( mLayer ? mLayer->geometryType() : QgsWkbTypes::UnknownGeometry );
195  widget->setContext( symbolContext );
196 
197  auto applySettings = [ = ]
198  {
199  const QgsLabelLineSettings widgetSettings = widget->settings();
200  mLineSettings.setLineAnchorPercent( widgetSettings.lineAnchorPercent() );
201  mLineSettings.setAnchorType( widgetSettings.anchorType() );
202  mLineSettings.setAnchorClipping( widgetSettings.anchorClipping() );
203  const QgsPropertyCollection obstacleDataDefinedProperties = widget->dataDefinedProperties();
204  widget->updateDataDefinedProperties( mDataDefinedProperties );
205  emit widgetChanged();
206  };
207 
209  if ( panel && panel->dockMode() )
210  {
211  connect( widget, &QgsLabelSettingsWidgetBase::changed, this, [ = ]
212  {
213  applySettings();
214  } );
215  panel->openPanel( widget );
216  }
217  else
218  {
219  QgsLabelSettingsWidgetDialog dialog( widget, this );
220  if ( dialog.exec() )
221  {
222  applySettings();
223  }
224  // reactivate button's window
225  activateWindow();
226  }
227 }
228 
229 QgsLabelingGui::QgsLabelingGui( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &layerSettings, QWidget *parent, QgsWkbTypes::GeometryType geomType )
230  : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, layer )
231  , mSettings( layerSettings )
232  , mMode( NoLabels )
233  , mCanvas( mapCanvas )
234 {
235  mGeomType = geomType;
236  static std::once_flag initialized;
237  std::call_once( initialized, [ = ]( )
238  {
239  initCalloutWidgets();
240  } );
241 
242  mFontMultiLineAlignComboBox->addItem( tr( "Left" ), QgsPalLayerSettings::MultiLeft );
243  mFontMultiLineAlignComboBox->addItem( tr( "Center" ), QgsPalLayerSettings::MultiCenter );
244  mFontMultiLineAlignComboBox->addItem( tr( "Right" ), QgsPalLayerSettings::MultiRight );
245  mFontMultiLineAlignComboBox->addItem( tr( "Justify" ), QgsPalLayerSettings::MultiJustify );
246 
247  // connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
248  connect( mBufferDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
249  connect( mBufferDrawDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsLabelingGui::updateUi );
250  connect( mEnableMaskChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
251  connect( mShapeDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
252  connect( mCalloutsDrawCheckBox, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
253  connect( mShadowDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
254  connect( mDirectSymbChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
255  connect( mFormatNumChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
256  connect( mScaleBasedVisibilityChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
257  connect( mFontLimitPixelChkBox, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
258  connect( mGeometryGeneratorGroupBox, &QGroupBox::toggled, this, &QgsLabelingGui::updateGeometryTypeBasedWidgets );
259  connect( mGeometryGeneratorType, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::updateGeometryTypeBasedWidgets );
260  connect( mGeometryGeneratorExpressionButton, &QToolButton::clicked, this, &QgsLabelingGui::showGeometryGeneratorExpressionBuilder );
261  connect( mGeometryGeneratorGroupBox, &QGroupBox::toggled, this, &QgsLabelingGui::validateGeometryGeneratorExpression );
262  connect( mGeometryGenerator, &QgsCodeEditorExpression::textChanged, this, &QgsLabelingGui::validateGeometryGeneratorExpression );
263  connect( mGeometryGeneratorType, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::validateGeometryGeneratorExpression );
264  connect( mObstacleSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingGui::showObstacleSettings );
265  connect( mLineAnchorSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingGui::showLineAnchorSettings );
266 
267  mFieldExpressionWidget->registerExpressionContextGenerator( this );
268 
269  mMinScaleWidget->setMapCanvas( mCanvas );
270  mMinScaleWidget->setShowCurrentScaleButton( true );
271  mMaxScaleWidget->setMapCanvas( mCanvas );
272  mMaxScaleWidget->setShowCurrentScaleButton( true );
273 
274  const QStringList calloutTypes = QgsApplication::calloutRegistry()->calloutTypes();
275  for ( const QString &type : calloutTypes )
276  {
277  mCalloutStyleComboBox->addItem( QgsApplication::calloutRegistry()->calloutMetadata( type )->icon(),
278  QgsApplication::calloutRegistry()->calloutMetadata( type )->visibleName(), type );
279  }
280 
281  mGeometryGeneratorWarningLabel->setStyleSheet( QStringLiteral( "color: #FFC107;" ) );
282  mGeometryGeneratorWarningLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
283  connect( mGeometryGeneratorWarningLabel, &QLabel::linkActivated, this, [this]( const QString & link )
284  {
285  if ( link == QLatin1String( "#determineGeometryGeneratorType" ) )
286  determineGeometryGeneratorType();
287  } );
288 
289  connect( mCalloutStyleComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::calloutTypeChanged );
290 
291  mLblNoObstacle1->installEventFilter( this );
292 
293  setLayer( layer );
294 }
295 
296 void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
297 {
298  mPreviewFeature = QgsFeature();
299 
300  if ( ( !mapLayer || mapLayer->type() != QgsMapLayerType::VectorLayer ) && mGeomType == QgsWkbTypes::UnknownGeometry )
301  {
302  setEnabled( false );
303  return;
304  }
305 
306  setEnabled( true );
307 
308  QgsVectorLayer *layer = static_cast<QgsVectorLayer *>( mapLayer );
309  mLayer = layer;
310 
311  mTextFormatsListWidget->setLayerType( mLayer ? mLayer->geometryType() : mGeomType );
312  mBackgroundMarkerSymbolButton->setLayer( mLayer );
313  mBackgroundFillSymbolButton->setLayer( mLayer );
314 
315  // load labeling settings from layer
316  updateGeometryTypeBasedWidgets();
317 
318  mFieldExpressionWidget->setLayer( mLayer );
319  QgsDistanceArea da;
320  if ( mLayer )
321  da.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
322  da.setEllipsoid( QgsProject::instance()->ellipsoid() );
323  mFieldExpressionWidget->setGeomCalculator( da );
324 
325  mFieldExpressionWidget->setEnabled( mMode == Labels || !mLayer );
326  mLabelingFrame->setEnabled( mMode == Labels || !mLayer );
327 
328  blockInitSignals( true );
329 
330  mGeometryGenerator->setText( mSettings.geometryGenerator );
331  mGeometryGeneratorGroupBox->setChecked( mSettings.geometryGeneratorEnabled );
332  if ( !mSettings.geometryGeneratorEnabled )
333  mGeometryGeneratorGroupBox->setCollapsed( true );
334  mGeometryGeneratorType->setCurrentIndex( mGeometryGeneratorType->findData( mSettings.geometryGeneratorType ) );
335 
336  updateWidgetForFormat( mSettings.format().isValid() ? mSettings.format() : QgsStyle::defaultStyle()->defaultTextFormat( QgsStyle::TextFormatContext::Labeling ) );
337 
338  mFieldExpressionWidget->setRow( -1 );
339  mFieldExpressionWidget->setField( mSettings.fieldName );
340  mCheckBoxSubstituteText->setChecked( mSettings.useSubstitutions );
341  mSubstitutions = mSettings.substitutions;
342 
343  // populate placement options
344  mCentroidRadioWhole->setChecked( mSettings.centroidWhole );
345  mCentroidInsideCheckBox->setChecked( mSettings.centroidInside );
346  mFitInsidePolygonCheckBox->setChecked( mSettings.fitInPolygonOnly );
347  mLineDistanceSpnBx->setValue( mSettings.dist );
348  mLineDistanceUnitWidget->setUnit( mSettings.distUnits );
349  mLineDistanceUnitWidget->setMapUnitScale( mSettings.distMapUnitScale );
350  mOffsetTypeComboBox->setCurrentIndex( mOffsetTypeComboBox->findData( mSettings.offsetType ) );
351  mQuadrantBtnGrp->button( static_cast<int>( mSettings.quadOffset ) )->setChecked( true );
352  mPointOffsetXSpinBox->setValue( mSettings.xOffset );
353  mPointOffsetYSpinBox->setValue( mSettings.yOffset );
354  mPointOffsetUnitWidget->setUnit( mSettings.offsetUnits );
355  mPointOffsetUnitWidget->setMapUnitScale( mSettings.labelOffsetMapUnitScale );
356  mPointAngleSpinBox->setValue( mSettings.angleOffset );
357  chkLineAbove->setChecked( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::AboveLine );
358  chkLineBelow->setChecked( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::BelowLine );
359  chkLineOn->setChecked( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::OnLine );
360  chkLineOrientationDependent->setChecked( !( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::MapOrientation ) );
361 
362  mCheckAllowLabelsOutsidePolygons->setChecked( mSettings.polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
363 
364  const int placementIndex = mPlacementModeComboBox->findData( mSettings.placement );
365  if ( placementIndex >= 0 )
366  {
367  mPlacementModeComboBox->setCurrentIndex( placementIndex );
368  }
369  else
370  {
371  // use default placement for layer type
372  mPlacementModeComboBox->setCurrentIndex( 0 );
373  }
374 
375  // Label repeat distance
376  mRepeatDistanceSpinBox->setValue( mSettings.repeatDistance );
377  mRepeatDistanceUnitWidget->setUnit( mSettings.repeatDistanceUnit );
378  mRepeatDistanceUnitWidget->setMapUnitScale( mSettings.repeatDistanceMapUnitScale );
379 
380  mOverrunDistanceSpinBox->setValue( mSettings.lineSettings().overrunDistance() );
381  mOverrunDistanceUnitWidget->setUnit( mSettings.lineSettings().overrunDistanceUnit() );
382  mOverrunDistanceUnitWidget->setMapUnitScale( mSettings.lineSettings().overrunDistanceMapUnitScale() );
383 
384  mPrioritySlider->setValue( mSettings.priority );
385  mChkNoObstacle->setChecked( mSettings.obstacleSettings().isObstacle() );
386 
387  mObstacleSettings = mSettings.obstacleSettings();
388  mLineSettings = mSettings.lineSettings();
389 
390  chkLabelPerFeaturePart->setChecked( mSettings.labelPerPart );
391  mPalShowAllLabelsForLayerChkBx->setChecked( mSettings.displayAll );
392  chkMergeLines->setChecked( mSettings.lineSettings().mergeLines() );
393  mMinSizeSpinBox->setValue( mSettings.thinningSettings().minimumFeatureSize() );
394  mLimitLabelChkBox->setChecked( mSettings.thinningSettings().limitNumberOfLabelsEnabled() );
395  mLimitLabelSpinBox->setValue( mSettings.thinningSettings().maximumNumberLabels() );
396 
397  // direction symbol(s)
398  mDirectSymbChkBx->setChecked( mSettings.lineSettings().addDirectionSymbol() );
399  mDirectSymbLeftLineEdit->setText( mSettings.lineSettings().leftDirectionSymbol() );
400  mDirectSymbRightLineEdit->setText( mSettings.lineSettings().rightDirectionSymbol() );
401  mDirectSymbRevChkBx->setChecked( mSettings.lineSettings().reverseDirectionSymbol() );
402 
403  mDirectSymbBtnGrp->button( static_cast<int>( mSettings.lineSettings().directionSymbolPlacement() ) )->setChecked( true );
404  mUpsidedownBtnGrp->button( static_cast<int>( mSettings.upsidedownLabels ) )->setChecked( true );
405 
406  // curved label max character angles
407  mMaxCharAngleInDSpinBox->setValue( mSettings.maxCurvedCharAngleIn );
408  // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
409  mMaxCharAngleOutDSpinBox->setValue( std::fabs( mSettings.maxCurvedCharAngleOut ) );
410 
411  wrapCharacterEdit->setText( mSettings.wrapChar );
412  mAutoWrapLengthSpinBox->setValue( mSettings.autoWrapLength );
413  mAutoWrapTypeComboBox->setCurrentIndex( mSettings.useMaxLineLengthForAutoWrap ? 0 : 1 );
414 
415  if ( mFontMultiLineAlignComboBox->findData( mSettings.multilineAlign ) != -1 )
416  {
417  mFontMultiLineAlignComboBox->setCurrentIndex( mFontMultiLineAlignComboBox->findData( mSettings.multilineAlign ) );
418  }
419  else
420  {
421  // the default pal layer settings for multiline alignment is to follow label placement, which isn't always available
422  // revert to left alignment in such case
423  mFontMultiLineAlignComboBox->setCurrentIndex( 0 );
424  }
425 
426  chkPreserveRotation->setChecked( mSettings.preserveRotation );
427 
428  mScaleBasedVisibilityChkBx->setChecked( mSettings.scaleVisibility );
429  mMinScaleWidget->setScale( mSettings.minimumScale );
430  mMaxScaleWidget->setScale( mSettings.maximumScale );
431 
432  mFormatNumChkBx->setChecked( mSettings.formatNumbers );
433  mFormatNumDecimalsSpnBx->setValue( mSettings.decimals );
434  mFormatNumPlusSignChkBx->setChecked( mSettings.plusSign );
435 
436  // set pixel size limiting checked state before unit choice so limiting can be
437  // turned on as a default for map units, if minimum trigger value of 0 is used
438  mFontLimitPixelChkBox->setChecked( mSettings.fontLimitPixelSize );
439  mMinPixelLimit = mSettings.fontMinPixelSize; // ignored after first settings save
440  mFontMinPixelSpinBox->setValue( mSettings.fontMinPixelSize == 0 ? 3 : mSettings.fontMinPixelSize );
441  mFontMaxPixelSpinBox->setValue( mSettings.fontMaxPixelSize );
442 
443  mZIndexSpinBox->setValue( mSettings.zIndex );
444 
445  mDataDefinedProperties = mSettings.dataDefinedProperties();
446 
447  // callout settings, to move to custom widget when multiple styles exist
448  if ( auto *lCallout = mSettings.callout() )
449  {
450  whileBlocking( mCalloutsDrawCheckBox )->setChecked( lCallout->enabled() );
451  whileBlocking( mCalloutStyleComboBox )->setCurrentIndex( mCalloutStyleComboBox->findData( lCallout->type() ) );
452  updateCalloutWidget( lCallout );
453  }
454  else
455  {
456  std::unique_ptr< QgsCallout > defaultCallout( QgsApplication::calloutRegistry()->defaultCallout() );
457  whileBlocking( mCalloutStyleComboBox )->setCurrentIndex( mCalloutStyleComboBox->findData( defaultCallout->type() ) );
458  whileBlocking( mCalloutsDrawCheckBox )->setChecked( false );
459  updateCalloutWidget( defaultCallout.get() );
460  }
461 
462  updatePlacementWidgets();
463  updateLinePlacementOptions();
464 
465  // needs to come before data defined setup, so connections work
466  blockInitSignals( false );
467 
468  // set up data defined toolbuttons
469  // do this after other widgets are configured, so they can be enabled/disabled
470  populateDataDefinedButtons();
471 
472  enableDataDefinedAlignment( mCoordXDDBtn->isActive() && mCoordYDDBtn->isActive() );
473  updateUi(); // should come after data defined button setup
474 }
475 
476 void QgsLabelingGui::setSettings( const QgsPalLayerSettings &settings )
477 {
478  mSettings = settings;
479  setLayer( mLayer );
480 }
481 
482 void QgsLabelingGui::blockInitSignals( bool block )
483 {
484  chkLineAbove->blockSignals( block );
485  chkLineBelow->blockSignals( block );
486  mPlacementModeComboBox->blockSignals( block );
487 }
488 
489 void QgsLabelingGui::setLabelMode( LabelMode mode )
490 {
491  mMode = mode;
492  mFieldExpressionWidget->setEnabled( mMode == Labels );
493  mLabelingFrame->setEnabled( mMode == Labels );
494 }
495 
496 QgsPalLayerSettings QgsLabelingGui::layerSettings()
497 {
499 
500  // restore properties which aren't exposed in GUI
501  lyr.setUnplacedVisibility( mSettings.unplacedVisibility() );
502 
503  lyr.drawLabels = ( mMode == Labels ) || !mLayer;
504 
505  bool isExpression;
506  lyr.fieldName = mFieldExpressionWidget->currentField( &isExpression );
507  lyr.isExpression = isExpression;
508 
509  lyr.dist = 0;
510 
511  QgsLabeling::PolygonPlacementFlags polygonPlacementFlags = QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
512  if ( mCheckAllowLabelsOutsidePolygons->isChecked() )
513  polygonPlacementFlags |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
514  lyr.setPolygonPlacementFlags( polygonPlacementFlags );
515 
516  lyr.centroidWhole = mCentroidRadioWhole->isChecked();
517  lyr.centroidInside = mCentroidInsideCheckBox->isChecked();
518  lyr.fitInPolygonOnly = mFitInsidePolygonCheckBox->isChecked();
519  lyr.dist = mLineDistanceSpnBx->value();
520  lyr.distUnits = mLineDistanceUnitWidget->unit();
521  lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale();
522  lyr.offsetType = static_cast< QgsPalLayerSettings::OffsetType >( mOffsetTypeComboBox->currentData().toInt() );
523  if ( mQuadrantBtnGrp )
524  {
525  lyr.quadOffset = static_cast< QgsPalLayerSettings::QuadrantPosition >( mQuadrantBtnGrp->checkedId() );
526  }
527  lyr.xOffset = mPointOffsetXSpinBox->value();
528  lyr.yOffset = mPointOffsetYSpinBox->value();
529  lyr.offsetUnits = mPointOffsetUnitWidget->unit();
530  lyr.labelOffsetMapUnitScale = mPointOffsetUnitWidget->getMapUnitScale();
531  lyr.angleOffset = mPointAngleSpinBox->value();
532 
533  QgsLabeling::LinePlacementFlags linePlacementFlags = QgsLabeling::LinePlacementFlags();
534  if ( chkLineAbove->isChecked() )
535  linePlacementFlags |= QgsLabeling::LinePlacementFlag::AboveLine;
536  if ( chkLineBelow->isChecked() )
537  linePlacementFlags |= QgsLabeling::LinePlacementFlag::BelowLine;
538  if ( chkLineOn->isChecked() )
539  linePlacementFlags |= QgsLabeling::LinePlacementFlag::OnLine;
540  if ( ! chkLineOrientationDependent->isChecked() )
541  linePlacementFlags |= QgsLabeling::LinePlacementFlag::MapOrientation;
542  lyr.lineSettings().setPlacementFlags( linePlacementFlags );
543 
544  lyr.placement = static_cast< QgsPalLayerSettings::Placement >( mPlacementModeComboBox->currentData().toInt() );
545 
546  lyr.repeatDistance = mRepeatDistanceSpinBox->value();
547  lyr.repeatDistanceUnit = mRepeatDistanceUnitWidget->unit();
548  lyr.repeatDistanceMapUnitScale = mRepeatDistanceUnitWidget->getMapUnitScale();
549 
550  lyr.lineSettings().setOverrunDistance( mOverrunDistanceSpinBox->value() );
551  lyr.lineSettings().setOverrunDistanceUnit( mOverrunDistanceUnitWidget->unit() );
552  lyr.lineSettings().setOverrunDistanceMapUnitScale( mOverrunDistanceUnitWidget->getMapUnitScale() );
553 
554  lyr.priority = mPrioritySlider->value();
555 
556  mObstacleSettings.setIsObstacle( mChkNoObstacle->isChecked() || mMode == ObstaclesOnly );
557  lyr.setObstacleSettings( mObstacleSettings );
558 
559  lyr.lineSettings().setLineAnchorPercent( mLineSettings.lineAnchorPercent() );
560  lyr.lineSettings().setAnchorType( mLineSettings.anchorType() );
561  lyr.lineSettings().setAnchorClipping( mLineSettings.anchorClipping() );
562 
563  lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
564  lyr.displayAll = mPalShowAllLabelsForLayerChkBx->isChecked();
565  lyr.lineSettings().setMergeLines( chkMergeLines->isChecked() );
566 
567  lyr.scaleVisibility = mScaleBasedVisibilityChkBx->isChecked();
568  lyr.minimumScale = mMinScaleWidget->scale();
569  lyr.maximumScale = mMaxScaleWidget->scale();
570  lyr.useSubstitutions = mCheckBoxSubstituteText->isChecked();
571  lyr.substitutions = mSubstitutions;
572 
573  lyr.setFormat( format( false ) );
574 
575  // format numbers
576  lyr.formatNumbers = mFormatNumChkBx->isChecked();
577  lyr.decimals = mFormatNumDecimalsSpnBx->value();
578  lyr.plusSign = mFormatNumPlusSignChkBx->isChecked();
579 
580  // direction symbol(s)
581  lyr.lineSettings().setAddDirectionSymbol( mDirectSymbChkBx->isChecked() );
582  lyr.lineSettings().setLeftDirectionSymbol( mDirectSymbLeftLineEdit->text() );
583  lyr.lineSettings().setRightDirectionSymbol( mDirectSymbRightLineEdit->text() );
584  lyr.lineSettings().setReverseDirectionSymbol( mDirectSymbRevChkBx->isChecked() );
585  if ( mDirectSymbBtnGrp )
586  {
587  lyr.lineSettings().setDirectionSymbolPlacement( static_cast< QgsLabelLineSettings::DirectionSymbolPlacement >( mDirectSymbBtnGrp->checkedId() ) );
588  }
589  if ( mUpsidedownBtnGrp )
590  {
591  lyr.upsidedownLabels = static_cast< QgsPalLayerSettings::UpsideDownLabels >( mUpsidedownBtnGrp->checkedId() );
592  }
593 
594  lyr.maxCurvedCharAngleIn = mMaxCharAngleInDSpinBox->value();
595  // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
596  lyr.maxCurvedCharAngleOut = -mMaxCharAngleOutDSpinBox->value();
597 
598 
599  lyr.thinningSettings().setMinimumFeatureSize( mMinSizeSpinBox->value() );
600  lyr.thinningSettings().setLimitNumberLabelsEnabled( mLimitLabelChkBox->isChecked() );
601  lyr.thinningSettings().setMaximumNumberLabels( mLimitLabelSpinBox->value() );
602  lyr.fontLimitPixelSize = mFontLimitPixelChkBox->isChecked();
603  lyr.fontMinPixelSize = mFontMinPixelSpinBox->value();
604  lyr.fontMaxPixelSize = mFontMaxPixelSpinBox->value();
605  lyr.wrapChar = wrapCharacterEdit->text();
606  lyr.autoWrapLength = mAutoWrapLengthSpinBox->value();
607  lyr.useMaxLineLengthForAutoWrap = mAutoWrapTypeComboBox->currentIndex() == 0;
608  lyr.multilineAlign = static_cast< QgsPalLayerSettings::MultiLineAlign >( mFontMultiLineAlignComboBox->currentData().toInt() );
609  lyr.preserveRotation = chkPreserveRotation->isChecked();
610  lyr.geometryGenerator = mGeometryGenerator->text();
611  lyr.geometryGeneratorType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
612  lyr.geometryGeneratorEnabled = mGeometryGeneratorGroupBox->isChecked();
613 
614  lyr.layerType = mLayer ? mLayer->geometryType() : mGeomType;
615 
616  lyr.zIndex = mZIndexSpinBox->value();
617 
618  lyr.setDataDefinedProperties( mDataDefinedProperties );
619 
620  // callout settings
621  const QString calloutType = mCalloutStyleComboBox->currentData().toString();
622  std::unique_ptr< QgsCallout > callout;
623  if ( QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
624  {
625  callout.reset( pew->callout()->clone() );
626  }
627  if ( !callout )
628  callout.reset( QgsApplication::calloutRegistry()->createCallout( calloutType ) );
629 
630  callout->setEnabled( mCalloutsDrawCheckBox->isChecked() );
631  lyr.setCallout( callout.release() );
632 
633  return lyr;
634 }
635 
636 void QgsLabelingGui::syncDefinedCheckboxFrame( QgsPropertyOverrideButton *ddBtn, QCheckBox *chkBx, QFrame *f )
637 {
638  f->setEnabled( chkBx->isChecked() || ddBtn->isActive() );
639 }
640 
641 bool QgsLabelingGui::eventFilter( QObject *object, QEvent *event )
642 {
643  if ( object == mLblNoObstacle1 )
644  {
645  if ( event->type() == QEvent::MouseButtonPress && qgis::down_cast< QMouseEvent * >( event )->button() == Qt::LeftButton )
646  {
647  // clicking the obstacle label toggles the checkbox, just like a "normal" checkbox label...
648  mChkNoObstacle->setChecked( !mChkNoObstacle->isChecked() );
649  return true;
650  }
651  return false;
652  }
653  return QgsTextFormatWidget::eventFilter( object, event );
654 }
655 
656 void QgsLabelingGui::updateUi()
657 {
658  // enable/disable inline groupbox-like setups (that need to honor data defined setting)
659 
660  syncDefinedCheckboxFrame( mBufferDrawDDBtn, mBufferDrawChkBx, mBufferFrame );
661  syncDefinedCheckboxFrame( mEnableMaskDDBtn, mEnableMaskChkBx, mMaskFrame );
662  syncDefinedCheckboxFrame( mShapeDrawDDBtn, mShapeDrawChkBx, mShapeFrame );
663  syncDefinedCheckboxFrame( mShadowDrawDDBtn, mShadowDrawChkBx, mShadowFrame );
664  syncDefinedCheckboxFrame( mCalloutDrawDDBtn, mCalloutsDrawCheckBox, mCalloutFrame );
665 
666  syncDefinedCheckboxFrame( mDirectSymbDDBtn, mDirectSymbChkBx, mDirectSymbFrame );
667  syncDefinedCheckboxFrame( mFormatNumDDBtn, mFormatNumChkBx, mFormatNumFrame );
668  syncDefinedCheckboxFrame( mScaleBasedVisibilityDDBtn, mScaleBasedVisibilityChkBx, mScaleBasedVisibilityFrame );
669  syncDefinedCheckboxFrame( mFontLimitPixelDDBtn, mFontLimitPixelChkBox, mFontLimitPixelFrame );
670 
671  chkMergeLines->setEnabled( !mDirectSymbChkBx->isChecked() );
672  if ( mDirectSymbChkBx->isChecked() )
673  {
674  chkMergeLines->setToolTip( tr( "This option is not compatible with line direction symbols." ) );
675  }
676  else
677  {
678  chkMergeLines->setToolTip( QString() );
679  }
680 }
681 
682 void QgsLabelingGui::setFormatFromStyle( const QString &name, QgsStyle::StyleEntity type )
683 {
684  switch ( type )
685  {
688  case QgsStyle::TagEntity:
693  {
695  return;
696  }
697 
699  {
700  if ( !QgsStyle::defaultStyle()->labelSettingsNames().contains( name ) )
701  return;
702 
704  if ( settings.fieldName.isEmpty() )
705  {
706  // if saved settings doesn't have a field name stored, retain the current one
707  bool isExpression;
708  settings.fieldName = mFieldExpressionWidget->currentField( &isExpression );
709  settings.isExpression = isExpression;
710  }
711  setSettings( settings );
712  break;
713  }
714  }
715 }
716 
717 void QgsLabelingGui::setContext( const QgsSymbolWidgetContext &context )
718 {
719  if ( QgsCalloutWidget *cw = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
720  {
721  cw->setContext( context );
722  }
724 }
725 
726 void QgsLabelingGui::saveFormat()
727 {
728  QgsStyle *style = QgsStyle::defaultStyle();
729  if ( !style )
730  return;
731 
733  saveDlg.setDefaultTags( mTextFormatsListWidget->currentTagFilter() );
734  if ( !saveDlg.exec() )
735  return;
736 
737  if ( saveDlg.name().isEmpty() )
738  return;
739 
740  switch ( saveDlg.selectedType() )
741  {
743  {
744  // check if there is no format with same name
745  if ( style->textFormatNames().contains( saveDlg.name() ) )
746  {
747  int res = QMessageBox::warning( this, tr( "Save Text Format" ),
748  tr( "Format with name '%1' already exists. Overwrite?" )
749  .arg( saveDlg.name() ),
750  QMessageBox::Yes | QMessageBox::No );
751  if ( res != QMessageBox::Yes )
752  {
753  return;
754  }
755  style->removeTextFormat( saveDlg.name() );
756  }
757  QStringList symbolTags = saveDlg.tags().split( ',' );
758 
759  QgsTextFormat newFormat = format();
760  style->addTextFormat( saveDlg.name(), newFormat );
761  style->saveTextFormat( saveDlg.name(), newFormat, saveDlg.isFavorite(), symbolTags );
762  break;
763  }
764 
766  {
767  // check if there is no settings with same name
768  if ( style->labelSettingsNames().contains( saveDlg.name() ) )
769  {
770  int res = QMessageBox::warning( this, tr( "Save Label Settings" ),
771  tr( "Label settings with the name '%1' already exist. Overwrite?" )
772  .arg( saveDlg.name() ),
773  QMessageBox::Yes | QMessageBox::No );
774  if ( res != QMessageBox::Yes )
775  {
776  return;
777  }
778  style->removeLabelSettings( saveDlg.name() );
779  }
780  QStringList symbolTags = saveDlg.tags().split( ',' );
781 
782  QgsPalLayerSettings newSettings = layerSettings();
783  style->addLabelSettings( saveDlg.name(), newSettings );
784  style->saveLabelSettings( saveDlg.name(), newSettings, saveDlg.isFavorite(), symbolTags );
785  break;
786  }
787 
790  case QgsStyle::TagEntity:
794  break;
795  }
796 }
797 
798 void QgsLabelingGui::updateGeometryTypeBasedWidgets()
799 {
800  QgsWkbTypes::GeometryType geometryType = mGeomType;
801 
802  if ( mGeometryGeneratorGroupBox->isChecked() )
803  geometryType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
804  else if ( mLayer )
805  geometryType = mLayer->geometryType();
806 
807  // show/hide options based upon geometry type
808  chkMergeLines->setVisible( geometryType == QgsWkbTypes::LineGeometry );
809  mDirectSymbolsFrame->setVisible( geometryType == QgsWkbTypes::LineGeometry );
810  mMinSizeFrame->setVisible( geometryType != QgsWkbTypes::PointGeometry );
811  mPolygonFeatureOptionsFrame->setVisible( geometryType == QgsWkbTypes::PolygonGeometry );
812 
813 
814  const QgsPalLayerSettings::Placement prevPlacement = static_cast< QgsPalLayerSettings::Placement >( mPlacementModeComboBox->currentData().toInt() );
815  mPlacementModeComboBox->clear();
816 
817  switch ( geometryType )
818  {
820  mPlacementModeComboBox->addItem( tr( "Cartographic" ), QgsPalLayerSettings::OrderedPositionsAroundPoint );
821  mPlacementModeComboBox->addItem( tr( "Around Point" ), QgsPalLayerSettings::AroundPoint );
822  mPlacementModeComboBox->addItem( tr( "Offset from Point" ), QgsPalLayerSettings::OverPoint );
823  break;
824 
826  mPlacementModeComboBox->addItem( tr( "Parallel" ), QgsPalLayerSettings::Line );
827  mPlacementModeComboBox->addItem( tr( "Curved" ), QgsPalLayerSettings::Curved );
828  mPlacementModeComboBox->addItem( tr( "Horizontal" ), QgsPalLayerSettings::Horizontal );
829  break;
830 
832  mPlacementModeComboBox->addItem( tr( "Offset from Centroid" ), QgsPalLayerSettings::OverPoint );
833  mPlacementModeComboBox->addItem( tr( "Around Centroid" ), QgsPalLayerSettings::AroundPoint );
834  mPlacementModeComboBox->addItem( tr( "Horizontal" ), QgsPalLayerSettings::Horizontal );
835  mPlacementModeComboBox->addItem( tr( "Free (Angled)" ), QgsPalLayerSettings::Free );
836  mPlacementModeComboBox->addItem( tr( "Using Perimeter" ), QgsPalLayerSettings::Line );
837  mPlacementModeComboBox->addItem( tr( "Using Perimeter (Curved)" ), QgsPalLayerSettings::PerimeterCurved );
838  mPlacementModeComboBox->addItem( tr( "Outside Polygons" ), QgsPalLayerSettings::OutsidePolygons );
839  break;
840 
842  break;
844  qFatal( "unknown geometry type unexpected" );
845  }
846 
847  if ( mPlacementModeComboBox->findData( prevPlacement ) != -1 )
848  {
849  mPlacementModeComboBox->setCurrentIndex( mPlacementModeComboBox->findData( prevPlacement ) );
850  }
851 
852  if ( geometryType == QgsWkbTypes::PointGeometry || geometryType == QgsWkbTypes::PolygonGeometry )
853  {
854  // follow placement alignment is only valid for point or polygon layers
855  if ( mFontMultiLineAlignComboBox->findData( QgsPalLayerSettings::MultiFollowPlacement ) == -1 )
856  mFontMultiLineAlignComboBox->addItem( tr( "Follow Label Placement" ), QgsPalLayerSettings::MultiFollowPlacement );
857  }
858  else
859  {
860  int idx = mFontMultiLineAlignComboBox->findData( QgsPalLayerSettings::MultiFollowPlacement );
861  if ( idx >= 0 )
862  mFontMultiLineAlignComboBox->removeItem( idx );
863  }
864 
865  updatePlacementWidgets();
866  updateLinePlacementOptions();
867 }
868 
869 void QgsLabelingGui::showGeometryGeneratorExpressionBuilder()
870 {
871  QgsExpressionBuilderDialog expressionBuilder( mLayer );
872 
873  expressionBuilder.setExpressionText( mGeometryGenerator->text() );
874  expressionBuilder.setExpressionContext( createExpressionContext() );
875 
876  if ( expressionBuilder.exec() )
877  {
878  mGeometryGenerator->setText( expressionBuilder.expressionText() );
879  }
880 }
881 
882 void QgsLabelingGui::validateGeometryGeneratorExpression()
883 {
884  bool valid = true;
885 
886  if ( mGeometryGeneratorGroupBox->isChecked() )
887  {
888  if ( !mPreviewFeature.isValid() && mLayer )
889  mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( mPreviewFeature );
890 
891  QgsExpression expression( mGeometryGenerator->text() );
892  QgsExpressionContext context = createExpressionContext();
893  context.setFeature( mPreviewFeature );
894 
895  expression.prepare( &context );
896 
897  if ( expression.hasParserError() )
898  {
899  mGeometryGeneratorWarningLabel->setText( expression.parserErrorString() );
900  valid = false;
901  }
902  else
903  {
904  const QVariant result = expression.evaluate( &context );
905  const QgsGeometry geometry = result.value<QgsGeometry>();
906  QgsWkbTypes::GeometryType configuredGeometryType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
907  if ( geometry.isNull() )
908  {
909  mGeometryGeneratorWarningLabel->setText( tr( "Result of the expression is not a geometry" ) );
910  valid = false;
911  }
912  else if ( geometry.type() != configuredGeometryType )
913  {
914  mGeometryGeneratorWarningLabel->setText( QStringLiteral( "<p>%1</p><p><a href=\"#determineGeometryGeneratorType\">%2</a></p>" ).arg(
915  tr( "Result of the expression does not match configured geometry type." ),
916  tr( "Change to %1" ).arg( QgsWkbTypes::geometryDisplayString( geometry.type() ) ) ) );
917  valid = false;
918  }
919  }
920  }
921 
922  // The collapsible groupbox internally changes the visibility of this
923  // Work around by setting the visibility deferred in the next event loop cycle.
924  QTimer *timer = new QTimer();
925  connect( timer, &QTimer::timeout, this, [this, valid]()
926  {
927  mGeometryGeneratorWarningLabel->setVisible( !valid );
928  } );
929  connect( timer, &QTimer::timeout, timer, &QTimer::deleteLater );
930  timer->start( 0 );
931 }
932 
933 void QgsLabelingGui::determineGeometryGeneratorType()
934 {
935  if ( !mPreviewFeature.isValid() && mLayer )
936  mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( mPreviewFeature );
937 
938  QgsExpression expression( mGeometryGenerator->text() );
939  QgsExpressionContext context = createExpressionContext();
940  context.setFeature( mPreviewFeature );
941 
942  expression.prepare( &context );
943  const QgsGeometry geometry = expression.evaluate( &context ).value<QgsGeometry>();
944 
945  mGeometryGeneratorType->setCurrentIndex( mGeometryGeneratorType->findData( geometry.type() ) );
946 }
947 
948 void QgsLabelingGui::calloutTypeChanged()
949 {
950  QString newCalloutType = mCalloutStyleComboBox->currentData().toString();
951  QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() );
952  if ( pew )
953  {
954  if ( pew->callout() && pew->callout()->type() == newCalloutType )
955  return;
956  }
957 
958  // get creation function for new callout from registry
960  QgsCalloutAbstractMetadata *am = registry->calloutMetadata( newCalloutType );
961  if ( !am ) // check whether the metadata is assigned
962  return;
963 
964  // change callout to a new one (with different type)
965  // base new callout on existing callout's properties
966  std::unique_ptr< QgsCallout > newCallout( am->createCallout( pew && pew->callout() ? pew->callout()->properties( QgsReadWriteContext() ) : QVariantMap(), QgsReadWriteContext() ) );
967  if ( !newCallout )
968  return;
969 
970  updateCalloutWidget( newCallout.get() );
971  updatePreview();
972 }
973 
974 
975 //
976 // QgsLabelSettingsDialog
977 //
978 
979 QgsLabelSettingsDialog::QgsLabelSettingsDialog( const QgsPalLayerSettings &settings, QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, QWidget *parent,
980  QgsWkbTypes::GeometryType geomType )
981  : QDialog( parent )
982 {
983  QVBoxLayout *vLayout = new QVBoxLayout();
984  mWidget = new QgsLabelingGui( layer, mapCanvas, settings, nullptr, geomType );
985  vLayout->addWidget( mWidget );
986  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok, Qt::Horizontal );
987  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
988  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
989  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsLabelSettingsDialog::showHelp );
990  vLayout->addWidget( mButtonBox );
991  setLayout( vLayout );
992  setWindowTitle( tr( "Label Settings" ) );
993 }
994 
995 QDialogButtonBox *QgsLabelSettingsDialog::buttonBox() const
996 {
997  return mButtonBox;
998 }
999 
1000 void QgsLabelSettingsDialog::showHelp()
1001 {
1002  QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html" ) );
1003 }
1004 
1005 
1006 
static QgsCalloutRegistry * calloutRegistry()
Returns the application's callout registry, used for managing callout types.
Stores metadata about one callout renderer class.
virtual QgsCallout * createCallout(const QVariantMap &properties, const QgsReadWriteContext &context)=0
Create a callout of this type given the map of properties.
Convenience metadata class that uses static functions to create callouts and their widgets.
void setWidgetFunction(QgsCalloutWidgetFunc f)
Registry of available callout classes.
QgsCalloutAbstractMetadata * calloutMetadata(const QString &type) const
Returns the metadata for specified the specified callout type.
QStringList calloutTypes() const
Returns a list of all available callout types.
QgsCallout * createCallout(const QString &type, const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext()) const
Creates a new instance of a callout, given the callout type and properties.
Base class for widgets which allow control over the properties of callouts.
void changed()
Should be emitted whenever configuration changes happened on this symbol layer configuration.
virtual QgsCallout * callout()=0
Returns the callout defined by the current settings in the widget.
Abstract base class for callout renderers.
Definition: qgscallout.h:52
void setEnabled(bool enabled)
Sets whether the callout is enabled.
Definition: qgscallout.cpp:187
virtual QString type() const =0
Returns a unique string representing the callout type.
virtual QVariantMap properties(const QgsReadWriteContext &context) const
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:79
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 generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * updateSymbolScope(const QgsSymbol *symbol, QgsExpressionContextScope *symbolScope=nullptr)
Updates a symbol scope related to a QgsSymbol to an expression context.
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 setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
static const QString EXPR_SYMBOL_COLOR
Inbuilt variable name for symbol color variable.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
static const QString EXPR_ORIGINAL_VALUE
Inbuilt variable name for value original value variable.
Class for parsing and evaluation of expressions (formerly called "search strings").
This class wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:127
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
A widget for customising label line anchor settings.
void updateDataDefinedProperties(QgsPropertyCollection &properties) override
Updates a data defined properties collection, correctly setting the values for any properties related...
QgsLabelLineSettings settings() const
Returns the line settings defined by the widget.
void setSettings(const QgsLabelLineSettings &settings)
Sets the line settings to show in the widget.
Contains settings related to how the label engine places and formats labels for line features (or pol...
void setPlacementFlags(QgsLabeling::LinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setLineAnchorPercent(double percent)
Sets the percent along the line at which labels should be placed.
void setDirectionSymbolPlacement(DirectionSymbolPlacement placement)
Sets the placement for direction symbols.
AnchorType anchorType() const
Returns the line anchor type, which dictates how the lineAnchorPercent() setting is handled.
void setLeftDirectionSymbol(const QString &symbol)
Sets the string to use for left direction arrows.
void setMergeLines(bool merge)
Sets whether connected line features with identical label text should be merged prior to generating l...
DirectionSymbolPlacement
Placement options for direction symbols.
void setRightDirectionSymbol(const QString &symbol)
Sets the string to use for right direction arrows.
void setAnchorClipping(AnchorClipping clipping)
Sets the line anchor clipping mode, which dictates how line strings are clipped before calculating th...
void setOverrunDistanceMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for label overrun distance.
double lineAnchorPercent() const
Returns the percent along the line at which labels should be placed.
void setAnchorType(AnchorType type)
Sets the line anchor type, which dictates how the lineAnchorPercent() setting is handled.
void setOverrunDistance(double distance)
Sets the distance which labels are allowed to overrun past the start or end of line features.
AnchorClipping anchorClipping() const
Returns the line anchor clipping mode, which dictates how line strings are clipped before calculating...
void setOverrunDistanceUnit(const QgsUnitTypes::RenderUnit &unit)
Sets the unit for label overrun distance.
void setReverseDirectionSymbol(bool reversed)
Sets whether the direction symbols should be reversed.
void setAddDirectionSymbol(bool enabled)
Sets whether '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) will...
A widget for customising label obstacle settings.
QgsLabelObstacleSettings settings() const
Returns the obstacle settings defined by the widget.
void updateDataDefinedProperties(QgsPropertyCollection &properties) override
Updates a data defined properties collection, correctly setting the values for any properties related...
void setSettings(const QgsLabelObstacleSettings &settings)
Sets the obstacle settings to show in the widget.
void setGeometryType(QgsWkbTypes::GeometryType type) override
Sets the geometry type of the features to customize the widget accordingly.
void changed()
Emitted when any of the settings described by the widget are changed.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
void setDataDefinedProperties(const QgsPropertyCollection &dataDefinedProperties)
Sets the current data defined properties to show in the widget.
virtual void setGeometryType(QgsWkbTypes::GeometryType type)
Sets the geometry type of the features to customize the widget accordingly.
QgsPropertyCollection dataDefinedProperties() const
Returns the current data defined properties state as specified in the widget.
A blocking dialog containing a QgsLabelSettingsWidgetBase.
void setMaximumNumberLabels(int number)
Sets the maximum number of labels which should be drawn for this layer.
void setLimitNumberLabelsEnabled(bool enabled)
Sets whether the the number of labels drawn for the layer should be limited.
void setMinimumFeatureSize(double size)
Sets the minimum feature size (in millimeters) for a feature to be labelled.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
Base class for all map layer types.
Definition: qgsmaplayer.h:70
QgsMapLayerType type
Definition: qgsmaplayer.h:77
Contains settings for how a map layer will be labeled.
bool fitInPolygonOnly
true if only labels which completely fit within a polygon are allowed.
double yOffset
Vertical offset of label.
QgsMapUnitScale labelOffsetMapUnitScale
Map unit scale for label offset.
int fontMaxPixelSize
Maximum pixel size for showing rendered map unit labels (1 - 10000).
void setObstacleSettings(const QgsLabelObstacleSettings &settings)
Sets the label obstacle settings.
double maxCurvedCharAngleIn
Maximum angle between inside curved label characters (valid range 20.0 to 60.0).
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double zIndex
Z-Index of label, where labels with a higher z-index are rendered on top of labels with a lower z-ind...
bool displayAll
If true, all features will be labelled even when overlaps occur.
QString wrapChar
Wrapping character string.
double xOffset
Horizontal offset of label.
bool drawLabels
Whether to draw labels for this layer.
bool fontLimitPixelSize
true if label sizes should be limited by pixel size.
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
double minimumScale
The minimum map scale (i.e.
QgsWkbTypes::GeometryType geometryGeneratorType
The type of the result geometry of the geometry generator.
QgsUnitTypes::RenderUnit offsetUnits
Units for offsets of label.
bool scaleVisibility
Set to true to limit label visibility to a range of scales.
double repeatDistance
Distance for repeating labels for a single feature.
bool geometryGeneratorEnabled
Defines if the geometry generator is enabled or not. If disabled, the standard geometry will be taken...
Placement
Placement modes which determine how label candidates are generated for a feature.
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
bool centroidInside
true if centroid positioned labels must be placed inside their corresponding feature polygon,...
int priority
Label priority.
bool labelPerPart
true if every part of a multi-part feature should be labeled.
@ MultiJustify
Justified.
QgsUnitTypes::RenderUnit distUnits
Units the distance from feature to the label.
QgsUnitTypes::RenderUnit repeatDistanceUnit
Units for repeating labels for a single feature.
OffsetType offsetType
Offset type for layer (only applies in certain placement modes)
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
int fontMinPixelSize
Minimum pixel size for showing rendered map unit labels (1 - 1000).
double angleOffset
Label rotation, in degrees clockwise.
double maxCurvedCharAngleOut
Maximum angle between outside curved label characters (valid range -20.0 to -95.0)
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
void setPolygonPlacementFlags(QgsLabeling::PolygonPlacementFlags flags)
Sets the polygon placement flags, which dictate how polygon labels can be placed.
OffsetType
Behavior modifier for label offset and distance, only applies in some label placement modes.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
bool preserveRotation
True if label rotation should be preserved during label pin/unpin operations.
bool plusSign
Whether '+' signs should be prepended to positive numeric labels.
QString geometryGenerator
The geometry generator expression. Null if disabled.
QgsMapUnitScale distMapUnitScale
Map unit scale for label feature distance.
QgsStringReplacementCollection substitutions
Substitution collection for automatic text substitution with labels.
int decimals
Number of decimal places to show for numeric labels.
double dist
Distance from feature to the label.
QgsMapUnitScale repeatDistanceMapUnitScale
Map unit scale for repeating labels for a single feature.
bool centroidWhole
true if feature centroid should be calculated from the whole feature, or false if only the visible pa...
UpsideDownLabels upsidedownLabels
Controls whether upside down labels are displayed and how they are handled.
QString fieldName
Name of field (or an expression) to use for label text.
bool formatNumbers
Set to true to format numeric label text as numbers (e.g.
QgsWkbTypes::GeometryType layerType
Geometry type of layers associated with these settings.
void setCallout(QgsCallout *callout)
Sets the label callout renderer, responsible for drawing label callouts.
double maximumScale
The maximum map scale (i.e.
int autoWrapLength
If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified num...
bool useMaxLineLengthForAutoWrap
If true, indicates that when auto wrapping label text the autoWrapLength length indicates the maximum...
void setUnplacedVisibility(Qgis::UnplacedLabelVisibility visibility)
Sets the layer's unplaced label visibility.
const QgsLabelThinningSettings & thinningSettings() const
Returns the label thinning settings.
bool useSubstitutions
True if substitutions should be applied.
Base class for any widget that can be shown as a inline panel.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
bool dockMode()
Returns the dock mode state.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
A button for controlling property overrides which may apply to a widget.
bool isActive() const
Returns true if the button has an active property.
void changed()
Emitted when property definition changes.
The class is used as a container of context for various read/write operations on other objects.
a dialog for setting properties of a newly saved style.
bool removeLabelSettings(const QString &name)
Removes label settings from the style.
Definition: qgsstyle.cpp:1069
bool saveLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool favorite, const QStringList &tags)
Adds label settings to the database.
Definition: qgsstyle.cpp:1033
QgsTextFormat defaultTextFormat(QgsStyle::TextFormatContext context=QgsStyle::TextFormatContext::Labeling) const
Returns the default text format to use for new text based objects in the specified context.
Definition: qgsstyle.cpp:1228
QStringList textFormatNames() const
Returns a list of names of text formats in the style.
Definition: qgsstyle.cpp:2132
bool removeTextFormat(const QString &name)
Removes a text format from the style.
Definition: qgsstyle.cpp:993
StyleEntity
Enum for Entities involved in a style.
Definition: qgsstyle.h:179
@ LabelSettingsEntity
Label settings.
Definition: qgsstyle.h:185
@ TextFormatEntity
Text formats.
Definition: qgsstyle.h:184
@ SmartgroupEntity
Smart groups.
Definition: qgsstyle.h:183
@ Symbol3DEntity
3D symbol entity (since QGIS 3.14)
Definition: qgsstyle.h:187
@ SymbolEntity
Symbols.
Definition: qgsstyle.h:180
@ TagEntity
Tags.
Definition: qgsstyle.h:181
@ ColorrampEntity
Color ramps.
Definition: qgsstyle.h:182
@ LegendPatchShapeEntity
Legend patch shape (since QGIS 3.14)
Definition: qgsstyle.h:186
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:131
QStringList labelSettingsNames() const
Returns a list of names of label settings in the style.
Definition: qgsstyle.cpp:2196
bool addTextFormat(const QString &name, const QgsTextFormat &format, bool update=false)
Adds a text format with the specified name to the style.
Definition: qgsstyle.cpp:321
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
Definition: qgsstyle.cpp:2142
@ Labeling
Text format used in labeling.
bool saveTextFormat(const QString &name, const QgsTextFormat &format, bool favorite, const QStringList &tags)
Adds a text format to the database.
Definition: qgsstyle.cpp:957
bool addLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool update=false)
Adds label settings with the specified name to the style.
Definition: qgsstyle.cpp:342
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
void setExpressionContext(QgsExpressionContext *context)
Sets the optional expression context used for the widget.
A widget for customizing text formatting settings.
virtual void setFormatFromStyle(const QString &name, QgsStyle::StyleEntity type)
Sets the current text settings from a style entry.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the widget is shown, e.g., the associated map canvas and expression context...
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
Represents a vector layer which manages a vector based data sets.
static QString geometryDisplayString(GeometryType type) SIP_HOLDGIL
Returns a display string for a geometry type.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:537
QgsCalloutWidget *(* QgsCalloutWidgetFunc)(QgsVectorLayer *)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38