QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
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
389QgsSymbolSelectorWidget *QgsSymbolSelectorWidget::createWidgetWithSymbolOwnership( std::unique_ptr<QgsSymbol> symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent )
390{
391 QgsSymbolSelectorWidget *widget = new QgsSymbolSelectorWidget( symbol.get(), style, vl, parent );
392 // transfer ownership of symbol to widget, so that we are guaranteed it will last for the duration of the widget
393 widget->mOwnedSymbol = std::move( symbol );
394 return widget;
395}
396
398{
399 if ( !mAdvancedMenu )
400 {
401 mAdvancedMenu = new QMenu( this );
402 // Brute force method to activate the Advanced menu
403 layerChanged();
404 }
405 return mAdvancedMenu;
406}
407
409{
410 mContext = context;
411
412 if ( auto *lExpressionContext = mContext.expressionContext() )
413 {
414 mPreviewExpressionContext = *lExpressionContext;
415 if ( mVectorLayer )
416 mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
417
418 mPreviewExpressionContext.setFeature( mPreviewFeature );
419 }
420
421 QWidget *widget = stackedWidget->currentWidget();
422 if ( QgsLayerPropertiesWidget *layerProp = qobject_cast< QgsLayerPropertiesWidget * >( widget ) )
423 layerProp->setContext( context );
424 else if ( QgsSymbolsListWidget *listWidget = qobject_cast< QgsSymbolsListWidget * >( widget ) )
425 listWidget->setContext( context );
426
427 layerChanged();
429}
430
432{
433 return mContext;
434}
435
436void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
437{
438 if ( !symbol )
439 return;
440
441 if ( !parent )
442 {
443 mSymbol = symbol;
444 mSymbolLayersModel->clear();
445 parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
446 }
447
448 SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol, mVectorLayer, screen() );
449 QFont boldFont = symbolItem->font();
450 boldFont.setBold( true );
451 symbolItem->setFont( boldFont );
452 parent->appendRow( symbolItem );
453
454 const int count = symbol->symbolLayerCount();
455 for ( int i = count - 1; i >= 0; i-- )
456 {
457 SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ), symbol->type(), mVectorLayer, screen() );
458 layerItem->setEditable( false );
459 symbolItem->appendRow( layerItem );
460 if ( symbol->symbolLayer( i )->subSymbol() )
461 {
462 loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
463 }
464 layersTree->setExpanded( layerItem->index(), true );
465 }
466 layersTree->setExpanded( symbolItem->index(), true );
467
468 if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
469 {
470 // make sure root item for symbol is selected in tree
471 layersTree->setCurrentIndex( symbolItem->index() );
472 }
473}
474
475void QgsSymbolSelectorWidget::reloadSymbol()
476{
477 mSymbolLayersModel->clear();
478 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
479}
480
481void QgsSymbolSelectorWidget::updateUi()
482{
483 const QModelIndex currentIdx = layersTree->currentIndex();
484 if ( !currentIdx.isValid() )
485 return;
486
487 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
488 if ( !item->isLayer() )
489 {
490 btnUp->setEnabled( false );
491 btnDown->setEnabled( false );
492 btnRemoveLayer->setEnabled( false );
493 btnLock->setEnabled( false );
494 btnDuplicate->setEnabled( false );
495 return;
496 }
497
498 const int rowCount = item->parent()->rowCount();
499 const int currentRow = item->row();
500
501 btnUp->setEnabled( currentRow > 0 );
502 btnDown->setEnabled( currentRow < rowCount - 1 );
503 btnRemoveLayer->setEnabled( rowCount > 1 );
504 btnLock->setEnabled( true );
505 btnDuplicate->setEnabled( true );
506}
507
509{
510 if ( !mSymbol )
511 return;
512
513 std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
514 const QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlag::FlagIncludeCrosshairsForMarkerSymbols, QgsScreenProperties( screen() ) );
515 lblPreview->setPixmap( QPixmap::fromImage( preview ) );
516 // Hope this is a appropriate place
517 if ( !mBlockModified )
518 emit symbolModified();
519}
520
522{
523 // get current layer item and update its icon
524 SymbolLayerItem *item = currentLayerItem();
525 if ( item )
526 item->updatePreview();
527 // update also preview of the whole symbol
529}
530
531SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
532{
533 const QModelIndex idx = layersTree->currentIndex();
534 if ( !idx.isValid() )
535 return nullptr;
536
537 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
538 if ( !item->isLayer() )
539 return nullptr;
540
541 return item;
542}
543
544QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
545{
546 const QModelIndex idx = layersTree->currentIndex();
547 if ( !idx.isValid() )
548 return nullptr;
549
550 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
551 if ( item->isLayer() )
552 return item->layer();
553
554 return nullptr;
555}
556
558{
559 updateUi();
560
561 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
562 if ( !currentItem )
563 return;
564
565 if ( currentItem->isLayer() )
566 {
567 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
568 mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
569 QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
570 layerProp->setDockMode( this->dockMode() );
571 layerProp->setContext( mContext );
572 setWidget( layerProp );
573 connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
575 // This connection when layer type is changed
577
578 connectChildPanel( layerProp );
579 }
580 else
581 {
582 // then it must be a symbol
583 mDataDefineRestorer.reset();
585 currentItem->symbol()->setLayer( mVectorLayer );
587 // Now populate symbols of that type using the symbols list widget:
588 QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
589 symbolsList->setContext( mContext );
590
591 setWidget( symbolsList );
593 }
594 updateLockButton();
595}
596
598{
599 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
600 if ( !currentItem || currentItem->isLayer() )
601 return;
602 // disconnect to avoid recreating widget
603 disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
604 if ( currentItem->parent() )
605 {
606 // it is a sub-symbol
607 QgsSymbol *symbol = currentItem->symbol();
608 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
609 parent->removeRow( 0 );
610 loadSymbol( symbol, parent );
611 layersTree->setCurrentIndex( parent->child( 0 )->index() );
612 parent->updatePreview();
613 }
614 else
615 {
616 //it is the symbol itself
617 reloadSymbol();
618 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
619 layersTree->setCurrentIndex( newIndex );
620 }
622 // connect it back once things are set
623 connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
624}
625
626void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
627{
628 const int index = stackedWidget->addWidget( widget );
629 stackedWidget->setCurrentIndex( index );
630 if ( mPresentWidget )
631 mPresentWidget->deleteLater();
632 mPresentWidget = widget;
633}
634
635void QgsSymbolSelectorWidget::updateLockButton()
636{
637 QgsSymbolLayer *layer = currentLayer();
638 if ( !layer )
639 return;
640 mLockColorAction->setChecked( layer->isLocked() );
641 mLockSelectionColorAction->setChecked( layer->userFlags() & Qgis::SymbolLayerUserFlag::DisableSelectionRecoloring );
642
643 updateLockButtonIcon();
644}
645
646void QgsSymbolSelectorWidget::updateLockButtonIcon()
647{
648 if ( mLockColorAction->isChecked() && mLockSelectionColorAction->isChecked() )
649 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "locked.svg" ) ) );
650 else if ( mLockColorAction->isChecked() )
651 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconColorLocked.svg" ) ) );
652 else if ( mLockSelectionColorAction->isChecked() )
653 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconSelectLocked.svg" ) ) );
654 else
655 btnLock->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "unlocked.svg" ) ) );
656}
657
659{
660 const QModelIndex idx = layersTree->currentIndex();
661 if ( !idx.isValid() )
662 return;
663
664 int insertIdx = -1;
665 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
666 if ( item->isLayer() )
667 {
668 insertIdx = item->row();
669 item = static_cast<SymbolLayerItem *>( item->parent() );
670 }
671
672 QgsSymbol *parentSymbol = item->symbol();
673
674 // save data-defined values at marker level
675 const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker
676 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
677 : QgsProperty() );
678 const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker
679 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
680 : QgsProperty() );
681 const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line
682 ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
683 : QgsProperty() );
684
686 if ( insertIdx == -1 )
687 parentSymbol->appendSymbolLayer( newLayer );
688 else
689 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
690
691 // restore data-defined values at marker level
692 if ( ddSize )
693 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
694 if ( ddAngle )
695 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
696 if ( ddWidth )
697 static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
698
699 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
700 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
701 item->updatePreview();
702
703 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
704 updateUi();
706}
707
709{
710 SymbolLayerItem *item = currentLayerItem();
711 const int row = item->row();
712 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
713
714 const int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
715 QgsSymbol *parentSymbol = parent->symbol();
716 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
717
718 parent->removeRow( row );
719 parent->updatePreview();
720
721 const QModelIndex newIdx = parent->child( 0 )->index();
722 layersTree->setCurrentIndex( newIdx );
723
724 updateUi();
726 //finally delete the removed layer pointer
727 delete tmpLayer;
728}
729
731{
732 moveLayerByOffset( + 1 );
733}
734
736{
737 moveLayerByOffset( -1 );
738}
739
740void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
741{
742 SymbolLayerItem *item = currentLayerItem();
743 if ( !item )
744 return;
745 const int row = item->row();
746
747 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
748 QgsSymbol *parentSymbol = parent->symbol();
749
750 const int layerIdx = parent->rowCount() - row - 1;
751 // switch layers
752 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
753 parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
754
755 QList<QStandardItem *> rowItems = parent->takeRow( row );
756 parent->insertRows( row + offset, rowItems );
757 parent->updatePreview();
758
759 const QModelIndex newIdx = rowItems[ 0 ]->index();
760 layersTree->setCurrentIndex( newIdx );
761
763 updateUi();
764}
765
767{
768 QgsSymbolLayer *layer = currentLayer();
769 if ( !layer )
770 return;
771 layer->setLocked( mLockColorAction->isChecked() );
772 updateLockButtonIcon();
773 emit symbolModified();
774}
775
777{
778 const QModelIndex idx = layersTree->currentIndex();
779 if ( !idx.isValid() )
780 return;
781
782 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
783 if ( !item->isLayer() )
784 return;
785
786 QgsSymbolLayer *source = item->layer();
787
788 const int insertIdx = item->row();
789 item = static_cast<SymbolLayerItem *>( item->parent() );
790
791 QgsSymbol *parentSymbol = item->symbol();
792
793 QgsSymbolLayer *newLayer = source->clone();
795 if ( insertIdx == -1 )
796 parentSymbol->appendSymbolLayer( newLayer );
797 else
798 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
799
800 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
801 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
802 if ( newLayer->subSymbol() )
803 {
804 loadSymbol( newLayer->subSymbol(), newLayerItem );
805 layersTree->setExpanded( newLayerItem->index(), true );
806 }
807 item->updatePreview();
808
809 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
810 updateUi();
812}
813
815{
816 SymbolLayerItem *item = currentLayerItem();
817 QgsSymbolLayer *layer = item->layer();
818
819 if ( layer->subSymbol() )
820 {
821 item->removeRow( 0 );
822 }
823 QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
824
825 // update symbol layer item
826 item->setLayer( newLayer, symbol->type() );
827 // When it is a marker symbol
828 if ( newLayer->subSymbol() )
829 {
830 loadSymbol( newLayer->subSymbol(), item );
831 layersTree->setExpanded( item->index(), true );
832 }
833
834 // Change the symbol at last to avoid deleting item's layer
835 const int layerIdx = item->parent()->rowCount() - item->row() - 1;
836 symbol->changeSymbolLayer( layerIdx, newLayer );
837
838 item->updatePreview();
840 // Important: This lets the layer have its own layer properties widget
841 layerChanged();
842}
843
844QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
845 : QDialog( parent )
846{
847 setLayout( new QVBoxLayout() );
848
849 mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
850 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
851
852 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
853 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
854 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
855
856 layout()->addWidget( mSelectorWidget );
857 layout()->addWidget( mButtonBox );
858
859 connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
860
861 mSelectorWidget->setMinimumSize( 460, 560 );
862 setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
864
865 // Can be embedded in renderer properties dialog
866 if ( embedded )
867 {
868 mButtonBox->hide();
869 layout()->setContentsMargins( 0, 0, 0, 0 );
870 }
871 else
872 {
873 setWindowTitle( tr( "Symbol Selector" ) );
874 }
875 mSelectorWidget->setDockMode( embedded );
876}
877
879{
880 return mSelectorWidget->advancedMenu();
881}
882
884{
885 mContext = context;
886}
887
889{
890 return mContext;
891}
892
894{
895 return mSelectorWidget->symbol();
896}
897
899{
900 // Ignore the ESC key to avoid close the dialog without the properties window
901 if ( !isWindow() && e->key() == Qt::Key_Escape )
902 {
903 e->ignore();
904 }
905 else
906 {
907 QDialog::keyPressEvent( e );
908 }
909}
910
911void QgsSymbolSelectorDialog::reloadSymbol()
912{
913 mSelectorWidget->reloadSymbol();
914}
915
916void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
917{
918 mSelectorWidget->loadSymbol( symbol, parent );
919}
920
921void QgsSymbolSelectorDialog::updateUi()
922{
923 mSelectorWidget->updateUi();
924}
925
926void QgsSymbolSelectorDialog::updateLockButton()
927{
928 mSelectorWidget->updateLockButton();
929}
930
931SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
932{
933 return mSelectorWidget->currentLayerItem();
934}
935
936QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
937{
938 return mSelectorWidget->currentLayer();
939}
940
941void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
942{
943 mSelectorWidget->moveLayerByOffset( offset );
944}
945
946void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
947{
948 mSelectorWidget->setWidget( widget );
949}
950
952{
953 mSelectorWidget->moveLayerDown();
954}
955
957{
958 mSelectorWidget->moveLayerUp();
959}
960
962{
963 mSelectorWidget->addLayer();
964}
965
967{
968 mSelectorWidget->removeLayer();
969}
970
972{
973 mSelectorWidget->lockLayer();
974}
975
977{
978 mSelectorWidget->duplicateLayer();
979}
980
982{
983 mSelectorWidget->layerChanged();
984}
985
990
992{
993 mSelectorWidget->updatePreview();
994}
995
997{
998 mSelectorWidget->symbolChanged();
999}
1000
1002{
1003 mSelectorWidget->changeLayer( layer );
1004}
1005
1006QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
1007{
1008 return mButtonBox;
1009}
1010
1011void QgsSymbolSelectorDialog::showHelp()
1012{
1013 QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
1014}
1015
1016void QgsSymbolSelectorWidget::projectDataChanged()
1017{
1018 mBlockModified = true;
1019 symbolChanged();
1020 updatePreview();
1021 mBlockModified = false;
1022}
1023
1024void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
1025{
1026 if ( mVectorLayer && layers.contains( mVectorLayer ) )
1027 {
1028 disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
1029 }
1030}
@ 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:368
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:4086
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.
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,...
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:4916
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:4915