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