QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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  mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleDegrees ), QgsUnitTypes::AngleDegrees );
248  mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleRadians ), QgsUnitTypes::AngleRadians );
249  mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleGon ), QgsUnitTypes::AngleGon );
252  mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleTurn ), QgsUnitTypes::AngleTurn );
254  mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleMilNATO ), QgsUnitTypes::AngleMilNATO );
255 
256  // connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
257  connect( mBufferDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
258  connect( mBufferDrawDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsLabelingGui::updateUi );
259  connect( mEnableMaskChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
260  connect( mShapeDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
261  connect( mCalloutsDrawCheckBox, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
262  connect( mShadowDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
263  connect( mDirectSymbChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
264  connect( mFormatNumChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
265  connect( mScaleBasedVisibilityChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
266  connect( mFontLimitPixelChkBox, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
267  connect( mGeometryGeneratorGroupBox, &QGroupBox::toggled, this, &QgsLabelingGui::updateGeometryTypeBasedWidgets );
268  connect( mGeometryGeneratorType, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::updateGeometryTypeBasedWidgets );
269  connect( mGeometryGeneratorExpressionButton, &QToolButton::clicked, this, &QgsLabelingGui::showGeometryGeneratorExpressionBuilder );
270  connect( mGeometryGeneratorGroupBox, &QGroupBox::toggled, this, &QgsLabelingGui::validateGeometryGeneratorExpression );
271  connect( mGeometryGenerator, &QgsCodeEditorExpression::textChanged, this, &QgsLabelingGui::validateGeometryGeneratorExpression );
272  connect( mGeometryGeneratorType, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::validateGeometryGeneratorExpression );
273  connect( mObstacleSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingGui::showObstacleSettings );
274  connect( mLineAnchorSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingGui::showLineAnchorSettings );
275 
276  mFieldExpressionWidget->registerExpressionContextGenerator( this );
277 
278  mMinScaleWidget->setMapCanvas( mCanvas );
279  mMinScaleWidget->setShowCurrentScaleButton( true );
280  mMaxScaleWidget->setMapCanvas( mCanvas );
281  mMaxScaleWidget->setShowCurrentScaleButton( true );
282 
283  const QStringList calloutTypes = QgsApplication::calloutRegistry()->calloutTypes();
284  for ( const QString &type : calloutTypes )
285  {
286  mCalloutStyleComboBox->addItem( QgsApplication::calloutRegistry()->calloutMetadata( type )->icon(),
287  QgsApplication::calloutRegistry()->calloutMetadata( type )->visibleName(), type );
288  }
289 
290  mGeometryGeneratorWarningLabel->setStyleSheet( QStringLiteral( "color: #FFC107;" ) );
291  mGeometryGeneratorWarningLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
292  connect( mGeometryGeneratorWarningLabel, &QLabel::linkActivated, this, [this]( const QString & link )
293  {
294  if ( link == QLatin1String( "#determineGeometryGeneratorType" ) )
295  determineGeometryGeneratorType();
296  } );
297 
298  connect( mCalloutStyleComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::calloutTypeChanged );
299 
300  mLblNoObstacle1->installEventFilter( this );
301 
302  setLayer( layer );
303 }
304 
305 void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
306 {
307  mPreviewFeature = QgsFeature();
308 
309  if ( ( !mapLayer || mapLayer->type() != QgsMapLayerType::VectorLayer ) && mGeomType == QgsWkbTypes::UnknownGeometry )
310  {
311  setEnabled( false );
312  return;
313  }
314 
315  setEnabled( true );
316 
317  QgsVectorLayer *layer = static_cast<QgsVectorLayer *>( mapLayer );
318  mLayer = layer;
319 
320  mTextFormatsListWidget->setLayerType( mLayer ? mLayer->geometryType() : mGeomType );
321  mBackgroundMarkerSymbolButton->setLayer( mLayer );
322  mBackgroundFillSymbolButton->setLayer( mLayer );
323 
324  // load labeling settings from layer
325  updateGeometryTypeBasedWidgets();
326 
327  mFieldExpressionWidget->setLayer( mLayer );
328  QgsDistanceArea da;
329  if ( mLayer )
330  da.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
331  da.setEllipsoid( QgsProject::instance()->ellipsoid() );
332  mFieldExpressionWidget->setGeomCalculator( da );
333 
334  mFieldExpressionWidget->setEnabled( mMode == Labels || !mLayer );
335  mLabelingFrame->setEnabled( mMode == Labels || !mLayer );
336 
337  blockInitSignals( true );
338 
339  mGeometryGenerator->setText( mSettings.geometryGenerator );
340  mGeometryGeneratorGroupBox->setChecked( mSettings.geometryGeneratorEnabled );
341  if ( !mSettings.geometryGeneratorEnabled )
342  mGeometryGeneratorGroupBox->setCollapsed( true );
343  mGeometryGeneratorType->setCurrentIndex( mGeometryGeneratorType->findData( mSettings.geometryGeneratorType ) );
344 
345  updateWidgetForFormat( mSettings.format().isValid() ? mSettings.format() : QgsStyle::defaultStyle()->defaultTextFormat( QgsStyle::TextFormatContext::Labeling ) );
346 
347  mFieldExpressionWidget->setRow( -1 );
348  mFieldExpressionWidget->setField( mSettings.fieldName );
349  mCheckBoxSubstituteText->setChecked( mSettings.useSubstitutions );
350  mSubstitutions = mSettings.substitutions;
351 
352  // populate placement options
353  mCentroidRadioWhole->setChecked( mSettings.centroidWhole );
354  mCentroidInsideCheckBox->setChecked( mSettings.centroidInside );
355  mFitInsidePolygonCheckBox->setChecked( mSettings.fitInPolygonOnly );
356  mLineDistanceSpnBx->setValue( mSettings.dist );
357  mLineDistanceUnitWidget->setUnit( mSettings.distUnits );
358  mLineDistanceUnitWidget->setMapUnitScale( mSettings.distMapUnitScale );
359  mOffsetTypeComboBox->setCurrentIndex( mOffsetTypeComboBox->findData( mSettings.offsetType ) );
360  mQuadrantBtnGrp->button( static_cast<int>( mSettings.quadOffset ) )->setChecked( true );
361  mPointOffsetXSpinBox->setValue( mSettings.xOffset );
362  mPointOffsetYSpinBox->setValue( mSettings.yOffset );
363  mPointOffsetUnitWidget->setUnit( mSettings.offsetUnits );
364  mPointOffsetUnitWidget->setMapUnitScale( mSettings.labelOffsetMapUnitScale );
365  mPointAngleSpinBox->setValue( mSettings.angleOffset );
366  chkLineAbove->setChecked( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::AboveLine );
367  chkLineBelow->setChecked( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::BelowLine );
368  chkLineOn->setChecked( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::OnLine );
369  chkLineOrientationDependent->setChecked( !( mSettings.lineSettings().placementFlags() & QgsLabeling::LinePlacementFlag::MapOrientation ) );
370 
371  mCheckAllowLabelsOutsidePolygons->setChecked( mSettings.polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
372 
373  const int placementIndex = mPlacementModeComboBox->findData( mSettings.placement );
374  if ( placementIndex >= 0 )
375  {
376  mPlacementModeComboBox->setCurrentIndex( placementIndex );
377  }
378  else
379  {
380  // use default placement for layer type
381  mPlacementModeComboBox->setCurrentIndex( 0 );
382  }
383 
384  // Label repeat distance
385  mRepeatDistanceSpinBox->setValue( mSettings.repeatDistance );
386  mRepeatDistanceUnitWidget->setUnit( mSettings.repeatDistanceUnit );
387  mRepeatDistanceUnitWidget->setMapUnitScale( mSettings.repeatDistanceMapUnitScale );
388 
389  mOverrunDistanceSpinBox->setValue( mSettings.lineSettings().overrunDistance() );
390  mOverrunDistanceUnitWidget->setUnit( mSettings.lineSettings().overrunDistanceUnit() );
391  mOverrunDistanceUnitWidget->setMapUnitScale( mSettings.lineSettings().overrunDistanceMapUnitScale() );
392 
393  mPrioritySlider->setValue( mSettings.priority );
394  mChkNoObstacle->setChecked( mSettings.obstacleSettings().isObstacle() );
395 
396  mObstacleSettings = mSettings.obstacleSettings();
397  mLineSettings = mSettings.lineSettings();
398 
399  chkLabelPerFeaturePart->setChecked( mSettings.labelPerPart );
400  mPalShowAllLabelsForLayerChkBx->setChecked( mSettings.displayAll );
401  chkMergeLines->setChecked( mSettings.lineSettings().mergeLines() );
402  mMinSizeSpinBox->setValue( mSettings.thinningSettings().minimumFeatureSize() );
403  mLimitLabelChkBox->setChecked( mSettings.thinningSettings().limitNumberOfLabelsEnabled() );
404  mLimitLabelSpinBox->setValue( mSettings.thinningSettings().maximumNumberLabels() );
405 
406  // direction symbol(s)
407  mDirectSymbChkBx->setChecked( mSettings.lineSettings().addDirectionSymbol() );
408  mDirectSymbLeftLineEdit->setText( mSettings.lineSettings().leftDirectionSymbol() );
409  mDirectSymbRightLineEdit->setText( mSettings.lineSettings().rightDirectionSymbol() );
410  mDirectSymbRevChkBx->setChecked( mSettings.lineSettings().reverseDirectionSymbol() );
411 
412  mDirectSymbBtnGrp->button( static_cast<int>( mSettings.lineSettings().directionSymbolPlacement() ) )->setChecked( true );
413  mUpsidedownBtnGrp->button( static_cast<int>( mSettings.upsidedownLabels ) )->setChecked( true );
414 
415  // curved label max character angles
416  mMaxCharAngleInDSpinBox->setValue( mSettings.maxCurvedCharAngleIn );
417  // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
418  mMaxCharAngleOutDSpinBox->setValue( std::fabs( mSettings.maxCurvedCharAngleOut ) );
419 
420  wrapCharacterEdit->setText( mSettings.wrapChar );
421  mAutoWrapLengthSpinBox->setValue( mSettings.autoWrapLength );
422  mAutoWrapTypeComboBox->setCurrentIndex( mSettings.useMaxLineLengthForAutoWrap ? 0 : 1 );
423 
424  if ( mFontMultiLineAlignComboBox->findData( mSettings.multilineAlign ) != -1 )
425  {
426  mFontMultiLineAlignComboBox->setCurrentIndex( mFontMultiLineAlignComboBox->findData( mSettings.multilineAlign ) );
427  }
428  else
429  {
430  // the default pal layer settings for multiline alignment is to follow label placement, which isn't always available
431  // revert to left alignment in such case
432  mFontMultiLineAlignComboBox->setCurrentIndex( 0 );
433  }
434 
435  chkPreserveRotation->setChecked( mSettings.preserveRotation );
436 
437  mCoordRotationUnitComboBox->setCurrentIndex( 0 );
438  if ( mCoordRotationUnitComboBox->findData( static_cast< unsigned int >( mSettings.rotationUnit() ) ) >= 0 )
439  mCoordRotationUnitComboBox->setCurrentIndex( mCoordRotationUnitComboBox->findData( static_cast< unsigned int >( mSettings.rotationUnit() ) ) );
440 
441  mScaleBasedVisibilityChkBx->setChecked( mSettings.scaleVisibility );
442  mMinScaleWidget->setScale( mSettings.minimumScale );
443  mMaxScaleWidget->setScale( mSettings.maximumScale );
444 
445  mFormatNumChkBx->setChecked( mSettings.formatNumbers );
446  mFormatNumDecimalsSpnBx->setValue( mSettings.decimals );
447  mFormatNumPlusSignChkBx->setChecked( mSettings.plusSign );
448 
449  // set pixel size limiting checked state before unit choice so limiting can be
450  // turned on as a default for map units, if minimum trigger value of 0 is used
451  mFontLimitPixelChkBox->setChecked( mSettings.fontLimitPixelSize );
452  mMinPixelLimit = mSettings.fontMinPixelSize; // ignored after first settings save
453  mFontMinPixelSpinBox->setValue( mSettings.fontMinPixelSize == 0 ? 3 : mSettings.fontMinPixelSize );
454  mFontMaxPixelSpinBox->setValue( mSettings.fontMaxPixelSize );
455 
456  mZIndexSpinBox->setValue( mSettings.zIndex );
457 
458  mDataDefinedProperties = mSettings.dataDefinedProperties();
459 
460  // callout settings, to move to custom widget when multiple styles exist
461  if ( auto *lCallout = mSettings.callout() )
462  {
463  whileBlocking( mCalloutsDrawCheckBox )->setChecked( lCallout->enabled() );
464  whileBlocking( mCalloutStyleComboBox )->setCurrentIndex( mCalloutStyleComboBox->findData( lCallout->type() ) );
465  updateCalloutWidget( lCallout );
466  }
467  else
468  {
469  std::unique_ptr< QgsCallout > defaultCallout( QgsCalloutRegistry::defaultCallout() );
470  whileBlocking( mCalloutStyleComboBox )->setCurrentIndex( mCalloutStyleComboBox->findData( defaultCallout->type() ) );
471  whileBlocking( mCalloutsDrawCheckBox )->setChecked( false );
472  updateCalloutWidget( defaultCallout.get() );
473  }
474 
475  updatePlacementWidgets();
476  updateLinePlacementOptions();
477 
478  // needs to come before data defined setup, so connections work
479  blockInitSignals( false );
480 
481  // set up data defined toolbuttons
482  // do this after other widgets are configured, so they can be enabled/disabled
483  populateDataDefinedButtons();
484 
485  updateUi(); // should come after data defined button setup
486 }
487 
488 void QgsLabelingGui::setSettings( const QgsPalLayerSettings &settings )
489 {
490  mSettings = settings;
491  setLayer( mLayer );
492 }
493 
494 void QgsLabelingGui::blockInitSignals( bool block )
495 {
496  chkLineAbove->blockSignals( block );
497  chkLineBelow->blockSignals( block );
498  mPlacementModeComboBox->blockSignals( block );
499 }
500 
501 void QgsLabelingGui::setLabelMode( LabelMode mode )
502 {
503  mMode = mode;
504  mFieldExpressionWidget->setEnabled( mMode == Labels );
505  mLabelingFrame->setEnabled( mMode == Labels );
506 }
507 
508 QgsPalLayerSettings QgsLabelingGui::layerSettings()
509 {
511 
512  // restore properties which aren't exposed in GUI
513  lyr.setUnplacedVisibility( mSettings.unplacedVisibility() );
514 
515  lyr.drawLabels = ( mMode == Labels ) || !mLayer;
516 
517  bool isExpression;
518  lyr.fieldName = mFieldExpressionWidget->currentField( &isExpression );
519  lyr.isExpression = isExpression;
520 
521  lyr.dist = 0;
522 
523  QgsLabeling::PolygonPlacementFlags polygonPlacementFlags = QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
524  if ( mCheckAllowLabelsOutsidePolygons->isChecked() )
525  polygonPlacementFlags |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
526  lyr.setPolygonPlacementFlags( polygonPlacementFlags );
527 
528  lyr.centroidWhole = mCentroidRadioWhole->isChecked();
529  lyr.centroidInside = mCentroidInsideCheckBox->isChecked();
530  lyr.fitInPolygonOnly = mFitInsidePolygonCheckBox->isChecked();
531  lyr.dist = mLineDistanceSpnBx->value();
532  lyr.distUnits = mLineDistanceUnitWidget->unit();
533  lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale();
534  lyr.offsetType = static_cast< QgsPalLayerSettings::OffsetType >( mOffsetTypeComboBox->currentData().toInt() );
535  if ( mQuadrantBtnGrp )
536  {
537  lyr.quadOffset = static_cast< QgsPalLayerSettings::QuadrantPosition >( mQuadrantBtnGrp->checkedId() );
538  }
539  lyr.xOffset = mPointOffsetXSpinBox->value();
540  lyr.yOffset = mPointOffsetYSpinBox->value();
541  lyr.offsetUnits = mPointOffsetUnitWidget->unit();
542  lyr.labelOffsetMapUnitScale = mPointOffsetUnitWidget->getMapUnitScale();
543  lyr.angleOffset = mPointAngleSpinBox->value();
544 
545  QgsLabeling::LinePlacementFlags linePlacementFlags = QgsLabeling::LinePlacementFlags();
546  if ( chkLineAbove->isChecked() )
547  linePlacementFlags |= QgsLabeling::LinePlacementFlag::AboveLine;
548  if ( chkLineBelow->isChecked() )
549  linePlacementFlags |= QgsLabeling::LinePlacementFlag::BelowLine;
550  if ( chkLineOn->isChecked() )
551  linePlacementFlags |= QgsLabeling::LinePlacementFlag::OnLine;
552  if ( ! chkLineOrientationDependent->isChecked() )
553  linePlacementFlags |= QgsLabeling::LinePlacementFlag::MapOrientation;
554  lyr.lineSettings().setPlacementFlags( linePlacementFlags );
555 
556  lyr.placement = static_cast< QgsPalLayerSettings::Placement >( mPlacementModeComboBox->currentData().toInt() );
557 
558  lyr.repeatDistance = mRepeatDistanceSpinBox->value();
559  lyr.repeatDistanceUnit = mRepeatDistanceUnitWidget->unit();
560  lyr.repeatDistanceMapUnitScale = mRepeatDistanceUnitWidget->getMapUnitScale();
561 
562  lyr.lineSettings().setOverrunDistance( mOverrunDistanceSpinBox->value() );
563  lyr.lineSettings().setOverrunDistanceUnit( mOverrunDistanceUnitWidget->unit() );
564  lyr.lineSettings().setOverrunDistanceMapUnitScale( mOverrunDistanceUnitWidget->getMapUnitScale() );
565 
566  lyr.priority = mPrioritySlider->value();
567 
568  mObstacleSettings.setIsObstacle( mChkNoObstacle->isChecked() || mMode == ObstaclesOnly );
569  lyr.setObstacleSettings( mObstacleSettings );
570 
571  lyr.lineSettings().setLineAnchorPercent( mLineSettings.lineAnchorPercent() );
572  lyr.lineSettings().setAnchorType( mLineSettings.anchorType() );
573  lyr.lineSettings().setAnchorClipping( mLineSettings.anchorClipping() );
574 
575  lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
576  lyr.displayAll = mPalShowAllLabelsForLayerChkBx->isChecked();
577  lyr.lineSettings().setMergeLines( chkMergeLines->isChecked() );
578 
579  lyr.scaleVisibility = mScaleBasedVisibilityChkBx->isChecked();
580  lyr.minimumScale = mMinScaleWidget->scale();
581  lyr.maximumScale = mMaxScaleWidget->scale();
582  lyr.useSubstitutions = mCheckBoxSubstituteText->isChecked();
583  lyr.substitutions = mSubstitutions;
584 
585  lyr.setFormat( format( false ) );
586 
587  // format numbers
588  lyr.formatNumbers = mFormatNumChkBx->isChecked();
589  lyr.decimals = mFormatNumDecimalsSpnBx->value();
590  lyr.plusSign = mFormatNumPlusSignChkBx->isChecked();
591 
592  // direction symbol(s)
593  lyr.lineSettings().setAddDirectionSymbol( mDirectSymbChkBx->isChecked() );
594  lyr.lineSettings().setLeftDirectionSymbol( mDirectSymbLeftLineEdit->text() );
595  lyr.lineSettings().setRightDirectionSymbol( mDirectSymbRightLineEdit->text() );
596  lyr.lineSettings().setReverseDirectionSymbol( mDirectSymbRevChkBx->isChecked() );
597  if ( mDirectSymbBtnGrp )
598  {
599  lyr.lineSettings().setDirectionSymbolPlacement( static_cast< QgsLabelLineSettings::DirectionSymbolPlacement >( mDirectSymbBtnGrp->checkedId() ) );
600  }
601  if ( mUpsidedownBtnGrp )
602  {
603  lyr.upsidedownLabels = static_cast< QgsPalLayerSettings::UpsideDownLabels >( mUpsidedownBtnGrp->checkedId() );
604  }
605 
606  lyr.maxCurvedCharAngleIn = mMaxCharAngleInDSpinBox->value();
607  // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
608  lyr.maxCurvedCharAngleOut = -mMaxCharAngleOutDSpinBox->value();
609 
610 
611  lyr.thinningSettings().setMinimumFeatureSize( mMinSizeSpinBox->value() );
612  lyr.thinningSettings().setLimitNumberLabelsEnabled( mLimitLabelChkBox->isChecked() );
613  lyr.thinningSettings().setMaximumNumberLabels( mLimitLabelSpinBox->value() );
614  lyr.fontLimitPixelSize = mFontLimitPixelChkBox->isChecked();
615  lyr.fontMinPixelSize = mFontMinPixelSpinBox->value();
616  lyr.fontMaxPixelSize = mFontMaxPixelSpinBox->value();
617  lyr.wrapChar = wrapCharacterEdit->text();
618  lyr.autoWrapLength = mAutoWrapLengthSpinBox->value();
619  lyr.useMaxLineLengthForAutoWrap = mAutoWrapTypeComboBox->currentIndex() == 0;
620  lyr.multilineAlign = static_cast< QgsPalLayerSettings::MultiLineAlign >( mFontMultiLineAlignComboBox->currentData().toInt() );
621  lyr.preserveRotation = chkPreserveRotation->isChecked();
622  lyr.setRotationUnit( static_cast< QgsUnitTypes::AngleUnit >( mCoordRotationUnitComboBox->currentData().toInt() ) );
623  lyr.geometryGenerator = mGeometryGenerator->text();
624  lyr.geometryGeneratorType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
625  lyr.geometryGeneratorEnabled = mGeometryGeneratorGroupBox->isChecked();
626 
627  lyr.layerType = mLayer ? mLayer->geometryType() : mGeomType;
628 
629  lyr.zIndex = mZIndexSpinBox->value();
630 
631  lyr.setDataDefinedProperties( mDataDefinedProperties );
632 
633  // callout settings
634  const QString calloutType = mCalloutStyleComboBox->currentData().toString();
635  std::unique_ptr< QgsCallout > callout;
636  if ( QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
637  {
638  callout.reset( pew->callout()->clone() );
639  }
640  if ( !callout )
641  callout.reset( QgsApplication::calloutRegistry()->createCallout( calloutType ) );
642 
643  callout->setEnabled( mCalloutsDrawCheckBox->isChecked() );
644  lyr.setCallout( callout.release() );
645 
646  return lyr;
647 }
648 
649 void QgsLabelingGui::syncDefinedCheckboxFrame( QgsPropertyOverrideButton *ddBtn, QCheckBox *chkBx, QFrame *f )
650 {
651  f->setEnabled( chkBx->isChecked() || ddBtn->isActive() );
652 }
653 
654 bool QgsLabelingGui::eventFilter( QObject *object, QEvent *event )
655 {
656  if ( object == mLblNoObstacle1 )
657  {
658  if ( event->type() == QEvent::MouseButtonPress && qgis::down_cast< QMouseEvent * >( event )->button() == Qt::LeftButton )
659  {
660  // clicking the obstacle label toggles the checkbox, just like a "normal" checkbox label...
661  mChkNoObstacle->setChecked( !mChkNoObstacle->isChecked() );
662  return true;
663  }
664  return false;
665  }
666  return QgsTextFormatWidget::eventFilter( object, event );
667 }
668 
669 void QgsLabelingGui::updateUi()
670 {
671  // enable/disable inline groupbox-like setups (that need to honor data defined setting)
672 
673  syncDefinedCheckboxFrame( mBufferDrawDDBtn, mBufferDrawChkBx, mBufferFrame );
674  syncDefinedCheckboxFrame( mEnableMaskDDBtn, mEnableMaskChkBx, mMaskFrame );
675  syncDefinedCheckboxFrame( mShapeDrawDDBtn, mShapeDrawChkBx, mShapeFrame );
676  syncDefinedCheckboxFrame( mShadowDrawDDBtn, mShadowDrawChkBx, mShadowFrame );
677  syncDefinedCheckboxFrame( mCalloutDrawDDBtn, mCalloutsDrawCheckBox, mCalloutFrame );
678 
679  syncDefinedCheckboxFrame( mDirectSymbDDBtn, mDirectSymbChkBx, mDirectSymbFrame );
680  syncDefinedCheckboxFrame( mFormatNumDDBtn, mFormatNumChkBx, mFormatNumFrame );
681  syncDefinedCheckboxFrame( mScaleBasedVisibilityDDBtn, mScaleBasedVisibilityChkBx, mScaleBasedVisibilityFrame );
682  syncDefinedCheckboxFrame( mFontLimitPixelDDBtn, mFontLimitPixelChkBox, mFontLimitPixelFrame );
683 
684  chkMergeLines->setEnabled( !mDirectSymbChkBx->isChecked() );
685  if ( mDirectSymbChkBx->isChecked() )
686  {
687  chkMergeLines->setToolTip( tr( "This option is not compatible with line direction symbols." ) );
688  }
689  else
690  {
691  chkMergeLines->setToolTip( QString() );
692  }
693 }
694 
695 void QgsLabelingGui::setFormatFromStyle( const QString &name, QgsStyle::StyleEntity type )
696 {
697  switch ( type )
698  {
701  case QgsStyle::TagEntity:
706  {
708  return;
709  }
710 
712  {
713  if ( !QgsStyle::defaultStyle()->labelSettingsNames().contains( name ) )
714  return;
715 
717  if ( settings.fieldName.isEmpty() )
718  {
719  // if saved settings doesn't have a field name stored, retain the current one
720  bool isExpression;
721  settings.fieldName = mFieldExpressionWidget->currentField( &isExpression );
722  settings.isExpression = isExpression;
723  }
724  setSettings( settings );
725  break;
726  }
727  }
728 }
729 
730 void QgsLabelingGui::setContext( const QgsSymbolWidgetContext &context )
731 {
732  if ( QgsCalloutWidget *cw = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
733  {
734  cw->setContext( context );
735  }
737 }
738 
739 void QgsLabelingGui::saveFormat()
740 {
741  QgsStyle *style = QgsStyle::defaultStyle();
742  if ( !style )
743  return;
744 
746  saveDlg.setDefaultTags( mTextFormatsListWidget->currentTagFilter() );
747  if ( !saveDlg.exec() )
748  return;
749 
750  if ( saveDlg.name().isEmpty() )
751  return;
752 
753  switch ( saveDlg.selectedType() )
754  {
756  {
757  // check if there is no format with same name
758  if ( style->textFormatNames().contains( saveDlg.name() ) )
759  {
760  const int res = QMessageBox::warning( this, tr( "Save Text Format" ),
761  tr( "Format with name '%1' already exists. Overwrite?" )
762  .arg( saveDlg.name() ),
763  QMessageBox::Yes | QMessageBox::No );
764  if ( res != QMessageBox::Yes )
765  {
766  return;
767  }
768  style->removeTextFormat( saveDlg.name() );
769  }
770  const QStringList symbolTags = saveDlg.tags().split( ',' );
771 
772  const QgsTextFormat newFormat = format();
773  style->addTextFormat( saveDlg.name(), newFormat );
774  style->saveTextFormat( saveDlg.name(), newFormat, saveDlg.isFavorite(), symbolTags );
775  break;
776  }
777 
779  {
780  // check if there is no settings with same name
781  if ( style->labelSettingsNames().contains( saveDlg.name() ) )
782  {
783  const int res = QMessageBox::warning( this, tr( "Save Label Settings" ),
784  tr( "Label settings with the name '%1' already exist. Overwrite?" )
785  .arg( saveDlg.name() ),
786  QMessageBox::Yes | QMessageBox::No );
787  if ( res != QMessageBox::Yes )
788  {
789  return;
790  }
791  style->removeLabelSettings( saveDlg.name() );
792  }
793  const QStringList symbolTags = saveDlg.tags().split( ',' );
794 
795  const QgsPalLayerSettings newSettings = layerSettings();
796  style->addLabelSettings( saveDlg.name(), newSettings );
797  style->saveLabelSettings( saveDlg.name(), newSettings, saveDlg.isFavorite(), symbolTags );
798  break;
799  }
800 
803  case QgsStyle::TagEntity:
807  break;
808  }
809 }
810 
811 void QgsLabelingGui::updateGeometryTypeBasedWidgets()
812 {
813  QgsWkbTypes::GeometryType geometryType = mGeomType;
814 
815  if ( mGeometryGeneratorGroupBox->isChecked() )
816  geometryType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
817  else if ( mLayer )
818  geometryType = mLayer->geometryType();
819 
820  // show/hide options based upon geometry type
821  chkMergeLines->setVisible( geometryType == QgsWkbTypes::LineGeometry );
822  mDirectSymbolsFrame->setVisible( geometryType == QgsWkbTypes::LineGeometry );
823  mMinSizeFrame->setVisible( geometryType != QgsWkbTypes::PointGeometry );
824  mPolygonFeatureOptionsFrame->setVisible( geometryType == QgsWkbTypes::PolygonGeometry );
825 
826 
827  const QgsPalLayerSettings::Placement prevPlacement = static_cast< QgsPalLayerSettings::Placement >( mPlacementModeComboBox->currentData().toInt() );
828  mPlacementModeComboBox->clear();
829 
830  switch ( geometryType )
831  {
833  mPlacementModeComboBox->addItem( tr( "Cartographic" ), QgsPalLayerSettings::OrderedPositionsAroundPoint );
834  mPlacementModeComboBox->addItem( tr( "Around Point" ), QgsPalLayerSettings::AroundPoint );
835  mPlacementModeComboBox->addItem( tr( "Offset from Point" ), QgsPalLayerSettings::OverPoint );
836  break;
837 
839  mPlacementModeComboBox->addItem( tr( "Parallel" ), QgsPalLayerSettings::Line );
840  mPlacementModeComboBox->addItem( tr( "Curved" ), QgsPalLayerSettings::Curved );
841  mPlacementModeComboBox->addItem( tr( "Horizontal" ), QgsPalLayerSettings::Horizontal );
842  break;
843 
845  mPlacementModeComboBox->addItem( tr( "Offset from Centroid" ), QgsPalLayerSettings::OverPoint );
846  mPlacementModeComboBox->addItem( tr( "Around Centroid" ), QgsPalLayerSettings::AroundPoint );
847  mPlacementModeComboBox->addItem( tr( "Horizontal" ), QgsPalLayerSettings::Horizontal );
848  mPlacementModeComboBox->addItem( tr( "Free (Angled)" ), QgsPalLayerSettings::Free );
849  mPlacementModeComboBox->addItem( tr( "Using Perimeter" ), QgsPalLayerSettings::Line );
850  mPlacementModeComboBox->addItem( tr( "Using Perimeter (Curved)" ), QgsPalLayerSettings::PerimeterCurved );
851  mPlacementModeComboBox->addItem( tr( "Outside Polygons" ), QgsPalLayerSettings::OutsidePolygons );
852  break;
853 
855  break;
857  qFatal( "unknown geometry type unexpected" );
858  }
859 
860  if ( mPlacementModeComboBox->findData( prevPlacement ) != -1 )
861  {
862  mPlacementModeComboBox->setCurrentIndex( mPlacementModeComboBox->findData( prevPlacement ) );
863  }
864 
865  if ( geometryType == QgsWkbTypes::PointGeometry || geometryType == QgsWkbTypes::PolygonGeometry )
866  {
867  // follow placement alignment is only valid for point or polygon layers
868  if ( mFontMultiLineAlignComboBox->findData( QgsPalLayerSettings::MultiFollowPlacement ) == -1 )
869  mFontMultiLineAlignComboBox->addItem( tr( "Follow Label Placement" ), QgsPalLayerSettings::MultiFollowPlacement );
870  }
871  else
872  {
873  const int idx = mFontMultiLineAlignComboBox->findData( QgsPalLayerSettings::MultiFollowPlacement );
874  if ( idx >= 0 )
875  mFontMultiLineAlignComboBox->removeItem( idx );
876  }
877 
878  updatePlacementWidgets();
879  updateLinePlacementOptions();
880 }
881 
882 void QgsLabelingGui::showGeometryGeneratorExpressionBuilder()
883 {
884  QgsExpressionBuilderDialog expressionBuilder( mLayer );
885 
886  expressionBuilder.setExpressionText( mGeometryGenerator->text() );
887  expressionBuilder.setExpressionContext( createExpressionContext() );
888 
889  if ( expressionBuilder.exec() )
890  {
891  mGeometryGenerator->setText( expressionBuilder.expressionText() );
892  }
893 }
894 
895 void QgsLabelingGui::validateGeometryGeneratorExpression()
896 {
897  bool valid = true;
898 
899  if ( mGeometryGeneratorGroupBox->isChecked() )
900  {
901  if ( !mPreviewFeature.isValid() && mLayer )
902  mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( mPreviewFeature );
903 
904  QgsExpression expression( mGeometryGenerator->text() );
905  QgsExpressionContext context = createExpressionContext();
906  context.setFeature( mPreviewFeature );
907 
908  expression.prepare( &context );
909 
910  if ( expression.hasParserError() )
911  {
912  mGeometryGeneratorWarningLabel->setText( expression.parserErrorString() );
913  valid = false;
914  }
915  else
916  {
917  const QVariant result = expression.evaluate( &context );
918  const QgsGeometry geometry = result.value<QgsGeometry>();
919  const QgsWkbTypes::GeometryType configuredGeometryType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
920  if ( geometry.isNull() )
921  {
922  mGeometryGeneratorWarningLabel->setText( tr( "Result of the expression is not a geometry" ) );
923  valid = false;
924  }
925  else if ( geometry.type() != configuredGeometryType )
926  {
927  mGeometryGeneratorWarningLabel->setText( QStringLiteral( "<p>%1</p><p><a href=\"#determineGeometryGeneratorType\">%2</a></p>" ).arg(
928  tr( "Result of the expression does not match configured geometry type." ),
929  tr( "Change to %1" ).arg( QgsWkbTypes::geometryDisplayString( geometry.type() ) ) ) );
930  valid = false;
931  }
932  }
933  }
934 
935  // The collapsible groupbox internally changes the visibility of this
936  // Work around by setting the visibility deferred in the next event loop cycle.
937  QTimer *timer = new QTimer();
938  connect( timer, &QTimer::timeout, this, [this, valid]()
939  {
940  mGeometryGeneratorWarningLabel->setVisible( !valid );
941  } );
942  connect( timer, &QTimer::timeout, timer, &QTimer::deleteLater );
943  timer->start( 0 );
944 }
945 
946 void QgsLabelingGui::determineGeometryGeneratorType()
947 {
948  if ( !mPreviewFeature.isValid() && mLayer )
949  mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( mPreviewFeature );
950 
951  QgsExpression expression( mGeometryGenerator->text() );
952  QgsExpressionContext context = createExpressionContext();
953  context.setFeature( mPreviewFeature );
954 
955  expression.prepare( &context );
956  const QgsGeometry geometry = expression.evaluate( &context ).value<QgsGeometry>();
957 
958  mGeometryGeneratorType->setCurrentIndex( mGeometryGeneratorType->findData( geometry.type() ) );
959 }
960 
961 void QgsLabelingGui::calloutTypeChanged()
962 {
963  const QString newCalloutType = mCalloutStyleComboBox->currentData().toString();
964  QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() );
965  if ( pew )
966  {
967  if ( pew->callout() && pew->callout()->type() == newCalloutType )
968  return;
969  }
970 
971  // get creation function for new callout from registry
973  QgsCalloutAbstractMetadata *am = registry->calloutMetadata( newCalloutType );
974  if ( !am ) // check whether the metadata is assigned
975  return;
976 
977  // change callout to a new one (with different type)
978  // base new callout on existing callout's properties
979  const std::unique_ptr< QgsCallout > newCallout( am->createCallout( pew && pew->callout() ? pew->callout()->properties( QgsReadWriteContext() ) : QVariantMap(), QgsReadWriteContext() ) );
980  if ( !newCallout )
981  return;
982 
983  updateCalloutWidget( newCallout.get() );
984  updatePreview();
985 }
986 
987 
988 //
989 // QgsLabelSettingsDialog
990 //
991 
992 QgsLabelSettingsDialog::QgsLabelSettingsDialog( const QgsPalLayerSettings &settings, QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, QWidget *parent,
993  QgsWkbTypes::GeometryType geomType )
994  : QDialog( parent )
995 {
996  QVBoxLayout *vLayout = new QVBoxLayout();
997  mWidget = new QgsLabelingGui( layer, mapCanvas, settings, nullptr, geomType );
998  vLayout->addWidget( mWidget );
999  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok, Qt::Horizontal );
1000  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
1001  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
1002  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsLabelSettingsDialog::showHelp );
1003  vLayout->addWidget( mButtonBox );
1004  setLayout( vLayout );
1005  setWindowTitle( tr( "Label Settings" ) );
1006 }
1007 
1008 QDialogButtonBox *QgsLabelSettingsDialog::buttonBox() const
1009 {
1010  return mButtonBox;
1011 }
1012 
1013 void QgsLabelSettingsDialog::showHelp()
1014 {
1015  QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html" ) );
1016 }
1017 
1018 
1019 
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.
static QgsCallout * defaultCallout()
Create a new instance of a callout with default settings.
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:53
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:125
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:128
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:90
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsMapLayerType type
Definition: qgsmaplayer.h:80
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.
void setRotationUnit(QgsUnitTypes::AngleUnit angleUnit)
Set unit for rotation of labels.
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:470
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
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:1072
bool saveLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool favorite, const QStringList &tags)
Adds label settings to the database.
Definition: qgsstyle.cpp:1036
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:1231
QStringList textFormatNames() const
Returns a list of names of text formats in the style.
Definition: qgsstyle.cpp:2135
bool removeTextFormat(const QString &name)
Removes a text format from the style.
Definition: qgsstyle.cpp:996
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:2199
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:324
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
Definition: qgsstyle.cpp:2145
@ 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:960
bool addLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool update=false)
Adds label settings with the specified name to the style.
Definition: qgsstyle.cpp:345
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
AngleUnit
Units of angles.
Definition: qgsunittypes.h:132
@ AngleGon
Gon/gradian.
Definition: qgsunittypes.h:135
@ AngleRadians
Square kilometers.
Definition: qgsunittypes.h:134
@ AngleDegrees
Degrees.
Definition: qgsunittypes.h:133
@ AngleMilNATO
Angular mil (NATO definition, 6400 mil = 2PI radians)
Definition: qgsunittypes.h:140
@ AngleMilliradiansSI
Angular milliradians (SI definition, 1/1000 of radian)
Definition: qgsunittypes.h:139
@ AngleTurn
Turn/revolutions.
Definition: qgsunittypes.h:138
@ AngleMinutesOfArc
Minutes of arc.
Definition: qgsunittypes.h:136
@ AngleSecondsOfArc
Seconds of arc.
Definition: qgsunittypes.h:137
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
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
@ VectorLayer
Vector layer.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1517
QgsCalloutWidget *(* QgsCalloutWidgetFunc)(QgsVectorLayer *)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38