QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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< int >( 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  const 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  const 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 ) ? static_cast<Qt::Alignment::Int>( Qt::AlignHCenter ) : static_cast<Qt::Alignment::Int>( 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  if ( renderer )
514  }
515 
516  const QString attrName = mRenderer->classAttribute();
517  mOldClassificationAttribute = attrName;
518 
519  // setup user interface
520  setupUi( this );
521  layout()->setContentsMargins( 0, 0, 0, 0 );
522 
523  mExpressionWidget->setLayer( mLayer );
524  btnChangeCategorizedSymbol->setLayer( mLayer );
525  btnChangeCategorizedSymbol->registerExpressionContextGenerator( this );
526 
527  // initiate color ramp button to random
528  btnColorRamp->setShowRandomColorRamp( true );
529 
530  // set project default color ramp
531  const QString defaultColorRamp = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ), QString() );
532  if ( !defaultColorRamp.isEmpty() )
533  {
534  btnColorRamp->setColorRampFromName( defaultColorRamp );
535  }
536  else
537  {
538  btnColorRamp->setRandomColorRamp();
539  }
540 
542  if ( mCategorizedSymbol )
543  {
544  btnChangeCategorizedSymbol->setSymbolType( mCategorizedSymbol->type() );
545  btnChangeCategorizedSymbol->setSymbol( mCategorizedSymbol->clone() );
546  }
547 
548  mModel = new QgsCategorizedSymbolRendererModel( this );
549  mModel->setRenderer( mRenderer.get() );
550 
551  // update GUI from renderer
553 
554  viewCategories->setModel( mModel );
555  viewCategories->resizeColumnToContents( 0 );
556  viewCategories->resizeColumnToContents( 1 );
557  viewCategories->resizeColumnToContents( 2 );
558 
559  viewCategories->setStyle( new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
560  connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
561 
562  connect( mModel, &QgsCategorizedSymbolRendererModel::rowsMoved, this, &QgsCategorizedSymbolRendererWidget::rowsMoved );
563  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
564 
565  connect( mExpressionWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsCategorizedSymbolRendererWidget::categoryColumnChanged );
566 
567  connect( viewCategories, &QAbstractItemView::doubleClicked, this, &QgsCategorizedSymbolRendererWidget::categoriesDoubleClicked );
568  connect( viewCategories, &QTreeView::customContextMenuRequested, this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
569 
570  connect( btnChangeCategorizedSymbol, &QgsSymbolButton::changed, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
571 
572  connect( btnAddCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategories );
573  connect( btnDeleteCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteCategories );
574  connect( btnDeleteAllCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteAllCategories );
575  connect( btnAddCategory, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategory );
576 
578 
579  // menus for data-defined rotation/size
580  QMenu *advMenu = new QMenu;
581 
582  advMenu->addAction( tr( "Match to Saved Symbols" ), this, &QgsCategorizedSymbolRendererWidget::matchToSymbolsFromLibrary );
583  advMenu->addAction( tr( "Match to Symbols from File…" ), this, &QgsCategorizedSymbolRendererWidget::matchToSymbolsFromXml );
584  mActionLevels = advMenu->addAction( tr( "Symbol Levels…" ), this, &QgsCategorizedSymbolRendererWidget::showSymbolLevels );
586  {
587  QAction *actionDdsLegend = advMenu->addAction( tr( "Data-defined Size Legend…" ) );
588  // only from Qt 5.6 there is convenience addAction() with new style connection
589  connect( actionDdsLegend, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
590  }
591 
592  btnAdvanced->setMenu( advMenu );
593 
594  mExpressionWidget->registerExpressionContextGenerator( this );
595 
596  mMergeCategoriesAction = new QAction( tr( "Merge Categories" ), this );
597  connect( mMergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
598  mUnmergeCategoriesAction = new QAction( tr( "Unmerge Categories" ), this );
599  connect( mUnmergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
600 
601  connect( mContextMenu, &QMenu::aboutToShow, this, [ = ]
602  {
603  const std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
604  mPasteSymbolAction->setEnabled( static_cast< bool >( tempSymbol ) );
605  } );
606 }
607 
609 {
610  delete mModel;
611 }
612 
614 {
615  // Note: This assumes that the signals for UI element changes have not
616  // yet been connected, so that the updates to color ramp, symbol, etc
617  // don't override existing customizations.
618 
619  //mModel->setRenderer ( mRenderer ); // necessary?
620 
621  // set column
622  const QString attrName = mRenderer->classAttribute();
623  mExpressionWidget->setField( attrName );
624 
625  // set source symbol
626  if ( mRenderer->sourceSymbol() )
627  {
628  mCategorizedSymbol.reset( mRenderer->sourceSymbol()->clone() );
629  whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mCategorizedSymbol->clone() );
630  }
631 
632  // if a color ramp attached to the renderer, enable the color ramp button
633  if ( mRenderer->sourceColorRamp() )
634  {
635  btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
636  }
637 }
638 
640 {
641  return mRenderer.get();
642 }
643 
645 {
647  btnChangeCategorizedSymbol->setMapCanvas( context.mapCanvas() );
648  btnChangeCategorizedSymbol->setMessageBar( context.messageBar() );
649 }
650 
652 {
653  delete mActionLevels;
654  mActionLevels = nullptr;
655 }
656 
658 {
659  const QList<int> selectedCats = selectedCategories();
660 
661  if ( !selectedCats.isEmpty() )
662  {
663  QgsSymbol *newSymbol = mCategorizedSymbol->clone();
664  QgsSymbolSelectorDialog dlg( newSymbol, mStyle, mLayer, this );
665  dlg.setContext( context() );
666  if ( !dlg.exec() )
667  {
668  delete newSymbol;
669  return;
670  }
671 
672  const auto constSelectedCats = selectedCats;
673  for ( const int idx : constSelectedCats )
674  {
675  const QgsRendererCategory category = mRenderer->categories().value( idx );
676 
677  QgsSymbol *newCatSymbol = newSymbol->clone();
678  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
679  mRenderer->updateCategorySymbol( idx, newCatSymbol );
680  }
681  }
682 }
683 
685 {
687  std::unique_ptr<QgsSymbol> newSymbol( mCategorizedSymbol->clone() );
688  if ( panel && panel->dockMode() )
689  {
690  // bit tricky here - the widget doesn't take ownership of the symbol. So we need it to last for the duration of the
691  // panel's existence. Accordingly, just kinda give it ownership here, and clean up in cleanUpSymbolSelector
692  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( newSymbol.release(), mStyle, mLayer, panel );
693  dlg->setContext( mContext );
694  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
695  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
696  openPanel( dlg );
697  }
698  else
699  {
700  QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
701  dlg.setContext( mContext );
702  if ( !dlg.exec() || !newSymbol )
703  {
704  return;
705  }
706 
707  mCategorizedSymbol = std::move( newSymbol );
709  }
710 }
711 
712 
714 {
715 }
716 
718 {
719  mRenderer->setClassAttribute( field );
720  emit widgetChanged();
721 }
722 
724 {
725  if ( idx.isValid() && idx.column() == 0 )
727 }
728 
730 {
731  const QgsRendererCategory category = mRenderer->categories().value( currentCategoryRow() );
732 
733  std::unique_ptr< QgsSymbol > symbol;
734 
735  if ( auto *lSymbol = category.symbol() )
736  {
737  symbol.reset( lSymbol->clone() );
738  }
739  else
740  {
741  symbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
742  }
743 
745  if ( panel && panel->dockMode() )
746  {
747  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( symbol.release(), mStyle, mLayer, panel );
748  dlg->setContext( mContext );
749  dlg->setPanelTitle( category.label() );
750  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
751  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
752  openPanel( dlg );
753  }
754  else
755  {
756  QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, mLayer, panel );
757  dlg.setContext( mContext );
758  if ( !dlg.exec() || !symbol )
759  {
760  return;
761  }
762 
763  mCategorizedSymbol = std::move( symbol );
765  }
766 }
767 
768 
770 {
771  const QString attrName = mExpressionWidget->currentField();
772  const int idx = mLayer->fields().lookupField( attrName );
773  QList<QVariant> uniqueValues;
774  if ( idx == -1 )
775  {
776  // Lets assume it's an expression
777  QgsExpression *expression = new QgsExpression( attrName );
783 
784  expression->prepare( &context );
786  QgsFeature feature;
787  while ( fit.nextFeature( feature ) )
788  {
789  context.setFeature( feature );
790  const QVariant value = expression->evaluate( &context );
791  if ( uniqueValues.contains( value ) )
792  continue;
793  uniqueValues << value;
794  }
795  }
796  else
797  {
798  uniqueValues = qgis::setToList( mLayer->uniqueValues( idx ) );
799  }
800 
801  // ask to abort if too many classes
802  if ( uniqueValues.size() >= 1000 )
803  {
804  const int res = QMessageBox::warning( nullptr, tr( "Classify Categories" ),
805  tr( "High number of classes. Classification would yield %1 entries which might not be expected. Continue?" ).arg( uniqueValues.size() ),
806  QMessageBox::Ok | QMessageBox::Cancel,
807  QMessageBox::Cancel );
808  if ( res == QMessageBox::Cancel )
809  {
810  return;
811  }
812  }
813 
814 #if 0
815  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
816  if ( !dlg.exec() )
817  return;
818 #endif
819 
821  bool deleteExisting = false;
822 
823  if ( !mOldClassificationAttribute.isEmpty() &&
824  attrName != mOldClassificationAttribute &&
825  !mRenderer->categories().isEmpty() )
826  {
827  const int res = QMessageBox::question( this,
828  tr( "Delete Classification" ),
829  tr( "The classification field was changed from '%1' to '%2'.\n"
830  "Should the existing classes be deleted before classification?" )
831  .arg( mOldClassificationAttribute, attrName ),
832  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
833  if ( res == QMessageBox::Cancel )
834  {
835  return;
836  }
837 
838  deleteExisting = ( res == QMessageBox::Yes );
839  }
840 
841  // First element to apply coloring to
842  bool keepExistingColors = false;
843  if ( !deleteExisting )
844  {
845  QgsCategoryList prevCats = mRenderer->categories();
846  keepExistingColors = !prevCats.isEmpty();
847  QgsRandomColorRamp randomColors;
848  if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
849  randomColors.setTotalColorCount( cats.size() );
850  for ( int i = 0; i < cats.size(); ++i )
851  {
852  bool contains = false;
853  const QVariant value = cats.at( i ).value();
854  for ( int j = 0; j < prevCats.size() && !contains; ++j )
855  {
856  const QVariant prevCatValue = prevCats.at( j ).value();
857  if ( prevCatValue.type() == QVariant::List )
858  {
859  const QVariantList list = prevCatValue.toList();
860  for ( const QVariant &v : list )
861  {
862  if ( v == value )
863  {
864  contains = true;
865  break;
866  }
867  }
868  }
869  else
870  {
871  if ( prevCats.at( j ).value() == value )
872  {
873  contains = true;
874  }
875  }
876  if ( contains )
877  break;
878  }
879 
880  if ( !contains )
881  {
882  if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
883  {
884  // insure that append symbols have random colors
885  cats.at( i ).symbol()->setColor( randomColors.color( i ) );
886  }
887  prevCats.append( cats.at( i ) );
888  }
889  }
890  cats = prevCats;
891  }
892 
893  mOldClassificationAttribute = attrName;
894 
895  // TODO: if not all categories are desired, delete some!
896  /*
897  if (not dlg.readAllCats.isChecked())
898  {
899  cats2 = {}
900  for item in dlg.listCategories.selectedItems():
901  for k,c in cats.iteritems():
902  if item.text() == k.toString():
903  break
904  cats2[k] = c
905  cats = cats2
906  }
907  */
908 
909  // recreate renderer
910  std::unique_ptr< QgsCategorizedSymbolRenderer > r = std::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
911  r->setSourceSymbol( mCategorizedSymbol->clone() );
912  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
913  if ( ramp )
914  r->setSourceColorRamp( ramp->clone() );
915 
916  if ( mModel )
917  {
918  mModel->setRenderer( r.get() );
919  }
920  mRenderer = std::move( r );
921  if ( ! keepExistingColors && ramp )
922  applyColorRamp();
923  emit widgetChanged();
924 }
925 
927 {
928  if ( !btnColorRamp->isNull() )
929  {
930  mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
931  }
932  mModel->updateSymbology();
933 }
934 
936 {
937  const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
938  if ( !idx.isValid() )
939  return -1;
940  return idx.row();
941 }
942 
944 {
945  QList<int> rows;
946  const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
947 
948  const auto constSelectedRows = selectedRows;
949  for ( const QModelIndex &r : constSelectedRows )
950  {
951  if ( r.isValid() )
952  {
953  rows.append( r.row() );
954  }
955  }
956  return rows;
957 }
958 
960 {
961  const QList<int> categoryIndexes = selectedCategories();
962  mModel->deleteRows( categoryIndexes );
963  emit widgetChanged();
964 }
965 
967 {
968  mModel->removeAllRows();
969  emit widgetChanged();
970 }
971 
973 {
974  if ( !mModel ) return;
976  const QgsRendererCategory cat( QString(), symbol, QString(), true );
977  mModel->addCategory( cat );
978  emit widgetChanged();
979 }
980 
982 {
983  QList<QgsSymbol *> selectedSymbols;
984 
985  QItemSelectionModel *m = viewCategories->selectionModel();
986  const QModelIndexList selectedIndexes = m->selectedRows( 1 );
987 
988  if ( !selectedIndexes.isEmpty() )
989  {
990  const QgsCategoryList &categories = mRenderer->categories();
991  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
992  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
993  {
994  const int row = ( *indexIt ).row();
995  QgsSymbol *s = categories[row].symbol();
996  if ( s )
997  {
998  selectedSymbols.append( s );
999  }
1000  }
1001  }
1002  return selectedSymbols;
1003 }
1004 
1006 {
1007  QgsCategoryList cl;
1008 
1009  QItemSelectionModel *m = viewCategories->selectionModel();
1010  const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1011 
1012  if ( !selectedIndexes.isEmpty() )
1013  {
1014  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1015  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1016  {
1017  cl.append( mModel->category( *indexIt ) );
1018  }
1019  }
1020  return cl;
1021 }
1022 
1024 {
1026  emit widgetChanged();
1027 }
1028 
1030 {
1032 }
1033 
1035 {
1036  viewCategories->selectionModel()->clear();
1037 }
1038 
1040 {
1041  const int matched = matchToSymbols( QgsStyle::defaultStyle() );
1042  if ( matched > 0 )
1043  {
1044  QMessageBox::information( this, tr( "Matched Symbols" ),
1045  tr( "Matched %1 categories to symbols." ).arg( matched ) );
1046  }
1047  else
1048  {
1049  QMessageBox::warning( this, tr( "Matched Symbols" ),
1050  tr( "No categories could be matched to symbols in library." ) );
1051  }
1052 }
1053 
1055 {
1056  if ( !mLayer || !style )
1057  return 0;
1058 
1062 
1063  QVariantList unmatchedCategories;
1064  QStringList unmatchedSymbols;
1065  const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1066 
1067  mModel->updateSymbology();
1068  return matched;
1069 }
1070 
1072 {
1073  QgsSettings settings;
1074  const QString openFileDir = settings.value( QStringLiteral( "UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1075 
1076  const QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to Symbols from File" ), openFileDir,
1077  tr( "XML files (*.xml *.XML)" ) );
1078  if ( fileName.isEmpty() )
1079  {
1080  return;
1081  }
1082 
1083  const QFileInfo openFileInfo( fileName );
1084  settings.setValue( QStringLiteral( "UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1085 
1086  QgsStyle importedStyle;
1087  if ( !importedStyle.importXml( fileName ) )
1088  {
1089  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1090  tr( "An error occurred while reading file:\n%1" ).arg( importedStyle.errorString() ) );
1091  return;
1092  }
1093 
1094  const int matched = matchToSymbols( &importedStyle );
1095  if ( matched > 0 )
1096  {
1097  QMessageBox::information( this, tr( "Match to Symbols from File" ),
1098  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
1099  }
1100  else
1101  {
1102  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1103  tr( "No categories could be matched to symbols in file." ) );
1104  }
1105 }
1106 
1108 {
1109  for ( const QgsLegendSymbolItem &legendSymbol : levels )
1110  {
1111  QgsSymbol *sym = legendSymbol.symbol();
1112  for ( int layer = 0; layer < sym->symbolLayerCount(); layer++ )
1113  {
1114  mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->clone() );
1115  }
1116  }
1117  mRenderer->setUsingSymbolLevels( enabled );
1118  mModel->updateSymbology();
1119  emit widgetChanged();
1120 }
1121 
1123 {
1124  std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
1125  if ( !tempSymbol )
1126  return;
1127 
1128  const QList<int> selectedCats = selectedCategories();
1129  if ( !selectedCats.isEmpty() )
1130  {
1131  for ( const int idx : selectedCats )
1132  {
1133  if ( mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1134  continue;
1135 
1136  std::unique_ptr< QgsSymbol > newCatSymbol( tempSymbol->clone() );
1137  if ( selectedCats.count() > 1 )
1138  {
1139  //if updating multiple categories, retain the existing category colors
1140  newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1141  }
1142  mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1143  }
1144  emit widgetChanged();
1145  }
1146 }
1147 
1148 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container )
1149 {
1150  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( container );
1151  if ( !dlg )
1152  return;
1153 
1154  delete dlg->symbol();
1155 }
1156 
1157 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1158 {
1159  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( sender() );
1160  mCategorizedSymbol.reset( dlg->symbol()->clone() );
1161 
1163 }
1164 
1165 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1166 {
1167  mCategorizedSymbol.reset( btnChangeCategorizedSymbol->symbol()->clone() );
1168 
1170 }
1171 
1173 {
1174  // When there is a selection, change the selected symbols only
1175  QItemSelectionModel *m = viewCategories->selectionModel();
1176  const QModelIndexList i = m->selectedRows();
1177 
1178  if ( !i.isEmpty() )
1179  {
1180  const QList<int> selectedCats = selectedCategories();
1181 
1182  if ( !selectedCats.isEmpty() )
1183  {
1184  const auto constSelectedCats = selectedCats;
1185  for ( const int idx : constSelectedCats )
1186  {
1187  QgsSymbol *newCatSymbol = mCategorizedSymbol->clone();
1188  if ( selectedCats.count() > 1 )
1189  {
1190  //if updating multiple categories, retain the existing category colors
1191  newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1192  }
1193  mRenderer->updateCategorySymbol( idx, newCatSymbol );
1194  }
1195  }
1196  }
1197  else
1198  {
1199  mRenderer->updateSymbols( mCategorizedSymbol.get() );
1200  }
1201 
1202  mModel->updateSymbology();
1203  emit widgetChanged();
1204 }
1205 
1207 {
1208  if ( !event )
1209  {
1210  return;
1211  }
1212 
1213  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1214  {
1215  mCopyBuffer.clear();
1216  mCopyBuffer = selectedCategoryList();
1217  }
1218  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1219  {
1220  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1221  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1222  {
1223  mModel->addCategory( *rIt );
1224  }
1225  }
1226 }
1227 
1228 QgsExpressionContext QgsCategorizedSymbolRendererWidget::createExpressionContext() const
1229 {
1230  QgsExpressionContext expContext;
1234 
1235  if ( auto *lMapCanvas = mContext.mapCanvas() )
1236  {
1237  expContext << QgsExpressionContextUtils::mapSettingsScope( lMapCanvas->mapSettings() )
1238  << new QgsExpressionContextScope( lMapCanvas->expressionContextScope() );
1239  if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( lMapCanvas->temporalController() ) )
1240  {
1241  expContext << generator->createExpressionContextScope();
1242  }
1243  }
1244  else
1245  {
1247  }
1248 
1249  if ( auto *lVectorLayer = vectorLayer() )
1250  expContext << QgsExpressionContextUtils::layerScope( lVectorLayer );
1251 
1252  // additional scopes
1253  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
1254  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
1255  {
1256  expContext.appendScope( new QgsExpressionContextScope( scope ) );
1257  }
1258 
1259  return expContext;
1260 }
1261 
1262 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1263 {
1264  QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( mCategorizedSymbol.get() ); // this should be only enabled for marker symbols
1265  QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1266  if ( panel )
1267  {
1268  connect( panel, &QgsPanelWidget::widgetChanged, this, [ = ]
1269  {
1270  mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1271  emit widgetChanged();
1272  } );
1273  openPanel( panel ); // takes ownership of the panel
1274  }
1275 }
1276 
1277 void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1278 {
1279  const QgsCategoryList &categories = mRenderer->categories();
1280 
1281  const QList<int> selectedCategoryIndexes = selectedCategories();
1282  QList< int > categoryIndexes;
1283 
1284  // filter out "" entry
1285  for ( const int i : selectedCategoryIndexes )
1286  {
1287  const QVariant v = categories.at( i ).value();
1288 
1289  if ( !v.isValid() || v == "" )
1290  {
1291  continue;
1292  }
1293 
1294  categoryIndexes.append( i );
1295  }
1296 
1297  if ( categoryIndexes.count() < 2 )
1298  return;
1299 
1300  QStringList labels;
1301  QVariantList values;
1302  values.reserve( categoryIndexes.count() );
1303  labels.reserve( categoryIndexes.count() );
1304  for ( const int i : categoryIndexes )
1305  {
1306  const QVariant v = categories.at( i ).value();
1307 
1308  if ( v.type() == QVariant::List )
1309  {
1310  values.append( v.toList() );
1311  }
1312  else
1313  values << v;
1314 
1315  labels << categories.at( i ).label();
1316  }
1317 
1318  // modify first category (basically we "merge up" into the first selected category)
1319  mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join( ',' ) );
1320  mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1321 
1322  categoryIndexes.pop_front();
1323  mModel->deleteRows( categoryIndexes );
1324 
1325  emit widgetChanged();
1326 }
1327 
1328 void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1329 {
1330  const QList<int> categoryIndexes = selectedCategories();
1331  if ( categoryIndexes.isEmpty() )
1332  return;
1333 
1334  const QgsCategoryList &categories = mRenderer->categories();
1335  for ( const int i : categoryIndexes )
1336  {
1337  const QVariant v = categories.at( i ).value();
1338  if ( v.type() != QVariant::List )
1339  continue;
1340 
1341  const QVariantList list = v.toList();
1342  for ( int j = 1; j < list.count(); ++j )
1343  {
1344  mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1345  }
1346  mRenderer->updateCategoryValue( i, list.at( 0 ) );
1347  mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1348  }
1349 
1350  emit widgetChanged();
1351 }
1352 
1353 void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1354 {
1355  mContextMenu->clear();
1356  const QList< QAction * > actions = contextMenu->actions();
1357  for ( QAction *act : actions )
1358  {
1359  mContextMenu->addAction( act );
1360  }
1361 
1362  mContextMenu->addSeparator();
1363 
1364  if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1365  {
1366  mContextMenu->addAction( mMergeCategoriesAction );
1367  }
1368  if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1369  {
1370  const QList<int> categoryIndexes = selectedCategories();
1371  const QgsCategoryList &categories = mRenderer->categories();
1372  const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1373  if ( v.type() == QVariant::List )
1374  mContextMenu->addAction( mUnmergeCategoriesAction );
1375  }
1376  else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1377  {
1378  mContextMenu->addAction( mUnmergeCategoriesAction );
1379  }
1380 
1381  mContextMenu->exec( QCursor::pos() );
1382 }
1383 
1384 void QgsCategorizedSymbolRendererWidget::selectionChanged( const QItemSelection &, const QItemSelection & )
1385 {
1386  const QList<int> selectedCats = selectedCategories();
1387  if ( !selectedCats.isEmpty() )
1388  {
1389  whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1390  }
1391  else if ( mRenderer->sourceSymbol() )
1392  {
1393  whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->sourceSymbol()->clone() );
1394  }
1395  btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ? mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr( "Symbol Settings" ) );
1396 }
SymbolType
Symbol types.
Definition: qgis.h:169
@ 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)
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:52
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:457
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
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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:1185
QList< QgsRendererCategory > QgsCategoryList
const QgsField & field
Definition: qgsfield.h:463
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsg(str)
Definition: qgslogger.h:38