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