QGIS API Documentation  3.6.0-Noosa (5873452)
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  Q_FOREACH ( const QModelIndex &index, indexes )
373  {
374  if ( !index.isValid() || index.column() != 0 )
375  continue;
376 
377  stream << index.row();
378  }
379  mimeData->setData( mMimeFormat, encodedData );
380  return mimeData;
381 }
382 
383 bool QgsCategorizedSymbolRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
384 {
385  Q_UNUSED( row );
386  Q_UNUSED( column );
387  if ( action != Qt::MoveAction ) return true;
388 
389  if ( !data->hasFormat( mMimeFormat ) ) return false;
390 
391  QByteArray encodedData = data->data( mMimeFormat );
392  QDataStream stream( &encodedData, QIODevice::ReadOnly );
393 
394  QVector<int> rows;
395  while ( !stream.atEnd() )
396  {
397  int r;
398  stream >> r;
399  rows.append( r );
400  }
401 
402  int to = parent.row();
403  // to is -1 if dragged outside items, i.e. below any item,
404  // then move to the last position
405  if ( to == -1 ) to = mRenderer->categories().size(); // out of rang ok, will be decreased
406  for ( int i = rows.size() - 1; i >= 0; i-- )
407  {
408  QgsDebugMsg( QStringLiteral( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
409  int t = to;
410  // moveCategory first removes and then inserts
411  if ( rows[i] < t ) t--;
412  mRenderer->moveCategory( rows[i], t );
413  // current moved under another, shift its index up
414  for ( int j = 0; j < i; j++ )
415  {
416  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
417  }
418  // removed under 'to' so the target shifted down
419  if ( rows[i] < to ) to--;
420  }
421  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
422  emit rowsMoved();
423  return false;
424 }
425 
426 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
427 {
428  std::sort( rows.begin(), rows.end() ); // list might be unsorted, depending on how the user selected the rows
429  for ( int i = rows.size() - 1; i >= 0; i-- )
430  {
431  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
432  mRenderer->deleteCategory( rows[i] );
433  endRemoveRows();
434  }
435 }
436 
437 void QgsCategorizedSymbolRendererModel::removeAllRows()
438 {
439  beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
440  mRenderer->deleteAllCategories();
441  endRemoveRows();
442 }
443 
444 void QgsCategorizedSymbolRendererModel::sort( int column, Qt::SortOrder order )
445 {
446  if ( column == 0 )
447  {
448  return;
449  }
450  if ( column == 1 )
451  {
452  mRenderer->sortByValue( order );
453  }
454  else if ( column == 2 )
455  {
456  mRenderer->sortByLabel( order );
457  }
458  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
459 }
460 
461 void QgsCategorizedSymbolRendererModel::updateSymbology()
462 {
463  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
464 }
465 
466 // ------------------------------ View style --------------------------------
467 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
468  : QgsProxyStyle( parent )
469 {}
470 
471 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const
472 {
473  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
474  {
475  QStyleOption opt( *option );
476  opt.rect.setLeft( 0 );
477  // draw always as line above, because we move item to that index
478  opt.rect.setHeight( 0 );
479  if ( widget ) opt.rect.setRight( widget->width() );
480  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
481  return;
482  }
483  QProxyStyle::drawPrimitive( element, option, painter, widget );
484 }
485 
487 
488 // ------------------------------ Widget ------------------------------------
490 {
491  return new QgsCategorizedSymbolRendererWidget( layer, style, renderer );
492 }
493 
495  : QgsRendererWidget( layer, style )
496  , mContextMenu( new QMenu( this ) )
497 {
498 
499  // try to recognize the previous renderer
500  // (null renderer means "no previous renderer")
501  if ( renderer )
502  {
504  }
505  if ( !mRenderer )
506  {
507  mRenderer = qgis::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
508  }
509 
510  QString attrName = mRenderer->classAttribute();
511  mOldClassificationAttribute = attrName;
512 
513  // setup user interface
514  setupUi( this );
515  this->layout()->setContentsMargins( 0, 0, 0, 0 );
516 
517  mExpressionWidget->setLayer( mLayer );
518 
519  // initiate color ramp button to random
520  btnColorRamp->setShowRandomColorRamp( true );
521 
522  // set project default color ramp
523  QString defaultColorRamp = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ), QString() );
524  if ( !defaultColorRamp.isEmpty() )
525  {
526  btnColorRamp->setColorRampFromName( defaultColorRamp );
527  }
528  else
529  {
530  btnColorRamp->setRandomColorRamp();
531  }
532 
534 
535  mModel = new QgsCategorizedSymbolRendererModel( this );
536  mModel->setRenderer( mRenderer.get() );
537 
538  // update GUI from renderer
540 
541  viewCategories->setModel( mModel );
542  viewCategories->resizeColumnToContents( 0 );
543  viewCategories->resizeColumnToContents( 1 );
544  viewCategories->resizeColumnToContents( 2 );
545 
546  viewCategories->setStyle( new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
547 
548  connect( mModel, &QgsCategorizedSymbolRendererModel::rowsMoved, this, &QgsCategorizedSymbolRendererWidget::rowsMoved );
549  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
550 
551  connect( mExpressionWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsCategorizedSymbolRendererWidget::categoryColumnChanged );
552 
553  connect( viewCategories, &QAbstractItemView::doubleClicked, this, &QgsCategorizedSymbolRendererWidget::categoriesDoubleClicked );
554  connect( viewCategories, &QTreeView::customContextMenuRequested, this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
555 
556  connect( btnChangeCategorizedSymbol, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::changeCategorizedSymbol );
557  connect( btnAddCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategories );
558  connect( btnDeleteCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteCategories );
559  connect( btnDeleteAllCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteAllCategories );
560  connect( btnAddCategory, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategory );
561 
563 
564  // menus for data-defined rotation/size
565  QMenu *advMenu = new QMenu;
566 
567  advMenu->addAction( tr( "Match to Saved Symbols" ), this, SLOT( matchToSymbolsFromLibrary() ) );
568  advMenu->addAction( tr( "Match to Symbols from File…" ), this, SLOT( matchToSymbolsFromXml() ) );
569  advMenu->addAction( tr( "Symbol Levels…" ), this, SLOT( showSymbolLevels() ) );
570  if ( mCategorizedSymbol->type() == QgsSymbol::Marker )
571  {
572  QAction *actionDdsLegend = advMenu->addAction( tr( "Data-defined Size Legend…" ) );
573  // only from Qt 5.6 there is convenience addAction() with new style connection
574  connect( actionDdsLegend, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
575  }
576 
577  btnAdvanced->setMenu( advMenu );
578 
579  mExpressionWidget->registerExpressionContextGenerator( this );
580 
581  mMergeCategoriesAction = new QAction( tr( "Merge Categories" ), this );
582  connect( mMergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
583  mUnmergeCategoriesAction = new QAction( tr( "Unmerge Categories" ), this );
584  connect( mUnmergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
585 }
586 
588 {
589  delete mModel;
590 }
591 
593 {
594  // Note: This assumes that the signals for UI element changes have not
595  // yet been connected, so that the updates to color ramp, symbol, etc
596  // don't override existing customizations.
597 
599 
600  //mModel->setRenderer ( mRenderer ); // necessary?
601 
602  // set column
603  QString attrName = mRenderer->classAttribute();
604  mExpressionWidget->setField( attrName );
605 
606  // set source symbol
607  if ( mRenderer->sourceSymbol() )
608  {
609  mCategorizedSymbol.reset( mRenderer->sourceSymbol()->clone() );
611  }
612 
613  // if a color ramp attached to the renderer, enable the color ramp button
614  if ( mRenderer->sourceColorRamp() )
615  {
616  btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
617  }
618 }
619 
621 {
622  return mRenderer.get();
623 }
624 
626 {
627  QList<int> selectedCats = selectedCategories();
628 
629  if ( !selectedCats.isEmpty() )
630  {
631  QgsSymbol *newSymbol = mCategorizedSymbol->clone();
632  QgsSymbolSelectorDialog dlg( newSymbol, mStyle, mLayer, this );
633  dlg.setContext( context() );
634  if ( !dlg.exec() )
635  {
636  delete newSymbol;
637  return;
638  }
639 
640  Q_FOREACH ( int idx, selectedCats )
641  {
642  QgsRendererCategory category = mRenderer->categories().value( idx );
643 
644  QgsSymbol *newCatSymbol = newSymbol->clone();
645  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
646  mRenderer->updateCategorySymbol( idx, newCatSymbol );
647  }
648  }
649 }
650 
652 {
654  std::unique_ptr<QgsSymbol> newSymbol( mCategorizedSymbol->clone() );
655  if ( panel && panel->dockMode() )
656  {
657  // bit tricky here - the widget doesn't take ownership of the symbol. So we need it to last for the duration of the
658  // panel's existence. Accordingly, just kinda give it ownership here, and clean up in cleanUpSymbolSelector
659  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( newSymbol.release(), mStyle, mLayer, panel );
660  dlg->setContext( mContext );
661  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
662  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
664  openPanel( dlg );
665  }
666  else
667  {
668  QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
669  dlg.setContext( mContext );
670  if ( !dlg.exec() || !newSymbol )
671  {
672  return;
673  }
674 
675  mCategorizedSymbol = std::move( newSymbol );
678  }
679 }
680 
682 {
683  if ( !mCategorizedSymbol )
684  return;
685 
686  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mCategorizedSymbol.get(), btnChangeCategorizedSymbol->iconSize() );
687  btnChangeCategorizedSymbol->setIcon( icon );
688 }
689 
691 {
692 }
693 
695 {
696  mRenderer->setClassAttribute( field );
697  emit widgetChanged();
698 }
699 
701 {
702  if ( idx.isValid() && idx.column() == 0 )
704 }
705 
707 {
708  QgsRendererCategory category = mRenderer->categories().value( currentCategoryRow() );
709 
710  std::unique_ptr< QgsSymbol > symbol;
711 
712  if ( category.symbol() )
713  {
714  symbol.reset( category.symbol()->clone() );
715  }
716  else
717  {
718  symbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
719  }
720 
722  if ( panel && panel->dockMode() )
723  {
724  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( symbol.release(), mStyle, mLayer, panel );
725  dlg->setContext( mContext );
726  dlg->setPanelTitle( category.label() );
727  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
728  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
729  openPanel( dlg );
730  }
731  else
732  {
733  QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, mLayer, panel );
734  dlg.setContext( mContext );
735  if ( !dlg.exec() || !symbol )
736  {
737  return;
738  }
739 
740  mCategorizedSymbol = std::move( symbol );
742  }
743 }
744 
745 
747 {
748  QString attrName = mExpressionWidget->currentField();
749  int idx = mLayer->fields().lookupField( attrName );
750  QList<QVariant> uniqueValues;
751  if ( idx == -1 )
752  {
753  // Lets assume it's an expression
754  QgsExpression *expression = new QgsExpression( attrName );
760 
761  expression->prepare( &context );
763  QgsFeature feature;
764  while ( fit.nextFeature( feature ) )
765  {
766  context.setFeature( feature );
767  QVariant value = expression->evaluate( &context );
768  if ( uniqueValues.contains( value ) )
769  continue;
770  uniqueValues << value;
771  }
772  }
773  else
774  {
775  uniqueValues = mLayer->uniqueValues( idx ).toList();
776  }
777 
778  // ask to abort if too many classes
779  if ( uniqueValues.size() >= 1000 )
780  {
781  int res = QMessageBox::warning( nullptr, tr( "Classify Categories" ),
782  tr( "High number of classes. Classification would yield %1 entries which might not be expected. Continue?" ).arg( uniqueValues.size() ),
783  QMessageBox::Ok | QMessageBox::Cancel,
784  QMessageBox::Cancel );
785  if ( res == QMessageBox::Cancel )
786  {
787  return;
788  }
789  }
790 
791 #if 0
792  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
793  if ( !dlg.exec() )
794  return;
795 #endif
796 
798  bool deleteExisting = false;
799 
800  if ( !mOldClassificationAttribute.isEmpty() &&
801  attrName != mOldClassificationAttribute &&
802  !mRenderer->categories().isEmpty() )
803  {
804  int res = QMessageBox::question( this,
805  tr( "Delete Classification" ),
806  tr( "The classification field was changed from '%1' to '%2'.\n"
807  "Should the existing classes be deleted before classification?" )
808  .arg( mOldClassificationAttribute, attrName ),
809  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
810  if ( res == QMessageBox::Cancel )
811  {
812  return;
813  }
814 
815  deleteExisting = ( res == QMessageBox::Yes );
816  }
817 
818  // First element to apply coloring to
819  bool keepExistingColors = false;
820  if ( !deleteExisting )
821  {
822  QgsCategoryList prevCats = mRenderer->categories();
823  keepExistingColors = !prevCats.isEmpty();
824  for ( int i = 0; i < cats.size(); ++i )
825  {
826  bool contains = false;
827  QVariant value = cats.at( i ).value();
828  for ( int j = 0; j < prevCats.size() && !contains; ++j )
829  {
830  const QVariant prevCatValue = prevCats.at( j ).value();
831  if ( prevCatValue.type() == QVariant::List )
832  {
833  const QVariantList list = prevCatValue.toList();
834  for ( const QVariant &v : list )
835  {
836  if ( v == value )
837  {
838  contains = true;
839  break;
840  }
841  }
842  }
843  else
844  {
845  if ( prevCats.at( j ).value() == value )
846  {
847  contains = true;
848  }
849  }
850  if ( contains )
851  break;
852  }
853 
854  if ( !contains )
855  prevCats.append( cats.at( i ) );
856  }
857  cats = prevCats;
858  }
859 
860  mOldClassificationAttribute = attrName;
861 
862  // TODO: if not all categories are desired, delete some!
863  /*
864  if (not dlg.readAllCats.isChecked())
865  {
866  cats2 = {}
867  for item in dlg.listCategories.selectedItems():
868  for k,c in cats.iteritems():
869  if item.text() == k.toString():
870  break
871  cats2[k] = c
872  cats = cats2
873  }
874  */
875 
876  // recreate renderer
877  std::unique_ptr< QgsCategorizedSymbolRenderer > r = qgis::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
878  r->setSourceSymbol( mCategorizedSymbol->clone() );
879  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
880  if ( ramp )
881  r->setSourceColorRamp( ramp->clone() );
882 
883  if ( mModel )
884  {
885  mModel->setRenderer( r.get() );
886  }
887  mRenderer = std::move( r );
888  if ( ! keepExistingColors && ramp )
889  applyColorRamp();
890  emit widgetChanged();
891 }
892 
894 {
895  if ( !btnColorRamp->isNull() )
896  {
897  mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
898  }
899  mModel->updateSymbology();
900 }
901 
903 {
904  QModelIndex idx = viewCategories->selectionModel()->currentIndex();
905  if ( !idx.isValid() )
906  return -1;
907  return idx.row();
908 }
909 
911 {
912  QList<int> rows;
913  QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
914 
915  Q_FOREACH ( const QModelIndex &r, selectedRows )
916  {
917  if ( r.isValid() )
918  {
919  rows.append( r.row() );
920  }
921  }
922  return rows;
923 }
924 
926 {
927  QList<int> categoryIndexes = selectedCategories();
928  mModel->deleteRows( categoryIndexes );
929  emit widgetChanged();
930 }
931 
933 {
934  mModel->removeAllRows();
935  emit widgetChanged();
936 }
937 
939 {
940  if ( !mModel ) return;
942  QgsRendererCategory cat( QString(), symbol, QString(), true );
943  mModel->addCategory( cat );
944  emit widgetChanged();
945 }
946 
948 {
949  QList<QgsSymbol *> selectedSymbols;
950 
951  QItemSelectionModel *m = viewCategories->selectionModel();
952  QModelIndexList selectedIndexes = m->selectedRows( 1 );
953 
954  if ( m && !selectedIndexes.isEmpty() )
955  {
956  const QgsCategoryList &categories = mRenderer->categories();
957  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
958  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
959  {
960  int row = ( *indexIt ).row();
961  QgsSymbol *s = categories[row].symbol();
962  if ( s )
963  {
964  selectedSymbols.append( s );
965  }
966  }
967  }
968  return selectedSymbols;
969 }
970 
972 {
973  QgsCategoryList cl;
974 
975  QItemSelectionModel *m = viewCategories->selectionModel();
976  QModelIndexList selectedIndexes = m->selectedRows( 1 );
977 
978  if ( m && !selectedIndexes.isEmpty() )
979  {
980  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
981  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
982  {
983  cl.append( mModel->category( *indexIt ) );
984  }
985  }
986  return cl;
987 }
988 
990 {
992  emit widgetChanged();
993 }
994 
996 {
998 }
999 
1001 {
1002  viewCategories->selectionModel()->clear();
1003 }
1004 
1006 {
1007  int matched = matchToSymbols( QgsStyle::defaultStyle() );
1008  if ( matched > 0 )
1009  {
1010  QMessageBox::information( this, tr( "Matched Symbols" ),
1011  tr( "Matched %1 categories to symbols." ).arg( matched ) );
1012  }
1013  else
1014  {
1015  QMessageBox::warning( this, tr( "Matched Symbols" ),
1016  tr( "No categories could be matched to symbols in library." ) );
1017  }
1018 }
1019 
1021 {
1022  if ( !mLayer || !style )
1023  return 0;
1024 
1027  : QgsSymbol::Fill;
1028 
1029  QVariantList unmatchedCategories;
1030  QStringList unmatchedSymbols;
1031  const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1032 
1033  mModel->updateSymbology();
1034  return matched;
1035 }
1036 
1038 {
1039  QgsSettings settings;
1040  QString openFileDir = settings.value( QStringLiteral( "UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1041 
1042  QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to Symbols from File" ), openFileDir,
1043  tr( "XML files (*.xml *.XML)" ) );
1044  if ( fileName.isEmpty() )
1045  {
1046  return;
1047  }
1048 
1049  QFileInfo openFileInfo( fileName );
1050  settings.setValue( QStringLiteral( "UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1051 
1052  QgsStyle importedStyle;
1053  if ( !importedStyle.importXml( fileName ) )
1054  {
1055  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1056  tr( "An error occurred while reading file:\n%1" ).arg( importedStyle.errorString() ) );
1057  return;
1058  }
1059 
1060  int matched = matchToSymbols( &importedStyle );
1061  if ( matched > 0 )
1062  {
1063  QMessageBox::information( this, tr( "Match to Symbols from File" ),
1064  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
1065  }
1066  else
1067  {
1068  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1069  tr( "No categories could be matched to symbols in file." ) );
1070  }
1071 }
1072 
1073 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container )
1074 {
1075  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( container );
1076  if ( !dlg )
1077  return;
1078 
1079  delete dlg->symbol();
1080 }
1081 
1082 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1083 {
1084  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( sender() );
1085  mCategorizedSymbol.reset( dlg->symbol()->clone() );
1086 
1088 }
1089 
1091 {
1092  // When there is a selection, change the selected symbols only
1093  QItemSelectionModel *m = viewCategories->selectionModel();
1094  QModelIndexList i = m->selectedRows();
1095 
1096  if ( m && !i.isEmpty() )
1097  {
1098  QList<int> selectedCats = selectedCategories();
1099 
1100  if ( !selectedCats.isEmpty() )
1101  {
1102  Q_FOREACH ( int idx, selectedCats )
1103  {
1104  QgsSymbol *newCatSymbol = mCategorizedSymbol->clone();
1105  if ( selectedCats.count() > 1 )
1106  {
1107  //if updating multiple categories, retain the existing category colors
1108  newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1109  }
1110  mRenderer->updateCategorySymbol( idx, newCatSymbol );
1111  }
1112  emit widgetChanged();
1113  }
1114  }
1115  else
1116  {
1117  mRenderer->updateSymbols( mCategorizedSymbol.get() );
1118  }
1119 
1120  emit widgetChanged();
1121 }
1122 
1124 {
1125  if ( !event )
1126  {
1127  return;
1128  }
1129 
1130  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1131  {
1132  mCopyBuffer.clear();
1133  mCopyBuffer = selectedCategoryList();
1134  }
1135  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1136  {
1137  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1138  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1139  {
1140  mModel->addCategory( *rIt );
1141  }
1142  }
1143 }
1144 
1145 QgsExpressionContext QgsCategorizedSymbolRendererWidget::createExpressionContext() const
1146 {
1147  QgsExpressionContext expContext;
1151 
1152  if ( mContext.mapCanvas() )
1153  {
1156  }
1157  else
1158  {
1160  }
1161 
1162  if ( vectorLayer() )
1164 
1165  // additional scopes
1167  {
1168  expContext.appendScope( new QgsExpressionContextScope( scope ) );
1169  }
1170 
1171  return expContext;
1172 }
1173 
1174 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1175 {
1176  QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( mCategorizedSymbol.get() ); // this should be only enabled for marker symbols
1177  QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1178  if ( panel )
1179  {
1180  connect( panel, &QgsPanelWidget::widgetChanged, this, [ = ]
1181  {
1182  mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1183  emit widgetChanged();
1184  } );
1185  openPanel( panel ); // takes ownership of the panel
1186  }
1187 }
1188 
1189 void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1190 {
1191  const QgsCategoryList &categories = mRenderer->categories();
1192 
1193  QList<int> selectedCategoryIndexes = selectedCategories();
1194  QList< int > categoryIndexes;
1195 
1196  // filter out "" entry
1197  for ( int i : selectedCategoryIndexes )
1198  {
1199  QVariant v = categories.at( i ).value();
1200 
1201  if ( !v.isValid() || v == "" )
1202  {
1203  continue;
1204  }
1205 
1206  categoryIndexes.append( i );
1207  }
1208 
1209  if ( categoryIndexes.count() < 2 )
1210  return;
1211 
1212  QStringList labels;
1213  QVariantList values;
1214  values.reserve( categoryIndexes.count() );
1215  labels.reserve( categoryIndexes.count() );
1216  for ( int i : categoryIndexes )
1217  {
1218  QVariant v = categories.at( i ).value();
1219 
1220  if ( v.type() == QVariant::List )
1221  {
1222  values.append( v.toList() );
1223  }
1224  else
1225  values << v;
1226 
1227  labels << categories.at( i ).label();
1228  }
1229 
1230  // modify first category (basically we "merge up" into the first selected category)
1231  mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join( ',' ) );
1232  mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1233 
1234  categoryIndexes.pop_front();
1235  mModel->deleteRows( categoryIndexes );
1236 
1237  emit widgetChanged();
1238 }
1239 
1240 void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1241 {
1242  const QList<int> categoryIndexes = selectedCategories();
1243  if ( categoryIndexes.isEmpty() )
1244  return;
1245 
1246  const QgsCategoryList &categories = mRenderer->categories();
1247  for ( int i : categoryIndexes )
1248  {
1249  const QVariant v = categories.at( i ).value();
1250  if ( v.type() != QVariant::List )
1251  continue;
1252 
1253  const QVariantList list = v.toList();
1254  for ( int j = 1; j < list.count(); ++j )
1255  {
1256  mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1257  }
1258  mRenderer->updateCategoryValue( i, list.at( 0 ) );
1259  mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1260  }
1261 
1262  emit widgetChanged();
1263 }
1264 
1265 void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1266 {
1267  mContextMenu->clear();
1268  const QList< QAction * > actions = contextMenu->actions();
1269  for ( QAction *act : actions )
1270  {
1271  mContextMenu->addAction( act );
1272  }
1273 
1274  mContextMenu->addSeparator();
1275 
1276  if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1277  {
1278  mContextMenu->addAction( mMergeCategoriesAction );
1279  }
1280  if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1281  {
1282  const QList<int> categoryIndexes = selectedCategories();
1283  const QgsCategoryList &categories = mRenderer->categories();
1284  const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1285  if ( v.type() == QVariant::List )
1286  mContextMenu->addAction( mUnmergeCategoriesAction );
1287  }
1288  else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1289  {
1290  mContextMenu->addAction( mUnmergeCategoriesAction );
1291  }
1292 
1293  mContextMenu->exec( QCursor::pos() );
1294 }
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:732
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:277
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:1568
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.
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)
the signal is 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:559
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 null). 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:430
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query 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:452