QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgscalloutwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscalloutwidget.cpp
3  ---------------------
4  begin : July 2019
5  copyright : (C) 2019 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgscalloutwidget.h"
17 #include "qgsvectorlayer.h"
19 #include "qgsunitselectionwidget.h"
20 #include "qgscallout.h"
23 #include "qgsauxiliarystorage.h"
24 
26 {
27  if ( auto *lExpressionContext = mContext.expressionContext() )
28  return *lExpressionContext;
29 
33  expContext << symbolScope;
34 
35  // additional scopes
36  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
37  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
38  {
39  expContext.appendScope( new QgsExpressionContextScope( scope ) );
40  }
41 
42  //TODO - show actual value
43  expContext.setOriginalValueVariable( QVariant() );
44 
46 
47  return expContext;
48 }
49 
51 {
52  mContext = context;
53  const auto unitSelectionWidgets = findChildren<QgsUnitSelectionWidget *>();
54  for ( QgsUnitSelectionWidget *unitWidget : unitSelectionWidgets )
55  {
56  unitWidget->setMapCanvas( mContext.mapCanvas() );
57  }
58  const auto symbolButtonWidgets = findChildren<QgsSymbolButton *>();
59  for ( QgsSymbolButton *symbolWidget : symbolButtonWidgets )
60  {
61  symbolWidget->setMapCanvas( mContext.mapCanvas() );
62  symbolWidget->setMessageBar( mContext.messageBar() );
63  }
64 }
65 
67 {
68  return mContext;
69 }
70 
72 {
73  button->init( key, callout()->dataDefinedProperties(), QgsCallout::propertyDefinitions(), mVectorLayer, true );
74  connect( button, &QgsPropertyOverrideButton::changed, this, &QgsCalloutWidget::updateDataDefinedProperty );
75  connect( button, &QgsPropertyOverrideButton::createAuxiliaryField, this, &QgsCalloutWidget::createAuxiliaryField );
76 
77  button->registerExpressionContextGenerator( this );
78 }
79 
80 void QgsCalloutWidget::createAuxiliaryField()
81 {
82  // try to create an auxiliary layer if not yet created
83  if ( !mVectorLayer->auxiliaryLayer() )
84  {
85  QgsNewAuxiliaryLayerDialog dlg( mVectorLayer, this );
86  dlg.exec();
87  }
88 
89  // return if still not exists
90  if ( !mVectorLayer->auxiliaryLayer() )
91  return;
92 
93  QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
94  QgsCallout::Property key = static_cast< QgsCallout::Property >( button->propertyKey() );
96 
97  // create property in auxiliary storage if necessary
98  if ( !mVectorLayer->auxiliaryLayer()->exists( def ) )
99  {
100  QgsNewAuxiliaryFieldDialog dlg( def, mVectorLayer, true, this );
101  if ( dlg.exec() == QDialog::Accepted )
102  def = dlg.propertyDefinition();
103  }
104 
105  // return if still not exist
106  if ( !mVectorLayer->auxiliaryLayer()->exists( def ) )
107  return;
108 
109  // update property with join field name from auxiliary storage
110  QgsProperty property = button->toProperty();
111  property.setField( QgsAuxiliaryLayer::nameFromProperty( def, true ) );
112  property.setActive( true );
113  button->updateFieldLists();
114  button->setToProperty( property );
115 
116  callout()->dataDefinedProperties().setProperty( key, button->toProperty() );
117 
118  emit changed();
119 }
120 
121 void QgsCalloutWidget::updateDataDefinedProperty()
122 {
123  QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
124  QgsCallout::Property key = static_cast< QgsCallout::Property >( button->propertyKey() );
125  callout()->dataDefinedProperties().setProperty( key, button->toProperty() );
126  emit changed();
127 }
128 
130 
131 //
132 // QgsSimpleLineCalloutWidget
133 //
134 
135 QgsSimpleLineCalloutWidget::QgsSimpleLineCalloutWidget( QgsVectorLayer *vl, QWidget *parent )
136  : QgsCalloutWidget( parent, vl )
137 {
138  setupUi( this );
139 
140  // Callout options - to move to custom widgets when multiple callout styles exist
141  mCalloutLineStyleButton->setSymbolType( QgsSymbol::Line );
142  mCalloutLineStyleButton->setDialogTitle( tr( "Callout Symbol" ) );
143  mCalloutLineStyleButton->registerExpressionContextGenerator( this );
144 
145  mCalloutLineStyleButton->setLayer( vl );
152 
153  connect( mMinCalloutWidthUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsSimpleLineCalloutWidget::minimumLengthUnitWidgetChanged );
154  connect( mMinCalloutLengthSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSimpleLineCalloutWidget::minimumLengthChanged );
155 
156  connect( mOffsetFromAnchorUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsSimpleLineCalloutWidget::offsetFromAnchorUnitWidgetChanged );
157  connect( mOffsetFromAnchorSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSimpleLineCalloutWidget::offsetFromAnchorChanged );
158  connect( mOffsetFromLabelUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsSimpleLineCalloutWidget::offsetFromLabelUnitWidgetChanged );
159  connect( mOffsetFromLabelSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSimpleLineCalloutWidget::offsetFromLabelChanged );
160 
161  connect( mDrawToAllPartsCheck, &QCheckBox::toggled, this, &QgsSimpleLineCalloutWidget::drawToAllPartsToggled );
162 
163  // Anchor point options
164  mAnchorPointComboBox->addItem( tr( "Pole of Inaccessibility" ), static_cast< int >( QgsCallout::PoleOfInaccessibility ) );
165  mAnchorPointComboBox->addItem( tr( "Point on Exterior" ), static_cast< int >( QgsCallout::PointOnExterior ) );
166  mAnchorPointComboBox->addItem( tr( "Point on Surface" ), static_cast< int >( QgsCallout::PointOnSurface ) );
167  mAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::Centroid ) );
168  connect( mAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged );
169 
170  mLabelAnchorPointComboBox->addItem( tr( "Closest Point" ), static_cast< int >( QgsCallout::LabelPointOnExterior ) );
171  mLabelAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::LabelCentroid ) );
172  mLabelAnchorPointComboBox->addItem( tr( "Top Left" ), static_cast< int >( QgsCallout::LabelTopLeft ) );
173  mLabelAnchorPointComboBox->addItem( tr( "Top Center" ), static_cast< int >( QgsCallout::LabelTopMiddle ) );
174  mLabelAnchorPointComboBox->addItem( tr( "Top Right" ), static_cast< int >( QgsCallout::LabelTopRight ) );
175  mLabelAnchorPointComboBox->addItem( tr( "Left Middle" ), static_cast< int >( QgsCallout::LabelMiddleLeft ) );
176  mLabelAnchorPointComboBox->addItem( tr( "Right Middle" ), static_cast< int >( QgsCallout::LabelMiddleRight ) );
177  mLabelAnchorPointComboBox->addItem( tr( "Bottom Left" ), static_cast< int >( QgsCallout::LabelBottomLeft ) );
178  mLabelAnchorPointComboBox->addItem( tr( "Bottom Center" ), static_cast< int >( QgsCallout::LabelBottomMiddle ) );
179  mLabelAnchorPointComboBox->addItem( tr( "Bottom Right" ), static_cast< int >( QgsCallout::LabelBottomRight ) );
180  connect( mLabelAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged );
181 
182  connect( mCalloutLineStyleButton, &QgsSymbolButton::changed, this, &QgsSimpleLineCalloutWidget::lineSymbolChanged );
183 }
184 
185 void QgsSimpleLineCalloutWidget::setCallout( QgsCallout *callout )
186 {
187  if ( !callout )
188  return;
189 
190  mCallout.reset( dynamic_cast<QgsSimpleLineCallout *>( callout->clone() ) );
191  if ( !mCallout )
192  return;
193 
194  mMinCalloutWidthUnitWidget->blockSignals( true );
195  mMinCalloutWidthUnitWidget->setUnit( mCallout->minimumLengthUnit() );
196  mMinCalloutWidthUnitWidget->setMapUnitScale( mCallout->minimumLengthMapUnitScale() );
197  mMinCalloutWidthUnitWidget->blockSignals( false );
198 
199  whileBlocking( mMinCalloutLengthSpin )->setValue( mCallout->minimumLength() );
200 
201  mOffsetFromAnchorUnitWidget->blockSignals( true );
202  mOffsetFromAnchorUnitWidget->setUnit( mCallout->offsetFromAnchorUnit() );
203  mOffsetFromAnchorUnitWidget->setMapUnitScale( mCallout->offsetFromAnchorMapUnitScale() );
204  mOffsetFromAnchorUnitWidget->blockSignals( false );
205  mOffsetFromLabelUnitWidget->blockSignals( true );
206  mOffsetFromLabelUnitWidget->setUnit( mCallout->offsetFromLabelUnit() );
207  mOffsetFromLabelUnitWidget->setMapUnitScale( mCallout->offsetFromLabelMapUnitScale() );
208  mOffsetFromLabelUnitWidget->blockSignals( false );
209  whileBlocking( mOffsetFromAnchorSpin )->setValue( mCallout->offsetFromAnchor() );
210  whileBlocking( mOffsetFromLabelSpin )->setValue( mCallout->offsetFromLabel() );
211 
212  whileBlocking( mCalloutLineStyleButton )->setSymbol( mCallout->lineSymbol()->clone() );
213 
214  whileBlocking( mDrawToAllPartsCheck )->setChecked( mCallout->drawCalloutToAllParts() );
215 
216  whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
217  whileBlocking( mLabelAnchorPointComboBox )->setCurrentIndex( mLabelAnchorPointComboBox->findData( static_cast< int >( callout->labelAnchorPoint() ) ) );
218 
219  registerDataDefinedButton( mMinCalloutLengthDDBtn, QgsCallout::MinimumCalloutLength );
220  registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
221  registerDataDefinedButton( mOffsetFromLabelDDBtn, QgsCallout::OffsetFromLabel );
222  registerDataDefinedButton( mDrawToAllPartsDDBtn, QgsCallout::DrawCalloutToAllParts );
223  registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
224  registerDataDefinedButton( mLabelAnchorPointDDBtn, QgsCallout::LabelAnchorPointPosition );
225 }
226 
227 void QgsSimpleLineCalloutWidget::setGeometryType( QgsWkbTypes::GeometryType type )
228 {
229  bool isPolygon = type == QgsWkbTypes::PolygonGeometry;
230  mAnchorPointLbl->setEnabled( isPolygon );
231  mAnchorPointLbl->setVisible( isPolygon );
232  mAnchorPointComboBox->setEnabled( isPolygon );
233  mAnchorPointComboBox->setVisible( isPolygon );
234  mAnchorPointDDBtn->setEnabled( isPolygon );
235  mAnchorPointDDBtn->setVisible( isPolygon );
236 }
237 
238 QgsCallout *QgsSimpleLineCalloutWidget::callout()
239 {
240  return mCallout.get();
241 }
242 
243 void QgsSimpleLineCalloutWidget::minimumLengthChanged()
244 {
245  mCallout->setMinimumLength( mMinCalloutLengthSpin->value() );
246  emit changed();
247 }
248 
249 void QgsSimpleLineCalloutWidget::minimumLengthUnitWidgetChanged()
250 {
251  mCallout->setMinimumLengthUnit( mMinCalloutWidthUnitWidget->unit() );
252  mCallout->setMinimumLengthMapUnitScale( mMinCalloutWidthUnitWidget->getMapUnitScale() );
253  emit changed();
254 }
255 
256 void QgsSimpleLineCalloutWidget::offsetFromAnchorUnitWidgetChanged()
257 {
258  mCallout->setOffsetFromAnchorUnit( mOffsetFromAnchorUnitWidget->unit() );
259  mCallout->setOffsetFromAnchorMapUnitScale( mOffsetFromAnchorUnitWidget->getMapUnitScale() );
260  emit changed();
261 }
262 
263 void QgsSimpleLineCalloutWidget::offsetFromAnchorChanged()
264 {
265  mCallout->setOffsetFromAnchor( mOffsetFromAnchorSpin->value() );
266  emit changed();
267 }
268 
269 void QgsSimpleLineCalloutWidget::offsetFromLabelUnitWidgetChanged()
270 {
271  mCallout->setOffsetFromLabelUnit( mOffsetFromLabelUnitWidget->unit() );
272  mCallout->setOffsetFromLabelMapUnitScale( mOffsetFromLabelUnitWidget->getMapUnitScale() );
273  emit changed();
274 }
275 
276 void QgsSimpleLineCalloutWidget::offsetFromLabelChanged()
277 {
278  mCallout->setOffsetFromLabel( mOffsetFromLabelSpin->value() );
279  emit changed();
280 }
281 
282 void QgsSimpleLineCalloutWidget::lineSymbolChanged()
283 {
284  mCallout->setLineSymbol( mCalloutLineStyleButton->clonedSymbol< QgsLineSymbol >() );
285  emit changed();
286 }
287 
288 void QgsSimpleLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged( int index )
289 {
290  mCallout->setAnchorPoint( static_cast<QgsCallout::AnchorPoint>( mAnchorPointComboBox->itemData( index ).toInt() ) );
291  emit changed();
292 }
293 
294 void QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged( int index )
295 {
296  mCallout->setLabelAnchorPoint( static_cast<QgsCallout::LabelAnchorPoint>( mLabelAnchorPointComboBox->itemData( index ).toInt() ) );
297  emit changed();
298 }
299 
300 void QgsSimpleLineCalloutWidget::drawToAllPartsToggled( bool active )
301 {
302  mCallout->setDrawCalloutToAllParts( active );
303  emit changed();
304 }
305 
306 
307 //
308 // QgsManhattanLineCalloutWidget
309 //
310 
311 QgsManhattanLineCalloutWidget::QgsManhattanLineCalloutWidget( QgsVectorLayer *vl, QWidget *parent )
312  : QgsSimpleLineCalloutWidget( vl, parent )
313 {
314 
315 }
316 
317 
319 
static QString nameFromProperty(const QgsPropertyDefinition &def, bool joined=false)
Returns the name of the auxiliary field for a property definition.
bool exists(const QgsPropertyDefinition &definition) const
Returns true if the property is stored in the layer already, false otherwise.
Base class for widgets which allow control over the properties of callouts.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
void changed()
Should be emitted whenever configuration changes happened on this symbol layer configuration.
void registerDataDefinedButton(QgsPropertyOverrideButton *button, QgsCallout::Property key)
Registers a data defined override button.
virtual QgsCallout * callout()=0
Returns the callout defined by the current settings in the widget.
Abstract base class for callout renderers.
Definition: qgscallout.h:47
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
Definition: qgscallout.cpp:150
AnchorPoint anchorPoint() const
Returns the feature's anchor point position.
Definition: qgscallout.h:289
LabelAnchorPoint labelAnchorPoint() const
Returns the label's anchor point position.
Definition: qgscallout.h:322
AnchorPoint
Feature's anchor point position.
Definition: qgscallout.h:88
@ PointOnExterior
A point on the surface's outline closest to the label is used as anchor for polygon geometries.
Definition: qgscallout.h:90
@ Centroid
The surface's centroid is used as anchor for polygon geometries.
Definition: qgscallout.h:92
@ PointOnSurface
A point guaranteed to be on the surface is used as anchor for polygon geometries.
Definition: qgscallout.h:91
@ PoleOfInaccessibility
The surface's pole of inaccessibility used as anchor for polygon geometries.
Definition: qgscallout.h:89
virtual QgsCallout * clone() const =0
Duplicates a callout by creating a deep copy of the callout.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout's property collection, used for data defined overrides.
Definition: qgscallout.h:259
LabelAnchorPoint
Label's anchor point position.
Definition: qgscallout.h:100
@ LabelPointOnExterior
The point on the label's boundary closest to the feature.
Definition: qgscallout.h:101
@ LabelBottomLeft
Bottom left corner of the label's boundary.
Definition: qgscallout.h:108
@ LabelBottomMiddle
Bottom middle of the label's boundary.
Definition: qgscallout.h:109
@ LabelMiddleLeft
Middle left of the label's boundary.
Definition: qgscallout.h:106
@ LabelBottomRight
Bottom right corner of the label's boundary.
Definition: qgscallout.h:110
@ LabelMiddleRight
Middle right of the label's boundary.
Definition: qgscallout.h:107
@ LabelTopMiddle
Top middle of the label's boundary.
Definition: qgscallout.h:104
@ LabelTopLeft
Top left corner of the label's boundary.
Definition: qgscallout.h:103
@ LabelCentroid
The labe's centroid.
Definition: qgscallout.h:102
@ LabelTopRight
Top right corner of the label's boundary.
Definition: qgscallout.h:105
Property
Data definable properties.
Definition: qgscallout.h:70
@ AnchorPointPosition
Feature's anchor point position.
Definition: qgscallout.h:75
@ LabelAnchorPointPosition
Label's anchor point position.
Definition: qgscallout.h:76
@ DrawCalloutToAllParts
Whether callout lines should be drawn to all feature parts.
Definition: qgscallout.h:74
@ OffsetFromAnchor
Distance to offset lines from anchor points.
Definition: qgscallout.h:72
@ OffsetFromLabel
Distance to offset lines from label area.
Definition: qgscallout.h:73
@ MinimumCalloutLength
Minimum length of callouts.
Definition: qgscallout.h:71
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QgsExpressionContextScope * updateSymbolScope(const QgsSymbol *symbol, QgsExpressionContextScope *symbolScope=nullptr)
Updates a symbol scope related to a QgsSymbol to an expression 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 appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of 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.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1204
A dialog to create a new auxiliary field.
A dialog to create a new auxiliary layer.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
Definition for a property.
Definition: qgsproperty.h:48
A button for controlling property overrides which may apply to a widget.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void updateFieldLists()
Updates list of fields.
void changed()
Emitted when property definition changes.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout).
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
int propertyKey() const
Returns the property key linked to the button.
void setToProperty(const QgsProperty &property)
Sets the widget to reflect the current state of a QgsProperty.
void createAuxiliaryField()
Emitted when creating a new auxiliary field.
A store for object properties.
Definition: qgsproperty.h:232
void setField(const QString &field)
Sets the field name the property references.
A simple direct line callout style.
Definition: qgscallout.h:402
A button for creating and modifying QgsSymbol settings.
void changed()
Emitted when the symbol's settings are changed.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer.
QList< QgsExpressionContextScope * > globalProjectAtlasMapLayerScopes(const QgsMapLayer *layer) const
Returns list of scopes: global, project, atlas, map, layer.
QgsExpressionContext * expressionContext() const
Returns the expression context used for the widget, if set.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
@ Line
Line symbol.
Definition: qgssymbol.h:89
A widget displaying a combobox allowing the user to choose between various display units,...
QList< QgsUnitTypes::RenderUnit > RenderUnitList
List of render units.
Definition: qgsunittypes.h:239
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:175
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:172
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:170
@ RenderInches
Inches.
Definition: qgsunittypes.h:173
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:169
Represents a vector layer which manages a vector based data sets.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:263
Single variable definition for use within a QgsExpressionContextScope.