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