QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsrulebasedrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrulebasedrendererwidget.cpp - Settings widget for rule-based renderer
3  ---------------------
4  begin : May 2010
5  copyright : (C) 2010 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 
18 #include "qgsrulebasedrenderer.h"
19 #include "qgsfeatureiterator.h"
20 #include "qgssymbollayerutils.h"
21 #include "qgssymbol.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsapplication.h"
24 #include "qgsexpression.h"
26 #include "qgslogger.h"
27 #include "qgsreadwritecontext.h"
28 #include "qstring.h"
30 #include "qgspanelwidget.h"
31 #include "qgsmapcanvas.h"
32 #include "qgssettings.h"
33 #include "qgsguiutils.h"
34 
35 #include <QKeyEvent>
36 #include <QMenu>
37 #include <QProgressDialog>
38 #include <QTreeWidgetItem>
39 #include <QVBoxLayout>
40 #include <QMessageBox>
41 #include <QClipboard>
42 
43 #ifdef ENABLE_MODELTEST
44 #include "modeltest.h"
45 #endif
46 
47 
49 {
50  return new QgsRuleBasedRendererWidget( layer, style, renderer );
51 }
52 
54  : QgsRendererWidget( layer, style )
55  , mContextMenu( new QMenu( this ) )
56 {
57  mRenderer = nullptr;
58  // try to recognize the previous renderer
59  // (null renderer means "no previous renderer")
60 
61 
62  if ( renderer )
63  {
65  }
66  if ( !mRenderer )
67  {
68  // some default options
70 
71  mRenderer = std::make_unique< QgsRuleBasedRenderer >( symbol );
72  }
73 
74  setupUi( this );
75  this->layout()->setContentsMargins( 0, 0, 0, 0 );
76 
77  mModel = new QgsRuleBasedRendererModel( mRenderer.get(), viewRules );
78 #ifdef ENABLE_MODELTEST
79  new ModelTest( mModel, this ); // for model validity checking
80 #endif
81  viewRules->setModel( mModel );
82 
83  mDeleteAction = new QAction( tr( "Remove Rule" ), this );
84  mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
85 
86  viewRules->addAction( mCopyAction );
87  viewRules->addAction( mPasteAction );
88  viewRules->addAction( mDeleteAction );
89 
90  mRefineMenu = new QMenu( tr( "Refine Current Rule" ), btnRefineRule );
91  mRefineMenu->addAction( tr( "Add Scales to Rule" ), this, SLOT( refineRuleScales() ) );
92  mRefineMenu->addAction( tr( "Add Categories to Rule" ), this, SLOT( refineRuleCategories() ) );
93  mRefineMenu->addAction( tr( "Add Ranges to Rule" ), this, SLOT( refineRuleRanges() ) );
94  btnRefineRule->setMenu( mRefineMenu );
95 
96  btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
97  btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
98  btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
99 
100  connect( viewRules, &QAbstractItemView::doubleClicked, this, static_cast < void ( QgsRuleBasedRendererWidget::* )( const QModelIndex &index ) > ( &QgsRuleBasedRendererWidget::editRule ) );
101 
102  // support for context menu (now handled generically)
103  connect( viewRules, &QWidget::customContextMenuRequested, this, &QgsRuleBasedRendererWidget::showContextMenu );
104 
105  connect( viewRules->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsRuleBasedRendererWidget::currentRuleChanged );
106  connect( viewRules->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsRuleBasedRendererWidget::selectedRulesChanged );
107 
108  connect( btnAddRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::addRule );
109  connect( btnEditRule, &QAbstractButton::clicked, this, static_cast < void ( QgsRuleBasedRendererWidget::* )() > ( &QgsRuleBasedRendererWidget::editRule ) );
110  connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::removeRule );
111  connect( mDeleteAction, &QAction::triggered, this, &QgsRuleBasedRendererWidget::removeRule );
112  connect( btnCountFeatures, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::countFeatures );
113 
114  connect( btnRenderingOrder, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::setRenderingOrder );
115 
116  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
117  connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsPanelWidget::widgetChanged );
118  connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsPanelWidget::widgetChanged );
119 
122 
123  // store/restore header section widths
124  connect( viewRules->header(), &QHeaderView::sectionResized, this, &QgsRuleBasedRendererWidget::saveSectionWidth );
125 
127 
128  connect( mContextMenu, &QMenu::aboutToShow, this, [ = ]
129  {
130  std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
131  mPasteSymbolAction->setEnabled( static_cast< bool >( tempSymbol ) );
132  } );
133 }
134 
136 {
137  qDeleteAll( mCopyBuffer );
138 }
139 
141 {
142  return mRenderer.get();
143 }
144 
146 {
147  if ( dockMode )
148  {
149  // when in dock mode, these shortcuts conflict with the main window shortcuts and cannot be used
150  if ( mDeleteAction )
151  mDeleteAction->setShortcut( QKeySequence() );
152  }
154 }
155 
157 {
160 
162  if ( current )
163  {
164  // add after this rule
165  QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
166  mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
167  QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
168  viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
169  }
170  else
171  {
172  // append to root rule
173  int rows = mModel->rowCount();
174  mModel->insertRule( QModelIndex(), rows, newrule );
175  QModelIndex newindex = mModel->index( rows, 0 );
176  viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
177  }
178  editRule();
179 }
180 
182 {
183  QItemSelectionModel *sel = viewRules->selectionModel();
184  QModelIndex idx = sel->currentIndex();
185  if ( !idx.isValid() )
186  return nullptr;
187  return mModel->ruleForIndex( idx );
188 }
189 
191 {
192  editRule( viewRules->selectionModel()->currentIndex() );
193 }
194 
195 void QgsRuleBasedRendererWidget::editRule( const QModelIndex &index )
196 {
197  if ( !index.isValid() )
198  return;
199 
202 
203  if ( panel && panel->dockMode() )
204  {
205  QgsRendererRulePropsWidget *widget = new QgsRendererRulePropsWidget( rule, mLayer, mStyle, this, mContext );//panel?
206  widget->setPanelTitle( tr( "Edit Rule" ) );
207  connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted );
208  connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel );
209  openPanel( widget );
210  return;
211  }
212 
213  QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle, this, mContext );
214  if ( dlg.exec() )
215  {
216  mModel->updateRule( index.parent(), index.row() );
218  emit widgetChanged();
219  }
220 }
221 
223 {
224  QItemSelection sel = viewRules->selectionModel()->selection();
225  QgsDebugMsg( QStringLiteral( "REMOVE RULES!!! ranges: %1" ).arg( sel.count() ) );
226  const auto constSel = sel;
227  for ( const QItemSelectionRange &range : constSel )
228  {
229  QgsDebugMsg( QStringLiteral( "RANGE: r %1 - %2" ).arg( range.top() ).arg( range.bottom() ) );
230  if ( range.isValid() )
231  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
232  }
233  // make sure that the selection is gone
234  viewRules->selectionModel()->clear();
236 }
237 
238 void QgsRuleBasedRendererWidget::currentRuleChanged( const QModelIndex &current, const QModelIndex &previous )
239 {
240  Q_UNUSED( previous )
241  btnEditRule->setEnabled( current.isValid() );
242 }
243 
244 
250 #include <QDialogButtonBox>
251 #include <QInputDialog>
252 #include <QClipboard>
253 
255 {
256  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
257 
258  if ( indexlist.isEmpty() )
259  return;
260 
261 
262  if ( type == 0 ) // categories
264  else if ( type == 1 ) // ranges
266  else // scales
267  refineRuleScalesGui( indexlist );
268 
269  // TODO: set initial rule's symbol to NULL (?)
270 
271  // show the newly added rules
272  const auto constIndexlist = indexlist;
273  for ( const QModelIndex &index : constIndexlist )
274  viewRules->expand( index );
275 }
276 
278 {
279  refineRule( 0 );
280 }
281 
283 {
284  refineRule( 1 );
285 }
286 
288 {
289  refineRule( 2 );
290 }
291 
293 {
295  w->setPanelTitle( tr( "Add Categories to Rules" ) );
296  connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted );
297  w->setContext( mContext );
298  openPanel( w );
299 }
300 
302 {
304  w->setPanelTitle( tr( "Add Ranges to Rules" ) );
305  connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleRangesAccepted );
306  w->setContext( mContext );
307  openPanel( w );
308 }
309 
310 void QgsRuleBasedRendererWidget::refineRuleScalesGui( const QModelIndexList &indexList )
311 {
312  for ( const QModelIndex &index : indexList )
313  {
314  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
315 
316  // If any of the rules don't have a symbol let the user know and exit.
317  if ( !initialRule->symbol() )
318  {
319  QMessageBox::warning( this, tr( "Scale Refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
320  return;
321  }
322  }
323 
324  QString txt = QInputDialog::getText( this,
325  tr( "Scale Refinement" ),
326  tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
327  if ( txt.isEmpty() )
328  return;
329 
330  QList<int> scales;
331  bool ok;
332  const auto constSplit = txt.split( ',' );
333  for ( const QString &item : constSplit )
334  {
335  int scale = item.toInt( &ok );
336  if ( ok )
337  scales.append( scale );
338  else
339  QMessageBox::information( this, tr( "Scale Refinement" ), tr( "\"%1\" is not valid scale denominator, ignoring it." ).arg( item ) );
340  }
341 
342  for ( const QModelIndex &index : indexList )
343  {
344  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
345  mModel->willAddRules( index, scales.count() + 1 );
346  QgsRuleBasedRenderer::refineRuleScales( initialRule, scales );
347  }
349 }
350 
351 void QgsRuleBasedRendererWidget::setSymbolLevels( const QList<QgsLegendSymbolItem> &levels, bool )
352 {
353  if ( !mRenderer )
354  return;
355 
356  for ( const QgsLegendSymbolItem &legendSymbol : std::as_const( levels ) )
357  {
358  QgsSymbol *sym = legendSymbol.symbol();
359  for ( int layer = 0; layer < sym->symbolLayerCount(); layer++ )
360  {
361  mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->clone() );
362  }
363  }
364 
365  emit widgetChanged();
366 }
367 
369 {
370  QList<QgsSymbol *> symbolList;
371 
372  if ( !mRenderer )
373  {
374  return symbolList;
375  }
376 
377  QItemSelection sel = viewRules->selectionModel()->selection();
378  const auto constSel = sel;
379  for ( const QItemSelectionRange &range : constSel )
380  {
381  QModelIndex parent = range.parent();
382  QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
383  const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
384  for ( int row = range.top(); row <= range.bottom(); row++ )
385  {
386  symbolList.append( children.at( row )->symbol() );
387  }
388  }
389 
390  return symbolList;
391 }
392 
394 {
396  QItemSelection sel = viewRules->selectionModel()->selection();
397  const auto constSel = sel;
398  for ( const QItemSelectionRange &range : constSel )
399  {
400  QModelIndex parent = range.parent();
401  QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
402  const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
403  for ( int row = range.top(); row <= range.bottom(); row++ )
404  {
405  rl.append( children.at( row )->clone() );
406  }
407  }
408  return rl;
409 }
410 
412 {
413  // TODO: model/view
414  /*
415  if ( treeRules )
416  {
417  treeRules->populateRules();
418  }
419  */
420  emit widgetChanged();
421 }
422 
424 {
425  if ( !event )
426  {
427  return;
428  }
429 
430  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
431  {
432  qDeleteAll( mCopyBuffer );
433  mCopyBuffer.clear();
435  }
436  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
437  {
438  QgsRuleBasedRenderer::RuleList::const_iterator rIt = mCopyBuffer.constBegin();
439  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
440  {
441  int rows = mModel->rowCount();
442  mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
443  }
444  }
445 }
446 
447 #include "qgssymbollevelsdialog.h"
448 
450 {
452  if ( panel && panel->dockMode() )
453  {
454  QgsSymbolLevelsWidget *widget = new QgsSymbolLevelsWidget( mRenderer.get(), true, panel );
455  widget->setForceOrderingEnabled( true );
456  widget->setPanelTitle( tr( "Symbol Levels" ) );
457  connect( widget, &QgsPanelWidget::widgetChanged, this, [ = ]()
458  {
459  setSymbolLevels( widget->symbolLevels(), widget->usingLevels() );
460  } );
461  panel->openPanel( widget );
462  }
463  else
464  {
465  QgsSymbolLevelsDialog dlg( mRenderer.get(), true, panel );
466  dlg.setForceOrderingEnabled( true );
467  if ( dlg.exec() )
468  {
469  setSymbolLevels( dlg.symbolLevels(), dlg.usingLevels() );
470  }
471  }
472 }
473 
474 void QgsRuleBasedRendererWidget::saveSectionWidth( int section, int oldSize, int newSize )
475 {
476  Q_UNUSED( oldSize )
477  // skip last section, as it stretches
478  if ( section == 5 )
479  return;
480  QgsSettings settings;
481  QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
482  settings.setValue( path, newSize );
483 }
484 
486 {
487  QgsSettings settings;
488  QString path = QStringLiteral( "/Windows/RuleBasedTree/sectionWidth/" );
489  QHeaderView *head = viewRules->header();
490  head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
491  head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
492  head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
493  head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
494  head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
495  head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
496 }
497 
499 {
500  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
501  QgsDebugMsgLevel( QString::number( indexlist.count() ), 2 );
502 
503  if ( indexlist.isEmpty() )
504  return;
505 
506  QMimeData *mime = mModel->mimeData( indexlist );
507  QApplication::clipboard()->setMimeData( mime );
508 }
509 
511 {
512  const QMimeData *mime = QApplication::clipboard()->mimeData();
513  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
514  QModelIndex index;
515  if ( indexlist.isEmpty() )
516  index = mModel->index( mModel->rowCount(), 0 );
517  else
518  index = indexlist.first();
519  mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
520 }
521 
523 {
524  std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
525  if ( !tempSymbol )
526  return;
527 
528  const QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
529  for ( const QModelIndex &index : indexList )
530  {
531  if ( QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index ) )
532  {
533  if ( !rule->symbol() || rule->symbol()->type() != tempSymbol->type() )
534  continue;
535 
536  mModel->setSymbol( index, tempSymbol->clone() );
537  }
538  }
539  emit widgetChanged();
540 }
541 
542 void QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted( QgsPanelWidget *panel )
543 {
544  QgsCategorizedSymbolRendererWidget *w = qobject_cast<QgsCategorizedSymbolRendererWidget *>( panel );
545 
546  // create new rules
548  QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
549  const auto constIndexList = indexList;
550  for ( const QModelIndex &index : constIndexList )
551  {
552  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
553  mModel->willAddRules( index, r->categories().count() );
555  }
557 }
558 
559 void QgsRuleBasedRendererWidget::refineRuleRangesAccepted( QgsPanelWidget *panel )
560 {
561  QgsGraduatedSymbolRendererWidget *w = qobject_cast<QgsGraduatedSymbolRendererWidget *>( panel );
562  // create new rules
564  QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
565  const auto constIndexList = indexList;
566  for ( const QModelIndex &index : constIndexList )
567  {
568  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
569  mModel->willAddRules( index, r->ranges().count() );
570  QgsRuleBasedRenderer::refineRuleRanges( initialRule, r );
571  }
573 }
574 
575 void QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
576 {
577  QgsRendererRulePropsWidget *widget = qobject_cast<QgsRendererRulePropsWidget *>( panel );
578  if ( !widget )
579  return;
580 
581  widget->apply();
582 
583  // model should know about the change and emit dataChanged signal for the view
584  QModelIndex index = viewRules->selectionModel()->currentIndex();
585  mModel->updateRule( index.parent(), index.row() );
587 }
588 
589 void QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel()
590 {
591  ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
592 }
593 
594 void QgsRuleBasedRendererWidget::showContextMenu( QPoint )
595 {
596  mContextMenu->clear();
597  mContextMenu->addAction( mCopyAction );
598  mContextMenu->addAction( mPasteAction );
599 
600  const QList< QAction * > actions = contextMenu->actions();
601  for ( QAction *act : actions )
602  {
603  mContextMenu->addAction( act );
604  }
605 
606  mContextMenu->addMenu( mRefineMenu );
607 
608  mContextMenu->exec( QCursor::pos() );
609 }
610 
611 
613 {
614  if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
615  {
616  return;
617  }
618  QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> countMap;
619 
620  QgsRuleBasedRenderer::RuleList ruleList = mRenderer->rootRule()->descendants();
621  // insert all so that we have counts 0
622  const auto constRuleList = ruleList;
623  for ( QgsRuleBasedRenderer::Rule *rule : constRuleList )
624  {
625  countMap[rule].count = 0;
626  countMap[rule].duplicateCount = 0;
627  }
628 
629  QgsRenderContext renderContext;
630  renderContext.setRendererScale( 0 ); // ignore scale
631 
633 
634  // additional scopes
635  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
636  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
637  {
638  context.appendScope( new QgsExpressionContextScope( scope ) );
639  }
640 
641  renderContext.setExpressionContext( context );
642 
643  mRenderer->startRender( renderContext, mLayer->fields() );
644  // QgsRuleBasedRenderer::filter must be called after startRender
647  req.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mLayer->fields() );
648  QgsFeatureIterator fit = mLayer->getFeatures( req );
649 
650  long long nFeatures = mLayer->featureCount();
651  QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, 100 );
652  p.setWindowModality( Qt::WindowModal );
653  long long featuresCounted = 0;
654 
655  QgsFeature f;
656  while ( fit.nextFeature( f ) )
657  {
658  renderContext.expressionContext().setFeature( f );
659  QgsRuleBasedRenderer::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
660 
661  const auto constFeatureRuleList = featureRuleList;
662  for ( QgsRuleBasedRenderer::Rule *rule : constFeatureRuleList )
663  {
664  countMap[rule].count++;
665  if ( featureRuleList.size() > 1 )
666  {
667  countMap[rule].duplicateCount++;
668  }
669  const auto constFeatureRuleList = featureRuleList;
670  for ( QgsRuleBasedRenderer::Rule *duplicateRule : constFeatureRuleList )
671  {
672  if ( duplicateRule == rule ) continue;
673  countMap[rule].duplicateCountMap[duplicateRule] += 1;
674  }
675  }
676  ++featuresCounted;
677  if ( featuresCounted % 50 == 0 )
678  {
679  if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
680  {
681  p.setMaximum( 0 );
682  }
683  p.setValue( static_cast< double >( featuresCounted ) / nFeatures * 100.0 );
684  if ( p.wasCanceled() )
685  {
686  return;
687  }
688  }
689  }
690  p.setValue( nFeatures );
691 
692  mRenderer->stopRender( renderContext );
693 
694 #ifdef QGISDEBUG
695  const auto constKeys = countMap.keys();
696  for ( QgsRuleBasedRenderer::Rule *rule : constKeys )
697  {
698  QgsDebugMsg( QStringLiteral( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
699  }
700 #endif
701 
702  mModel->setFeatureCounts( countMap );
703 }
704 
706 {
707  bool enabled = !viewRules->selectionModel()->selectedIndexes().isEmpty();
708  btnRefineRule->setEnabled( enabled );
709  btnRemoveRule->setEnabled( enabled );
710 }
711 
713 
715  : QgsPanelWidget( parent )
716  , mRule( rule )
717  , mLayer( layer )
718  , mContext( context )
719 {
720  setupUi( this );
721  layout()->setContentsMargins( 0, 0, 0, 0 );
722 
723  mElseRadio->setChecked( mRule->isElse() );
724  mFilterRadio->setChecked( !mRule->isElse() );
725  editFilter->setText( mRule->filterExpression() );
726  editFilter->setToolTip( mRule->filterExpression() );
727  editLabel->setText( mRule->label() );
728  editDescription->setText( mRule->description() );
729  editDescription->setToolTip( mRule->description() );
730 
731  if ( mRule->dependsOnScale() )
732  {
733  groupScale->setChecked( true );
734  mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ),
735  std::max( rule->maximumScale(), 0.0 ) );
736  }
737  mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
738 
739  if ( mRule->symbol() )
740  {
741  groupSymbol->setChecked( true );
742  mSymbol = mRule->symbol()->clone(); // use a clone!
743  }
744  else
745  {
746  groupSymbol->setChecked( false );
748  }
749 
750  mSymbolSelector = new QgsSymbolSelectorWidget( mSymbol, style, mLayer, this );
754 
755  QVBoxLayout *l = new QVBoxLayout;
756  l->addWidget( mSymbolSelector );
757  groupSymbol->setLayout( l );
758 
759  connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
760  connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
761  connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
762  connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
763  connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
764  connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
765  connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
766  connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
767  connect( mFilterRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
768  connect( mElseRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) );} );
769 }
770 
771 #include "qgsvscrollarea.h"
772 
774  : QDialog( parent )
775 {
776 
777 #ifdef Q_OS_MAC
778  setWindowModality( Qt::WindowModal );
779 #endif
780 
781  QVBoxLayout *layout = new QVBoxLayout( this );
782  QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
783  scrollArea->setFrameShape( QFrame::NoFrame );
784  layout->addWidget( scrollArea );
785 
786  buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
787  mPropsWidget = new QgsRendererRulePropsWidget( rule, layer, style, this, context );
788 
789  scrollArea->setWidget( mPropsWidget );
790  layout->addWidget( buttonBox );
791  this->setWindowTitle( "Edit Rule" );
793 
794  connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererRulePropsDialog::accept );
795  connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
796  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRendererRulePropsDialog::showHelp );
797 }
798 
800 {
801  mPropsWidget->testFilter();
802 }
803 
805 {
806  mPropsWidget->buildExpression();
807 }
808 
810 {
811  mPropsWidget->apply();
812  QDialog::accept();
813 }
814 
815 void QgsRendererRulePropsDialog::showHelp()
816 {
817  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-rendering" ) );
818 }
819 
820 
822 {
824 
825  // additional scopes
826  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
827  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
828  {
829  context.appendScope( new QgsExpressionContextScope( scope ) );
830  }
831 
832  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
833 
834  if ( dlg.exec() )
835  editFilter->setText( dlg.expressionText() );
836 }
837 
839 {
840  if ( !mFilterRadio->isChecked() )
841  return;
842 
843  QgsExpression filter( editFilter->text() );
844  if ( filter.hasParserError() )
845  {
846  QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
847  return;
848  }
849 
851 
852  // additional scopes
853  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
854  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
855  {
856  context.appendScope( new QgsExpressionContextScope( scope ) );
857  }
858 
859  if ( !filter.prepare( &context ) )
860  {
861  QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
862  return;
863  }
864 
865  QApplication::setOverrideCursor( Qt::WaitCursor );
866 
869  .setFilterExpression( editFilter->text() )
870  .setExpressionContext( context );
871 
872  QgsFeatureIterator fit = mLayer->getFeatures( req );
873 
874  int count = 0;
875  QgsFeature f;
876  while ( fit.nextFeature( f ) )
877  {
878  count++;
879  }
880 
881  QApplication::restoreOverrideCursor();
882 
883  QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
884 }
885 
887 {
888  QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text();
889  mRule->setFilterExpression( filter );
890  mRule->setLabel( editLabel->text() );
891  mRule->setDescription( editDescription->text() );
892  // caution: rule uses scale denom, scale widget uses true scales
893  mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
894  mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
895  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
896 }
897 
899 {
902 }
903 
905 
906 /*
907  setDragEnabled(true);
908  viewport()->setAcceptDrops(true);
909  setDropIndicatorShown(true);
910  setDragDropMode(QAbstractItemView::InternalMove);
911 */
912 
914 
916  : QAbstractItemModel( parent )
917  , mR( renderer )
918 {
919 }
920 
921 Qt::ItemFlags QgsRuleBasedRendererModel::flags( const QModelIndex &index ) const
922 {
923  if ( !index.isValid() )
924  return Qt::ItemIsDropEnabled;
925 
926  // allow drop only at first column
927  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
928 
929  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
930 
931  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
932  Qt::ItemIsEditable | checkable |
933  Qt::ItemIsDragEnabled | drop;
934 }
935 
936 QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) const
937 {
938  if ( !index.isValid() )
939  return QVariant();
940 
942 
943  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
944  {
945  switch ( index.column() )
946  {
947  case 0:
948  return rule->label();
949  case 1:
950  if ( rule->isElse() )
951  {
952  return "ELSE";
953  }
954  else
955  {
956  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
957  }
958  case 2:
959  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
960  case 3:
961  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
962  case 4:
963  if ( mFeatureCountMap.count( rule ) == 1 )
964  {
965  return QVariant( mFeatureCountMap[rule].count );
966  }
967  return QVariant();
968  case 5:
969  if ( mFeatureCountMap.count( rule ) == 1 )
970  {
971  if ( role == Qt::DisplayRole )
972  {
973  return QVariant( mFeatureCountMap[rule].duplicateCount );
974  }
975  else // tooltip - detailed info about duplicates
976  {
977  if ( mFeatureCountMap[rule].duplicateCount > 0 )
978  {
979  QString tip = QStringLiteral( "<p style='margin:0px;'><ul>" );
980  const auto duplicateMap = mFeatureCountMap[rule].duplicateCountMap;
981  for ( auto it = duplicateMap.constBegin(); it != duplicateMap.constEnd(); ++it )
982  {
983  QString label = it.key()->label().replace( '&', QLatin1String( "&amp;" ) ).replace( '>', QLatin1String( "&gt;" ) ).replace( '<', QLatin1String( "&lt;" ) );
984  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( it.value() ).arg( label );
985  }
986  tip += QLatin1String( "</ul>" );
987  return tip;
988  }
989  else
990  {
991  return 0;
992  }
993  }
994  }
995  return QVariant();
996  default:
997  return QVariant();
998  }
999  }
1000  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
1001  {
1002  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
1003  return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( iconSize, iconSize ) );
1004  }
1005  else if ( role == Qt::TextAlignmentRole )
1006  {
1007  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
1008  }
1009  else if ( role == Qt::FontRole && index.column() == 1 )
1010  {
1011  if ( rule->isElse() )
1012  {
1013  QFont italicFont;
1014  italicFont.setItalic( true );
1015  return italicFont;
1016  }
1017  return QVariant();
1018  }
1019  else if ( role == Qt::EditRole )
1020  {
1021  switch ( index.column() )
1022  {
1023  case 0:
1024  return rule->label();
1025  case 1:
1026  return rule->filterExpression();
1027  case 2:
1028  return rule->minimumScale();
1029  case 3:
1030  return rule->maximumScale();
1031  default:
1032  return QVariant();
1033  }
1034  }
1035  else if ( role == Qt::CheckStateRole )
1036  {
1037  if ( index.column() != 0 )
1038  return QVariant();
1039  return rule->active() ? Qt::Checked : Qt::Unchecked;
1040  }
1041  else
1042  return QVariant();
1043 }
1044 
1045 QVariant QgsRuleBasedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
1046 {
1047  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
1048  {
1049  QStringList lst;
1050  lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Count" ) << tr( "Duplicate Count" );
1051  return lst[section];
1052  }
1053  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
1054  {
1055  if ( section == 4 ) // Count
1056  {
1057  return tr( "Number of features in this rule." );
1058  }
1059  else if ( section == 5 ) // Duplicate count
1060  {
1061  return tr( "Number of features in this rule which are also present in other rule(s)." );
1062  }
1063  }
1064 
1065  return QVariant();
1066 }
1067 
1068 int QgsRuleBasedRendererModel::rowCount( const QModelIndex &parent ) const
1069 {
1070  if ( parent.column() > 0 )
1071  return 0;
1072 
1074 
1075  return parentRule->children().count();
1076 }
1077 
1078 int QgsRuleBasedRendererModel::columnCount( const QModelIndex & ) const
1079 {
1080  return 6;
1081 }
1082 
1083 QModelIndex QgsRuleBasedRendererModel::index( int row, int column, const QModelIndex &parent ) const
1084 {
1085  if ( hasIndex( row, column, parent ) )
1086  {
1088  QgsRuleBasedRenderer::Rule *childRule = parentRule->children()[row];
1089  return createIndex( row, column, childRule );
1090  }
1091  return QModelIndex();
1092 }
1093 
1094 QModelIndex QgsRuleBasedRendererModel::parent( const QModelIndex &index ) const
1095 {
1096  if ( !index.isValid() )
1097  return QModelIndex();
1098 
1100  QgsRuleBasedRenderer::Rule *parentRule = childRule->parent();
1101 
1102  if ( parentRule == mR->rootRule() )
1103  return QModelIndex();
1104 
1105  // this is right: we need to know row number of our parent (in our grandparent)
1106  int row = parentRule->parent()->children().indexOf( parentRule );
1107 
1108  return createIndex( row, 0, parentRule );
1109 }
1110 
1111 bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
1112 {
1113  if ( !index.isValid() )
1114  return false;
1115 
1117 
1118  if ( role == Qt::CheckStateRole )
1119  {
1120  rule->setActive( value.toInt() == Qt::Checked );
1121  emit dataChanged( index, index );
1122  return true;
1123  }
1124 
1125  if ( role != Qt::EditRole )
1126  return false;
1127 
1128  switch ( index.column() )
1129  {
1130  case 0: // label
1131  rule->setLabel( value.toString() );
1132  break;
1133  case 1: // filter
1134  rule->setFilterExpression( value.toString() );
1135  break;
1136  case 2: // scale min
1137  rule->setMinimumScale( value.toDouble() );
1138  break;
1139  case 3: // scale max
1140  rule->setMaximumScale( value.toDouble() );
1141  break;
1142  default:
1143  return false;
1144  }
1145 
1146  emit dataChanged( index, index );
1147  return true;
1148 }
1149 
1151 {
1152  return Qt::MoveAction; // | Qt::CopyAction
1153 }
1154 
1156 {
1157  QStringList types;
1158  types << QStringLiteral( "application/vnd.text.list" );
1159  return types;
1160 }
1161 
1162 QMimeData *QgsRuleBasedRendererModel::mimeData( const QModelIndexList &indexes ) const
1163 {
1164  QMimeData *mimeData = new QMimeData();
1165  QByteArray encodedData;
1166 
1167  QDataStream stream( &encodedData, QIODevice::WriteOnly );
1168 
1169  const auto constIndexes = indexes;
1170  for ( const QModelIndex &index : constIndexes )
1171  {
1172  // each item consists of several columns - let's add it with just first one
1173  if ( !index.isValid() || index.column() != 0 )
1174  continue;
1175 
1176  // we use a clone of the existing rule because it has a new unique rule key
1177  // non-unique rule keys would confuse other components using them (e.g. legend)
1179  QDomDocument doc;
1180  QgsSymbolMap symbols;
1181 
1182  QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
1183  rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "renderer" ) ); // for determining whether rules are from renderer or labeling
1184  QDomElement rulesElem = rule->save( doc, symbols );
1185  rootElem.appendChild( rulesElem );
1186  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
1187  rootElem.appendChild( symbolsElem );
1188  doc.appendChild( rootElem );
1189 
1190  delete rule;
1191 
1192  stream << doc.toString( -1 );
1193  }
1194 
1195  mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
1196  return mimeData;
1197 }
1198 
1199 
1200 // manipulate DOM before dropping it so that rules are more useful
1201 void _labeling2rendererRules( QDomElement &ruleElem )
1202 {
1203  // labeling rules recognize only "description"
1204  if ( ruleElem.hasAttribute( QStringLiteral( "description" ) ) )
1205  ruleElem.setAttribute( QStringLiteral( "label" ), ruleElem.attribute( QStringLiteral( "description" ) ) );
1206 
1207  // run recursively
1208  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
1209  while ( !childRuleElem.isNull() )
1210  {
1211  _labeling2rendererRules( childRuleElem );
1212  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
1213  }
1214 }
1215 
1216 
1217 bool QgsRuleBasedRendererModel::dropMimeData( const QMimeData *data,
1218  Qt::DropAction action, int row, int column, const QModelIndex &parent )
1219 {
1220  Q_UNUSED( column )
1221 
1222  if ( action == Qt::IgnoreAction )
1223  return true;
1224 
1225  if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
1226  return false;
1227 
1228  if ( parent.column() > 0 )
1229  return false;
1230 
1231  QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
1232  QDataStream stream( &encodedData, QIODevice::ReadOnly );
1233  int rows = 0;
1234 
1235  if ( row == -1 )
1236  {
1237  // the item was dropped at a parent - we may decide where to put the items - let's append them
1238  row = rowCount( parent );
1239  }
1240 
1241  while ( !stream.atEnd() )
1242  {
1243  QString text;
1244  stream >> text;
1245 
1246  QDomDocument doc;
1247  if ( !doc.setContent( text ) )
1248  continue;
1249  QDomElement rootElem = doc.documentElement();
1250  if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
1251  continue;
1252  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1253  rootElem.appendChild( doc.createElement( QStringLiteral( "symbols" ) ) );
1254  QDomElement symbolsElem = rootElem.firstChildElement( QStringLiteral( "symbols" ) );
1255  if ( symbolsElem.isNull() )
1256  continue;
1258  QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
1259  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1260  _labeling2rendererRules( ruleElem );
1261  QgsRuleBasedRenderer::Rule *rule = QgsRuleBasedRenderer::Rule::create( ruleElem, symbolMap );
1262 
1263  insertRule( parent, row + rows, rule );
1264 
1265  ++rows;
1266  }
1267  return true;
1268 }
1269 
1271 {
1272  if ( index.isValid() )
1273  return static_cast<QgsRuleBasedRenderer::Rule *>( index.internalPointer() );
1274  return mR->rootRule();
1275 }
1276 
1277 bool QgsRuleBasedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
1278 {
1280 
1281  if ( row < 0 || row >= parentRule->children().count() )
1282  return false;
1283 
1284  QgsDebugMsg( QStringLiteral( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1285 
1286  beginRemoveRows( parent, row, row + count - 1 );
1287 
1288  for ( int i = 0; i < count; i++ )
1289  {
1290  if ( row < parentRule->children().count() )
1291  {
1292  //QgsRuleBasedRenderer::Rule* r = parentRule->children()[row];
1293  parentRule->removeChildAt( row );
1294  //parentRule->takeChildAt( row );
1295  }
1296  else
1297  {
1298  QgsDebugMsg( QStringLiteral( "trying to remove invalid index - this should not happen!" ) );
1299  }
1300  }
1301 
1302  endRemoveRows();
1303 
1304  return true;
1305 }
1306 
1307 
1308 void QgsRuleBasedRendererModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule )
1309 {
1310  beginInsertRows( parent, before, before );
1311 
1312  QgsDebugMsg( QStringLiteral( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1313 
1315  parentRule->insertChild( before, newrule );
1316 
1317  endInsertRows();
1318 }
1319 
1320 void QgsRuleBasedRendererModel::updateRule( const QModelIndex &parent, int row )
1321 {
1322  emit dataChanged( index( row, 0, parent ),
1323  index( row, columnCount( parent ), parent ) );
1324 }
1325 
1326 void QgsRuleBasedRendererModel::updateRule( const QModelIndex &idx )
1327 {
1328  emit dataChanged( index( 0, 0, idx ),
1329  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1330 
1331  for ( int i = 0; i < rowCount( idx ); i++ )
1332  {
1333  updateRule( index( i, 0, idx ) );
1334  }
1335 }
1336 
1337 
1338 void QgsRuleBasedRendererModel::removeRule( const QModelIndex &index )
1339 {
1340  if ( !index.isValid() )
1341  return;
1342 
1343  beginRemoveRows( index.parent(), index.row(), index.row() );
1344 
1346  rule->parent()->removeChild( rule );
1347 
1348  endRemoveRows();
1349 }
1350 
1351 void QgsRuleBasedRendererModel::setSymbol( const QModelIndex &index, QgsSymbol *symbol )
1352 {
1354  rule->setSymbol( symbol );
1355  emit dataChanged( index, index );
1356 }
1357 
1358 void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int count )
1359 {
1360  int row = rowCount( parent ); // only consider appending
1361  beginInsertRows( parent, row, row + count - 1 );
1362 }
1363 
1365 {
1366  endInsertRows();
1367 }
1368 
1369 void QgsRuleBasedRendererModel::setFeatureCounts( const QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> &countMap )
1370 {
1371  mFeatureCountMap = countMap;
1372  updateRule( QModelIndex() );
1373 }
1374 
1376 {
1377  mFeatureCountMap.clear();
1378  updateRule( QModelIndex() );
1379 }
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
A generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
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.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for 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.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString evalErrorString() const
Returns evaluation error.
QString parserErrorString() const
Returns parser error.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
const QgsRangeList & ranges() const
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:65
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:156
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Base class for any widget that can be shown as a inline panel.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
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.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
bool dockMode()
Returns the dock mode state.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
QgsRuleBasedRenderer::Rule * rule()
QgsRendererRulePropsDialog(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Constructor for QgsRendererRulePropsDialog.
void buildExpression()
Open the expression builder widget to check if the.
void setDockMode(bool dockMode) override
Set the widget in dock mode.
QgsRendererRulePropsWidget(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Widget to edit the details of a rule based renderer rule.
QgsSymbolSelectorWidget * mSymbolSelector
void apply()
Apply any changes from the widget to the set rule.
QgsRuleBasedRenderer::Rule * mRule
void testFilter()
Test the filter that is set in the widget.
QgsRuleBasedRenderer::Rule * rule()
Returns the current set rule.
Base class for renderer settings widgets.
QAction * mPasteSymbolAction
Paste symbol action.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsSymbolWidgetContext mContext
Context in which widget is shown.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
QgsVectorLayer * mLayer
QgsRuleBasedRenderer::Rule * ruleForIndex(const QModelIndex &index) const
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void updateRule(const QModelIndex &parent, int row)
void willAddRules(const QModelIndex &parent, int count)
Qt::ItemFlags flags(const QModelIndex &index) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setSymbol(const QModelIndex &index, QgsSymbol *symbol)
Sets the symbol for the rule at the specified index.
QStringList mimeTypes() const override
void removeRule(const QModelIndex &index)
int columnCount(const QModelIndex &=QModelIndex()) const override
Qt::DropActions supportedDropActions() const override
void setFeatureCounts(const QHash< QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount > &countMap)
QModelIndex parent(const QModelIndex &index) const override
QHash< QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount > mFeatureCountMap
QMimeData * mimeData(const QModelIndexList &indexes) const override
QgsRuleBasedRendererModel(QgsRuleBasedRenderer *renderer, QObject *parent)
Constructor for QgsRuleBasedRendererModel, for the specified renderer.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void insertRule(const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule)
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
void refineRuleCategoriesGui()
Opens the dialog for refining a rule using categories.
QgsRuleBasedRenderer::RuleList selectedRules()
void refineRuleScalesGui(const QModelIndexList &index)
void refineRuleRangesGui()
Opens the dialog for refining a rule using ranges.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
QList< QgsSymbol * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
QgsRuleBasedRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
QgsRuleBasedRenderer::RuleList mCopyBuffer
void currentRuleChanged(const QModelIndex &current=QModelIndex(), const QModelIndex &previous=QModelIndex())
QgsRuleBasedRendererModel * mModel
void setSymbolLevels(const QList< QgsLegendSymbolItem > &levels, bool enabled) override
Sets the symbol levels for the renderer defined in the widget.
std::unique_ptr< QgsRuleBasedRenderer > mRenderer
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
void keyPressEvent(QKeyEvent *event) override
QgsRuleBasedRenderer::Rule * currentRule()
void saveSectionWidth(int section, int oldSize, int newSize)
This class keeps data about a rules for rule-based renderer.
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
void setDescription(const QString &description)
Set a human readable description for this rule.
double maximumScale() const
Returns the maximum map scale (i.e.
bool isElse() const
Check if this rule is an ELSE rule.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap)
Create a rule from an XML definition.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
void removeChildAt(int i)
delete child rule
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
void setActive(bool state)
Sets if this rule is active.
QDomElement save(QDomDocument &doc, QgsSymbolMap &symbolMap) const
double minimumScale() const
Returns the minimum map scale (i.e.
QString description() const
A human readable description for this rule.
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
const QgsRuleBasedRenderer::RuleList & children()
Returns all children rules of this rule.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
void setLabel(const QString &label)
QString dump(int indent=0) const
Dump for debug purpose.
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
Rule based renderer.
static void refineRuleCategories(QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer
static void refineRuleRanges(QgsRuleBasedRenderer::Rule *initialRule, QgsGraduatedSymbolRenderer *r)
take a rule and create a list of new rules based on the ranges from graduated symbol renderer
QgsRuleBasedRenderer::Rule * rootRule()
static QgsRuleBasedRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsRuleBasedRenderer from an existing renderer.
static void refineRuleScales(QgsRuleBasedRenderer::Rule *initialRule, QList< int > scales)
take a rule and create a list of new rules with intervals of scales given by the passed scale denomin...
QList< QgsRuleBasedRenderer::Rule * > RuleList
static QString toString(double scale)
Helper function to convert a scale double to scale string.
void rangeChanged(double min, double max)
Emitted when the scale range set in the widget is 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.
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
A dialog which allows the user to modify the rendering order of symbol layers.
QgsLegendSymbolList symbolLevels() const
Returns the current legend symbols with rendering passes set, as defined in the widget.
bool usingLevels() const
Returns whether the level ordering is enabled.
void setForceOrderingEnabled(bool enabled)
A widget which allows the user to modify the rendering order of symbol layers.
void setForceOrderingEnabled(bool enabled)
Sets whether the level ordering is always forced on and hide the checkbox (used by rule-based rendere...
bool usingLevels() const
Returns whether the level ordering is enabled.
QgsLegendSymbolList symbolLevels() const
Returns the current legend symbols with rendering passes set, as defined in the widget.
Symbol selector widget that can be used to select and build a symbol.
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.
QList< QgsExpressionContextScope * > globalProjectAtlasMapLayerScopes(const QgsMapLayer *layer) const
Returns list of scopes: global, project, atlas, map, layer.
QgsMapCanvas * mapCanvas() const
Returns the map canvas 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
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.
QgsVScrollArea is a QScrollArea subclass which only displays a vertical scrollbar and fits the width ...
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
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.
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,...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:44
void _labeling2rendererRules(QDomElement &ruleElem)