QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
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 "moc_qgslabelinggui.cpp"
20#include "qgsvectorlayer.h"
21#include "qgsmapcanvas.h"
22#include "qgsproject.h"
25#include "qgshelp.h"
26#include "qgsstylesavedialog.h"
27#include "qgscallout.h"
28#include "qgsapplication.h"
29#include "qgscalloutsregistry.h"
34#include "qgsgui.h"
35
36#include <QButtonGroup>
37#include <QMessageBox>
38
40
41QgsExpressionContext QgsLabelingGui::createExpressionContext() const
42{
43 QgsExpressionContext expContext;
44 if ( mCanvas )
45 {
46 expContext = mCanvas->createExpressionContext();
47 }
48 else
49 {
53 }
54
55 if ( mLayer )
56 expContext << QgsExpressionContextUtils::layerScope( mLayer );
57
58 if ( mLayer && mLayer->type() == Qgis::LayerType::Mesh )
59 {
60 if ( mGeomType == Qgis::GeometryType::Point )
62 else if ( mGeomType == Qgis::GeometryType::Polygon )
64 }
65
67
68 //TODO - show actual value
69 expContext.setOriginalValueVariable( QVariant() );
71
72 return expContext;
73}
74
75void QgsLabelingGui::updateCalloutWidget( QgsCallout *callout )
76{
77 if ( !callout )
78 {
79 mCalloutStackedWidget->setCurrentWidget( pageDummy );
80 return;
81 }
82
83 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
84 if ( !vLayer )
85 {
86 mCalloutStackedWidget->setCurrentWidget( pageDummy );
87 return;
88 }
89
90 if ( mCalloutStackedWidget->currentWidget() != pageDummy )
91 {
92 // stop updating from the original widget
93 if ( QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
94 disconnect( pew, &QgsCalloutWidget::changed, this, &QgsLabelingGui::updatePreview );
95 }
96
98 if ( QgsCalloutAbstractMetadata *am = registry->calloutMetadata( callout->type() ) )
99 {
100 if ( QgsCalloutWidget *w = am->createCalloutWidget( vLayer ) )
101 {
102
103 Qgis::GeometryType geometryType = mGeomType;
104 if ( mGeometryGeneratorGroupBox->isChecked() )
105 geometryType = mGeometryGeneratorType->currentData().value<Qgis::GeometryType>();
106 else if ( vLayer )
107 geometryType = vLayer->geometryType();
108 w->setGeometryType( geometryType );
109 w->setCallout( callout );
110
111 w->setContext( context() );
112 mCalloutStackedWidget->addWidget( w );
113 mCalloutStackedWidget->setCurrentWidget( w );
114 // start receiving updates from widget
115 connect( w, &QgsCalloutWidget::changed, this, &QgsLabelingGui::updatePreview );
116 return;
117 }
118 }
119 // When anything is not right
120 mCalloutStackedWidget->setCurrentWidget( pageDummy );
121}
122
123void QgsLabelingGui::showObstacleSettings()
124{
125 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
126 if ( !vLayer )
127 {
128 return;
129 }
130
131 QgsExpressionContext context = createExpressionContext();
132
133 QgsSymbolWidgetContext symbolContext;
134 symbolContext.setExpressionContext( &context );
135 symbolContext.setMapCanvas( mMapCanvas );
136
137 QgsLabelObstacleSettingsWidget *widget = new QgsLabelObstacleSettingsWidget( nullptr, vLayer );
138 widget->setDataDefinedProperties( mDataDefinedProperties );
139 widget->setSettings( mObstacleSettings );
140 widget->setGeometryType( vLayer ? vLayer->geometryType() : Qgis::GeometryType::Unknown );
141 widget->setContext( symbolContext );
142
143 auto applySettings = [ = ]
144 {
145 mObstacleSettings = widget->settings();
146 const QgsPropertyCollection obstacleDataDefinedProperties = widget->dataDefinedProperties();
147 widget->updateDataDefinedProperties( mDataDefinedProperties );
148 emit widgetChanged();
149 };
150
152 if ( panel && panel->dockMode() )
153 {
154 connect( widget, &QgsLabelSettingsWidgetBase::changed, this, [ = ]
155 {
156 applySettings();
157 } );
158 panel->openPanel( widget );
159 }
160 else
161 {
162 QgsLabelSettingsWidgetDialog dialog( widget, this );
163
164 dialog.buttonBox()->addButton( QDialogButtonBox::Help );
165 connect( dialog.buttonBox(), &QDialogButtonBox::helpRequested, this, [ = ]
166 {
167 QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html#obstacles" ) );
168 } );
169
170 if ( dialog.exec() )
171 {
172 applySettings();
173 }
174 // reactivate button's window
175 activateWindow();
176 }
177}
178
179void QgsLabelingGui::showLineAnchorSettings()
180{
181 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
182 if ( !vLayer )
183 {
184 return;
185 }
186
187 QgsExpressionContext context = createExpressionContext();
188
189 QgsSymbolWidgetContext symbolContext;
190 symbolContext.setExpressionContext( &context );
191 symbolContext.setMapCanvas( mMapCanvas );
192
193 QgsLabelLineAnchorWidget *widget = new QgsLabelLineAnchorWidget( nullptr, vLayer );
194 widget->setDataDefinedProperties( mDataDefinedProperties );
195 widget->setSettings( mLineSettings );
196 widget->setGeometryType( vLayer ? vLayer->geometryType() : Qgis::GeometryType::Unknown );
197 widget->setContext( symbolContext );
198
199 auto applySettings = [ = ]
200 {
201 const QgsLabelLineSettings widgetSettings = widget->settings();
202 mLineSettings.setLineAnchorPercent( widgetSettings.lineAnchorPercent() );
203 mLineSettings.setAnchorType( widgetSettings.anchorType() );
204 mLineSettings.setAnchorClipping( widgetSettings.anchorClipping() );
205 mLineSettings.setAnchorTextPoint( widgetSettings.anchorTextPoint() );
206 const QgsPropertyCollection obstacleDataDefinedProperties = widget->dataDefinedProperties();
207 widget->updateDataDefinedProperties( mDataDefinedProperties );
208 emit widgetChanged();
209 };
210
212 if ( panel && panel->dockMode() )
213 {
214 connect( widget, &QgsLabelSettingsWidgetBase::changed, this, [ = ]
215 {
216 applySettings();
217 } );
218 panel->openPanel( widget );
219 }
220 else
221 {
222 QgsLabelSettingsWidgetDialog dialog( widget, this );
223
224 dialog.buttonBox()->addButton( QDialogButtonBox::Help );
225 connect( dialog.buttonBox(), &QDialogButtonBox::helpRequested, this, [ = ]
226 {
227 QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html#placement-for-line-layers" ) );
228 } );
229
230 if ( dialog.exec() )
231 {
232 applySettings();
233 }
234 // reactivate button's window
235 activateWindow();
236 }
237}
238
239QgsLabelingGui::QgsLabelingGui( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &layerSettings, QWidget *parent, Qgis::GeometryType geomType )
240 : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, layer )
241 , mSettings( layerSettings )
242 , mMode( NoLabels )
243 , mCanvas( mapCanvas )
244{
246
247 mGeomType = geomType;
248
249 mFontMultiLineAlignComboBox->addItem( tr( "Left" ), static_cast< int >( Qgis::LabelMultiLineAlignment::Left ) );
250 mFontMultiLineAlignComboBox->addItem( tr( "Center" ), static_cast< int >( Qgis::LabelMultiLineAlignment::Center ) );
251 mFontMultiLineAlignComboBox->addItem( tr( "Right" ), static_cast< int >( Qgis::LabelMultiLineAlignment::Right ) );
252 mFontMultiLineAlignComboBox->addItem( tr( "Justify" ), static_cast< int >( Qgis::LabelMultiLineAlignment::Justify ) );
253
254 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::Degrees ), static_cast< int >( Qgis::AngleUnit::Degrees ) );
255 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::Radians ), static_cast< int >( Qgis::AngleUnit::Radians ) );
256 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::Gon ), static_cast< int >( Qgis::AngleUnit::Gon ) );
257 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::MinutesOfArc ), static_cast< int >( Qgis::AngleUnit::MinutesOfArc ) );
258 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::SecondsOfArc ), static_cast< int >( Qgis::AngleUnit::SecondsOfArc ) );
259 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::Turn ), static_cast< int >( Qgis::AngleUnit::Turn ) );
260 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::MilliradiansSI ), static_cast< int >( Qgis::AngleUnit::MilliradiansSI ) );
261 mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( Qgis::AngleUnit::MilNATO ), static_cast< int >( Qgis::AngleUnit::MilNATO ) );
262
263 // connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
264 connect( mBufferDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
265 connect( mBufferDrawDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsLabelingGui::updateUi );
266 connect( mEnableMaskChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
267 connect( mShapeDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
268 connect( mCalloutsDrawCheckBox, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
269 connect( mShadowDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
270 connect( mDirectSymbChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
271 connect( mFormatNumChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
272 connect( mScaleBasedVisibilityChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
273 connect( mFontLimitPixelChkBox, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
274 connect( mGeometryGeneratorGroupBox, &QGroupBox::toggled, this, &QgsLabelingGui::updateGeometryTypeBasedWidgets );
275 connect( mGeometryGeneratorType, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::updateGeometryTypeBasedWidgets );
276 connect( mGeometryGeneratorExpressionButton, &QToolButton::clicked, this, &QgsLabelingGui::showGeometryGeneratorExpressionBuilder );
277 connect( mGeometryGeneratorGroupBox, &QGroupBox::toggled, this, &QgsLabelingGui::validateGeometryGeneratorExpression );
278 connect( mGeometryGenerator, &QgsCodeEditorExpression::textChanged, this, &QgsLabelingGui::validateGeometryGeneratorExpression );
279 connect( mGeometryGeneratorType, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::validateGeometryGeneratorExpression );
280 connect( mObstacleSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingGui::showObstacleSettings );
281 connect( mLineAnchorSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingGui::showLineAnchorSettings );
282
283 mFieldExpressionWidget->registerExpressionContextGenerator( this );
284
285 mMinScaleWidget->setMapCanvas( mCanvas );
286 mMinScaleWidget->setShowCurrentScaleButton( true );
287 mMaxScaleWidget->setMapCanvas( mCanvas );
288 mMaxScaleWidget->setShowCurrentScaleButton( true );
289
290 mGeometryGeneratorExpressionButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
291 mGeometryGeneratorExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
292
293 const QStringList calloutTypes = QgsApplication::calloutRegistry()->calloutTypes();
294 for ( const QString &type : calloutTypes )
295 {
296 mCalloutStyleComboBox->addItem( QgsApplication::calloutRegistry()->calloutMetadata( type )->icon(),
297 QgsApplication::calloutRegistry()->calloutMetadata( type )->visibleName(), type );
298 }
299
300 mGeometryGeneratorWarningLabel->setStyleSheet( QStringLiteral( "color: #FFC107;" ) );
301 mGeometryGeneratorWarningLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
302 connect( mGeometryGeneratorWarningLabel, &QLabel::linkActivated, this, [this]( const QString & link )
303 {
304 if ( link == QLatin1String( "#determineGeometryGeneratorType" ) )
305 determineGeometryGeneratorType();
306 } );
307
308 connect( mCalloutStyleComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::calloutTypeChanged );
309
310 mLblNoObstacle1->installEventFilter( this );
311
312 setLayer( layer );
313}
314
315void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
316{
317 mPreviewFeature = QgsFeature();
318
319 if ( ( !mapLayer || mapLayer->type() != Qgis::LayerType::Vector ) && mGeomType == Qgis::GeometryType::Unknown )
320 {
321 setEnabled( false );
322 return;
323 }
324
325 setEnabled( true );
326
327 mLayer = mapLayer;
328 QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer );
329
330 mTextFormatsListWidget->setLayerType( vLayer ? vLayer->geometryType() : mGeomType );
331 mBackgroundMarkerSymbolButton->setLayer( vLayer );
332 mBackgroundFillSymbolButton->setLayer( vLayer );
333
334 // load labeling settings from layer
335 updateGeometryTypeBasedWidgets();
336
337 mFieldExpressionWidget->setLayer( mapLayer );
339 if ( mLayer )
340 da.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
341 da.setEllipsoid( QgsProject::instance()->ellipsoid() );
342 mFieldExpressionWidget->setGeomCalculator( da );
343
344 mFieldExpressionWidget->setEnabled( mMode == Labels || !mLayer );
345 mLabelingFrame->setEnabled( mMode == Labels || !mLayer );
346
347 blockInitSignals( true );
348
349 mGeometryGenerator->setText( mSettings.geometryGenerator );
350 mGeometryGeneratorGroupBox->setChecked( mSettings.geometryGeneratorEnabled );
351 if ( !mSettings.geometryGeneratorEnabled )
352 mGeometryGeneratorGroupBox->setCollapsed( true );
353 mGeometryGeneratorType->setCurrentIndex( mGeometryGeneratorType->findData( QVariant::fromValue( mSettings.geometryGeneratorType ) ) );
354
355 updateWidgetForFormat( mSettings.format().isValid() ? mSettings.format() : QgsStyle::defaultTextFormatForProject( QgsProject::instance(), QgsStyle::TextFormatContext::Labeling ) );
356
357 mFieldExpressionWidget->setRow( -1 );
358 mFieldExpressionWidget->setField( mSettings.fieldName );
359 mCheckBoxSubstituteText->setChecked( mSettings.useSubstitutions );
360 mSubstitutions = mSettings.substitutions;
361
362 // populate placement options
363 mCentroidRadioWhole->setChecked( mSettings.centroidWhole );
364 mCentroidInsideCheckBox->setChecked( mSettings.centroidInside );
365 mFitInsidePolygonCheckBox->setChecked( mSettings.fitInPolygonOnly );
366 mLineDistanceSpnBx->setValue( mSettings.dist );
367 mLineDistanceUnitWidget->setUnit( mSettings.distUnits );
368 mLineDistanceUnitWidget->setMapUnitScale( mSettings.distMapUnitScale );
369
370 mMaximumDistanceSpnBx->setValue( mSettings.pointSettings().maximumDistance() );
371 mMaximumDistanceUnitWidget->setUnit( mSettings.pointSettings().maximumDistanceUnit() );
372 mMaximumDistanceUnitWidget->setMapUnitScale( mSettings.pointSettings().maximumDistanceMapUnitScale() );
373
374 mOffsetTypeComboBox->setCurrentIndex( mOffsetTypeComboBox->findData( static_cast< int >( mSettings.offsetType ) ) );
375 mQuadrantBtnGrp->button( static_cast<int>( mSettings.pointSettings().quadrant() ) )->setChecked( true );
376 mPointOffsetXSpinBox->setValue( mSettings.xOffset );
377 mPointOffsetYSpinBox->setValue( mSettings.yOffset );
378 mPointOffsetUnitWidget->setUnit( mSettings.offsetUnits );
379 mPointOffsetUnitWidget->setMapUnitScale( mSettings.labelOffsetMapUnitScale );
380 mPointAngleSpinBox->setValue( mSettings.angleOffset );
381 chkLineAbove->setChecked( mSettings.lineSettings().placementFlags() & Qgis::LabelLinePlacementFlag::AboveLine );
382 chkLineBelow->setChecked( mSettings.lineSettings().placementFlags() & Qgis::LabelLinePlacementFlag::BelowLine );
383 chkLineOn->setChecked( mSettings.lineSettings().placementFlags() & Qgis::LabelLinePlacementFlag::OnLine );
384 chkLineOrientationDependent->setChecked( !( mSettings.lineSettings().placementFlags() & Qgis::LabelLinePlacementFlag::MapOrientation ) );
385
386 mCheckAllowLabelsOutsidePolygons->setChecked( mSettings.polygonPlacementFlags() & Qgis::LabelPolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
387
388 const int placementIndex = mPlacementModeComboBox->findData( static_cast< int >( mSettings.placement ) );
389 if ( placementIndex >= 0 )
390 {
391 mPlacementModeComboBox->setCurrentIndex( placementIndex );
392 }
393 else
394 {
395 // use default placement for layer type
396 mPlacementModeComboBox->setCurrentIndex( 0 );
397 }
398
399 // Label repeat distance
400 mRepeatDistanceSpinBox->setValue( mSettings.repeatDistance );
401 mRepeatDistanceUnitWidget->setUnit( mSettings.repeatDistanceUnit );
402 mRepeatDistanceUnitWidget->setMapUnitScale( mSettings.repeatDistanceMapUnitScale );
403
404 mOverrunDistanceSpinBox->setValue( mSettings.lineSettings().overrunDistance() );
405 mOverrunDistanceUnitWidget->setUnit( mSettings.lineSettings().overrunDistanceUnit() );
406 mOverrunDistanceUnitWidget->setMapUnitScale( mSettings.lineSettings().overrunDistanceMapUnitScale() );
407
408 mPrioritySlider->setValue( mSettings.priority );
409 mChkNoObstacle->setChecked( mSettings.obstacleSettings().isObstacle() );
410
411 mObstacleSettings = mSettings.obstacleSettings();
412 mLineSettings = mSettings.lineSettings();
413
414 chkLabelPerFeaturePart->setChecked( mSettings.labelPerPart );
415
416 mComboOverlapHandling->setCurrentIndex( mComboOverlapHandling->findData( static_cast< int >( mSettings.placementSettings().overlapHandling() ) ) );
417 mCheckAllowDegradedPlacement->setChecked( mSettings.placementSettings().allowDegradedPlacement() );
418 mPrioritizationComboBox->setCurrentIndex( mPrioritizationComboBox->findData( QVariant::fromValue( mSettings.placementSettings().prioritization() ) ) );
419
420 chkMergeLines->setChecked( mSettings.lineSettings().mergeLines() );
421 mMinSizeSpinBox->setValue( mSettings.thinningSettings().minimumFeatureSize() );
422 mLimitLabelChkBox->setChecked( mSettings.thinningSettings().limitNumberOfLabelsEnabled() );
423 mLimitLabelSpinBox->setValue( mSettings.thinningSettings().maximumNumberLabels() );
424
425 // direction symbol(s)
426 mDirectSymbChkBx->setChecked( mSettings.lineSettings().addDirectionSymbol() );
427 mDirectSymbLeftLineEdit->setText( mSettings.lineSettings().leftDirectionSymbol() );
428 mDirectSymbRightLineEdit->setText( mSettings.lineSettings().rightDirectionSymbol() );
429 mDirectSymbRevChkBx->setChecked( mSettings.lineSettings().reverseDirectionSymbol() );
430
431 mDirectSymbBtnGrp->button( static_cast<int>( mSettings.lineSettings().directionSymbolPlacement() ) )->setChecked( true );
432 mUpsidedownBtnGrp->button( static_cast<int>( mSettings.upsidedownLabels ) )->setChecked( true );
433
434 // curved label max character angles
435 mMaxCharAngleInDSpinBox->setValue( mSettings.maxCurvedCharAngleIn );
436 // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
437 mMaxCharAngleOutDSpinBox->setValue( std::fabs( mSettings.maxCurvedCharAngleOut ) );
438
439 wrapCharacterEdit->setText( mSettings.wrapChar );
440 mAutoWrapLengthSpinBox->setValue( mSettings.autoWrapLength );
441 mAutoWrapTypeComboBox->setCurrentIndex( mSettings.useMaxLineLengthForAutoWrap ? 0 : 1 );
442
443 if ( mFontMultiLineAlignComboBox->findData( static_cast< int >( mSettings.multilineAlign ) ) != -1 )
444 {
445 mFontMultiLineAlignComboBox->setCurrentIndex( mFontMultiLineAlignComboBox->findData( static_cast< int >( mSettings.multilineAlign ) ) );
446 }
447 else
448 {
449 // the default pal layer settings for multiline alignment is to follow label placement, which isn't always available
450 // revert to left alignment in such case
451 mFontMultiLineAlignComboBox->setCurrentIndex( 0 );
452 }
453
454 chkPreserveRotation->setChecked( mSettings.preserveRotation );
455
456 mCoordRotationUnitComboBox->setCurrentIndex( 0 );
457 if ( mCoordRotationUnitComboBox->findData( static_cast< unsigned int >( mSettings.rotationUnit() ) ) >= 0 )
458 mCoordRotationUnitComboBox->setCurrentIndex( mCoordRotationUnitComboBox->findData( static_cast< unsigned int >( mSettings.rotationUnit() ) ) );
459
460 mScaleBasedVisibilityChkBx->setChecked( mSettings.scaleVisibility );
461 mMinScaleWidget->setScale( mSettings.minimumScale );
462 mMaxScaleWidget->setScale( mSettings.maximumScale );
463
464 mFormatNumChkBx->setChecked( mSettings.formatNumbers );
465 mFormatNumDecimalsSpnBx->setValue( mSettings.decimals );
466 mFormatNumPlusSignChkBx->setChecked( mSettings.plusSign );
467
468 // set pixel size limiting checked state before unit choice so limiting can be
469 // turned on as a default for map units, if minimum trigger value of 0 is used
470 mFontLimitPixelChkBox->setChecked( mSettings.fontLimitPixelSize );
471 mMinPixelLimit = mSettings.fontMinPixelSize; // ignored after first settings save
472 mFontMinPixelSpinBox->setValue( mSettings.fontMinPixelSize == 0 ? 3 : mSettings.fontMinPixelSize );
473 mFontMaxPixelSpinBox->setValue( mSettings.fontMaxPixelSize );
474
475 mZIndexSpinBox->setValue( mSettings.zIndex );
476
477 mDataDefinedProperties = mSettings.dataDefinedProperties();
478
479 // callout settings, to move to custom widget when multiple styles exist
480 if ( auto *lCallout = mSettings.callout() )
481 {
482 whileBlocking( mCalloutsDrawCheckBox )->setChecked( lCallout->enabled() );
483 whileBlocking( mCalloutStyleComboBox )->setCurrentIndex( mCalloutStyleComboBox->findData( lCallout->type() ) );
484 updateCalloutWidget( lCallout );
485 }
486 else
487 {
488 std::unique_ptr< QgsCallout > defaultCallout( QgsCalloutRegistry::defaultCallout() );
489 whileBlocking( mCalloutStyleComboBox )->setCurrentIndex( mCalloutStyleComboBox->findData( defaultCallout->type() ) );
490 whileBlocking( mCalloutsDrawCheckBox )->setChecked( false );
491 updateCalloutWidget( defaultCallout.get() );
492 }
493
494 updatePlacementWidgets();
495 updateLinePlacementOptions();
496
497 // needs to come before data defined setup, so connections work
498 blockInitSignals( false );
499
500 // set up data defined toolbuttons
501 // do this after other widgets are configured, so they can be enabled/disabled
502 populateDataDefinedButtons();
503
504 updateUi(); // should come after data defined button setup
505}
506
507void QgsLabelingGui::setSettings( const QgsPalLayerSettings &settings )
508{
509 mSettings = settings;
510 setLayer( mLayer );
511}
512
513void QgsLabelingGui::blockInitSignals( bool block )
514{
515 chkLineAbove->blockSignals( block );
516 chkLineBelow->blockSignals( block );
517 mPlacementModeComboBox->blockSignals( block );
518}
519
520void QgsLabelingGui::setLabelMode( LabelMode mode )
521{
522 mMode = mode;
523 mFieldExpressionWidget->setEnabled( mMode == Labels );
524 mLabelingFrame->setEnabled( mMode == Labels );
525}
526
527QgsPalLayerSettings QgsLabelingGui::layerSettings()
528{
530
531 // restore properties which aren't exposed in GUI
532 lyr.setUnplacedVisibility( mSettings.unplacedVisibility() );
533
534 lyr.drawLabels = ( mMode == Labels ) || !mLayer;
535
536 bool isExpression;
537 lyr.fieldName = mFieldExpressionWidget->currentField( &isExpression );
538 lyr.isExpression = isExpression;
539
540 lyr.dist = 0;
541
543 if ( mCheckAllowLabelsOutsidePolygons->isChecked() )
545 lyr.setPolygonPlacementFlags( polygonPlacementFlags );
546
547 lyr.centroidWhole = mCentroidRadioWhole->isChecked();
548 lyr.centroidInside = mCentroidInsideCheckBox->isChecked();
549 lyr.fitInPolygonOnly = mFitInsidePolygonCheckBox->isChecked();
550 lyr.dist = mLineDistanceSpnBx->value();
551 lyr.distUnits = mLineDistanceUnitWidget->unit();
552 lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale();
553
554 lyr.pointSettings().setMaximumDistance( mMaximumDistanceSpnBx->value() );
555 lyr.pointSettings().setMaximumDistanceUnit( mMaximumDistanceUnitWidget->unit() );
556 lyr.pointSettings().setMaximumDistanceMapUnitScale( mMaximumDistanceUnitWidget->getMapUnitScale() );
557
558 lyr.offsetType = static_cast< Qgis::LabelOffsetType >( mOffsetTypeComboBox->currentData().toInt() );
559 if ( mQuadrantBtnGrp )
560 {
561 lyr.pointSettings().setQuadrant( static_cast< Qgis::LabelQuadrantPosition >( mQuadrantBtnGrp->checkedId() ) );
562 }
563 lyr.xOffset = mPointOffsetXSpinBox->value();
564 lyr.yOffset = mPointOffsetYSpinBox->value();
565 lyr.offsetUnits = mPointOffsetUnitWidget->unit();
566 lyr.labelOffsetMapUnitScale = mPointOffsetUnitWidget->getMapUnitScale();
567 lyr.angleOffset = mPointAngleSpinBox->value();
568
570 if ( chkLineAbove->isChecked() )
571 linePlacementFlags |= Qgis::LabelLinePlacementFlag::AboveLine;
572 if ( chkLineBelow->isChecked() )
573 linePlacementFlags |= Qgis::LabelLinePlacementFlag::BelowLine;
574 if ( chkLineOn->isChecked() )
575 linePlacementFlags |= Qgis::LabelLinePlacementFlag::OnLine;
576 if ( ! chkLineOrientationDependent->isChecked() )
578 lyr.lineSettings().setPlacementFlags( linePlacementFlags );
579
580 lyr.placement = static_cast< Qgis::LabelPlacement >( mPlacementModeComboBox->currentData().toInt() );
581
582 lyr.repeatDistance = mRepeatDistanceSpinBox->value();
583 lyr.repeatDistanceUnit = mRepeatDistanceUnitWidget->unit();
584 lyr.repeatDistanceMapUnitScale = mRepeatDistanceUnitWidget->getMapUnitScale();
585
586 lyr.lineSettings().setOverrunDistance( mOverrunDistanceSpinBox->value() );
587 lyr.lineSettings().setOverrunDistanceUnit( mOverrunDistanceUnitWidget->unit() );
588 lyr.lineSettings().setOverrunDistanceMapUnitScale( mOverrunDistanceUnitWidget->getMapUnitScale() );
589
590 lyr.priority = mPrioritySlider->value();
591
592 mObstacleSettings.setIsObstacle( mChkNoObstacle->isChecked() || mMode == ObstaclesOnly );
593 lyr.setObstacleSettings( mObstacleSettings );
594
595 lyr.lineSettings().setLineAnchorPercent( mLineSettings.lineAnchorPercent() );
596 lyr.lineSettings().setAnchorType( mLineSettings.anchorType() );
597 lyr.lineSettings().setAnchorClipping( mLineSettings.anchorClipping() );
598 lyr.lineSettings().setAnchorTextPoint( mLineSettings.anchorTextPoint() );
599
600 lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
601 lyr.placementSettings().setOverlapHandling( static_cast< Qgis::LabelOverlapHandling>( mComboOverlapHandling->currentData().toInt() ) );
602 lyr.placementSettings().setAllowDegradedPlacement( mCheckAllowDegradedPlacement->isChecked() );
603 lyr.placementSettings().setPrioritization( mPrioritizationComboBox->currentData().value< Qgis::LabelPrioritization >() );
604
605 lyr.lineSettings().setMergeLines( chkMergeLines->isChecked() );
606
607 lyr.scaleVisibility = mScaleBasedVisibilityChkBx->isChecked();
608 lyr.minimumScale = mMinScaleWidget->scale();
609 lyr.maximumScale = mMaxScaleWidget->scale();
610 lyr.useSubstitutions = mCheckBoxSubstituteText->isChecked();
611 lyr.substitutions = mSubstitutions;
612
613 lyr.setFormat( format( false ) );
614
615 // format numbers
616 lyr.formatNumbers = mFormatNumChkBx->isChecked();
617 lyr.decimals = mFormatNumDecimalsSpnBx->value();
618 lyr.plusSign = mFormatNumPlusSignChkBx->isChecked();
619
620 // direction symbol(s)
621 lyr.lineSettings().setAddDirectionSymbol( mDirectSymbChkBx->isChecked() );
622 lyr.lineSettings().setLeftDirectionSymbol( mDirectSymbLeftLineEdit->text() );
623 lyr.lineSettings().setRightDirectionSymbol( mDirectSymbRightLineEdit->text() );
624 lyr.lineSettings().setReverseDirectionSymbol( mDirectSymbRevChkBx->isChecked() );
625 if ( mDirectSymbBtnGrp )
626 {
627 lyr.lineSettings().setDirectionSymbolPlacement( static_cast< QgsLabelLineSettings::DirectionSymbolPlacement >( mDirectSymbBtnGrp->checkedId() ) );
628 }
629 if ( mUpsidedownBtnGrp )
630 {
631 lyr.upsidedownLabels = static_cast< Qgis::UpsideDownLabelHandling >( mUpsidedownBtnGrp->checkedId() );
632 }
633
634 lyr.maxCurvedCharAngleIn = mMaxCharAngleInDSpinBox->value();
635 // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
636 lyr.maxCurvedCharAngleOut = -mMaxCharAngleOutDSpinBox->value();
637
638
639 lyr.thinningSettings().setMinimumFeatureSize( mMinSizeSpinBox->value() );
640 lyr.thinningSettings().setLimitNumberLabelsEnabled( mLimitLabelChkBox->isChecked() );
641 lyr.thinningSettings().setMaximumNumberLabels( mLimitLabelSpinBox->value() );
642 lyr.fontLimitPixelSize = mFontLimitPixelChkBox->isChecked();
643 lyr.fontMinPixelSize = mFontMinPixelSpinBox->value();
644 lyr.fontMaxPixelSize = mFontMaxPixelSpinBox->value();
645 lyr.wrapChar = wrapCharacterEdit->text();
646 lyr.autoWrapLength = mAutoWrapLengthSpinBox->value();
647 lyr.useMaxLineLengthForAutoWrap = mAutoWrapTypeComboBox->currentIndex() == 0;
648 lyr.multilineAlign = static_cast< Qgis::LabelMultiLineAlignment >( mFontMultiLineAlignComboBox->currentData().toInt() );
649 lyr.preserveRotation = chkPreserveRotation->isChecked();
650 lyr.setRotationUnit( static_cast< Qgis::AngleUnit >( mCoordRotationUnitComboBox->currentData().toInt() ) );
651 lyr.geometryGenerator = mGeometryGenerator->text();
652 lyr.geometryGeneratorType = mGeometryGeneratorType->currentData().value<Qgis::GeometryType>();
653 lyr.geometryGeneratorEnabled = mGeometryGeneratorGroupBox->isChecked();
654
655 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
656 lyr.layerType = vLayer ? vLayer->geometryType() : mGeomType;
657
658 lyr.zIndex = mZIndexSpinBox->value();
659
660 lyr.setDataDefinedProperties( mDataDefinedProperties );
661
662 // callout settings
663 const QString calloutType = mCalloutStyleComboBox->currentData().toString();
664 std::unique_ptr< QgsCallout > callout;
665 if ( QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
666 {
667 callout.reset( pew->callout()->clone() );
668 }
669 if ( !callout )
670 callout.reset( QgsApplication::calloutRegistry()->createCallout( calloutType ) );
671
672 callout->setEnabled( mCalloutsDrawCheckBox->isChecked() );
673 lyr.setCallout( callout.release() );
674
675 return lyr;
676}
677
678void QgsLabelingGui::syncDefinedCheckboxFrame( QgsPropertyOverrideButton *ddBtn, QCheckBox *chkBx, QFrame *f )
679{
680 f->setEnabled( chkBx->isChecked() || ddBtn->isActive() );
681}
682
683bool QgsLabelingGui::eventFilter( QObject *object, QEvent *event )
684{
685 if ( object == mLblNoObstacle1 )
686 {
687 if ( event->type() == QEvent::MouseButtonPress && qgis::down_cast< QMouseEvent * >( event )->button() == Qt::LeftButton )
688 {
689 // clicking the obstacle label toggles the checkbox, just like a "normal" checkbox label...
690 mChkNoObstacle->setChecked( !mChkNoObstacle->isChecked() );
691 return true;
692 }
693 return false;
694 }
695 return QgsTextFormatWidget::eventFilter( object, event );
696}
697
698void QgsLabelingGui::updateUi()
699{
700 // enable/disable inline groupbox-like setups (that need to honor data defined setting)
701
702 syncDefinedCheckboxFrame( mBufferDrawDDBtn, mBufferDrawChkBx, mBufferFrame );
703 syncDefinedCheckboxFrame( mEnableMaskDDBtn, mEnableMaskChkBx, mMaskFrame );
704 syncDefinedCheckboxFrame( mShapeDrawDDBtn, mShapeDrawChkBx, mShapeFrame );
705 syncDefinedCheckboxFrame( mShadowDrawDDBtn, mShadowDrawChkBx, mShadowFrame );
706 syncDefinedCheckboxFrame( mCalloutDrawDDBtn, mCalloutsDrawCheckBox, mCalloutFrame );
707
708 syncDefinedCheckboxFrame( mDirectSymbDDBtn, mDirectSymbChkBx, mDirectSymbFrame );
709 syncDefinedCheckboxFrame( mFormatNumDDBtn, mFormatNumChkBx, mFormatNumFrame );
710 syncDefinedCheckboxFrame( mScaleBasedVisibilityDDBtn, mScaleBasedVisibilityChkBx, mScaleBasedVisibilityFrame );
711 syncDefinedCheckboxFrame( mFontLimitPixelDDBtn, mFontLimitPixelChkBox, mFontLimitPixelFrame );
712
713 chkMergeLines->setEnabled( !mDirectSymbChkBx->isChecked() );
714 if ( mDirectSymbChkBx->isChecked() )
715 {
716 chkMergeLines->setToolTip( tr( "This option is not compatible with line direction symbols." ) );
717 }
718 else
719 {
720 chkMergeLines->setToolTip( QString() );
721 }
722}
723
724void QgsLabelingGui::setFormatFromStyle( const QString &name, QgsStyle::StyleEntity type, const QString &stylePath )
725{
726 QgsStyle *style = QgsProject::instance()->styleSettings()->styleAtPath( stylePath );
727
728 if ( !style )
729 style = QgsStyle::defaultStyle();
730
731 switch ( type )
732 {
740 {
741 QgsTextFormatWidget::setFormatFromStyle( name, type, stylePath );
742 return;
743 }
744
746 {
747 if ( !style->labelSettingsNames().contains( name ) )
748 return;
749
750 QgsPalLayerSettings settings = style->labelSettings( name );
751 if ( settings.fieldName.isEmpty() )
752 {
753 // if saved settings doesn't have a field name stored, retain the current one
754 bool isExpression;
755 settings.fieldName = mFieldExpressionWidget->currentField( &isExpression );
756 settings.isExpression = isExpression;
757 }
758 setSettings( settings );
759 break;
760 }
761 }
762}
763
764void QgsLabelingGui::setContext( const QgsSymbolWidgetContext &context )
765{
766 if ( QgsCalloutWidget *cw = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() ) )
767 {
768 cw->setContext( context );
769 }
771}
772
773void QgsLabelingGui::saveFormat()
774{
776 saveDlg.setDefaultTags( mTextFormatsListWidget->currentTagFilter() );
777 if ( !saveDlg.exec() )
778 return;
779
780 if ( saveDlg.name().isEmpty() )
781 return;
782
783 QgsStyle *style = saveDlg.destinationStyle();
784 if ( !style )
785 return;
786
787 switch ( saveDlg.selectedType() )
788 {
790 {
791 // check if there is no format with same name
792 if ( style->textFormatNames().contains( saveDlg.name() ) )
793 {
794 const int res = QMessageBox::warning( this, tr( "Save Text Format" ),
795 tr( "Format with name '%1' already exists. Overwrite?" )
796 .arg( saveDlg.name() ),
797 QMessageBox::Yes | QMessageBox::No );
798 if ( res != QMessageBox::Yes )
799 {
800 return;
801 }
802 style->removeTextFormat( saveDlg.name() );
803 }
804 const QStringList symbolTags = saveDlg.tags().split( ',' );
805
806 const QgsTextFormat newFormat = format();
807 style->addTextFormat( saveDlg.name(), newFormat );
808 style->saveTextFormat( saveDlg.name(), newFormat, saveDlg.isFavorite(), symbolTags );
809 break;
810 }
811
813 {
814 // check if there is no settings with same name
815 if ( style->labelSettingsNames().contains( saveDlg.name() ) )
816 {
817 const int res = QMessageBox::warning( this, tr( "Save Label Settings" ),
818 tr( "Label settings with the name '%1' already exist. Overwrite?" )
819 .arg( saveDlg.name() ),
820 QMessageBox::Yes | QMessageBox::No );
821 if ( res != QMessageBox::Yes )
822 {
823 return;
824 }
825 style->removeLabelSettings( saveDlg.name() );
826 }
827 const QStringList symbolTags = saveDlg.tags().split( ',' );
828
829 const QgsPalLayerSettings newSettings = layerSettings();
830 style->addLabelSettings( saveDlg.name(), newSettings );
831 style->saveLabelSettings( saveDlg.name(), newSettings, saveDlg.isFavorite(), symbolTags );
832 break;
833 }
834
841 break;
842 }
843}
844
845void QgsLabelingGui::updateGeometryTypeBasedWidgets()
846{
847 Qgis::GeometryType geometryType = mGeomType;
848
849 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
850
851 if ( mGeometryGeneratorGroupBox->isChecked() )
852 geometryType = mGeometryGeneratorType->currentData().value<Qgis::GeometryType>();
853 else if ( vLayer )
854 geometryType = vLayer->geometryType();
855
856 // show/hide options based upon geometry type
857 chkMergeLines->setVisible( geometryType == Qgis::GeometryType::Line );
858 mDirectSymbolsFrame->setVisible( geometryType == Qgis::GeometryType::Line );
859 mMinSizeFrame->setVisible( geometryType != Qgis::GeometryType::Point );
860 mPolygonFeatureOptionsFrame->setVisible( geometryType == Qgis::GeometryType::Polygon );
861
862
863 const Qgis::LabelPlacement prevPlacement = static_cast< Qgis::LabelPlacement >( mPlacementModeComboBox->currentData().toInt() );
864 mPlacementModeComboBox->clear();
865
866 switch ( geometryType )
867 {
869 mPlacementModeComboBox->addItem( tr( "Cartographic" ), static_cast< int >( Qgis::LabelPlacement::OrderedPositionsAroundPoint ) );
870 mPlacementModeComboBox->addItem( tr( "Around Point" ), static_cast< int >( Qgis::LabelPlacement::AroundPoint ) );
871 mPlacementModeComboBox->addItem( tr( "Offset from Point" ), static_cast< int >( Qgis::LabelPlacement::OverPoint ) );
872 break;
873
875 mPlacementModeComboBox->addItem( tr( "Parallel" ), static_cast< int >( Qgis::LabelPlacement::Line ) );
876 mPlacementModeComboBox->addItem( tr( "Curved" ), static_cast< int >( Qgis::LabelPlacement::Curved ) );
877 mPlacementModeComboBox->addItem( tr( "Horizontal" ), static_cast< int >( Qgis::LabelPlacement::Horizontal ) );
878 break;
879
881 mPlacementModeComboBox->addItem( tr( "Offset from Centroid" ), static_cast< int >( Qgis::LabelPlacement::OverPoint ) );
882 mPlacementModeComboBox->addItem( tr( "Around Centroid" ), static_cast< int >( Qgis::LabelPlacement::AroundPoint ) );
883 mPlacementModeComboBox->addItem( tr( "Horizontal" ), static_cast< int >( Qgis::LabelPlacement::Horizontal ) );
884 mPlacementModeComboBox->addItem( tr( "Free (Angled)" ), static_cast< int >( Qgis::LabelPlacement::Free ) );
885 mPlacementModeComboBox->addItem( tr( "Using Perimeter" ), static_cast< int >( Qgis::LabelPlacement::Line ) );
886 mPlacementModeComboBox->addItem( tr( "Using Perimeter (Curved)" ), static_cast< int >( Qgis::LabelPlacement::PerimeterCurved ) );
887 mPlacementModeComboBox->addItem( tr( "Outside Polygons" ), static_cast< int >( Qgis::LabelPlacement::OutsidePolygons ) );
888 break;
889
891 break;
893 qFatal( "unknown geometry type unexpected" );
894 }
895
896 if ( mPlacementModeComboBox->findData( static_cast< int >( prevPlacement ) ) != -1 )
897 {
898 mPlacementModeComboBox->setCurrentIndex( mPlacementModeComboBox->findData( static_cast< int >( prevPlacement ) ) );
899 }
900
901 if ( geometryType == Qgis::GeometryType::Point || geometryType == Qgis::GeometryType::Polygon )
902 {
903 // follow placement alignment is only valid for point or polygon layers
904 if ( mFontMultiLineAlignComboBox->findData( static_cast< int >( Qgis::LabelMultiLineAlignment::FollowPlacement ) ) == -1 )
905 mFontMultiLineAlignComboBox->addItem( tr( "Follow Label Placement" ), static_cast< int >( Qgis::LabelMultiLineAlignment::FollowPlacement ) );
906 }
907 else
908 {
909 const int idx = mFontMultiLineAlignComboBox->findData( static_cast< int >( Qgis::LabelMultiLineAlignment::FollowPlacement ) );
910 if ( idx >= 0 )
911 mFontMultiLineAlignComboBox->removeItem( idx );
912 }
913
914 updatePlacementWidgets();
915 updateLinePlacementOptions();
916}
917
918void QgsLabelingGui::showGeometryGeneratorExpressionBuilder()
919{
920 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
921 QgsExpressionBuilderDialog expressionBuilder( vLayer );
922
923 expressionBuilder.setExpressionText( mGeometryGenerator->text() );
924 expressionBuilder.setExpressionContext( createExpressionContext() );
925
926 if ( expressionBuilder.exec() )
927 {
928 mGeometryGenerator->setText( expressionBuilder.expressionText() );
929 }
930}
931
932void QgsLabelingGui::validateGeometryGeneratorExpression()
933{
934 bool valid = true;
935
936 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
937
938 if ( mGeometryGeneratorGroupBox->isChecked() )
939 {
940 if ( !mPreviewFeature.isValid() && vLayer )
941 vLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( mPreviewFeature );
942
943 QgsExpression expression( mGeometryGenerator->text() );
944 QgsExpressionContext context = createExpressionContext();
945 context.setFeature( mPreviewFeature );
946
947 expression.prepare( &context );
948
949 if ( expression.hasParserError() )
950 {
951 mGeometryGeneratorWarningLabel->setText( expression.parserErrorString() );
952 valid = false;
953 }
954 else
955 {
956 const QVariant result = expression.evaluate( &context );
957 const QgsGeometry geometry = result.value<QgsGeometry>();
958 const Qgis::GeometryType configuredGeometryType = mGeometryGeneratorType->currentData().value<Qgis::GeometryType>();
959 if ( geometry.isNull() )
960 {
961 mGeometryGeneratorWarningLabel->setText( tr( "Result of the expression is not a geometry" ) );
962 valid = false;
963 }
964 else if ( geometry.type() != configuredGeometryType )
965 {
966 mGeometryGeneratorWarningLabel->setText( QStringLiteral( "<p>%1</p><p><a href=\"#determineGeometryGeneratorType\">%2</a></p>" ).arg(
967 tr( "Result of the expression does not match configured geometry type." ),
968 tr( "Change to %1" ).arg( QgsWkbTypes::geometryDisplayString( geometry.type() ) ) ) );
969 valid = false;
970 }
971 }
972 }
973
974 // The collapsible groupbox internally changes the visibility of this
975 // Work around by setting the visibility deferred in the next event loop cycle.
976 QTimer *timer = new QTimer();
977 connect( timer, &QTimer::timeout, this, [this, valid]()
978 {
979 mGeometryGeneratorWarningLabel->setVisible( !valid );
980 } );
981 connect( timer, &QTimer::timeout, timer, &QTimer::deleteLater );
982 timer->start( 0 );
983}
984
985void QgsLabelingGui::determineGeometryGeneratorType()
986{
987 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( mLayer );
988 if ( !mPreviewFeature.isValid() && vLayer )
989 vLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( mPreviewFeature );
990
991 QgsExpression expression( mGeometryGenerator->text() );
992 QgsExpressionContext context = createExpressionContext();
993 context.setFeature( mPreviewFeature );
994
995 expression.prepare( &context );
996 const QgsGeometry geometry = expression.evaluate( &context ).value<QgsGeometry>();
997
998 mGeometryGeneratorType->setCurrentIndex( mGeometryGeneratorType->findData( QVariant::fromValue( geometry.type() ) ) );
999}
1000
1001void QgsLabelingGui::calloutTypeChanged()
1002{
1003 const QString newCalloutType = mCalloutStyleComboBox->currentData().toString();
1004 QgsCalloutWidget *pew = qobject_cast< QgsCalloutWidget * >( mCalloutStackedWidget->currentWidget() );
1005 if ( pew )
1006 {
1007 if ( pew->callout() && pew->callout()->type() == newCalloutType )
1008 return;
1009 }
1010
1011 // get creation function for new callout from registry
1013 QgsCalloutAbstractMetadata *am = registry->calloutMetadata( newCalloutType );
1014 if ( !am ) // check whether the metadata is assigned
1015 return;
1016
1017 // change callout to a new one (with different type)
1018 // base new callout on existing callout's properties
1019 const std::unique_ptr< QgsCallout > newCallout( am->createCallout( pew && pew->callout() ? pew->callout()->properties( QgsReadWriteContext() ) : QVariantMap(), QgsReadWriteContext() ) );
1020 if ( !newCallout )
1021 return;
1022
1023 updateCalloutWidget( newCallout.get() );
1024 updatePreview();
1025}
1026
1027
1028//
1029// QgsLabelSettingsDialog
1030//
1031
1032QgsLabelSettingsDialog::QgsLabelSettingsDialog( const QgsPalLayerSettings &settings, QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, QWidget *parent,
1033 Qgis::GeometryType geomType )
1034 : QDialog( parent )
1035{
1036 QVBoxLayout *vLayout = new QVBoxLayout();
1037 mWidget = new QgsLabelingGui( layer, mapCanvas, settings, nullptr, geomType );
1038 vLayout->addWidget( mWidget );
1039 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok, Qt::Horizontal );
1040 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
1041 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
1042 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsLabelSettingsDialog::showHelp );
1043 vLayout->addWidget( mButtonBox );
1044 setLayout( vLayout );
1045 setWindowTitle( tr( "Label Settings" ) );
1046}
1047
1048QDialogButtonBox *QgsLabelSettingsDialog::buttonBox() const
1049{
1050 return mButtonBox;
1051}
1052
1053void QgsLabelSettingsDialog::showHelp()
1054{
1055 QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html" ) );
1056}
1057
1058
1059
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
AngleUnit
Units of angles.
Definition qgis.h:4802
@ SecondsOfArc
Seconds of arc.
@ Radians
Square kilometers.
@ Turn
Turn/revolutions.
@ MinutesOfArc
Minutes of arc.
@ MilliradiansSI
Angular milliradians (SI definition, 1/1000 of radian)
@ Degrees
Degrees.
@ Gon
Gon/gradian.
@ MilNATO
Angular mil (NATO definition, 6400 mil = 2PI radians)
LabelOffsetType
Behavior modifier for label offset and distance, only applies in some label placement modes.
Definition qgis.h:1172
LabelPrioritization
Label prioritization.
Definition qgis.h:1111
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
Definition qgis.h:1125
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ 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'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only.
@ AllowPlacementInsideOfPolygon
Labels can be placed inside a polygon feature.
@ AllowPlacementOutsideOfPolygon
Labels can be placed outside of a polygon feature.
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
Definition qgis.h:1221
QFlags< LabelPolygonPlacementFlag > LabelPolygonPlacementFlags
Polygon placement flags, which control how candidates are generated for a polygon feature.
Definition qgis.h:1243
LabelQuadrantPosition
Label quadrant positions.
Definition qgis.h:1186
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:337
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
LabelMultiLineAlignment
Text alignment for multi-line labels.
Definition qgis.h:1269
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
@ Vector
Vector layer.
@ Mesh
Mesh layer. Added in QGIS 3.2.
LabelOverlapHandling
Label overlap handling.
Definition qgis.h:1098
UpsideDownLabelHandling
Handling techniques for upside down labels.
Definition qgis.h:1254
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
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.
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.
Base class for widgets which allow control over the properties of callouts.
virtual QgsCallout * callout()=0
Returns the callout defined by the current settings in the widget.
void changed()
Should be emitted whenever configuration changes happened on this symbol layer configuration.
Abstract base class for callout renderers.
Definition qgscallout.h:54
void setEnabled(bool enabled)
Sets whether the callout is enabled.
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.
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 * meshExpressionScope(QgsMesh::ElementType elementType)
Creates a new scope which contains functions relating to mesh layer element elementType.
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").
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
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:58
A geometry is the spatial representation of a feature.
Qgis::GeometryType type
static void initCalloutWidgets()
Initializes callout widgets.
Definition qgsgui.cpp:497
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
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(Qgis::LabelLinePlacementFlags 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 setAnchorTextPoint(AnchorTextPoint point)
Sets the line anchor text point, which dictates which part of the label text should be placed at the ...
void setLeftDirectionSymbol(const QString &symbol)
Sets the string to use for left direction arrows.
AnchorTextPoint anchorTextPoint() const
Returns the line anchor text point, which dictates which part of the label text should be placed at t...
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 setOverrunDistanceUnit(const Qgis::RenderUnit &unit)
Sets the unit for label overrun distance.
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 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.
void setGeometryType(Qgis::GeometryType type) override
Sets the geometry type of the features to customize the widget accordingly.
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 setOverlapHandling(Qgis::LabelOverlapHandling handling)
Sets the technique used to handle overlapping labels.
void setPrioritization(Qgis::LabelPrioritization prioritization)
Sets the technique used to prioritize labels.
void setAllowDegradedPlacement(bool allow)
Sets whether labels can be placed in inferior fallback positions if they cannot otherwise be placed.
void setMaximumDistance(double distance)
Sets the maximum distance which labels are allowed to be from their corresponding points.
void setMaximumDistanceMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for label maximum distance.
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
void setMaximumDistanceUnit(Qgis::RenderUnit unit)
Sets the unit for label maximum distance.
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 ...
virtual void setGeometryType(Qgis::GeometryType type)
Sets the geometry type of the features to customize the widget accordingly.
void setDataDefinedProperties(const QgsPropertyCollection &dataDefinedProperties)
Sets the current data defined properties to show in the widget.
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.
Base class for all map layer types.
Definition qgsmaplayer.h:76
Qgis::LayerType type
Definition qgsmaplayer.h:86
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.
const QgsLabelPlacementSettings & placementSettings() const
Returns the label placement 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...
void setPolygonPlacementFlags(Qgis::LabelPolygonPlacementFlags flags)
Sets the polygon placement flags, which dictate how polygon labels can be placed.
QString wrapChar
Wrapping character string.
Qgis::LabelOffsetType offsetType
Offset type for layer (only applies in certain placement modes)
double xOffset
Horizontal offset of label.
Qgis::LabelPlacement placement
Label placement mode.
bool drawLabels
Whether to draw labels for this layer.
bool fontLimitPixelSize
true if label sizes should be limited by pixel size.
double minimumScale
The minimum map scale (i.e.
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...
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
bool centroidInside
true if centroid positioned labels must be placed inside their corresponding feature polygon,...
int priority
Label priority.
Qgis::GeometryType geometryGeneratorType
The type of the result geometry of the geometry generator.
bool labelPerPart
true if every part of a multi-part feature should be labeled.
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 QgsLabelThinningSettings & thinningSettings() const
Returns the label thinning settings.
Qgis::GeometryType layerType
Geometry type of layers associated with these settings.
Qgis::RenderUnit offsetUnits
Units for offsets of label.
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.
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
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.
void setRotationUnit(Qgis::AngleUnit angleUnit)
Set unit for rotation of labels.
QgsMapUnitScale repeatDistanceMapUnitScale
Map unit scale for repeating labels for a single feature.
Qgis::RenderUnit distUnits
Units the distance from feature to the label.
bool centroidWhole
true if feature centroid should be calculated from the whole feature, or false if only the visible pa...
Qgis::RenderUnit repeatDistanceUnit
Units for repeating labels for a single feature.
Qgis::UpsideDownLabelHandling 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.
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 QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
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.
QgsStyle * styleAtPath(const QString &path)
Returns a reference to the style database associated with the project with matching file path.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
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.
bool saveLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool favorite, const QStringList &tags)
Adds label settings to the database.
QStringList textFormatNames() const
Returns a list of names of text formats in the style.
bool removeTextFormat(const QString &name)
Removes a text format from the style.
StyleEntity
Enum for Entities involved in a style.
Definition qgsstyle.h:203
@ LabelSettingsEntity
Label settings.
Definition qgsstyle.h:209
@ TextFormatEntity
Text formats.
Definition qgsstyle.h:208
@ SmartgroupEntity
Smart groups.
Definition qgsstyle.h:207
@ Symbol3DEntity
3D symbol entity
Definition qgsstyle.h:211
@ SymbolEntity
Symbols.
Definition qgsstyle.h:204
@ TagEntity
Tags.
Definition qgsstyle.h:205
@ ColorrampEntity
Color ramps.
Definition qgsstyle.h:206
@ LegendPatchShapeEntity
Legend patch shape.
Definition qgsstyle.h:210
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:146
QStringList labelSettingsNames() const
Returns a list of names of label settings in the style.
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:370
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
bool saveTextFormat(const QString &name, const QgsTextFormat &format, bool favorite, const QStringList &tags)
Adds a text format to the database.
Definition qgsstyle.cpp:974
bool addLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool update=false)
Adds label settings with the specified name to the style.
Definition qgsstyle.cpp:391
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 setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the widget is shown, e.g., the associated map canvas and expression context...
virtual void setFormatFromStyle(const QString &name, QgsStyle::StyleEntity type, const QString &stylePath)
Sets the current text settings from a style entry.
Container for all settings relating to text rendering.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
@ Unknown
Unknown/invalid format.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5862