QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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 btnLock->setIcon( iconLock );
277 btnDuplicate->setIcon( QIcon( QgsApplication::iconPath( "mActionDuplicateLayer.svg" ) ) );
278 btnUp->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
279 btnDown->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
280
281 mSymbolLayersModel = new QStandardItemModel( layersTree );
282 // Set the symbol
283 layersTree->setModel( mSymbolLayersModel );
284 layersTree->setHeaderHidden( true );
285
286 //get first feature from layer for previews
287 if ( mVectorLayer )
288 {
289#if 0 // this is too expensive to do for many providers. TODO revisit when support for connection timeouts is complete across all providers
290 // short timeout for request - it doesn't really matter if we don't get the feature, and this call is blocking UI
291 QgsFeatureIterator it = mVectorLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ).setConnectionTimeout( 100 ) );
292 it.nextFeature( mPreviewFeature );
293#endif
294 mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
295#if 0
296 mPreviewExpressionContext.setFeature( mPreviewFeature );
297#endif
298 }
299 else
300 {
301 mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
302 }
303
304 QItemSelectionModel *selModel = layersTree->selectionModel();
305 connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
306
307 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
309
310 connect( btnUp, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerUp );
311 connect( btnDown, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerDown );
312 connect( btnAddLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::addLayer );
313 connect( btnRemoveLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::removeLayer );
314 connect( btnLock, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::lockLayer );
315 connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
317
318 updateUi();
319
320 // set symbol as active item in the tree
321 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
322 layersTree->setCurrentIndex( newIndex );
323
324 setPanelTitle( tr( "Symbol Selector" ) );
325
326 // when a remote svg has been fetched, update the widget's previews
327 // this is required if the symbol utilizes remote svgs, and the current previews
328 // have been generated using the temporary "downloading" svg. In this case
329 // we require the preview to be regenerated to use the correct fetched
330 // svg
331 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
332
333 // when a remote image has been fetched, update the widget's previews
334 // this is required if the symbol utilizes remote images, and the current previews
335 // have been generated using the temporary "downloading" image. In this case
336 // we require the preview to be regenerated to use the correct fetched
337 // image
338 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
339
340 // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
341 // need updating to reflect the new colors
342 connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
343
344 connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ), this, &QgsSymbolSelectorWidget::layersAboutToBeRemoved );
345}
346
348{
349 if ( !mAdvancedMenu )
350 {
351 mAdvancedMenu = new QMenu( this );
352 // Brute force method to activate the Advanced menu
353 layerChanged();
354 }
355 return mAdvancedMenu;
356}
357
359{
360 mContext = context;
361
362 if ( auto *lExpressionContext = mContext.expressionContext() )
363 {
364 mPreviewExpressionContext = *lExpressionContext;
365 if ( mVectorLayer )
366 mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
367
368 mPreviewExpressionContext.setFeature( mPreviewFeature );
369 }
370
371 QWidget *widget = stackedWidget->currentWidget();
372 if ( QgsLayerPropertiesWidget *layerProp = qobject_cast< QgsLayerPropertiesWidget * >( widget ) )
373 layerProp->setContext( context );
374 else if ( QgsSymbolsListWidget *listWidget = qobject_cast< QgsSymbolsListWidget * >( widget ) )
375 listWidget->setContext( context );
376
377 layerChanged();
379}
380
382{
383 return mContext;
384}
385
386void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
387{
388 if ( !symbol )
389 return;
390
391 if ( !parent )
392 {
393 mSymbol = symbol;
394 mSymbolLayersModel->clear();
395 parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
396 }
397
398 SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol, mVectorLayer, screen() );
399 QFont boldFont = symbolItem->font();
400 boldFont.setBold( true );
401 symbolItem->setFont( boldFont );
402 parent->appendRow( symbolItem );
403
404 const int count = symbol->symbolLayerCount();
405 for ( int i = count - 1; i >= 0; i-- )
406 {
407 SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ), symbol->type(), mVectorLayer, screen() );
408 layerItem->setEditable( false );
409 symbolItem->appendRow( layerItem );
410 if ( symbol->symbolLayer( i )->subSymbol() )
411 {
412 loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
413 }
414 layersTree->setExpanded( layerItem->index(), true );
415 }
416 layersTree->setExpanded( symbolItem->index(), true );
417
418 if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
419 {
420 // make sure root item for symbol is selected in tree
421 layersTree->setCurrentIndex( symbolItem->index() );
422 }
423}
424
425void QgsSymbolSelectorWidget::reloadSymbol()
426{
427 mSymbolLayersModel->clear();
428 loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
429}
430
431void QgsSymbolSelectorWidget::updateUi()
432{
433 const QModelIndex currentIdx = layersTree->currentIndex();
434 if ( !currentIdx.isValid() )
435 return;
436
437 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
438 if ( !item->isLayer() )
439 {
440 btnUp->setEnabled( false );
441 btnDown->setEnabled( false );
442 btnRemoveLayer->setEnabled( false );
443 btnLock->setEnabled( false );
444 btnDuplicate->setEnabled( false );
445 return;
446 }
447
448 const int rowCount = item->parent()->rowCount();
449 const int currentRow = item->row();
450
451 btnUp->setEnabled( currentRow > 0 );
452 btnDown->setEnabled( currentRow < rowCount - 1 );
453 btnRemoveLayer->setEnabled( rowCount > 1 );
454 btnLock->setEnabled( true );
455 btnDuplicate->setEnabled( true );
456}
457
459{
460 if ( !mSymbol )
461 return;
462
463 std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
464 const QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlag::FlagIncludeCrosshairsForMarkerSymbols, QgsScreenProperties( screen() ) );
465 lblPreview->setPixmap( QPixmap::fromImage( preview ) );
466 // Hope this is a appropriate place
467 if ( !mBlockModified )
468 emit symbolModified();
469}
470
472{
473 // get current layer item and update its icon
474 SymbolLayerItem *item = currentLayerItem();
475 if ( item )
476 item->updatePreview();
477 // update also preview of the whole symbol
479}
480
481SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
482{
483 const QModelIndex idx = layersTree->currentIndex();
484 if ( !idx.isValid() )
485 return nullptr;
486
487 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
488 if ( !item->isLayer() )
489 return nullptr;
490
491 return item;
492}
493
494QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
495{
496 const QModelIndex idx = layersTree->currentIndex();
497 if ( !idx.isValid() )
498 return nullptr;
499
500 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
501 if ( item->isLayer() )
502 return item->layer();
503
504 return nullptr;
505}
506
508{
509 updateUi();
510
511 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
512 if ( !currentItem )
513 return;
514
515 if ( currentItem->isLayer() )
516 {
517 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
518 mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
519 QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
520 layerProp->setDockMode( this->dockMode() );
521 layerProp->setContext( mContext );
522 setWidget( layerProp );
523 connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
525 // This connection when layer type is changed
527
528 connectChildPanel( layerProp );
529 }
530 else
531 {
532 // then it must be a symbol
533 mDataDefineRestorer.reset();
535 currentItem->symbol()->setLayer( mVectorLayer );
537 // Now populate symbols of that type using the symbols list widget:
538 QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
539 symbolsList->setContext( mContext );
540
541 setWidget( symbolsList );
543 }
544 updateLockButton();
545}
546
548{
549 SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
550 if ( !currentItem || currentItem->isLayer() )
551 return;
552 // disconnect to avoid recreating widget
553 disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
554 if ( currentItem->parent() )
555 {
556 // it is a sub-symbol
557 QgsSymbol *symbol = currentItem->symbol();
558 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
559 parent->removeRow( 0 );
560 loadSymbol( symbol, parent );
561 layersTree->setCurrentIndex( parent->child( 0 )->index() );
562 parent->updatePreview();
563 }
564 else
565 {
566 //it is the symbol itself
567 reloadSymbol();
568 const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
569 layersTree->setCurrentIndex( newIndex );
570 }
572 // connect it back once things are set
573 connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
574}
575
576void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
577{
578 const int index = stackedWidget->addWidget( widget );
579 stackedWidget->setCurrentIndex( index );
580 if ( mPresentWidget )
581 mPresentWidget->deleteLater();
582 mPresentWidget = widget;
583}
584
585void QgsSymbolSelectorWidget::updateLockButton()
586{
587 QgsSymbolLayer *layer = currentLayer();
588 if ( !layer )
589 return;
590 btnLock->setChecked( layer->isLocked() );
591}
592
594{
595 const QModelIndex idx = layersTree->currentIndex();
596 if ( !idx.isValid() )
597 return;
598
599 int insertIdx = -1;
600 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
601 if ( item->isLayer() )
602 {
603 insertIdx = item->row();
604 item = static_cast<SymbolLayerItem *>( item->parent() );
605 }
606
607 QgsSymbol *parentSymbol = item->symbol();
608
609 // save data-defined values at marker level
610 const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker
611 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
612 : QgsProperty() );
613 const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker
614 ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
615 : QgsProperty() );
616 const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line
617 ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
618 : QgsProperty() );
619
621 if ( insertIdx == -1 )
622 parentSymbol->appendSymbolLayer( newLayer );
623 else
624 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
625
626 // restore data-defined values at marker level
627 if ( ddSize )
628 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
629 if ( ddAngle )
630 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
631 if ( ddWidth )
632 static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
633
634 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
635 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
636 item->updatePreview();
637
638 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
639 updateUi();
641}
642
644{
645 SymbolLayerItem *item = currentLayerItem();
646 const int row = item->row();
647 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
648
649 const int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
650 QgsSymbol *parentSymbol = parent->symbol();
651 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
652
653 parent->removeRow( row );
654 parent->updatePreview();
655
656 const QModelIndex newIdx = parent->child( 0 )->index();
657 layersTree->setCurrentIndex( newIdx );
658
659 updateUi();
661 //finally delete the removed layer pointer
662 delete tmpLayer;
663}
664
666{
667 moveLayerByOffset( + 1 );
668}
669
671{
672 moveLayerByOffset( -1 );
673}
674
675void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
676{
677 SymbolLayerItem *item = currentLayerItem();
678 if ( !item )
679 return;
680 const int row = item->row();
681
682 SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
683 QgsSymbol *parentSymbol = parent->symbol();
684
685 const int layerIdx = parent->rowCount() - row - 1;
686 // switch layers
687 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
688 parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
689
690 QList<QStandardItem *> rowItems = parent->takeRow( row );
691 parent->insertRows( row + offset, rowItems );
692 parent->updatePreview();
693
694 const QModelIndex newIdx = rowItems[ 0 ]->index();
695 layersTree->setCurrentIndex( newIdx );
696
698 updateUi();
699}
700
702{
703 QgsSymbolLayer *layer = currentLayer();
704 if ( !layer )
705 return;
706 layer->setLocked( btnLock->isChecked() );
707 emit symbolModified();
708}
709
711{
712 const QModelIndex idx = layersTree->currentIndex();
713 if ( !idx.isValid() )
714 return;
715
716 SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
717 if ( !item->isLayer() )
718 return;
719
720 QgsSymbolLayer *source = item->layer();
721
722 const int insertIdx = item->row();
723 item = static_cast<SymbolLayerItem *>( item->parent() );
724
725 QgsSymbol *parentSymbol = item->symbol();
726
727 QgsSymbolLayer *newLayer = source->clone();
729 if ( insertIdx == -1 )
730 parentSymbol->appendSymbolLayer( newLayer );
731 else
732 parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
733
734 SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type(), mVectorLayer, screen() );
735 item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
736 if ( newLayer->subSymbol() )
737 {
738 loadSymbol( newLayer->subSymbol(), newLayerItem );
739 layersTree->setExpanded( newLayerItem->index(), true );
740 }
741 item->updatePreview();
742
743 layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
744 updateUi();
746}
747
749{
750 SymbolLayerItem *item = currentLayerItem();
751 QgsSymbolLayer *layer = item->layer();
752
753 if ( layer->subSymbol() )
754 {
755 item->removeRow( 0 );
756 }
757 QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
758
759 // update symbol layer item
760 item->setLayer( newLayer, symbol->type() );
761 // When it is a marker symbol
762 if ( newLayer->subSymbol() )
763 {
764 loadSymbol( newLayer->subSymbol(), item );
765 layersTree->setExpanded( item->index(), true );
766 }
767
768 // Change the symbol at last to avoid deleting item's layer
769 const int layerIdx = item->parent()->rowCount() - item->row() - 1;
770 symbol->changeSymbolLayer( layerIdx, newLayer );
771
772 item->updatePreview();
774 // Important: This lets the layer have its own layer properties widget
775 layerChanged();
776}
777
778QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
779 : QDialog( parent )
780{
781 setLayout( new QVBoxLayout() );
782
783 mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
784 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
785
786 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
787 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
788 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
789
790 layout()->addWidget( mSelectorWidget );
791 layout()->addWidget( mButtonBox );
792
793 connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
794
795 mSelectorWidget->setMinimumSize( 460, 560 );
796 setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
798
799 // Can be embedded in renderer properties dialog
800 if ( embedded )
801 {
802 mButtonBox->hide();
803 layout()->setContentsMargins( 0, 0, 0, 0 );
804 }
805 else
806 {
807 setWindowTitle( tr( "Symbol Selector" ) );
808 }
809 mSelectorWidget->setDockMode( embedded );
810}
811
813{
814 return mSelectorWidget->advancedMenu();
815}
816
818{
819 mContext = context;
820}
821
823{
824 return mContext;
825}
826
828{
829 return mSelectorWidget->symbol();
830}
831
833{
834 // Ignore the ESC key to avoid close the dialog without the properties window
835 if ( !isWindow() && e->key() == Qt::Key_Escape )
836 {
837 e->ignore();
838 }
839 else
840 {
841 QDialog::keyPressEvent( e );
842 }
843}
844
845void QgsSymbolSelectorDialog::reloadSymbol()
846{
847 mSelectorWidget->reloadSymbol();
848}
849
850void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
851{
852 mSelectorWidget->loadSymbol( symbol, parent );
853}
854
855void QgsSymbolSelectorDialog::updateUi()
856{
857 mSelectorWidget->updateUi();
858}
859
860void QgsSymbolSelectorDialog::updateLockButton()
861{
862 mSelectorWidget->updateLockButton();
863}
864
865SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
866{
867 return mSelectorWidget->currentLayerItem();
868}
869
870QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
871{
872 return mSelectorWidget->currentLayer();
873}
874
875void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
876{
877 mSelectorWidget->moveLayerByOffset( offset );
878}
879
880void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
881{
882 mSelectorWidget->setWidget( widget );
883}
884
886{
887 mSelectorWidget->moveLayerDown();
888}
889
891{
892 mSelectorWidget->moveLayerUp();
893}
894
896{
897 mSelectorWidget->addLayer();
898}
899
901{
902 mSelectorWidget->removeLayer();
903}
904
906{
907 mSelectorWidget->lockLayer();
908}
909
911{
912 mSelectorWidget->duplicateLayer();
913}
914
916{
917 mSelectorWidget->layerChanged();
918}
919
921{
922 mSelectorWidget->updateLayerPreview();
923}
924
926{
927 mSelectorWidget->updatePreview();
928}
929
931{
932 mSelectorWidget->symbolChanged();
933}
934
936{
937 mSelectorWidget->changeLayer( layer );
938}
939
940QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
941{
942 return mButtonBox;
943}
944
945void QgsSymbolSelectorDialog::showHelp()
946{
947 QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
948}
949
950void QgsSymbolSelectorWidget::projectDataChanged()
951{
952 mBlockModified = true;
955 mBlockModified = false;
956}
957
958void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
959{
960 if ( mVectorLayer && layers.contains( mVectorLayer ) )
961 {
962 disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
963 }
964}
@ FlagIncludeCrosshairsForMarkerSymbols
Include a crosshairs reference image in the background of marker symbol previews.
SymbolType
Attribute editing capabilities which may be supported by vector data providers.
Definition: qgis.h:340
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:3749
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:193
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:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:484
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
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...
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:94
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:759
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:782
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:769
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:812
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:803
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
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:1244
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:4572
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:4571