QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
38#include <QColorDialog>
39#include <QPainter>
40#include <QStandardItemModel>
41#include <QInputDialog>
42#include <QMessageBox>
43#include <QKeyEvent>
44#include <QMenu>
45
46#include <QWidget>
47#include <QFile>
48#include <QStandardItem>
49
51
52static const int SYMBOL_LAYER_ITEM_TYPE = QStandardItem::UserType + 1;
53
54DataDefinedRestorer::DataDefinedRestorer( QgsSymbol *symbol, const QgsSymbolLayer *symbolLayer )
55
56{
57 if ( symbolLayer->type() == Qgis::SymbolType::Marker && symbol->type() == Qgis::SymbolType::Marker )
58 {
59 Q_ASSERT( symbol->type() == Qgis::SymbolType::Marker );
60 mMarker = static_cast<QgsMarkerSymbol *>( symbol );
61 mMarkerSymbolLayer = static_cast<const QgsMarkerSymbolLayer *>( symbolLayer );
62 mDDSize = mMarker->dataDefinedSize();
63 mDDAngle = mMarker->dataDefinedAngle();
64 // check if restore is actually needed
65 if ( !mDDSize && !mDDAngle )
66 mMarker = nullptr;
67 }
68 else if ( symbolLayer->type() == Qgis::SymbolType::Line && symbol->type() == Qgis::SymbolType::Line )
69 {
70 mLine = static_cast<QgsLineSymbol *>( symbol );
71 mLineSymbolLayer = static_cast<const QgsLineSymbolLayer *>( symbolLayer );
72 mDDWidth = mLine->dataDefinedWidth();
73 // check if restore is actually needed
74 if ( !mDDWidth )
75 mLine = nullptr;
76 }
77 save();
78}
79
80void DataDefinedRestorer::save()
81{
82 if ( mMarker )
83 {
84 mSize = mMarkerSymbolLayer->size();
85 mAngle = mMarkerSymbolLayer->angle();
86 mMarkerOffset = mMarkerSymbolLayer->offset();
87 }
88 else if ( mLine )
89 {
90 mWidth = mLineSymbolLayer->width();
91 mLineOffset = mLineSymbolLayer->offset();
92 }
93}
94
95void DataDefinedRestorer::restore()
96{
97 if ( mMarker )
98 {
99 if ( mDDSize &&
100 ( mSize != mMarkerSymbolLayer->size() || mMarkerOffset != mMarkerSymbolLayer->offset() ) )
101 mMarker->setDataDefinedSize( mDDSize );
102 if ( mDDAngle &&
103 mAngle != mMarkerSymbolLayer->angle() )
104 mMarker->setDataDefinedAngle( mDDAngle );
105 }
106 else if ( mLine )
107 {
108 if ( mDDWidth &&
109 ( mWidth != mLineSymbolLayer->width() || mLineOffset != mLineSymbolLayer->offset() ) )
110 mLine->setDataDefinedWidth( mDDWidth );
111 }
112 save();
113}
114
115// Hybrid item which may represent a symbol or a layer
116// Check using item->isLayer()
117class SymbolLayerItem : public QStandardItem
118{
119 public:
120 explicit SymbolLayerItem( QgsSymbolLayer *layer, Qgis::SymbolType symbolType, QgsVectorLayer *vectorLayer )
121 : mVectorLayer( vectorLayer )
122 {
123 setLayer( layer, symbolType );
124 }
125
126 explicit SymbolLayerItem( QgsSymbol *symbol, QgsVectorLayer *vectorLayer )
127 : mVectorLayer( vectorLayer )
128 {
129 setSymbol( symbol );
130 }
131
132 void setLayer( QgsSymbolLayer *layer, Qgis::SymbolType symbolType )
133 {
134 mLayer = layer;
135 mIsLayer = true;
136 mSymbol = nullptr;
137 mSymbolType = symbolType;
138 updatePreview();
139 }
140
141 void setSymbol( QgsSymbol *symbol )
142 {
143 mSymbol = symbol;
144 mIsLayer = false;
145 mLayer = nullptr;
146 updatePreview();
147 }
148
149 void updatePreview()
150 {
151 if ( !mSize.isValid() )
152 {
153 const int size = QgsGuiUtils::scaleIconSize( 16 );
154 mSize = QSize( size, size );
155 }
156 QIcon icon;
157 if ( mIsLayer )
158 icon = QgsSymbolLayerUtils::symbolLayerPreviewIcon( mLayer, QgsUnitTypes::RenderMillimeters, mSize, QgsMapUnitScale(), mSymbol ? mSymbol->type() : mSymbolType, mVectorLayer ); //todo: make unit a parameter
159 else
160 {
161 QgsExpressionContext expContext;
163 icon = QIcon( QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol, mSize, 0, nullptr, false, &expContext, nullptr ) );
164 }
165 setIcon( icon );
166
167 if ( auto *lParent = parent() )
168 static_cast<SymbolLayerItem *>( lParent )->updatePreview();
169 }
170
171 int type() const override { return SYMBOL_LAYER_ITEM_TYPE; }
172 bool isLayer() { return mIsLayer; }
173
174 // returns the symbol pointer; helpful in determining a layer's parent symbol
175 QgsSymbol *symbol()
176 {
177 return mSymbol;
178 }
179
180 QgsSymbolLayer *layer()
181 {
182 return mLayer;
183 }
184
185 QVariant data( int role ) const override
186 {
187 if ( role == Qt::DisplayRole || role == Qt::EditRole )
188 {
189 if ( mIsLayer )
190 {
192 if ( m )
193 return m->visibleName();
194 else
195 return QString();
196 }
197 else
198 {
199 switch ( mSymbol->type() )
200 {
202 return QCoreApplication::translate( "SymbolLayerItem", "Marker" );
204 return QCoreApplication::translate( "SymbolLayerItem", "Fill" );
206 return QCoreApplication::translate( "SymbolLayerItem", "Line" );
207 default:
208 return "Symbol";
209 }
210 }
211 }
212 else if ( role == Qt::ForegroundRole && mIsLayer )
213 {
214 if ( !mLayer->enabled() )
215 {
216 QPalette pal = qApp->palette();
217 QBrush brush = QStandardItem::data( role ).value< QBrush >();
218 brush.setColor( pal.color( QPalette::Disabled, QPalette::WindowText ) );
219 return brush;
220 }
221 else
222 {
223 return QVariant();
224 }
225 }
226
227// if ( role == Qt::SizeHintRole )
228// return QVariant( QSize( 32, 32 ) );
229 if ( role == Qt::CheckStateRole )
230 return QVariant(); // could be true/false
231 return QStandardItem::data( role );
232 }
233
234 protected:
235 QgsSymbolLayer *mLayer = nullptr;
236 QgsSymbol *mSymbol = nullptr;
237 QPointer< QgsVectorLayer > mVectorLayer;
238 bool mIsLayer = false;
239 QSize mSize;
241};
242
244
246
248 : QgsPanelWidget( parent )
249 , mStyle( style )
250 , mSymbol( symbol )
251 , mVectorLayer( vl )
252{
253#ifdef Q_OS_MAC
254 setWindowModality( Qt::WindowModal );
255#endif
256
257 setupUi( this );
258 this->layout()->setContentsMargins( 0, 0, 0, 0 );
259
260 layersTree->setMaximumHeight( static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
261 layersTree->setMinimumHeight( layersTree->maximumHeight() );
262 lblPreview->setMaximumWidth( layersTree->maximumHeight() );
263
264 // setup icons
265 btnAddLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
266 btnRemoveLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
267 QIcon iconLock;
268 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
269 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
270 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
271 iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
272 btnLock->setIcon( iconLock );
273 btnDuplicate->setIcon( QIcon( QgsApplication::iconPath( "mActionDuplicateLayer.svg" ) ) );
274 btnUp->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
275 btnDown->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
276
277 mSymbolLayersModel = new QStandardItemModel( layersTree );
278 // Set the symbol
279 layersTree->setModel( mSymbolLayersModel );
280 layersTree->setHeaderHidden( true );
281
282 //get first feature from layer for previews
283 if ( mVectorLayer )
284 {
285#if 0 // this is too expensive to do for many providers. TODO revisit when support for connection timeouts is complete across all providers
286 // short timeout for request - it doesn't really matter if we don't get the feature, and this call is blocking UI
287 QgsFeatureIterator it = mVectorLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ).setConnectionTimeout( 100 ) );
288 it.nextFeature( mPreviewFeature );
289#endif
290 mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
291#if 0
292 mPreviewExpressionContext.setFeature( mPreviewFeature );
293#endif
294 }
295 else
296 {
297 mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
298 }
299
300 QItemSelectionModel *selModel = layersTree->selectionModel();
301 connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
302
303 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
305
306 connect( btnUp, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerUp );
307 connect( btnDown, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerDown );
308 connect( btnAddLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::addLayer );
309 connect( btnRemoveLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::removeLayer );
310 connect( btnLock, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::lockLayer );
311 connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
313
314 updateUi();
315
316 // set symbol as active item in the tree
317 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
318 layersTree->setCurrentIndex( newIndex );
319
320 setPanelTitle( tr( "Symbol Selector" ) );
321
322 // when a remote svg has been fetched, update the widget's previews
323 // this is required if the symbol utilizes remote svgs, and the current previews
324 // have been generated using the temporary "downloading" svg. In this case
325 // we require the preview to be regenerated to use the correct fetched
326 // svg
327 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
328
329 // when a remote image has been fetched, update the widget's previews
330 // this is required if the symbol utilizes remote images, and the current previews
331 // have been generated using the temporary "downloading" image. In this case
332 // we require the preview to be regenerated to use the correct fetched
333 // image
334 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
335
336 // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
337 // need updating to reflect the new colors
338 connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
339
340 connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ), this, &QgsSymbolSelectorWidget::layersAboutToBeRemoved );
341}
342
344{
345 if ( !mAdvancedMenu )
346 {
347 mAdvancedMenu = new QMenu( this );
348 // Brute force method to activate the Advanced menu
349 layerChanged();
350 }
351 return mAdvancedMenu;
352}
353
355{
356 mContext = context;
357
358 if ( auto *lExpressionContext = mContext.expressionContext() )
359 {
360 mPreviewExpressionContext = *lExpressionContext;
361 if ( mVectorLayer )
362 mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
363
364 mPreviewExpressionContext.setFeature( mPreviewFeature );
365 }
366
367 QWidget *widget = stackedWidget->currentWidget();
368 if ( QgsLayerPropertiesWidget *layerProp = qobject_cast< QgsLayerPropertiesWidget * >( widget ) )
369 layerProp->setContext( context );
370 else if ( QgsSymbolsListWidget *listWidget = qobject_cast< QgsSymbolsListWidget * >( widget ) )
371 listWidget->setContext( context );
372
373 layerChanged();
375}
376
378{
379 return mContext;
380}
381
382void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
383{
384 if ( !symbol )
385 return;
386
387 if ( !parent )
388 {
389 mSymbol = symbol;
390 mSymbolLayersModel->clear();
391 parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
392 }
393
394 SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol, mVectorLayer );
395 QFont boldFont = symbolItem->font();
396 boldFont.setBold( true );
397 symbolItem->setFont( boldFont );
398 parent->appendRow( symbolItem );
399
400 const int count = symbol->symbolLayerCount();
401 for ( int i = count - 1; i >= 0; i-- )
402 {
403 SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ), symbol->type(), mVectorLayer );
404 layerItem->setEditable( false );
405 symbolItem->appendRow( layerItem );
406 if ( symbol->symbolLayer( i )->subSymbol() )
407 {
408 loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
409 }
410 layersTree->setExpanded( layerItem->index(), true );
411 }
412 layersTree->setExpanded( symbolItem->index(), true );
413
414 if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
415 {
416 // make sure root item for symbol is selected in tree
417 layersTree->setCurrentIndex( symbolItem->index() );
418 }
419}
420
421void QgsSymbolSelectorWidget::reloadSymbol()
422{
423 mSymbolLayersModel->clear();
424 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
425}
426
427void QgsSymbolSelectorWidget::updateUi()
428{
429 const QModelIndex currentIdx = layersTree->currentIndex();
430 if ( !currentIdx.isValid() )
431 return;
432
433 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
434 if ( !item->isLayer() )
435 {
436 btnUp->setEnabled( false );
437 btnDown->setEnabled( false );
438 btnRemoveLayer->setEnabled( false );
439 btnLock->setEnabled( false );
440 btnDuplicate->setEnabled( false );
441 return;
442 }
443
444 const int rowCount = item->parent()->rowCount();
445 const int currentRow = item->row();
446
447 btnUp->setEnabled( currentRow > 0 );
448 btnDown->setEnabled( currentRow < rowCount - 1 );
449 btnRemoveLayer->setEnabled( rowCount > 1 );
450 btnLock->setEnabled( true );
451 btnDuplicate->setEnabled( true );
452}
453
455{
456 if ( !mSymbol )
457 return;
458
459 std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
460 const QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlags() );
461 lblPreview->setPixmap( QPixmap::fromImage( preview ) );
462 // Hope this is a appropriate place
463 if ( !mBlockModified )
464 emit symbolModified();
465}
466
468{
469 // get current layer item and update its icon
470 SymbolLayerItem *item = currentLayerItem();
471 if ( item )
472 item->updatePreview();
473 // update also preview of the whole symbol
475}
476
477SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
478{
479 const QModelIndex idx = layersTree->currentIndex();
480 if ( !idx.isValid() )
481 return nullptr;
482
483 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
484 if ( !item->isLayer() )
485 return nullptr;
486
487 return item;
488}
489
490QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
491{
492 const QModelIndex idx = layersTree->currentIndex();
493 if ( !idx.isValid() )
494 return nullptr;
495
496 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
497 if ( item->isLayer() )
498 return item->layer();
499
500 return nullptr;
501}
502
504{
505 updateUi();
506
507 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
508 if ( !currentItem )
509 return;
510
511 if ( currentItem->isLayer() )
512 {
513 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
514 mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
515 QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
516 layerProp->setDockMode( this->dockMode() );
517 layerProp->setContext( mContext );
518 setWidget( layerProp );
519 connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
521 // This connection when layer type is changed
523
524 connectChildPanel( layerProp );
525 }
526 else
527 {
528 // then it must be a symbol
529 mDataDefineRestorer.reset();
531 currentItem->symbol()->setLayer( mVectorLayer );
533 // Now populate symbols of that type using the symbols list widget:
534 QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
535 symbolsList->setContext( mContext );
536
537 setWidget( symbolsList );
539 }
540 updateLockButton();
541}
542
544{
545 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
546 if ( !currentItem || currentItem->isLayer() )
547 return;
548 // disconnect to avoid recreating widget
549 disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
550 if ( currentItem->parent() )
551 {
552 // it is a sub-symbol
553 QgsSymbol *symbol = currentItem->symbol();
554 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
555 parent->removeRow( 0 );
556 loadSymbol( symbol, parent );
557 layersTree->setCurrentIndex( parent->child( 0 )->index() );
558 parent->updatePreview();
559 }
560 else
561 {
562 //it is the symbol itself
563 reloadSymbol();
564 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
565 layersTree->setCurrentIndex( newIndex );
566 }
568 // connect it back once things are set
569 connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
570}
571
572void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
573{
574 const int index = stackedWidget->addWidget( widget );
575 stackedWidget->setCurrentIndex( index );
576 if ( mPresentWidget )
577 mPresentWidget->deleteLater();
578 mPresentWidget = widget;
579}
580
581void QgsSymbolSelectorWidget::updateLockButton()
582{
583 QgsSymbolLayer *layer = currentLayer();
584 if ( !layer )
585 return;
586 btnLock->setChecked( layer->isLocked() );
587}
588
590{
591 const QModelIndex idx = layersTree->currentIndex();
592 if ( !idx.isValid() )
593 return;
594
595 int insertIdx = -1;
596 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
597 if ( item->isLayer() )
598 {
599 insertIdx = item->row();
600 item = static_cast<SymbolLayerItem *>( item->parent() );
601 }
602
603 QgsSymbol *parentSymbol = item->symbol();
604
605 // save data-defined values at marker level
606 const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker
607 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
608 : QgsProperty() );
609 const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker
610 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
611 : QgsProperty() );
612 const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line
613 ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
614 : QgsProperty() );
615
617 if ( insertIdx == -1 )
618 parentSymbol->appendSymbolLayer( newLayer );
619 else
620 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
621
622 // restore data-defined values at marker level
623 if ( ddSize )
624 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
625 if ( ddAngle )
626 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
627 if ( ddWidth )
628 static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
629
630 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer );
631 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
632 item->updatePreview();
633
634 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
635 updateUi();
637}
638
640{
641 SymbolLayerItem *item = currentLayerItem();
642 const int row = item->row();
643 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
644
645 const int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
646 QgsSymbol *parentSymbol = parent->symbol();
647 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
648
649 parent->removeRow( row );
650 parent->updatePreview();
651
652 const QModelIndex newIdx = parent->child( 0 )->index();
653 layersTree->setCurrentIndex( newIdx );
654
655 updateUi();
657 //finally delete the removed layer pointer
658 delete tmpLayer;
659}
660
662{
663 moveLayerByOffset( + 1 );
664}
665
667{
668 moveLayerByOffset( -1 );
669}
670
671void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
672{
673 SymbolLayerItem *item = currentLayerItem();
674 if ( !item )
675 return;
676 const int row = item->row();
677
678 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
679 QgsSymbol *parentSymbol = parent->symbol();
680
681 const int layerIdx = parent->rowCount() - row - 1;
682 // switch layers
683 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
684 parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
685
686 QList<QStandardItem *> rowItems = parent->takeRow( row );
687 parent->insertRows( row + offset, rowItems );
688 parent->updatePreview();
689
690 const QModelIndex newIdx = rowItems[ 0 ]->index();
691 layersTree->setCurrentIndex( newIdx );
692
694 updateUi();
695}
696
698{
699 QgsSymbolLayer *layer = currentLayer();
700 if ( !layer )
701 return;
702 layer->setLocked( btnLock->isChecked() );
703 emit symbolModified();
704}
705
707{
708 const QModelIndex idx = layersTree->currentIndex();
709 if ( !idx.isValid() )
710 return;
711
712 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
713 if ( !item->isLayer() )
714 return;
715
716 QgsSymbolLayer *source = item->layer();
717
718 const int insertIdx = item->row();
719 item = static_cast<SymbolLayerItem *>( item->parent() );
720
721 QgsSymbol *parentSymbol = item->symbol();
722
723 QgsSymbolLayer *newLayer = source->clone();
724 if ( insertIdx == -1 )
725 parentSymbol->appendSymbolLayer( newLayer );
726 else
727 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
728
729 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer );
730 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
731 if ( newLayer->subSymbol() )
732 {
733 loadSymbol( newLayer->subSymbol(), newLayerItem );
734 layersTree->setExpanded( newLayerItem->index(), true );
735 }
736 item->updatePreview();
737
738 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
739 updateUi();
741}
742
744{
745 SymbolLayerItem *item = currentLayerItem();
746 QgsSymbolLayer *layer = item->layer();
747
748 if ( layer->subSymbol() )
749 {
750 item->removeRow( 0 );
751 }
752 QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
753
754 // update symbol layer item
755 item->setLayer( newLayer, symbol->type() );
756 // When it is a marker symbol
757 if ( newLayer->subSymbol() )
758 {
759 loadSymbol( newLayer->subSymbol(), item );
760 layersTree->setExpanded( item->index(), true );
761 }
762
763 // Change the symbol at last to avoid deleting item's layer
764 const int layerIdx = item->parent()->rowCount() - item->row() - 1;
765 symbol->changeSymbolLayer( layerIdx, newLayer );
766
767 item->updatePreview();
769 // Important: This lets the layer have its own layer properties widget
770 layerChanged();
771}
772
773QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
774 : QDialog( parent )
775{
776 setLayout( new QVBoxLayout() );
777
778 mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
779 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
780
781 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
782 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
783 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
784
785 layout()->addWidget( mSelectorWidget );
786 layout()->addWidget( mButtonBox );
787
788 connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
789
790 mSelectorWidget->setMinimumSize( 460, 560 );
791 setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
793
794 // Can be embedded in renderer properties dialog
795 if ( embedded )
796 {
797 mButtonBox->hide();
798 layout()->setContentsMargins( 0, 0, 0, 0 );
799 }
800 else
801 {
802 setWindowTitle( tr( "Symbol Selector" ) );
803 }
804 mSelectorWidget->setDockMode( embedded );
805}
806
808{
809 return mSelectorWidget->advancedMenu();
810}
811
813{
814 mContext = context;
815}
816
818{
819 return mContext;
820}
821
823{
824 return mSelectorWidget->symbol();
825}
826
828{
829 // Ignore the ESC key to avoid close the dialog without the properties window
830 if ( !isWindow() && e->key() == Qt::Key_Escape )
831 {
832 e->ignore();
833 }
834 else
835 {
836 QDialog::keyPressEvent( e );
837 }
838}
839
840void QgsSymbolSelectorDialog::reloadSymbol()
841{
842 mSelectorWidget->reloadSymbol();
843}
844
845void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
846{
847 mSelectorWidget->loadSymbol( symbol, parent );
848}
849
850void QgsSymbolSelectorDialog::updateUi()
851{
852 mSelectorWidget->updateUi();
853}
854
855void QgsSymbolSelectorDialog::updateLockButton()
856{
857 mSelectorWidget->updateLockButton();
858}
859
860SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
861{
862 return mSelectorWidget->currentLayerItem();
863}
864
865QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
866{
867 return mSelectorWidget->currentLayer();
868}
869
870void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
871{
872 mSelectorWidget->moveLayerByOffset( offset );
873}
874
875void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
876{
877 mSelectorWidget->setWidget( widget );
878}
879
881{
882 mSelectorWidget->moveLayerDown();
883}
884
886{
887 mSelectorWidget->moveLayerUp();
888}
889
891{
892 mSelectorWidget->addLayer();
893}
894
896{
897 mSelectorWidget->removeLayer();
898}
899
901{
902 mSelectorWidget->lockLayer();
903}
904
906{
907 mSelectorWidget->duplicateLayer();
908}
909
911{
912 mSelectorWidget->layerChanged();
913}
914
916{
917 mSelectorWidget->updateLayerPreview();
918}
919
921{
922 mSelectorWidget->updatePreview();
923}
924
926{
927 mSelectorWidget->symbolChanged();
928}
929
931{
932 mSelectorWidget->changeLayer( layer );
933}
934
935QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
936{
937 return mButtonBox;
938}
939
940void QgsSymbolSelectorDialog::showHelp()
941{
942 QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
943}
944
945void QgsSymbolSelectorWidget::projectDataChanged()
946{
947 mBlockModified = true;
950 mBlockModified = false;
951}
952
953void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
954{
955 if ( mVectorLayer && layers.contains( mVectorLayer ) )
956 {
957 disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
958 }
959}
SymbolType
Symbol types.
Definition: qgis.h:206
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:2304
static QgsSymbolLayerRegistry * symbolLayerRegistry()
Returns the application's symbol layer registry, used for managing symbol layers.
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:178
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.
Definition: qgslinesymbol.h:30
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:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
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.
Definition: qgsproperty.h:230
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, QgsUnitTypes::RenderUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::SymbolType parentSymbolType=Qgis::SymbolType::Hybrid, QgsMapLayer *mapLayer=nullptr)
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)
Returns a pixmap preview for a color ramp.
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...
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
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:93
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:756
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:779
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:766
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:809
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:800
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:215
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:1227
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
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:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3060