QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgscategorizedsymbolrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrendererwidget.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 #include "qgspanelwidget.h"
18 
20 
23 #include "qgssymbol.h"
24 #include "qgssymbollayerutils.h"
25 #include "qgscolorramp.h"
26 #include "qgscolorrampbutton.h"
27 #include "qgsstyle.h"
28 #include "qgslogger.h"
30 
33 
34 #include "qgsvectorlayer.h"
35 #include "qgsfeatureiterator.h"
36 
37 #include "qgsproject.h"
38 #include "qgsexpression.h"
39 #include "qgsmapcanvas.h"
40 #include "qgssettings.h"
41 #include "qgsguiutils.h"
42 
43 #include <QKeyEvent>
44 #include <QMenu>
45 #include <QMessageBox>
46 #include <QStandardItemModel>
47 #include <QStandardItem>
48 #include <QPen>
49 #include <QPainter>
50 #include <QFileDialog>
51 
53 
54 QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
55  , mMimeFormat( QStringLiteral( "application/x-qgscategorizedsymbolrendererv2model" ) )
56 {
57 }
58 
59 void QgsCategorizedSymbolRendererModel::setRenderer( QgsCategorizedSymbolRenderer *renderer )
60 {
61  if ( mRenderer )
62  {
63  beginRemoveRows( QModelIndex(), 0, std::max( mRenderer->categories().size() - 1, 0 ) );
64  mRenderer = nullptr;
65  endRemoveRows();
66  }
67  if ( renderer )
68  {
69  mRenderer = renderer;
70  if ( renderer->categories().size() > 0 )
71  {
72  beginInsertRows( QModelIndex(), 0, renderer->categories().size() - 1 );
73  endInsertRows();
74  }
75  }
76 }
77 
78 void QgsCategorizedSymbolRendererModel::addCategory( const QgsRendererCategory &cat )
79 {
80  if ( !mRenderer ) return;
81  int idx = mRenderer->categories().size();
82  beginInsertRows( QModelIndex(), idx, idx );
83  mRenderer->addCategory( cat );
84  endInsertRows();
85 }
86 
87 QgsRendererCategory QgsCategorizedSymbolRendererModel::category( const QModelIndex &index )
88 {
89  if ( !mRenderer )
90  {
91  return QgsRendererCategory();
92  }
93  const QgsCategoryList &catList = mRenderer->categories();
94  int row = index.row();
95  if ( row >= catList.size() )
96  {
97  return QgsRendererCategory();
98  }
99  return catList.at( row );
100 }
101 
102 
103 Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags( const QModelIndex &index ) const
104 {
105  if ( !index.isValid() || !mRenderer )
106  {
107  return Qt::ItemIsDropEnabled;
108  }
109 
110  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
111  if ( index.column() == 1 )
112  {
113  const QgsRendererCategory category = mRenderer->categories().value( index.row() );
114  if ( category.value().type() != QVariant::List )
115  {
116  flags |= Qt::ItemIsEditable;
117  }
118  }
119  else if ( index.column() == 2 )
120  {
121  flags |= Qt::ItemIsEditable;
122  }
123  return flags;
124 }
125 
126 Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions() const
127 {
128  return Qt::MoveAction;
129 }
130 
131 QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int role ) const
132 {
133  if ( !index.isValid() || !mRenderer )
134  return QVariant();
135 
136  const QgsRendererCategory category = mRenderer->categories().value( index.row() );
137 
138  switch ( role )
139  {
140  case Qt::CheckStateRole:
141  {
142  if ( index.column() == 0 )
143  {
144  return category.renderState() ? Qt::Checked : Qt::Unchecked;
145  }
146  break;
147  }
148 
149  case Qt::DisplayRole:
150  case Qt::ToolTipRole:
151  {
152  switch ( index.column() )
153  {
154  case 1:
155  {
156  if ( category.value().type() == QVariant::List )
157  {
158  QStringList res;
159  const QVariantList list = category.value().toList();
160  res.reserve( list.size() );
161  for ( const QVariant &v : list )
162  res << v.toString();
163 
164  if ( role == Qt::DisplayRole )
165  return res.join( ';' );
166  else // tooltip
167  return res.join( '\n' );
168  }
169  else if ( !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() )
170  {
171  return tr( "all other values" );
172  }
173  else
174  {
175  return category.value().toString();
176  }
177  }
178  case 2:
179  return category.label();
180  }
181  break;
182  }
183 
184  case Qt::FontRole:
185  {
186  if ( index.column() == 1 && category.value().type() != QVariant::List && ( !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() ) )
187  {
188  QFont italicFont;
189  italicFont.setItalic( true );
190  return italicFont;
191  }
192  return QVariant();
193  }
194 
195  case Qt::DecorationRole:
196  {
197  if ( index.column() == 0 && category.symbol() )
198  {
199  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
200  return QgsSymbolLayerUtils::symbolPreviewIcon( category.symbol(), QSize( iconSize, iconSize ) );
201  }
202  break;
203  }
204 
205  case Qt::ForegroundRole:
206  {
207  QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
208  if ( index.column() == 1 && ( category.value().type() == QVariant::List
209  || !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() ) )
210  {
211  QColor fadedTextColor = brush.color();
212  fadedTextColor.setAlpha( 128 );
213  brush.setColor( fadedTextColor );
214  }
215  return brush;
216  }
217 
218  case Qt::TextAlignmentRole:
219  {
220  return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
221  }
222 
223  case Qt::EditRole:
224  {
225  switch ( index.column() )
226  {
227  case 1:
228  {
229  if ( category.value().type() == QVariant::List )
230  {
231  QStringList res;
232  const QVariantList list = category.value().toList();
233  res.reserve( list.size() );
234  for ( const QVariant &v : list )
235  res << v.toString();
236 
237  return res.join( ';' );
238  }
239  else
240  {
241  return category.value();
242  }
243  }
244 
245  case 2:
246  return category.label();
247  }
248  break;
249  }
250  }
251 
252  return QVariant();
253 }
254 
255 bool QgsCategorizedSymbolRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
256 {
257  if ( !index.isValid() )
258  return false;
259 
260  if ( index.column() == 0 && role == Qt::CheckStateRole )
261  {
262  mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
263  emit dataChanged( index, index );
264  return true;
265  }
266 
267  if ( role != Qt::EditRole )
268  return false;
269 
270  switch ( index.column() )
271  {
272  case 1: // value
273  {
274  // try to preserve variant type for this value
275  QVariant val;
276  switch ( mRenderer->categories().value( index.row() ).value().type() )
277  {
278  case QVariant::Int:
279  val = value.toInt();
280  break;
281  case QVariant::Double:
282  val = value.toDouble();
283  break;
284  case QVariant::List:
285  {
286  const QStringList parts = value.toString().split( ';' );
287  QVariantList list;
288  list.reserve( parts.count() );
289  for ( const QString &p : parts )
290  list << p;
291 
292  if ( list.count() == 1 )
293  val = list.at( 0 );
294  else
295  val = list;
296  break;
297  }
298  default:
299  val = value.toString();
300  break;
301  }
302  mRenderer->updateCategoryValue( index.row(), val );
303  break;
304  }
305  case 2: // label
306  mRenderer->updateCategoryLabel( index.row(), value.toString() );
307  break;
308  default:
309  return false;
310  }
311 
312  emit dataChanged( index, index );
313  return true;
314 }
315 
316 QVariant QgsCategorizedSymbolRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
317 {
318  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
319  {
320  QStringList lst;
321  lst << tr( "Symbol" ) << tr( "Value" ) << tr( "Legend" );
322  return lst.value( section );
323  }
324  return QVariant();
325 }
326 
327 int QgsCategorizedSymbolRendererModel::rowCount( const QModelIndex &parent ) const
328 {
329  if ( parent.isValid() || !mRenderer )
330  {
331  return 0;
332  }
333  return mRenderer->categories().size();
334 }
335 
336 int QgsCategorizedSymbolRendererModel::columnCount( const QModelIndex &index ) const
337 {
338  Q_UNUSED( index )
339  return 3;
340 }
341 
342 QModelIndex QgsCategorizedSymbolRendererModel::index( int row, int column, const QModelIndex &parent ) const
343 {
344  if ( hasIndex( row, column, parent ) )
345  {
346  return createIndex( row, column );
347  }
348  return QModelIndex();
349 }
350 
351 QModelIndex QgsCategorizedSymbolRendererModel::parent( const QModelIndex &index ) const
352 {
353  Q_UNUSED( index )
354  return QModelIndex();
355 }
356 
357 QStringList QgsCategorizedSymbolRendererModel::mimeTypes() const
358 {
359  QStringList types;
360  types << mMimeFormat;
361  return types;
362 }
363 
364 QMimeData *QgsCategorizedSymbolRendererModel::mimeData( const QModelIndexList &indexes ) const
365 {
366  QMimeData *mimeData = new QMimeData();
367  QByteArray encodedData;
368 
369  QDataStream stream( &encodedData, QIODevice::WriteOnly );
370 
371  // Create list of rows
372  const auto constIndexes = indexes;
373  for ( const QModelIndex &index : constIndexes )
374  {
375  if ( !index.isValid() || index.column() != 0 )
376  continue;
377 
378  stream << index.row();
379  }
380  mimeData->setData( mMimeFormat, encodedData );
381  return mimeData;
382 }
383 
384 bool QgsCategorizedSymbolRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
385 {
386  Q_UNUSED( row )
387  Q_UNUSED( column )
388  if ( action != Qt::MoveAction ) return true;
389 
390  if ( !data->hasFormat( mMimeFormat ) ) return false;
391 
392  QByteArray encodedData = data->data( mMimeFormat );
393  QDataStream stream( &encodedData, QIODevice::ReadOnly );
394 
395  QVector<int> rows;
396  while ( !stream.atEnd() )
397  {
398  int r;
399  stream >> r;
400  rows.append( r );
401  }
402 
403  int to = parent.row();
404  // to is -1 if dragged outside items, i.e. below any item,
405  // then move to the last position
406  if ( to == -1 ) to = mRenderer->categories().size(); // out of rang ok, will be decreased
407  for ( int i = rows.size() - 1; i >= 0; i-- )
408  {
409  QgsDebugMsg( QStringLiteral( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
410  int t = to;
411  // moveCategory first removes and then inserts
412  if ( rows[i] < t ) t--;
413  mRenderer->moveCategory( rows[i], t );
414  // current moved under another, shift its index up
415  for ( int j = 0; j < i; j++ )
416  {
417  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
418  }
419  // removed under 'to' so the target shifted down
420  if ( rows[i] < to ) to--;
421  }
422  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
423  emit rowsMoved();
424  return false;
425 }
426 
427 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
428 {
429  std::sort( rows.begin(), rows.end() ); // list might be unsorted, depending on how the user selected the rows
430  for ( int i = rows.size() - 1; i >= 0; i-- )
431  {
432  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
433  mRenderer->deleteCategory( rows[i] );
434  endRemoveRows();
435  }
436 }
437 
438 void QgsCategorizedSymbolRendererModel::removeAllRows()
439 {
440  beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
441  mRenderer->deleteAllCategories();
442  endRemoveRows();
443 }
444 
445 void QgsCategorizedSymbolRendererModel::sort( int column, Qt::SortOrder order )
446 {
447  if ( column == 0 )
448  {
449  return;
450  }
451  if ( column == 1 )
452  {
453  mRenderer->sortByValue( order );
454  }
455  else if ( column == 2 )
456  {
457  mRenderer->sortByLabel( order );
458  }
459  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
460 }
461 
462 void QgsCategorizedSymbolRendererModel::updateSymbology()
463 {
464  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
465 }
466 
467 // ------------------------------ View style --------------------------------
468 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
469  : QgsProxyStyle( parent )
470 {}
471 
472 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const
473 {
474  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
475  {
476  QStyleOption opt( *option );
477  opt.rect.setLeft( 0 );
478  // draw always as line above, because we move item to that index
479  opt.rect.setHeight( 0 );
480  if ( widget ) opt.rect.setRight( widget->width() );
481  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
482  return;
483  }
484  QProxyStyle::drawPrimitive( element, option, painter, widget );
485 }
486 
488 
489 // ------------------------------ Widget ------------------------------------
491 {
492  return new QgsCategorizedSymbolRendererWidget( layer, style, renderer );
493 }
494 
496  : QgsRendererWidget( layer, style )
497  , mContextMenu( new QMenu( this ) )
498 {
499 
500  // try to recognize the previous renderer
501  // (null renderer means "no previous renderer")
502  if ( renderer )
503  {
505  }
506  if ( !mRenderer )
507  {
508  mRenderer = qgis::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
509  }
510 
511  QString attrName = mRenderer->classAttribute();
512  mOldClassificationAttribute = attrName;
513 
514  // setup user interface
515  setupUi( this );
516  this->layout()->setContentsMargins( 0, 0, 0, 0 );
517 
518  mExpressionWidget->setLayer( mLayer );
519 
520  // initiate color ramp button to random
521  btnColorRamp->setShowRandomColorRamp( true );
522 
523  // set project default color ramp
524  QString defaultColorRamp = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ), QString() );
525  if ( !defaultColorRamp.isEmpty() )
526  {
527  btnColorRamp->setColorRampFromName( defaultColorRamp );
528  }
529  else
530  {
531  btnColorRamp->setRandomColorRamp();
532  }
533 
535 
536  mModel = new QgsCategorizedSymbolRendererModel( this );
537  mModel->setRenderer( mRenderer.get() );
538 
539  // update GUI from renderer
541 
542  viewCategories->setModel( mModel );
543  viewCategories->resizeColumnToContents( 0 );
544  viewCategories->resizeColumnToContents( 1 );
545  viewCategories->resizeColumnToContents( 2 );
546 
547  viewCategories->setStyle( new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
548 
549  connect( mModel, &QgsCategorizedSymbolRendererModel::rowsMoved, this, &QgsCategorizedSymbolRendererWidget::rowsMoved );
550  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
551 
552  connect( mExpressionWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsCategorizedSymbolRendererWidget::categoryColumnChanged );
553 
554  connect( viewCategories, &QAbstractItemView::doubleClicked, this, &QgsCategorizedSymbolRendererWidget::categoriesDoubleClicked );
555  connect( viewCategories, &QTreeView::customContextMenuRequested, this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
556 
557  connect( btnChangeCategorizedSymbol, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::changeCategorizedSymbol );
558  connect( btnAddCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategories );
559  connect( btnDeleteCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteCategories );
560  connect( btnDeleteAllCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteAllCategories );
561  connect( btnAddCategory, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategory );
562 
564 
565  // menus for data-defined rotation/size
566  QMenu *advMenu = new QMenu;
567 
568  advMenu->addAction( tr( "Match to Saved Symbols" ), this, SLOT( matchToSymbolsFromLibrary() ) );
569  advMenu->addAction( tr( "Match to Symbols from File…" ), this, SLOT( matchToSymbolsFromXml() ) );
570  advMenu->addAction( tr( "Symbol Levels…" ), this, SLOT( showSymbolLevels() ) );
571  if ( mCategorizedSymbol->type() == QgsSymbol::Marker )
572  {
573  QAction *actionDdsLegend = advMenu->addAction( tr( "Data-defined Size Legend…" ) );
574  // only from Qt 5.6 there is convenience addAction() with new style connection
575  connect( actionDdsLegend, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
576  }
577 
578  btnAdvanced->setMenu( advMenu );
579 
580  mExpressionWidget->registerExpressionContextGenerator( this );
581 
582  mMergeCategoriesAction = new QAction( tr( "Merge Categories" ), this );
583  connect( mMergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
584  mUnmergeCategoriesAction = new QAction( tr( "Unmerge Categories" ), this );
585  connect( mUnmergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
586 }
587 
589 {
590  delete mModel;
591 }
592 
594 {
595  // Note: This assumes that the signals for UI element changes have not
596  // yet been connected, so that the updates to color ramp, symbol, etc
597  // don't override existing customizations.
598 
600 
601  //mModel->setRenderer ( mRenderer ); // necessary?
602 
603  // set column
604  QString attrName = mRenderer->classAttribute();
605  mExpressionWidget->setField( attrName );
606 
607  // set source symbol
608  if ( mRenderer->sourceSymbol() )
609  {
610  mCategorizedSymbol.reset( mRenderer->sourceSymbol()->clone() );
612  }
613 
614  // if a color ramp attached to the renderer, enable the color ramp button
615  if ( mRenderer->sourceColorRamp() )
616  {
617  btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
618  }
619 }
620 
622 {
623  return mRenderer.get();
624 }
625 
627 {
628  QList<int> selectedCats = selectedCategories();
629 
630  if ( !selectedCats.isEmpty() )
631  {
632  QgsSymbol *newSymbol = mCategorizedSymbol->clone();
633  QgsSymbolSelectorDialog dlg( newSymbol, mStyle, mLayer, this );
634  dlg.setContext( context() );
635  if ( !dlg.exec() )
636  {
637  delete newSymbol;
638  return;
639  }
640 
641  const auto constSelectedCats = selectedCats;
642  for ( int idx : constSelectedCats )
643  {
644  QgsRendererCategory category = mRenderer->categories().value( idx );
645 
646  QgsSymbol *newCatSymbol = newSymbol->clone();
647  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
648  mRenderer->updateCategorySymbol( idx, newCatSymbol );
649  }
650  }
651 }
652 
654 {
656  std::unique_ptr<QgsSymbol> newSymbol( mCategorizedSymbol->clone() );
657  if ( panel && panel->dockMode() )
658  {
659  // bit tricky here - the widget doesn't take ownership of the symbol. So we need it to last for the duration of the
660  // panel's existence. Accordingly, just kinda give it ownership here, and clean up in cleanUpSymbolSelector
661  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( newSymbol.release(), mStyle, mLayer, panel );
662  dlg->setContext( mContext );
663  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
664  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
666  openPanel( dlg );
667  }
668  else
669  {
670  QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
671  dlg.setContext( mContext );
672  if ( !dlg.exec() || !newSymbol )
673  {
674  return;
675  }
676 
677  mCategorizedSymbol = std::move( newSymbol );
680  }
681 }
682 
684 {
685  if ( !mCategorizedSymbol )
686  return;
687 
688  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mCategorizedSymbol.get(), btnChangeCategorizedSymbol->iconSize() );
689  btnChangeCategorizedSymbol->setIcon( icon );
690 }
691 
693 {
694 }
695 
697 {
698  mRenderer->setClassAttribute( field );
699  emit widgetChanged();
700 }
701 
703 {
704  if ( idx.isValid() && idx.column() == 0 )
706 }
707 
709 {
710  QgsRendererCategory category = mRenderer->categories().value( currentCategoryRow() );
711 
712  std::unique_ptr< QgsSymbol > symbol;
713 
714  if ( category.symbol() )
715  {
716  symbol.reset( category.symbol()->clone() );
717  }
718  else
719  {
720  symbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
721  }
722 
724  if ( panel && panel->dockMode() )
725  {
726  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( symbol.release(), mStyle, mLayer, panel );
727  dlg->setContext( mContext );
728  dlg->setPanelTitle( category.label() );
729  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
730  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
731  openPanel( dlg );
732  }
733  else
734  {
735  QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, mLayer, panel );
736  dlg.setContext( mContext );
737  if ( !dlg.exec() || !symbol )
738  {
739  return;
740  }
741 
742  mCategorizedSymbol = std::move( symbol );
744  }
745 }
746 
747 
749 {
750  QString attrName = mExpressionWidget->currentField();
751  int idx = mLayer->fields().lookupField( attrName );
752  QList<QVariant> uniqueValues;
753  if ( idx == -1 )
754  {
755  // Lets assume it's an expression
756  QgsExpression *expression = new QgsExpression( attrName );
762 
763  expression->prepare( &context );
765  QgsFeature feature;
766  while ( fit.nextFeature( feature ) )
767  {
768  context.setFeature( feature );
769  QVariant value = expression->evaluate( &context );
770  if ( uniqueValues.contains( value ) )
771  continue;
772  uniqueValues << value;
773  }
774  }
775  else
776  {
777  uniqueValues = mLayer->uniqueValues( idx ).toList();
778  }
779 
780  // ask to abort if too many classes
781  if ( uniqueValues.size() >= 1000 )
782  {
783  int res = QMessageBox::warning( nullptr, tr( "Classify Categories" ),
784  tr( "High number of classes. Classification would yield %1 entries which might not be expected. Continue?" ).arg( uniqueValues.size() ),
785  QMessageBox::Ok | QMessageBox::Cancel,
786  QMessageBox::Cancel );
787  if ( res == QMessageBox::Cancel )
788  {
789  return;
790  }
791  }
792 
793 #if 0
794  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
795  if ( !dlg.exec() )
796  return;
797 #endif
798 
800  bool deleteExisting = false;
801 
802  if ( !mOldClassificationAttribute.isEmpty() &&
803  attrName != mOldClassificationAttribute &&
804  !mRenderer->categories().isEmpty() )
805  {
806  int res = QMessageBox::question( this,
807  tr( "Delete Classification" ),
808  tr( "The classification field was changed from '%1' to '%2'.\n"
809  "Should the existing classes be deleted before classification?" )
810  .arg( mOldClassificationAttribute, attrName ),
811  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
812  if ( res == QMessageBox::Cancel )
813  {
814  return;
815  }
816 
817  deleteExisting = ( res == QMessageBox::Yes );
818  }
819 
820  // First element to apply coloring to
821  bool keepExistingColors = false;
822  if ( !deleteExisting )
823  {
824  QgsCategoryList prevCats = mRenderer->categories();
825  keepExistingColors = !prevCats.isEmpty();
826  for ( int i = 0; i < cats.size(); ++i )
827  {
828  bool contains = false;
829  QVariant value = cats.at( i ).value();
830  for ( int j = 0; j < prevCats.size() && !contains; ++j )
831  {
832  const QVariant prevCatValue = prevCats.at( j ).value();
833  if ( prevCatValue.type() == QVariant::List )
834  {
835  const QVariantList list = prevCatValue.toList();
836  for ( const QVariant &v : list )
837  {
838  if ( v == value )
839  {
840  contains = true;
841  break;
842  }
843  }
844  }
845  else
846  {
847  if ( prevCats.at( j ).value() == value )
848  {
849  contains = true;
850  }
851  }
852  if ( contains )
853  break;
854  }
855 
856  if ( !contains )
857  prevCats.append( cats.at( i ) );
858  }
859  cats = prevCats;
860  }
861 
862  mOldClassificationAttribute = attrName;
863 
864  // TODO: if not all categories are desired, delete some!
865  /*
866  if (not dlg.readAllCats.isChecked())
867  {
868  cats2 = {}
869  for item in dlg.listCategories.selectedItems():
870  for k,c in cats.iteritems():
871  if item.text() == k.toString():
872  break
873  cats2[k] = c
874  cats = cats2
875  }
876  */
877 
878  // recreate renderer
879  std::unique_ptr< QgsCategorizedSymbolRenderer > r = qgis::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
880  r->setSourceSymbol( mCategorizedSymbol->clone() );
881  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
882  if ( ramp )
883  r->setSourceColorRamp( ramp->clone() );
884 
885  if ( mModel )
886  {
887  mModel->setRenderer( r.get() );
888  }
889  mRenderer = std::move( r );
890  if ( ! keepExistingColors && ramp )
891  applyColorRamp();
892  emit widgetChanged();
893 }
894 
896 {
897  if ( !btnColorRamp->isNull() )
898  {
899  mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
900  }
901  mModel->updateSymbology();
902 }
903 
905 {
906  QModelIndex idx = viewCategories->selectionModel()->currentIndex();
907  if ( !idx.isValid() )
908  return -1;
909  return idx.row();
910 }
911 
913 {
914  QList<int> rows;
915  QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
916 
917  const auto constSelectedRows = selectedRows;
918  for ( const QModelIndex &r : constSelectedRows )
919  {
920  if ( r.isValid() )
921  {
922  rows.append( r.row() );
923  }
924  }
925  return rows;
926 }
927 
929 {
930  QList<int> categoryIndexes = selectedCategories();
931  mModel->deleteRows( categoryIndexes );
932  emit widgetChanged();
933 }
934 
936 {
937  mModel->removeAllRows();
938  emit widgetChanged();
939 }
940 
942 {
943  if ( !mModel ) return;
945  QgsRendererCategory cat( QString(), symbol, QString(), true );
946  mModel->addCategory( cat );
947  emit widgetChanged();
948 }
949 
951 {
952  QList<QgsSymbol *> selectedSymbols;
953 
954  QItemSelectionModel *m = viewCategories->selectionModel();
955  QModelIndexList selectedIndexes = m->selectedRows( 1 );
956 
957  if ( m && !selectedIndexes.isEmpty() )
958  {
959  const QgsCategoryList &categories = mRenderer->categories();
960  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
961  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
962  {
963  int row = ( *indexIt ).row();
964  QgsSymbol *s = categories[row].symbol();
965  if ( s )
966  {
967  selectedSymbols.append( s );
968  }
969  }
970  }
971  return selectedSymbols;
972 }
973 
975 {
976  QgsCategoryList cl;
977 
978  QItemSelectionModel *m = viewCategories->selectionModel();
979  QModelIndexList selectedIndexes = m->selectedRows( 1 );
980 
981  if ( m && !selectedIndexes.isEmpty() )
982  {
983  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
984  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
985  {
986  cl.append( mModel->category( *indexIt ) );
987  }
988  }
989  return cl;
990 }
991 
993 {
995  emit widgetChanged();
996 }
997 
999 {
1001 }
1002 
1004 {
1005  viewCategories->selectionModel()->clear();
1006 }
1007 
1009 {
1010  int matched = matchToSymbols( QgsStyle::defaultStyle() );
1011  if ( matched > 0 )
1012  {
1013  QMessageBox::information( this, tr( "Matched Symbols" ),
1014  tr( "Matched %1 categories to symbols." ).arg( matched ) );
1015  }
1016  else
1017  {
1018  QMessageBox::warning( this, tr( "Matched Symbols" ),
1019  tr( "No categories could be matched to symbols in library." ) );
1020  }
1021 }
1022 
1024 {
1025  if ( !mLayer || !style )
1026  return 0;
1027 
1030  : QgsSymbol::Fill;
1031 
1032  QVariantList unmatchedCategories;
1033  QStringList unmatchedSymbols;
1034  const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1035 
1036  mModel->updateSymbology();
1037  return matched;
1038 }
1039 
1041 {
1042  QgsSettings settings;
1043  QString openFileDir = settings.value( QStringLiteral( "UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1044 
1045  QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to Symbols from File" ), openFileDir,
1046  tr( "XML files (*.xml *.XML)" ) );
1047  if ( fileName.isEmpty() )
1048  {
1049  return;
1050  }
1051 
1052  QFileInfo openFileInfo( fileName );
1053  settings.setValue( QStringLiteral( "UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1054 
1055  QgsStyle importedStyle;
1056  if ( !importedStyle.importXml( fileName ) )
1057  {
1058  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1059  tr( "An error occurred while reading file:\n%1" ).arg( importedStyle.errorString() ) );
1060  return;
1061  }
1062 
1063  int matched = matchToSymbols( &importedStyle );
1064  if ( matched > 0 )
1065  {
1066  QMessageBox::information( this, tr( "Match to Symbols from File" ),
1067  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
1068  }
1069  else
1070  {
1071  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1072  tr( "No categories could be matched to symbols in file." ) );
1073  }
1074 }
1075 
1076 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container )
1077 {
1078  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( container );
1079  if ( !dlg )
1080  return;
1081 
1082  delete dlg->symbol();
1083 }
1084 
1085 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1086 {
1087  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( sender() );
1088  mCategorizedSymbol.reset( dlg->symbol()->clone() );
1089 
1091 }
1092 
1094 {
1095  // When there is a selection, change the selected symbols only
1096  QItemSelectionModel *m = viewCategories->selectionModel();
1097  QModelIndexList i = m->selectedRows();
1098 
1099  if ( m && !i.isEmpty() )
1100  {
1101  QList<int> selectedCats = selectedCategories();
1102 
1103  if ( !selectedCats.isEmpty() )
1104  {
1105  const auto constSelectedCats = selectedCats;
1106  for ( int idx : constSelectedCats )
1107  {
1108  QgsSymbol *newCatSymbol = mCategorizedSymbol->clone();
1109  if ( selectedCats.count() > 1 )
1110  {
1111  //if updating multiple categories, retain the existing category colors
1112  newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1113  }
1114  mRenderer->updateCategorySymbol( idx, newCatSymbol );
1115  }
1116  emit widgetChanged();
1117  }
1118  }
1119  else
1120  {
1121  mRenderer->updateSymbols( mCategorizedSymbol.get() );
1122  }
1123 
1124  emit widgetChanged();
1125 }
1126 
1128 {
1129  if ( !event )
1130  {
1131  return;
1132  }
1133 
1134  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1135  {
1136  mCopyBuffer.clear();
1137  mCopyBuffer = selectedCategoryList();
1138  }
1139  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1140  {
1141  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1142  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1143  {
1144  mModel->addCategory( *rIt );
1145  }
1146  }
1147 }
1148 
1149 QgsExpressionContext QgsCategorizedSymbolRendererWidget::createExpressionContext() const
1150 {
1151  QgsExpressionContext expContext;
1155 
1156  if ( mContext.mapCanvas() )
1157  {
1160  }
1161  else
1162  {
1164  }
1165 
1166  if ( vectorLayer() )
1168 
1169  // additional scopes
1170  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
1171  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
1172  {
1173  expContext.appendScope( new QgsExpressionContextScope( scope ) );
1174  }
1175 
1176  return expContext;
1177 }
1178 
1179 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1180 {
1181  QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( mCategorizedSymbol.get() ); // this should be only enabled for marker symbols
1182  QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1183  if ( panel )
1184  {
1185  connect( panel, &QgsPanelWidget::widgetChanged, this, [ = ]
1186  {
1187  mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1188  emit widgetChanged();
1189  } );
1190  openPanel( panel ); // takes ownership of the panel
1191  }
1192 }
1193 
1194 void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1195 {
1196  const QgsCategoryList &categories = mRenderer->categories();
1197 
1198  QList<int> selectedCategoryIndexes = selectedCategories();
1199  QList< int > categoryIndexes;
1200 
1201  // filter out "" entry
1202  for ( int i : selectedCategoryIndexes )
1203  {
1204  QVariant v = categories.at( i ).value();
1205 
1206  if ( !v.isValid() || v == "" )
1207  {
1208  continue;
1209  }
1210 
1211  categoryIndexes.append( i );
1212  }
1213 
1214  if ( categoryIndexes.count() < 2 )
1215  return;
1216 
1217  QStringList labels;
1218  QVariantList values;
1219  values.reserve( categoryIndexes.count() );
1220  labels.reserve( categoryIndexes.count() );
1221  for ( int i : categoryIndexes )
1222  {
1223  QVariant v = categories.at( i ).value();
1224 
1225  if ( v.type() == QVariant::List )
1226  {
1227  values.append( v.toList() );
1228  }
1229  else
1230  values << v;
1231 
1232  labels << categories.at( i ).label();
1233  }
1234 
1235  // modify first category (basically we "merge up" into the first selected category)
1236  mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join( ',' ) );
1237  mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1238 
1239  categoryIndexes.pop_front();
1240  mModel->deleteRows( categoryIndexes );
1241 
1242  emit widgetChanged();
1243 }
1244 
1245 void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1246 {
1247  const QList<int> categoryIndexes = selectedCategories();
1248  if ( categoryIndexes.isEmpty() )
1249  return;
1250 
1251  const QgsCategoryList &categories = mRenderer->categories();
1252  for ( int i : categoryIndexes )
1253  {
1254  const QVariant v = categories.at( i ).value();
1255  if ( v.type() != QVariant::List )
1256  continue;
1257 
1258  const QVariantList list = v.toList();
1259  for ( int j = 1; j < list.count(); ++j )
1260  {
1261  mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1262  }
1263  mRenderer->updateCategoryValue( i, list.at( 0 ) );
1264  mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1265  }
1266 
1267  emit widgetChanged();
1268 }
1269 
1270 void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1271 {
1272  mContextMenu->clear();
1273  const QList< QAction * > actions = contextMenu->actions();
1274  for ( QAction *act : actions )
1275  {
1276  mContextMenu->addAction( act );
1277  }
1278 
1279  mContextMenu->addSeparator();
1280 
1281  if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1282  {
1283  mContextMenu->addAction( mMergeCategoriesAction );
1284  }
1285  if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1286  {
1287  const QList<int> categoryIndexes = selectedCategories();
1288  const QgsCategoryList &categories = mRenderer->categories();
1289  const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1290  if ( v.type() == QVariant::List )
1291  mContextMenu->addAction( mUnmergeCategoriesAction );
1292  }
1293  else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1294  {
1295  mContextMenu->addAction( mUnmergeCategoriesAction );
1296  }
1297 
1298  mContextMenu->exec( QCursor::pos() );
1299 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
Class for parsing and evaluation of expressions (formerly called "search strings").
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
Wrapper for iterator of features from vector data provider or vector layer.
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
void applyColorRamp()
Applies the color ramp passed on by the color ramp button.
bool dockMode()
Returns the dock mode state.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void applyChangeToSymbol()
Applies current symbol to selected categories, or to all categories if none is selected.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
void showSymbolLevelsDialog(QgsFeatureRenderer *r)
show a dialog with renderer&#39;s symbol level settings
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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.
Base class for renderer settings widgets.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void changeSelectedSymbols()
Changes the selected symbols alone for the change button, if there is a selection.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QgsVectorLayer * mLayer
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QVariant evaluate()
Evaluate the feature and return the result.
void matchToSymbolsFromLibrary()
Replaces category symbols with the symbols from the users&#39; symbol library that have a matching name...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void matchToSymbolsFromXml()
Prompts for selection of an xml file, then replaces category symbols with the symbols from the XML fi...
Base class for any widget that can be shown as a inline panel.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
int currentCategoryRow()
Returns row index for the currently selected category (-1 if on no selection)
Line symbol.
Definition: qgssymbol.h:86
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style...
Definition: qgsproxystyle.h:30
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:766
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:46
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsRendererCategory > QgsCategoryList
SymbolType
Type of the symbol.
Definition: qgssymbol.h:83
static QgsCategoryList createCategories(const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &fieldName=QString())
Create categories for a list of values.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:293
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
Definition: qgsstyle.cpp:1570
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
Widget for configuration of appearance of legend for marker symbols with data-defined size...
QList< int > selectedCategories()
Returns a list of indexes for the categories under selection.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
std::unique_ptr< QgsCategorizedSymbolRenderer > mRenderer
QgsDataDefinedSizeLegendWidget * createDataDefinedSizeLegendWidget(const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend)
Creates widget to setup data-defined size legend.
Symbol selector widget that can be used to select and build a symbol.
Single scope for storing variables and functions for use within a QgsExpressionContext.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
void widgetChanged()
Emitted when the widget state changes.
QList< QgsSymbol * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
QVariant value() const
Returns the value corresponding to this category.
int matchToSymbols(QgsStyle *style)
Replaces category symbols with the symbols from a style that have a matching name.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
Marker symbol.
Definition: qgssymbol.h:85
Fill symbol.
Definition: qgssymbol.h:87
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
static QgsExpressionContextScope * atlasScope(QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsSymbolWidgetContext mContext
Context in which widget is shown.
QgsCategorizedSymbolRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:561
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be nullptr). Ownership is passed to the caller...
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString errorString()
Returns last error from load/save operation.
Definition: qgsstyle.h:384
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
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.
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer...
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:470