QGIS API Documentation 3.33.0-Master (bf22a165b3)
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
18#include "qgsstyle.h"
19#include "qgssymbol.h"
20#include "qgssymbollayer.h"
21#include "qgssymbollayerutils.h"
24
25// the widgets
28#include "qgsapplication.h"
29#include "qgsvectorlayer.h"
30#include "qgssvgcache.h"
31#include "qgsimagecache.h"
32#include "qgsproject.h"
33#include "qgsguiutils.h"
34#include "qgsgui.h"
35#include "qgsmarkersymbol.h"
36#include "qgslinesymbol.h"
37#include "qscreen.h"
38
39#include <QColorDialog>
40#include <QPainter>
41#include <QStandardItemModel>
42#include <QInputDialog>
43#include <QMessageBox>
44#include <QKeyEvent>
45#include <QMenu>
46
47#include <QWidget>
48#include <QFile>
49#include <QStandardItem>
50
52
53static const int SYMBOL_LAYER_ITEM_TYPE = QStandardItem::UserType + 1;
54
55DataDefinedRestorer::DataDefinedRestorer( QgsSymbol *symbol, const QgsSymbolLayer *symbolLayer )
56
57{
58 if ( symbolLayer->type() == Qgis::SymbolType::Marker && symbol->type() == Qgis::SymbolType::Marker )
59 {
60 Q_ASSERT( symbol->type() == Qgis::SymbolType::Marker );
61 mMarker = static_cast<QgsMarkerSymbol *>( symbol );
62 mMarkerSymbolLayer = static_cast<const QgsMarkerSymbolLayer *>( symbolLayer );
63 mDDSize = mMarker->dataDefinedSize();
64 mDDAngle = mMarker->dataDefinedAngle();
65 // check if restore is actually needed
66 if ( !mDDSize && !mDDAngle )
67 mMarker = nullptr;
68 }
69 else if ( symbolLayer->type() == Qgis::SymbolType::Line && symbol->type() == Qgis::SymbolType::Line )
70 {
71 mLine = static_cast<QgsLineSymbol *>( symbol );
72 mLineSymbolLayer = static_cast<const QgsLineSymbolLayer *>( symbolLayer );
73 mDDWidth = mLine->dataDefinedWidth();
74 // check if restore is actually needed
75 if ( !mDDWidth )
76 mLine = nullptr;
77 }
78 save();
79}
80
81void DataDefinedRestorer::save()
82{
83 if ( mMarker )
84 {
85 mSize = mMarkerSymbolLayer->size();
86 mAngle = mMarkerSymbolLayer->angle();
87 mMarkerOffset = mMarkerSymbolLayer->offset();
88 }
89 else if ( mLine )
90 {
91 mWidth = mLineSymbolLayer->width();
92 mLineOffset = mLineSymbolLayer->offset();
93 }
94}
95
96void DataDefinedRestorer::restore()
97{
98 if ( mMarker )
99 {
100 if ( mDDSize &&
101 ( mSize != mMarkerSymbolLayer->size() || mMarkerOffset != mMarkerSymbolLayer->offset() ) )
102 mMarker->setDataDefinedSize( mDDSize );
103 if ( mDDAngle &&
104 mAngle != mMarkerSymbolLayer->angle() )
105 mMarker->setDataDefinedAngle( mDDAngle );
106 }
107 else if ( mLine )
108 {
109 if ( mDDWidth &&
110 ( 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() { 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 {
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, [ = ]( bool checked )
344 {
345 QgsSymbolLayer *layer = currentLayer();
346 if ( !layer )
347 return;
348
349 Qgis::SymbolLayerUserFlags flags = layer->userFlags();
351 layer->setUserFlags( flags );
352 updateLockButtonIcon();
353 emit symbolModified();
354 } );
355 connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
357
358 updateLockButtonIcon();
359
360 updateUi();
361
362 // set symbol as active item in the tree
363 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
364 layersTree->setCurrentIndex( newIndex );
365
366 setPanelTitle( tr( "Symbol Selector" ) );
367
368 // when a remote svg has been fetched, update the widget's previews
369 // this is required if the symbol utilizes remote svgs, and the current previews
370 // have been generated using the temporary "downloading" svg. In this case
371 // we require the preview to be regenerated to use the correct fetched
372 // svg
373 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
374
375 // when a remote image has been fetched, update the widget's previews
376 // this is required if the symbol utilizes remote images, and the current previews
377 // have been generated using the temporary "downloading" image. In this case
378 // we require the preview to be regenerated to use the correct fetched
379 // image
380 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
381
382 // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
383 // need updating to reflect the new colors
384 connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
385
386 connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ), this, &QgsSymbolSelectorWidget::layersAboutToBeRemoved );
387}
388
390{
391 if ( !mAdvancedMenu )
392 {
393 mAdvancedMenu = new QMenu( this );
394 // Brute force method to activate the Advanced menu
395 layerChanged();
396 }
397 return mAdvancedMenu;
398}
399
401{
402 mContext = context;
403
404 if ( auto *lExpressionContext = mContext.expressionContext() )
405 {
406 mPreviewExpressionContext = *lExpressionContext;
407 if ( mVectorLayer )
408 mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
409
410 mPreviewExpressionContext.setFeature( mPreviewFeature );
411 }
412
413 QWidget *widget = stackedWidget->currentWidget();
414 if ( QgsLayerPropertiesWidget *layerProp = qobject_cast< QgsLayerPropertiesWidget * >( widget ) )
415 layerProp->setContext( context );
416 else if ( QgsSymbolsListWidget *listWidget = qobject_cast< QgsSymbolsListWidget * >( widget ) )
417 listWidget->setContext( context );
418
419 layerChanged();
421}
422
424{
425 return mContext;
426}
427
428void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
429{
430 if ( !symbol )
431 return;
432
433 if ( !parent )
434 {
435 mSymbol = symbol;
436 mSymbolLayersModel->clear();
437 parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
438 }
439
440 SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol, mVectorLayer, screen() );
441 QFont boldFont = symbolItem->font();
442 boldFont.setBold( true );
443 symbolItem->setFont( boldFont );
444 parent->appendRow( symbolItem );
445
446 const int count = symbol->symbolLayerCount();
447 for ( int i = count - 1; i >= 0; i-- )
448 {
449 SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ), symbol->type(), mVectorLayer, screen() );
450 layerItem->setEditable( false );
451 symbolItem->appendRow( layerItem );
452 if ( symbol->symbolLayer( i )->subSymbol() )
453 {
454 loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
455 }
456 layersTree->setExpanded( layerItem->index(), true );
457 }
458 layersTree->setExpanded( symbolItem->index(), true );
459
460 if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
461 {
462 // make sure root item for symbol is selected in tree
463 layersTree->setCurrentIndex( symbolItem->index() );
464 }
465}
466
467void QgsSymbolSelectorWidget::reloadSymbol()
468{
469 mSymbolLayersModel->clear();
470 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
471}
472
473void QgsSymbolSelectorWidget::updateUi()
474{
475 const QModelIndex currentIdx = layersTree->currentIndex();
476 if ( !currentIdx.isValid() )
477 return;
478
479 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
480 if ( !item->isLayer() )
481 {
482 btnUp->setEnabled( false );
483 btnDown->setEnabled( false );
484 btnRemoveLayer->setEnabled( false );
485 btnLock->setEnabled( false );
486 btnDuplicate->setEnabled( false );
487 return;
488 }
489
490 const int rowCount = item->parent()->rowCount();
491 const int currentRow = item->row();
492
493 btnUp->setEnabled( currentRow > 0 );
494 btnDown->setEnabled( currentRow < rowCount - 1 );
495 btnRemoveLayer->setEnabled( rowCount > 1 );
496 btnLock->setEnabled( true );
497 btnDuplicate->setEnabled( true );
498}
499
501{
502 if ( !mSymbol )
503 return;
504
505 std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
506 const QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlag::FlagIncludeCrosshairsForMarkerSymbols, QgsScreenProperties( screen() ) );
507 lblPreview->setPixmap( QPixmap::fromImage( preview ) );
508 // Hope this is a appropriate place
509 if ( !mBlockModified )
510 emit symbolModified();
511}
512
514{
515 // get current layer item and update its icon
516 SymbolLayerItem *item = currentLayerItem();
517 if ( item )
518 item->updatePreview();
519 // update also preview of the whole symbol
521}
522
523SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
524{
525 const QModelIndex idx = layersTree->currentIndex();
526 if ( !idx.isValid() )
527 return nullptr;
528
529 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
530 if ( !item->isLayer() )
531 return nullptr;
532
533 return item;
534}
535
536QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
537{
538 const QModelIndex idx = layersTree->currentIndex();
539 if ( !idx.isValid() )
540 return nullptr;
541
542 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
543 if ( item->isLayer() )
544 return item->layer();
545
546 return nullptr;
547}
548
550{
551 updateUi();
552
553 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
554 if ( !currentItem )
555 return;
556
557 if ( currentItem->isLayer() )
558 {
559 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
560 mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
561 QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
562 layerProp->setDockMode( this->dockMode() );
563 layerProp->setContext( mContext );
564 setWidget( layerProp );
565 connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
567 // This connection when layer type is changed
569
570 connectChildPanel( layerProp );
571 }
572 else
573 {
574 // then it must be a symbol
575 mDataDefineRestorer.reset();
577 currentItem->symbol()->setLayer( mVectorLayer );
579 // Now populate symbols of that type using the symbols list widget:
580 QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
581 symbolsList->setContext( mContext );
582
583 setWidget( symbolsList );
585 }
586 updateLockButton();
587}
588
590{
591 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
592 if ( !currentItem || currentItem->isLayer() )
593 return;
594 // disconnect to avoid recreating widget
595 disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
596 if ( currentItem->parent() )
597 {
598 // it is a sub-symbol
599 QgsSymbol *symbol = currentItem->symbol();
600 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
601 parent->removeRow( 0 );
602 loadSymbol( symbol, parent );
603 layersTree->setCurrentIndex( parent->child( 0 )->index() );
604 parent->updatePreview();
605 }
606 else
607 {
608 //it is the symbol itself
609 reloadSymbol();
610 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
611 layersTree->setCurrentIndex( newIndex );
612 }
614 // connect it back once things are set
615 connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
616}
617
618void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
619{
620 const int index = stackedWidget->addWidget( widget );
621 stackedWidget->setCurrentIndex( index );
622 if ( mPresentWidget )
623 mPresentWidget->deleteLater();
624 mPresentWidget = widget;
625}
626
627void QgsSymbolSelectorWidget::updateLockButton()
628{
629 QgsSymbolLayer *layer = currentLayer();
630 if ( !layer )
631 return;
632 mLockColorAction->setChecked( layer->isLocked() );
633 mLockSelectionColorAction->setChecked( layer->userFlags() & Qgis::SymbolLayerUserFlag::DisableSelectionRecoloring );
634
635 updateLockButtonIcon();
636}
637
638void QgsSymbolSelectorWidget::updateLockButtonIcon()
639{
640 if ( mLockColorAction->isChecked() && mLockSelectionColorAction->isChecked() )
641 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "locked.svg" ) ) );
642 else if ( mLockColorAction->isChecked() )
643 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconColorLocked.svg" ) ) );
644 else if ( mLockSelectionColorAction->isChecked() )
645 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconSelectLocked.svg" ) ) );
646 else
647 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "unlocked.svg" ) ) );
648}
649
651{
652 const QModelIndex idx = layersTree->currentIndex();
653 if ( !idx.isValid() )
654 return;
655
656 int insertIdx = -1;
657 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
658 if ( item->isLayer() )
659 {
660 insertIdx = item->row();
661 item = static_cast<SymbolLayerItem *>( item->parent() );
662 }
663
664 QgsSymbol *parentSymbol = item->symbol();
665
666 // save data-defined values at marker level
667 const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker
668 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
669 : QgsProperty() );
670 const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker
671 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
672 : QgsProperty() );
673 const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line
674 ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
675 : QgsProperty() );
676
678 if ( insertIdx == -1 )
679 parentSymbol->appendSymbolLayer( newLayer );
680 else
681 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
682
683 // restore data-defined values at marker level
684 if ( ddSize )
685 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
686 if ( ddAngle )
687 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
688 if ( ddWidth )
689 static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
690
691 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
692 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
693 item->updatePreview();
694
695 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
696 updateUi();
698}
699
701{
702 SymbolLayerItem *item = currentLayerItem();
703 const int row = item->row();
704 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
705
706 const int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
707 QgsSymbol *parentSymbol = parent->symbol();
708 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
709
710 parent->removeRow( row );
711 parent->updatePreview();
712
713 const QModelIndex newIdx = parent->child( 0 )->index();
714 layersTree->setCurrentIndex( newIdx );
715
716 updateUi();
718 //finally delete the removed layer pointer
719 delete tmpLayer;
720}
721
723{
724 moveLayerByOffset( + 1 );
725}
726
728{
729 moveLayerByOffset( -1 );
730}
731
732void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
733{
734 SymbolLayerItem *item = currentLayerItem();
735 if ( !item )
736 return;
737 const int row = item->row();
738
739 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
740 QgsSymbol *parentSymbol = parent->symbol();
741
742 const int layerIdx = parent->rowCount() - row - 1;
743 // switch layers
744 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
745 parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
746
747 QList<QStandardItem *> rowItems = parent->takeRow( row );
748 parent->insertRows( row + offset, rowItems );
749 parent->updatePreview();
750
751 const QModelIndex newIdx = rowItems[ 0 ]->index();
752 layersTree->setCurrentIndex( newIdx );
753
755 updateUi();
756}
757
759{
760 QgsSymbolLayer *layer = currentLayer();
761 if ( !layer )
762 return;
763 layer->setLocked( mLockColorAction->isChecked() );
764 updateLockButtonIcon();
765 emit symbolModified();
766}
767
769{
770 const QModelIndex idx = layersTree->currentIndex();
771 if ( !idx.isValid() )
772 return;
773
774 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
775 if ( !item->isLayer() )
776 return;
777
778 QgsSymbolLayer *source = item->layer();
779
780 const int insertIdx = item->row();
781 item = static_cast<SymbolLayerItem *>( item->parent() );
782
783 QgsSymbol *parentSymbol = item->symbol();
784
785 QgsSymbolLayer *newLayer = source->clone();
787 if ( insertIdx == -1 )
788 parentSymbol->appendSymbolLayer( newLayer );
789 else
790 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
791
792 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
793 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
794 if ( newLayer->subSymbol() )
795 {
796 loadSymbol( newLayer->subSymbol(), newLayerItem );
797 layersTree->setExpanded( newLayerItem->index(), true );
798 }
799 item->updatePreview();
800
801 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
802 updateUi();
804}
805
807{
808 SymbolLayerItem *item = currentLayerItem();
809 QgsSymbolLayer *layer = item->layer();
810
811 if ( layer->subSymbol() )
812 {
813 item->removeRow( 0 );
814 }
815 QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
816
817 // update symbol layer item
818 item->setLayer( newLayer, symbol->type() );
819 // When it is a marker symbol
820 if ( newLayer->subSymbol() )
821 {
822 loadSymbol( newLayer->subSymbol(), item );
823 layersTree->setExpanded( item->index(), true );
824 }
825
826 // Change the symbol at last to avoid deleting item's layer
827 const int layerIdx = item->parent()->rowCount() - item->row() - 1;
828 symbol->changeSymbolLayer( layerIdx, newLayer );
829
830 item->updatePreview();
832 // Important: This lets the layer have its own layer properties widget
833 layerChanged();
834}
835
836QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
837 : QDialog( parent )
838{
839 setLayout( new QVBoxLayout() );
840
841 mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
842 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
843
844 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
845 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
846 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
847
848 layout()->addWidget( mSelectorWidget );
849 layout()->addWidget( mButtonBox );
850
851 connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
852
853 mSelectorWidget->setMinimumSize( 460, 560 );
854 setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
856
857 // Can be embedded in renderer properties dialog
858 if ( embedded )
859 {
860 mButtonBox->hide();
861 layout()->setContentsMargins( 0, 0, 0, 0 );
862 }
863 else
864 {
865 setWindowTitle( tr( "Symbol Selector" ) );
866 }
867 mSelectorWidget->setDockMode( embedded );
868}
869
871{
872 return mSelectorWidget->advancedMenu();
873}
874
876{
877 mContext = context;
878}
879
881{
882 return mContext;
883}
884
886{
887 return mSelectorWidget->symbol();
888}
889
891{
892 // Ignore the ESC key to avoid close the dialog without the properties window
893 if ( !isWindow() && e->key() == Qt::Key_Escape )
894 {
895 e->ignore();
896 }
897 else
898 {
899 QDialog::keyPressEvent( e );
900 }
901}
902
903void QgsSymbolSelectorDialog::reloadSymbol()
904{
905 mSelectorWidget->reloadSymbol();
906}
907
908void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
909{
910 mSelectorWidget->loadSymbol( symbol, parent );
911}
912
913void QgsSymbolSelectorDialog::updateUi()
914{
915 mSelectorWidget->updateUi();
916}
917
918void QgsSymbolSelectorDialog::updateLockButton()
919{
920 mSelectorWidget->updateLockButton();
921}
922
923SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
924{
925 return mSelectorWidget->currentLayerItem();
926}
927
928QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
929{
930 return mSelectorWidget->currentLayer();
931}
932
933void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
934{
935 mSelectorWidget->moveLayerByOffset( offset );
936}
937
938void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
939{
940 mSelectorWidget->setWidget( widget );
941}
942
944{
945 mSelectorWidget->moveLayerDown();
946}
947
949{
950 mSelectorWidget->moveLayerUp();
951}
952
954{
955 mSelectorWidget->addLayer();
956}
957
959{
960 mSelectorWidget->removeLayer();
961}
962
964{
965 mSelectorWidget->lockLayer();
966}
967
969{
970 mSelectorWidget->duplicateLayer();
971}
972
974{
975 mSelectorWidget->layerChanged();
976}
977
982
984{
985 mSelectorWidget->updatePreview();
986}
987
989{
990 mSelectorWidget->symbolChanged();
991}
992
994{
995 mSelectorWidget->changeLayer( layer );
996}
997
998QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
999{
1000 return mButtonBox;
1001}
1002
1003void QgsSymbolSelectorDialog::showHelp()
1004{
1005 QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
1006}
1007
1008void QgsSymbolSelectorWidget::projectDataChanged()
1009{
1010 mBlockModified = true;
1011 symbolChanged();
1012 updatePreview();
1013 mBlockModified = false;
1014}
1015
1016void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
1017{
1018 if ( mVectorLayer && layers.contains( mVectorLayer ) )
1019 {
1020 disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
1021 }
1022}
@ Millimeters
Millimeters.
@ FlagIncludeCrosshairsForMarkerSymbols
Include a crosshairs reference image in the background of marker symbol previews.
@ DisableSelectionRecoloring
If present, indicates that the symbol layer should not be recolored when rendering selected features.
SymbolType
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:341
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:4016
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.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
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)
This class 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:194
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.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
void changeLayer(QgsSymbolLayer *)
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
Struct for storing maximum and minimum scales for measurements in map units.
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).
Base class for any widget that can be shown as a inline panel.
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 ...
void widgetChanged()
Emitted when the widget state changes.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
bool dockMode()
Returns the dock mode state.
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.
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.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
Stores metadata about one symbol layer class.
QgsSymbolLayerAbstractMetadata * symbolLayerMetadata(const QString &name) const
Returns metadata for specified symbol layer. Returns nullptr if not found.
static QgsSymbolLayer * defaultSymbolLayer(Qgis::SymbolType type)
create a new instance of symbol layer for specified symbol type with default settings
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.
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.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QgsExpressionContext * expressionContext() const
Returns the expression context used for the widget, if set.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:94
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
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.
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:216
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:153
Represents a vector layer which manages a vector based data sets.
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:4844
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:4843