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