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