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