QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgssymbolselectordialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssymbolselectordialog.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk 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
17
19#include "qgsstyle.h"
20#include "qgssymbol.h"
21#include "qgssymbollayer.h"
23#include "qgssymbollayerutils.h"
24
25#include "moc_qgssymbolselectordialog.cpp"
26
27// the widgets
30#include "qgsapplication.h"
31#include "qgsvectorlayer.h"
32#include "qgssvgcache.h"
33#include "qgsimagecache.h"
34#include "qgsproject.h"
35#include "qgsguiutils.h"
36#include "qgsgui.h"
37#include "qgsmarkersymbol.h"
38#include "qgslinesymbol.h"
39#include "qscreen.h"
40
41#include <QColorDialog>
42#include <QPainter>
43#include <QStandardItemModel>
44#include <QInputDialog>
45#include <QMessageBox>
46#include <QKeyEvent>
47#include <QMenu>
48
49#include <QWidget>
50#include <QFile>
51#include <QStandardItem>
52#include <memory>
53
55
56static const int SYMBOL_LAYER_ITEM_TYPE = QStandardItem::UserType + 1;
57
58DataDefinedRestorer::DataDefinedRestorer( QgsSymbol *symbol, const QgsSymbolLayer *symbolLayer )
59
60{
61 if ( symbolLayer->type() == Qgis::SymbolType::Marker && symbol->type() == Qgis::SymbolType::Marker )
62 {
63 Q_ASSERT( symbol->type() == Qgis::SymbolType::Marker );
64 mMarker = static_cast<QgsMarkerSymbol *>( symbol );
65 mMarkerSymbolLayer = static_cast<const QgsMarkerSymbolLayer *>( symbolLayer );
66 mDDSize = mMarker->dataDefinedSize();
67 mDDAngle = mMarker->dataDefinedAngle();
68 // check if restore is actually needed
69 if ( !mDDSize && !mDDAngle )
70 mMarker = nullptr;
71 }
72 else if ( symbolLayer->type() == Qgis::SymbolType::Line && symbol->type() == Qgis::SymbolType::Line )
73 {
74 mLine = static_cast<QgsLineSymbol *>( symbol );
75 mLineSymbolLayer = static_cast<const QgsLineSymbolLayer *>( symbolLayer );
76 mDDWidth = mLine->dataDefinedWidth();
77 // check if restore is actually needed
78 if ( !mDDWidth )
79 mLine = nullptr;
80 }
81 save();
82}
83
84void DataDefinedRestorer::save()
85{
86 if ( mMarker )
87 {
88 mSize = mMarkerSymbolLayer->size();
89 mAngle = mMarkerSymbolLayer->angle();
90 mMarkerOffset = mMarkerSymbolLayer->offset();
91 }
92 else if ( mLine )
93 {
94 mWidth = mLineSymbolLayer->width();
95 mLineOffset = mLineSymbolLayer->offset();
96 }
97}
98
99void DataDefinedRestorer::restore()
100{
101 if ( mMarker )
102 {
103 if ( mDDSize && ( mSize != mMarkerSymbolLayer->size() || mMarkerOffset != mMarkerSymbolLayer->offset() ) )
104 mMarker->setDataDefinedSize( mDDSize );
105 if ( mDDAngle && mAngle != mMarkerSymbolLayer->angle() )
106 mMarker->setDataDefinedAngle( mDDAngle );
107 }
108 else if ( mLine )
109 {
110 if ( mDDWidth && ( mWidth != mLineSymbolLayer->width() || mLineOffset != mLineSymbolLayer->offset() ) )
111 mLine->setDataDefinedWidth( mDDWidth );
112 }
113 save();
114}
115
116// Hybrid item which may represent a symbol or a layer
117// Check using item->isLayer()
118class SymbolLayerItem : public QStandardItem
119{
120 public:
121 explicit SymbolLayerItem( QgsSymbolLayer *layer, Qgis::SymbolType symbolType, QgsVectorLayer *vectorLayer, QScreen *screen )
122 : mVectorLayer( vectorLayer )
123 , mScreen( screen )
124 {
125 setLayer( layer, symbolType );
126 }
127
128 explicit SymbolLayerItem( QgsSymbol *symbol, QgsVectorLayer *vectorLayer, QScreen *screen )
129 : mVectorLayer( vectorLayer )
130 , mScreen( screen )
131 {
132 setSymbol( symbol );
133 }
134
135 void setLayer( QgsSymbolLayer *layer, Qgis::SymbolType symbolType )
136 {
137 mLayer = layer;
138 mIsLayer = true;
139 mSymbol = nullptr;
140 mSymbolType = symbolType;
141 updatePreview();
142 }
143
144 void setSymbol( QgsSymbol *symbol )
145 {
146 mSymbol = symbol;
147 mIsLayer = false;
148 mLayer = nullptr;
149 updatePreview();
150 }
151
152 void updatePreview()
153 {
154 if ( !mSize.isValid() )
155 {
156 const int size = QgsGuiUtils::scaleIconSize( 16 );
157 mSize = QSize( size, size );
158 }
159 QIcon icon;
160 if ( mIsLayer )
161 icon = QgsSymbolLayerUtils::symbolLayerPreviewIcon( mLayer, Qgis::RenderUnit::Millimeters, mSize, QgsMapUnitScale(), mSymbol ? mSymbol->type() : mSymbolType, mVectorLayer, QgsScreenProperties( mScreen.data() ) );
162 else
163 {
164 QgsExpressionContext expContext;
166 icon = QIcon( QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol, mSize, 0, nullptr, false, &expContext, nullptr, QgsScreenProperties( mScreen.data() ) ) );
167 }
168 setIcon( icon );
169
170 if ( auto *lParent = parent() )
171 static_cast<SymbolLayerItem *>( lParent )->updatePreview();
172 }
173
174 int type() const override { return SYMBOL_LAYER_ITEM_TYPE; }
175 bool isLayer() const { return mIsLayer; }
176
177 // returns the symbol pointer; helpful in determining a layer's parent symbol
178 QgsSymbol *symbol()
179 {
180 return mSymbol;
181 }
182
183 QgsSymbolLayer *layer()
184 {
185 return mLayer;
186 }
187
188 QVariant data( int role ) const override
189 {
190 if ( role == Qt::DisplayRole || role == Qt::EditRole )
191 {
192 if ( mIsLayer )
193 {
194 QgsSymbolLayerAbstractMetadata *m = QgsApplication::symbolLayerRegistry()->symbolLayerMetadata( mLayer->layerType() );
195 if ( m )
196 return m->visibleName();
197 else
198 return QString();
199 }
200 else
201 {
202 switch ( mSymbol->type() )
203 {
205 return QCoreApplication::translate( "SymbolLayerItem", "Marker" );
207 return QCoreApplication::translate( "SymbolLayerItem", "Fill" );
209 return QCoreApplication::translate( "SymbolLayerItem", "Line" );
210 default:
211 return "Symbol";
212 }
213 }
214 }
215 else if ( role == Qt::ForegroundRole && mIsLayer )
216 {
217 if ( !mLayer->enabled() )
218 {
219 QPalette pal = qApp->palette();
220 QBrush brush = QStandardItem::data( role ).value<QBrush>();
221 brush.setColor( pal.color( QPalette::Disabled, QPalette::WindowText ) );
222 return brush;
223 }
224 else
225 {
226 return QVariant();
227 }
228 }
229
230 // if ( role == Qt::SizeHintRole )
231 // return QVariant( QSize( 32, 32 ) );
232 if ( role == Qt::CheckStateRole )
233 return QVariant(); // could be true/false
234 return QStandardItem::data( role );
235 }
236
237 protected:
238 QgsSymbolLayer *mLayer = nullptr;
239 QgsSymbol *mSymbol = nullptr;
240 QPointer<QgsVectorLayer> mVectorLayer;
241 bool mIsLayer = false;
242 QSize mSize;
244 QPointer<QScreen> mScreen;
245};
246
248
250
252 : QgsPanelWidget( parent )
253 , mStyle( style )
254 , mSymbol( symbol )
255 , mVectorLayer( vl )
256{
257#ifdef Q_OS_MAC
258 setWindowModality( Qt::WindowModal );
259#endif
260
261 setupUi( this );
262 this->layout()->setContentsMargins( 0, 0, 0, 0 );
263
264 layersTree->setMaximumHeight( static_cast<int>( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
265 layersTree->setMinimumHeight( layersTree->maximumHeight() );
266 lblPreview->setMaximumWidth( layersTree->maximumHeight() );
267
268 // setup icons
269 btnAddLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
270 btnRemoveLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
271 QIcon iconLock;
272 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
273 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
274 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
275 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
276
277 QIcon iconColorLock;
278 iconColorLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconColorLocked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
279 iconColorLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconColorLocked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
280 iconColorLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconColorUnlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
281 iconColorLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconColorUnlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
282
283 mLockColorAction = new QAction( tr( "Lock Color" ), this );
284 mLockColorAction->setToolTip( tr( "Avoid changing the color of the layer when the symbol color is changed" ) );
285 mLockColorAction->setCheckable( true );
286 mLockColorAction->setIcon( iconColorLock );
287
288 QIcon iconSelectLock;
289 iconSelectLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconSelectLocked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
290 iconSelectLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconSelectLocked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
291 iconSelectLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconSelectUnlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
292 iconSelectLock.addFile( QgsApplication::iconPath( QStringLiteral( "mIconSelectUnlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
293
294 mLockSelectionColorAction = new QAction( tr( "Lock Color When Selected" ), this );
295 mLockSelectionColorAction->setToolTip( tr( "Avoid changing the color of the layer when a feature is selected" ) );
296 mLockSelectionColorAction->setCheckable( true );
297 mLockSelectionColorAction->setIcon( iconSelectLock );
298
299 QMenu *lockMenu = new QMenu( this );
300 lockMenu->addAction( mLockColorAction );
301 lockMenu->addAction( mLockSelectionColorAction );
302 btnLock->setMenu( lockMenu );
303 btnLock->setPopupMode( QToolButton::InstantPopup );
304
305 btnDuplicate->setIcon( QIcon( QgsApplication::iconPath( "mActionDuplicateLayer.svg" ) ) );
306 btnUp->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
307 btnDown->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
308
309 mSymbolLayersModel = new QStandardItemModel( layersTree );
310 // Set the symbol
311 layersTree->setModel( mSymbolLayersModel );
312 layersTree->setHeaderHidden( true );
313
314 //get first feature from layer for previews
315 if ( mVectorLayer )
316 {
317#if 0 // this is too expensive to do for many providers. TODO revisit when support for connection timeouts is complete across all providers
318 // short timeout for request - it doesn't really matter if we don't get the feature, and this call is blocking UI
319 QgsFeatureIterator it = mVectorLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ).setConnectionTimeout( 100 ) );
320 it.nextFeature( mPreviewFeature );
321#endif
322 mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
323#if 0
324 mPreviewExpressionContext.setFeature( mPreviewFeature );
325#endif
326 }
327 else
328 {
329 mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
330 }
331
332 QItemSelectionModel *selModel = layersTree->selectionModel();
333 connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
334
335 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
337
338 connect( btnUp, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerUp );
339 connect( btnDown, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerDown );
340 connect( btnAddLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::addLayer );
341 connect( btnRemoveLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::removeLayer );
342 connect( mLockColorAction, &QAction::toggled, this, &QgsSymbolSelectorWidget::lockLayer );
343 connect( mLockSelectionColorAction, &QAction::toggled, this, [this]( bool checked ) {
344 QgsSymbolLayer *layer = currentLayer();
345 if ( !layer )
346 return;
347
348 Qgis::SymbolLayerUserFlags flags = layer->userFlags();
350 layer->setUserFlags( flags );
351 updateLockButtonIcon();
352 emit symbolModified();
353 } );
354 connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
356
357 updateLockButtonIcon();
358
359 updateUi();
360
361 // set symbol as active item in the tree
362 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
363 layersTree->setCurrentIndex( newIndex );
364
365 setPanelTitle( tr( "Symbol Selector" ) );
366
367 // when a remote svg has been fetched, update the widget's previews
368 // this is required if the symbol utilizes remote svgs, and the current previews
369 // have been generated using the temporary "downloading" svg. In this case
370 // we require the preview to be regenerated to use the correct fetched
371 // svg
372 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
373
374 // when a remote image has been fetched, update the widget's previews
375 // this is required if the symbol utilizes remote images, and the current previews
376 // have been generated using the temporary "downloading" image. In this case
377 // we require the preview to be regenerated to use the correct fetched
378 // image
379 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
380
381 // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
382 // need updating to reflect the new colors
383 connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
384
385 connect( QgsProject::instance(), static_cast<void ( QgsProject::* )( const QList<QgsMapLayer *> &layers )>( &QgsProject::layersWillBeRemoved ), this, &QgsSymbolSelectorWidget::layersAboutToBeRemoved );
386}
387
389{
390 QgsSymbolSelectorWidget *widget = new QgsSymbolSelectorWidget( symbol.get(), style, vl, parent );
391 // transfer ownership of symbol to widget, so that we are guaranteed it will last for the duration of the widget
392 widget->mOwnedSymbol = std::move( symbol );
393 return widget;
394}
395
397{
398 if ( !mAdvancedMenu )
399 {
400 mAdvancedMenu = new QMenu( this );
401 // Brute force method to activate the Advanced menu
402 layerChanged();
403 }
404 return mAdvancedMenu;
405}
406
408{
409 mContext = context;
410
411 if ( auto *lExpressionContext = mContext.expressionContext() )
412 {
413 mPreviewExpressionContext = *lExpressionContext;
414 if ( mVectorLayer )
415 mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
416
417 mPreviewExpressionContext.setFeature( mPreviewFeature );
418 }
419
420 QWidget *widget = stackedWidget->currentWidget();
421 if ( QgsLayerPropertiesWidget *layerProp = qobject_cast<QgsLayerPropertiesWidget *>( widget ) )
422 {
423 layerProp->setContext( context );
424 }
425 else if ( QgsSymbolsListWidget *listWidget = qobject_cast<QgsSymbolsListWidget *>( widget ) )
426 {
427 listWidget->setContext( context );
428 }
429
430 layerChanged();
432}
433
435{
436 return mContext;
437}
438
439void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
440{
441 if ( !symbol )
442 return;
443
444 if ( !parent )
445 {
446 mSymbol = symbol;
447 mSymbolLayersModel->clear();
448 parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
449 }
450
451 SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol, mVectorLayer, screen() );
452 QFont boldFont = symbolItem->font();
453 boldFont.setBold( true );
454 symbolItem->setFont( boldFont );
455 parent->appendRow( symbolItem );
456
457 const int count = symbol->symbolLayerCount();
458 for ( int i = count - 1; i >= 0; i-- )
459 {
460 SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ), symbol->type(), mVectorLayer, screen() );
461 layerItem->setEditable( false );
462 symbolItem->appendRow( layerItem );
463 if ( symbol->symbolLayer( i )->subSymbol() )
464 {
465 loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
466 }
467 layersTree->setExpanded( layerItem->index(), true );
468 }
469 layersTree->setExpanded( symbolItem->index(), true );
470
471 if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
472 {
473 // make sure root item for symbol is selected in tree
474 layersTree->setCurrentIndex( symbolItem->index() );
475 }
476}
477
478void QgsSymbolSelectorWidget::reloadSymbol()
479{
480 mSymbolLayersModel->clear();
481 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
482}
483
484void QgsSymbolSelectorWidget::updateUi()
485{
486 const QModelIndex currentIdx = layersTree->currentIndex();
487 if ( !currentIdx.isValid() )
488 return;
489
490 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
491 if ( !item->isLayer() )
492 {
493 btnUp->setEnabled( false );
494 btnDown->setEnabled( false );
495 btnRemoveLayer->setEnabled( false );
496 btnLock->setEnabled( false );
497 btnDuplicate->setEnabled( false );
498 return;
499 }
500
501 const int rowCount = item->parent()->rowCount();
502 const int currentRow = item->row();
503
504 btnUp->setEnabled( currentRow > 0 );
505 btnDown->setEnabled( currentRow < rowCount - 1 );
506 btnRemoveLayer->setEnabled( rowCount > 1 );
507 btnLock->setEnabled( true );
508 btnDuplicate->setEnabled( true );
509}
510
512{
513 if ( !mSymbol )
514 return;
515
516 std::unique_ptr<QgsSymbol> symbolClone( mSymbol->clone() );
517 const QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlag::FlagIncludeCrosshairsForMarkerSymbols, QgsScreenProperties( screen() ) );
518 lblPreview->setPixmap( QPixmap::fromImage( preview ) );
519 // Hope this is a appropriate place
520 if ( !mBlockModified )
521 emit symbolModified();
522}
523
525{
526 // get current layer item and update its icon
527 SymbolLayerItem *item = currentLayerItem();
528 if ( item )
529 item->updatePreview();
530 // update also preview of the whole symbol
532}
533
534SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
535{
536 const QModelIndex idx = layersTree->currentIndex();
537 if ( !idx.isValid() )
538 return nullptr;
539
540 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
541 if ( !item->isLayer() )
542 return nullptr;
543
544 return item;
545}
546
547QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
548{
549 const QModelIndex idx = layersTree->currentIndex();
550 if ( !idx.isValid() )
551 return nullptr;
552
553 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
554 if ( item->isLayer() )
555 return item->layer();
556
557 return nullptr;
558}
559
561{
562 updateUi();
563
564 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
565 if ( !currentItem )
566 return;
567
568 if ( currentItem->isLayer() )
569 {
570 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
571 mDataDefineRestorer = std::make_unique<DataDefinedRestorer>( parent->symbol(), currentItem->layer() );
572 QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
573 layerProp->setDockMode( this->dockMode() );
574 layerProp->setContext( mContext );
575 setWidget( layerProp );
576 connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
578 // This connection when layer type is changed
580
581 connectChildPanel( layerProp );
582 }
583 else
584 {
585 // then it must be a symbol
586 mDataDefineRestorer.reset();
588 currentItem->symbol()->setLayer( mVectorLayer );
590 // Now populate symbols of that type using the symbols list widget:
591 QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
592 symbolsList->setContext( mContext );
593
594 setWidget( symbolsList );
596 }
597 updateLockButton();
598}
599
601{
602 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
603 if ( !currentItem || currentItem->isLayer() )
604 return;
605 // disconnect to avoid recreating widget
606 disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
607 if ( currentItem->parent() )
608 {
609 // it is a sub-symbol
610 QgsSymbol *symbol = currentItem->symbol();
611 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
612 parent->removeRow( 0 );
613 loadSymbol( symbol, parent );
614 layersTree->setCurrentIndex( parent->child( 0 )->index() );
615 parent->updatePreview();
616 }
617 else
618 {
619 //it is the symbol itself
620 reloadSymbol();
621 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
622 layersTree->setCurrentIndex( newIndex );
623 }
625 // connect it back once things are set
626 connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
627}
628
629void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
630{
631 const int index = stackedWidget->addWidget( widget );
632 stackedWidget->setCurrentIndex( index );
633 if ( mPresentWidget )
634 mPresentWidget->deleteLater();
635 mPresentWidget = widget;
636}
637
638void QgsSymbolSelectorWidget::updateLockButton()
639{
640 QgsSymbolLayer *layer = currentLayer();
641 if ( !layer )
642 return;
643 mLockColorAction->setChecked( layer->isLocked() );
644 mLockSelectionColorAction->setChecked( layer->userFlags() & Qgis::SymbolLayerUserFlag::DisableSelectionRecoloring );
645
646 updateLockButtonIcon();
647}
648
649void QgsSymbolSelectorWidget::updateLockButtonIcon()
650{
651 if ( mLockColorAction->isChecked() && mLockSelectionColorAction->isChecked() )
652 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "locked.svg" ) ) );
653 else if ( mLockColorAction->isChecked() )
654 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconColorLocked.svg" ) ) );
655 else if ( mLockSelectionColorAction->isChecked() )
656 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconSelectLocked.svg" ) ) );
657 else
658 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "unlocked.svg" ) ) );
659}
660
662{
663 const QModelIndex idx = layersTree->currentIndex();
664 if ( !idx.isValid() )
665 return;
666
667 int insertIdx = -1;
668 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
669 if ( item->isLayer() )
670 {
671 insertIdx = item->row();
672 item = static_cast<SymbolLayerItem *>( item->parent() );
673 }
674
675 QgsSymbol *parentSymbol = item->symbol();
676
677 // save data-defined values at marker level
678 const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize() : QgsProperty() );
679 const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle() : QgsProperty() );
680 const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth() : QgsProperty() );
681
682 QgsSymbolLayer *newLayerPtr = nullptr;
683 {
684 std::unique_ptr< QgsSymbolLayer > newLayer = QgsSymbolLayerRegistry::defaultSymbolLayer( parentSymbol->type() );
685 newLayerPtr = newLayer.get();
686 if ( insertIdx == -1 )
687 parentSymbol->appendSymbolLayer( newLayer.release() );
688 else
689 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer.release() );
690 }
691
692 // restore data-defined values at marker level
693 if ( ddSize )
694 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
695 if ( ddAngle )
696 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
697 if ( ddWidth )
698 static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
699
700 // TODO -- using newLayerPtr is not safe in some circumstances here. This needs reworking so that SymbolLayerItem does has
701 // its own owned QgsSymbolLayer clone, and isn't reliant on a pointer to the object owned by parentSymbol.
702 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayerPtr, parentSymbol->type(), mVectorLayer, screen() ); // cppcheck-suppress invalidLifetime
703 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
704 item->updatePreview();
705
706 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
707 updateUi();
709}
710
712{
713 SymbolLayerItem *item = currentLayerItem();
714 const int row = item->row();
715 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
716
717 const int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
718 QgsSymbol *parentSymbol = parent->symbol();
719 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
720
721 parent->removeRow( row );
722 parent->updatePreview();
723
724 const QModelIndex newIdx = parent->child( 0 )->index();
725 layersTree->setCurrentIndex( newIdx );
726
727 updateUi();
729 //finally delete the removed layer pointer
730 delete tmpLayer;
731}
732
734{
735 moveLayerByOffset( +1 );
736}
737
739{
740 moveLayerByOffset( -1 );
741}
742
743void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
744{
745 SymbolLayerItem *item = currentLayerItem();
746 if ( !item )
747 return;
748 const int row = item->row();
749
750 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
751 QgsSymbol *parentSymbol = parent->symbol();
752
753 const int layerIdx = parent->rowCount() - row - 1;
754 // switch layers
755 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
756 parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
757
758 QList<QStandardItem *> rowItems = parent->takeRow( row );
759 parent->insertRows( row + offset, rowItems );
760 parent->updatePreview();
761
762 const QModelIndex newIdx = rowItems[0]->index();
763 layersTree->setCurrentIndex( newIdx );
764
766 updateUi();
767}
768
770{
771 QgsSymbolLayer *layer = currentLayer();
772 if ( !layer )
773 return;
774 layer->setLocked( mLockColorAction->isChecked() );
775 updateLockButtonIcon();
776 emit symbolModified();
777}
778
780{
781 const QModelIndex idx = layersTree->currentIndex();
782 if ( !idx.isValid() )
783 return;
784
785 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
786 if ( !item->isLayer() )
787 return;
788
789 QgsSymbolLayer *source = item->layer();
790
791 const int insertIdx = item->row();
792 item = static_cast<SymbolLayerItem *>( item->parent() );
793
794 QgsSymbol *parentSymbol = item->symbol();
795
796 QgsSymbolLayer *newLayer = source->clone();
798 if ( insertIdx == -1 )
799 parentSymbol->appendSymbolLayer( newLayer );
800 else
801 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
802
803 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
804 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
805 if ( newLayer->subSymbol() )
806 {
807 loadSymbol( newLayer->subSymbol(), newLayerItem );
808 layersTree->setExpanded( newLayerItem->index(), true );
809 }
810 item->updatePreview();
811
812 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
813 updateUi();
815}
816
818{
819 SymbolLayerItem *item = currentLayerItem();
820
821 if ( item->rowCount() > 0 )
822 {
823 item->removeRow( 0 );
824 }
825 QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
826
827 // update symbol layer item
828 item->setLayer( newLayer, symbol->type() );
829 // When it is a marker symbol
830 if ( newLayer->subSymbol() )
831 {
832 loadSymbol( newLayer->subSymbol(), item );
833 layersTree->setExpanded( item->index(), true );
834 }
835
836 // Change the symbol at last to avoid deleting item's layer
837 const int layerIdx = item->parent()->rowCount() - item->row() - 1;
838 symbol->changeSymbolLayer( layerIdx, newLayer );
839
840 item->updatePreview();
842 // Important: This lets the layer have its own layer properties widget
843 layerChanged();
844}
845
847 : QDialog( parent )
848{
849 setLayout( new QVBoxLayout() );
850
851 mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
852 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
853
854 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
855 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
856 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
857
858 layout()->addWidget( mSelectorWidget );
859 layout()->addWidget( mButtonBox );
860
861 connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
862
863 mSelectorWidget->setMinimumSize( 460, 560 );
864 setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
866
867 // Can be embedded in renderer properties dialog
868 if ( embedded )
869 {
870 mButtonBox->hide();
871 layout()->setContentsMargins( 0, 0, 0, 0 );
872 }
873 else
874 {
875 setWindowTitle( tr( "Symbol Selector" ) );
876 }
877 mSelectorWidget->setDockMode( embedded );
878}
879
881{
882 return mSelectorWidget->advancedMenu();
883}
884
886{
887 mSelectorWidget->setContext( context );
888}
889
891{
892 return mSelectorWidget->context();
893}
894
896{
897 return mSelectorWidget->symbol();
898}
899
901{
902 // Ignore the ESC key to avoid close the dialog without the properties window
903 if ( !isWindow() && e->key() == Qt::Key_Escape )
904 {
905 e->ignore();
906 }
907 else
908 {
909 QDialog::keyPressEvent( e );
910 }
911}
912
913void QgsSymbolSelectorDialog::reloadSymbol()
914{
915 mSelectorWidget->reloadSymbol();
916}
917
918void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
919{
920 mSelectorWidget->loadSymbol( symbol, parent );
921}
922
923void QgsSymbolSelectorDialog::updateUi()
924{
925 mSelectorWidget->updateUi();
926}
927
928void QgsSymbolSelectorDialog::updateLockButton()
929{
930 mSelectorWidget->updateLockButton();
931}
932
933SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
934{
935 return mSelectorWidget->currentLayerItem();
936}
937
938QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
939{
940 return mSelectorWidget->currentLayer();
941}
942
943void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
944{
945 mSelectorWidget->moveLayerByOffset( offset );
946}
947
948void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
949{
950 mSelectorWidget->setWidget( widget );
951}
952
954{
955 mSelectorWidget->moveLayerDown();
956}
957
959{
960 mSelectorWidget->moveLayerUp();
961}
962
964{
965 mSelectorWidget->addLayer();
966}
967
969{
970 mSelectorWidget->removeLayer();
971}
972
974{
975 mSelectorWidget->lockLayer();
976}
977
979{
980 mSelectorWidget->duplicateLayer();
981}
982
984{
985 mSelectorWidget->layerChanged();
986}
987
989{
990 mSelectorWidget->updateLayerPreview();
991}
992
994{
995 mSelectorWidget->updatePreview();
996}
997
999{
1000 mSelectorWidget->symbolChanged();
1001}
1002
1004{
1005 mSelectorWidget->changeLayer( layer );
1006}
1007
1008QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
1009{
1010 return mButtonBox;
1011}
1012
1013void QgsSymbolSelectorDialog::showHelp()
1014{
1015 QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
1016}
1017
1018void QgsSymbolSelectorWidget::projectDataChanged()
1019{
1020 mBlockModified = true;
1021 symbolChanged();
1022 updatePreview();
1023 mBlockModified = false;
1024}
1025
1026void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
1027{
1028 if ( mVectorLayer && layers.contains( mVectorLayer ) )
1029 {
1030 disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
1031 }
1032}
QFlags< SymbolLayerUserFlag > SymbolLayerUserFlags
Symbol layer user flags.
Definition qgis.h:910
@ Millimeters
Millimeters.
Definition qgis.h:5184
@ FlagIncludeCrosshairsForMarkerSymbols
Include a crosshairs reference image in the background of marker symbol previews.
Definition qgis.h:863
@ DisableSelectionRecoloring
If present, indicates that the symbol layer should not be recolored when rendering selected features.
Definition qgis.h:901
SymbolType
Symbol types.
Definition qgis.h:610
@ Marker
Marker symbol.
Definition qgis.h:611
@ Line
Line symbol.
Definition qgis.h:612
@ Fill
Fill symbol.
Definition qgis.h:613
@ Hybrid
Hybrid symbol.
Definition qgis.h:614
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6222
static QgsSymbolLayerRegistry * symbolLayerRegistry()
Returns the application's symbol layer registry, used for managing symbol layers.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:221
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:38
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
A widget which allows configuration of the properties of a single QgsSymbolLayer.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
void changeLayer(QgsSymbolLayer *layer)
Emitted when the symbol layer is changed in the widget.
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()
Emitted when the symbol layer configuration is changed in the widget.
Abstract base class for line symbol layers.
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
Abstract base class for marker symbol layers.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsProperty dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
bool dockMode() const
Returns the dock mode state.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void connectChildPanel(QgsPanelWidget *panel)
Connect the given sub panel widgets showPanel signals to this current panels main showPanel event to ...
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
void widgetChanged()
Emitted when the widget state changes.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
static QgsProject * instance()
Returns the QgsProject singleton instance.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
A store for object properties.
Stores properties relating to a screen.
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:88
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
static std::unique_ptr< QgsSymbolLayer > defaultSymbolLayer(Qgis::SymbolType type)
create a new instance of symbol layer for specified symbol type with default settings
QgsSymbolLayerAbstractMetadata * symbolLayerMetadata(const QString &name) const
Returns metadata for specified symbol layer. Returns nullptr if not found.
static QIcon symbolLayerPreviewIcon(const QgsSymbolLayer *layer, Qgis::RenderUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::SymbolType parentSymbolType=Qgis::SymbolType::Hybrid, QgsMapLayer *mapLayer=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Draws a symbol layer preview to an icon.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a color ramp.
static void resetSymbolLayerIds(QgsSymbol *symbol)
Regenerate recursively unique id from all symbol symbol layers.
Abstract base class for symbol layers.
virtual QgsSymbolLayer * clone() const =0
Shall be reimplemented by subclasses to create a deep copy of the instance.
Qgis::SymbolType type() const
bool isLocked() const
Returns true if the symbol layer colors are locked and the layer will ignore any symbol-level color c...
void setUserFlags(Qgis::SymbolLayerUserFlags flags)
Sets user-controlled flags which control the symbol layer's behavior.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
Qgis::SymbolLayerUserFlags userFlags() const
Returns user-controlled flags which control the symbol layer's behavior.
void setLocked(bool locked)
Sets whether the layer's colors are locked.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
QgsSymbolSelectorDialog(QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr, bool embedded=false)
Constructor for QgsSymbolSelectorDialog.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
QMenu * advancedMenu()
Returns menu for "advanced" button - create it if doesn't exist and show the advanced button.
void symbolChanged()
Slot to update tree when a new symbol from style.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
void keyPressEvent(QKeyEvent *e) override
void duplicateLayer()
Duplicates the current symbol layer and places the duplicated layer above the current symbol layer.
void changeLayer(QgsSymbolLayer *layer)
Alters tree and sets proper widget when Layer Type is changed.
void loadSymbol(QgsSymbol *symbol, SymbolLayerItem *parent=nullptr)
Loads the given symbol into the widget.
Symbol selector widget that can be used to select and build a symbol.
void loadSymbol(QgsSymbol *symbol, SymbolLayerItem *parent=nullptr)
Loads the given symbol into the widget.
void symbolChanged()
Slot to update tree when a new symbol from style.
void addLayer()
Add a symbol layer to the bottom of the stack.
QMenu * advancedMenu()
Returns menu for "advanced" button - create it if doesn't exist and show the advanced button.
void layerChanged()
Called when the layer changes in the widget.
void changeLayer(QgsSymbolLayer *layer)
Alters tree and sets proper widget when Layer Type is changed.
void updatePreview()
Update the preview of the whole symbol in the interface.
QgsSymbolSelectorWidget(QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr)
Symbol selector widget that can be used to select and build a symbol.
void removeLayer()
Remove the current active symbol layer.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
void moveLayerDown()
Move the active symbol layer down.
void symbolModified()
Emitted when a symbol is modified in the widget.
void duplicateLayer()
Duplicates the current symbol layer and places the duplicated layer above the current symbol layer.
void lockLayer()
Lock the current active symbol layer.
void updateLayerPreview()
Update the single symbol layer preview in the widget.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
void moveLayerUp()
Move the active symbol layer up.
static QgsSymbolSelectorWidget * createWidgetWithSymbolOwnership(std::unique_ptr< QgsSymbol > symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr)
Creates a QgsSymbolSelectorWidget which takes ownership of a symbol and maintains the ownership for t...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
A widget which presents symbol-level properties (such as size), and allows selection of symbols from ...
void changed()
Emitted when the symbol is modified in the widget.
Represents a vector layer which manages a vector based dataset.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169