QGIS API Documentation  3.0.2-Girona (307d082)
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 
34 #include <QKeyEvent>
35 #include <QMenu>
36 #include <QProgressDialog>
37 #include <QTreeWidgetItem>
38 #include <QVBoxLayout>
39 #include <QMessageBox>
40 
41 #ifdef ENABLE_MODELTEST
42 #include "modeltest.h"
43 #endif
44 
45 
47 {
48  return new QgsRuleBasedRendererWidget( layer, style, renderer );
49 }
50 
52  : QgsRendererWidget( layer, style )
53 {
54  mRenderer = nullptr;
55  // try to recognize the previous renderer
56  // (null renderer means "no previous renderer")
57 
58 
59  if ( renderer )
60  {
62  }
63  if ( !mRenderer )
64  {
65  // some default options
67 
68  mRenderer = new QgsRuleBasedRenderer( symbol );
69  }
70 
71  setupUi( this );
72  this->layout()->setContentsMargins( 0, 0, 0, 0 );
73 
74  mModel = new QgsRuleBasedRendererModel( mRenderer, viewRules );
75 #ifdef ENABLE_MODELTEST
76  new ModelTest( mModel, this ); // for model validity checking
77 #endif
78  viewRules->setModel( mModel );
79 
80  mDeleteAction = new QAction( tr( "Remove Rule" ), this );
81  mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
82 
83  viewRules->addAction( mDeleteAction );
84  viewRules->addAction( mCopyAction );
85  viewRules->addAction( mPasteAction );
86 
87  mRefineMenu = new QMenu( tr( "Refine current rule" ), btnRefineRule );
88  mRefineMenu->addAction( tr( "Add scales to rule" ), this, SLOT( refineRuleScales() ) );
89  mRefineMenu->addAction( tr( "Add categories to rule" ), this, SLOT( refineRuleCategories() ) );
90  mRefineMenu->addAction( tr( "Add ranges to rule" ), this, SLOT( refineRuleRanges() ) );
91  btnRefineRule->setMenu( mRefineMenu );
92  contextMenu->addMenu( mRefineMenu );
93 
94  btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
95  btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
96  btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
97 
98  connect( viewRules, &QAbstractItemView::doubleClicked, this, static_cast < void ( QgsRuleBasedRendererWidget::* )( const QModelIndex &index ) > ( &QgsRuleBasedRendererWidget::editRule ) );
99 
100  // support for context menu (now handled generically)
101  connect( viewRules, &QWidget::customContextMenuRequested, this, &QgsRuleBasedRendererWidget::contextMenuViewCategories );
102 
103  connect( viewRules->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsRuleBasedRendererWidget::currentRuleChanged );
104  connect( viewRules->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsRuleBasedRendererWidget::selectedRulesChanged );
105 
106  connect( btnAddRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::addRule );
107  connect( btnEditRule, &QAbstractButton::clicked, this, static_cast < void ( QgsRuleBasedRendererWidget::* )() > ( &QgsRuleBasedRendererWidget::editRule ) );
108  connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::removeRule );
109  connect( mDeleteAction, &QAction::triggered, this, &QgsRuleBasedRendererWidget::removeRule );
110  connect( btnCountFeatures, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::countFeatures );
111 
112  connect( btnRenderingOrder, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::setRenderingOrder );
113 
114  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
115  connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsPanelWidget::widgetChanged );
116  connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsPanelWidget::widgetChanged );
117 
120 
121  // store/restore header section widths
122  connect( viewRules->header(), &QHeaderView::sectionResized, this, &QgsRuleBasedRendererWidget::saveSectionWidth );
123 
125 
126 }
127 
129 {
130  qDeleteAll( mCopyBuffer );
131  delete mRenderer;
132 }
133 
135 {
136  return mRenderer;
137 }
138 
140 {
143 
145  if ( current )
146  {
147  // add after this rule
148  QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
149  mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
150  QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
151  viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
152  }
153  else
154  {
155  // append to root rule
156  int rows = mModel->rowCount();
157  mModel->insertRule( QModelIndex(), rows, newrule );
158  QModelIndex newindex = mModel->index( rows, 0 );
159  viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
160  }
161  editRule();
162 }
163 
165 {
166  QItemSelectionModel *sel = viewRules->selectionModel();
167  QModelIndex idx = sel->currentIndex();
168  if ( !idx.isValid() )
169  return nullptr;
170  return mModel->ruleForIndex( idx );
171 }
172 
174 {
175  editRule( viewRules->selectionModel()->currentIndex() );
176 }
177 
178 void QgsRuleBasedRendererWidget::editRule( const QModelIndex &index )
179 {
180  if ( !index.isValid() )
181  return;
182 
185 
186  if ( panel && panel->dockMode() )
187  {
188  QgsRendererRulePropsWidget *widget = new QgsRendererRulePropsWidget( rule, mLayer, mStyle, this, mContext );//panel?
189  widget->setPanelTitle( tr( "Edit Rule" ) );
190  connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted );
191  connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel );
192  openPanel( widget );
193  return;
194  }
195 
196  QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle, this, mContext );
197  if ( dlg.exec() )
198  {
199  mModel->updateRule( index.parent(), index.row() );
201  emit widgetChanged();
202  }
203 }
204 
206 {
207  QItemSelection sel = viewRules->selectionModel()->selection();
208  QgsDebugMsg( QString( "REMOVE RULES!!! ranges: %1" ).arg( sel.count() ) );
209  Q_FOREACH ( const QItemSelectionRange &range, sel )
210  {
211  QgsDebugMsg( QString( "RANGE: r %1 - %2" ).arg( range.top() ).arg( range.bottom() ) );
212  if ( range.isValid() )
213  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
214  }
215  // make sure that the selection is gone
216  viewRules->selectionModel()->clear();
218 }
219 
220 void QgsRuleBasedRendererWidget::currentRuleChanged( const QModelIndex &current, const QModelIndex &previous )
221 {
222  Q_UNUSED( previous );
223  btnEditRule->setEnabled( current.isValid() );
224 }
225 
226 
232 #include <QDialogButtonBox>
233 #include <QInputDialog>
234 #include <QClipboard>
235 
237 {
238  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
239 
240  if ( indexlist.isEmpty() )
241  return;
242 
243 
244  if ( type == 0 ) // categories
246  else if ( type == 1 ) // ranges
248  else // scales
249  refineRuleScalesGui( indexlist );
250 
251  // TODO: set initial rule's symbol to NULL (?)
252 
253  // show the newly added rules
254  Q_FOREACH ( const QModelIndex &index, indexlist )
255  viewRules->expand( index );
256 }
257 
259 {
260  refineRule( 0 );
261 }
262 
264 {
265  refineRule( 1 );
266 }
267 
269 {
270  refineRule( 2 );
271 }
272 
274 {
276  w->setPanelTitle( tr( "Add Categories to Rules" ) );
277  connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted );
278  w->setContext( mContext );
279  openPanel( w );
280 }
281 
283 {
285  w->setPanelTitle( tr( "Add Ranges to Rules" ) );
286  connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleRangesAccepted );
287  w->setContext( mContext );
288  openPanel( w );
289 }
290 
291 void QgsRuleBasedRendererWidget::refineRuleScalesGui( const QModelIndexList &indexList )
292 {
293  Q_FOREACH ( const QModelIndex &index, indexList )
294  {
295  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
296 
297  // If any of the rules don't have a symbol let the user know and exit.
298  if ( !initialRule->symbol() )
299  {
300  QMessageBox::warning( this, tr( "Scale Refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
301  return;
302  }
303  }
304 
305  QString txt = QInputDialog::getText( this,
306  tr( "Scale Refinement" ),
307  tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
308  if ( txt.isEmpty() )
309  return;
310 
311  QList<int> scales;
312  bool ok;
313  Q_FOREACH ( const QString &item, txt.split( ',' ) )
314  {
315  int scale = item.toInt( &ok );
316  if ( ok )
317  scales.append( scale );
318  else
319  QMessageBox::information( this, tr( "Scale Refinement" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) );
320  }
321 
322  Q_FOREACH ( const QModelIndex &index, indexList )
323  {
324  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
325  mModel->willAddRules( index, scales.count() + 1 );
326  QgsRuleBasedRenderer::refineRuleScales( initialRule, scales );
327  }
329 }
330 
332 {
333  QList<QgsSymbol *> symbolList;
334 
335  if ( !mRenderer )
336  {
337  return symbolList;
338  }
339 
340  QItemSelection sel = viewRules->selectionModel()->selection();
341  Q_FOREACH ( const QItemSelectionRange &range, sel )
342  {
343  QModelIndex parent = range.parent();
344  QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
345  QgsRuleBasedRenderer::RuleList &children = parentRule->children();
346  for ( int row = range.top(); row <= range.bottom(); row++ )
347  {
348  symbolList.append( children[row]->symbol() );
349  }
350  }
351 
352  return symbolList;
353 }
354 
356 {
358  QItemSelection sel = viewRules->selectionModel()->selection();
359  Q_FOREACH ( const QItemSelectionRange &range, sel )
360  {
361  QModelIndex parent = range.parent();
362  QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
363  QgsRuleBasedRenderer::RuleList &children = parentRule->children();
364  for ( int row = range.top(); row <= range.bottom(); row++ )
365  {
366  rl.append( children[row]->clone() );
367  }
368  }
369  return rl;
370 }
371 
373 {
374  // TODO: model/view
375  /*
376  if ( treeRules )
377  {
378  treeRules->populateRules();
379  }
380  */
381  emit widgetChanged();
382 }
383 
385 {
386  if ( !event )
387  {
388  return;
389  }
390 
391  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
392  {
393  qDeleteAll( mCopyBuffer );
394  mCopyBuffer.clear();
396  }
397  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
398  {
399  QgsRuleBasedRenderer::RuleList::const_iterator rIt = mCopyBuffer.constBegin();
400  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
401  {
402  int rows = mModel->rowCount();
403  mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
404  }
405  }
406 }
407 
408 #include "qgssymbollevelsdialog.h"
409 
411 {
413  if ( panel && panel->dockMode() )
414  {
415  QgsSymbolLevelsWidget *widget = new QgsSymbolLevelsWidget( mRenderer, true, panel );
416  widget->setForceOrderingEnabled( true );
417  widget->setPanelTitle( tr( "Symbol Levels" ) );
418  connect( widget, &QgsPanelWidget::widgetChanged, widget, &QgsSymbolLevelsWidget::apply );
420  panel->openPanel( widget );
421  return;
422  }
423 
424  QgsSymbolLevelsDialog dlg( mRenderer, true, panel );
425  dlg.setForceOrderingEnabled( true );
426  if ( dlg.exec() )
427  {
428  emit widgetChanged();
429  }
430 }
431 
432 void QgsRuleBasedRendererWidget::saveSectionWidth( int section, int oldSize, int newSize )
433 {
434  Q_UNUSED( oldSize );
435  // skip last section, as it stretches
436  if ( section == 5 )
437  return;
438  QgsSettings settings;
439  QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
440  settings.setValue( path, newSize );
441 }
442 
444 {
445  QgsSettings settings;
446  QString path = QStringLiteral( "/Windows/RuleBasedTree/sectionWidth/" );
447  QHeaderView *head = viewRules->header();
448  head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
449  head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
450  head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
451  head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
452  head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
453  head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
454 }
455 
457 {
458  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
459  QgsDebugMsg( QString( "%1" ).arg( indexlist.count() ) );
460 
461  if ( indexlist.isEmpty() )
462  return;
463 
464  QMimeData *mime = mModel->mimeData( indexlist );
465  QApplication::clipboard()->setMimeData( mime );
466 }
467 
469 {
470  const QMimeData *mime = QApplication::clipboard()->mimeData();
471  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
472  QModelIndex index;
473  if ( indexlist.isEmpty() )
474  index = mModel->index( mModel->rowCount(), 0 );
475  else
476  index = indexlist.first();
477  mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
478 }
479 
480 void QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted( QgsPanelWidget *panel )
481 {
483 
484  // create new rules
486  QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
487  Q_FOREACH ( const QModelIndex &index, indexList )
488  {
489  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
490  mModel->willAddRules( index, r->categories().count() );
492  }
494 }
495 
496 void QgsRuleBasedRendererWidget::refineRuleRangesAccepted( QgsPanelWidget *panel )
497 {
499  // create new rules
501  QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
502  Q_FOREACH ( const QModelIndex &index, indexList )
503  {
504  QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
505  mModel->willAddRules( index, r->ranges().count() );
506  QgsRuleBasedRenderer::refineRuleRanges( initialRule, r );
507  }
509 }
510 
511 void QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
512 {
513  QgsRendererRulePropsWidget *widget = qobject_cast<QgsRendererRulePropsWidget *>( panel );
514  if ( !widget )
515  return;
516 
517  widget->apply();
518 
519  // model should know about the change and emit dataChanged signal for the view
520  QModelIndex index = viewRules->selectionModel()->currentIndex();
521  mModel->updateRule( index.parent(), index.row() );
523 }
524 
525 void QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel()
526 {
527  ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
528 }
529 
530 
532 {
533  if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
534  {
535  return;
536  }
537  QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> countMap;
538 
540  // insert all so that we have counts 0
541  Q_FOREACH ( QgsRuleBasedRenderer::Rule *rule, ruleList )
542  {
543  countMap[rule].count = 0;
544  countMap[rule].duplicateCount = 0;
545  }
546 
547  QgsRenderContext renderContext;
548  renderContext.setRendererScale( 0 ); // ignore scale
549 
551 
552  // additional scopes
554  {
555  context.appendScope( new QgsExpressionContextScope( scope ) );
556  }
557 
558  renderContext.setExpressionContext( context );
559 
560  mRenderer->startRender( renderContext, mLayer->fields() );
561  // QgsRuleBasedRenderer::filter must be called after startRender
564  req.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mLayer->fields() );
565  QgsFeatureIterator fit = mLayer->getFeatures( req );
566 
567  int nFeatures = mLayer->featureCount();
568  QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, nFeatures );
569  p.setWindowModality( Qt::WindowModal );
570  int featuresCounted = 0;
571 
572  QgsFeature f;
573  while ( fit.nextFeature( f ) )
574  {
575  renderContext.expressionContext().setFeature( f );
576  QgsRuleBasedRenderer::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
577 
578  Q_FOREACH ( QgsRuleBasedRenderer::Rule *rule, featureRuleList )
579  {
580  countMap[rule].count++;
581  if ( featureRuleList.size() > 1 )
582  {
583  countMap[rule].duplicateCount++;
584  }
585  Q_FOREACH ( QgsRuleBasedRenderer::Rule *duplicateRule, featureRuleList )
586  {
587  if ( duplicateRule == rule ) continue;
588  countMap[rule].duplicateCountMap[duplicateRule] += 1;
589  }
590  }
591  ++featuresCounted;
592  if ( featuresCounted % 50 == 0 )
593  {
594  if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
595  {
596  p.setMaximum( 0 );
597  }
598  p.setValue( featuresCounted );
599  if ( p.wasCanceled() )
600  {
601  return;
602  }
603  }
604  }
605  p.setValue( nFeatures );
606 
607  mRenderer->stopRender( renderContext );
608 
609 #ifdef QGISDEBUG
610  Q_FOREACH ( QgsRuleBasedRenderer::Rule *rule, countMap.keys() )
611  {
612  QgsDebugMsg( QString( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
613  }
614 #endif
615 
616  mModel->setFeatureCounts( countMap );
617 }
618 
620 {
621  bool enabled = !viewRules->selectionModel()->selectedIndexes().isEmpty();
622  btnRefineRule->setEnabled( enabled );
623  btnRemoveRule->setEnabled( enabled );
624 }
625 
627 
629  : QgsPanelWidget( parent )
630  , mRule( rule )
631  , mLayer( layer )
632  , mContext( context )
633 {
634  setupUi( this );
635  layout()->setMargin( 0 );
636  layout()->setContentsMargins( 0, 0, 0, 0 );
637 
638  editFilter->setText( mRule->filterExpression() );
639  editFilter->setToolTip( mRule->filterExpression() );
640  editLabel->setText( mRule->label() );
641  editDescription->setText( mRule->description() );
642  editDescription->setToolTip( mRule->description() );
643 
644  if ( mRule->dependsOnScale() )
645  {
646  groupScale->setChecked( true );
647  mScaleRangeWidget->setMaximumScale( std::max( rule->maximumScale(), 0.0 ) );
648  mScaleRangeWidget->setMinimumScale( std::max( rule->minimumScale(), 0.0 ) );
649  }
650  mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
651 
652  if ( mRule->symbol() )
653  {
654  groupSymbol->setChecked( true );
655  mSymbol = mRule->symbol()->clone(); // use a clone!
656  }
657  else
658  {
659  groupSymbol->setChecked( false );
661  }
662 
663  mSymbolSelector = new QgsSymbolSelectorWidget( mSymbol, style, mLayer, this );
664  mSymbolSelector->setContext( mContext );
665  connect( mSymbolSelector, &QgsPanelWidget::widgetChanged, this, &QgsPanelWidget::widgetChanged );
666  connect( mSymbolSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
667 
668  QVBoxLayout *l = new QVBoxLayout;
669  l->addWidget( mSymbolSelector );
670  groupSymbol->setLayout( l );
671 
672  connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
673  connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
674  connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
675  connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
676  connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
677  connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
678  connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
679  connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
680 }
681 
682 #include "qgsvscrollarea.h"
683 
685  : QDialog( parent )
686 {
687 
688 #ifdef Q_OS_MAC
689  setWindowModality( Qt::WindowModal );
690 #endif
691 
692  QVBoxLayout *layout = new QVBoxLayout( this );
693  QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
694  layout->addWidget( scrollArea );
695 
696  buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
697  mPropsWidget = new QgsRendererRulePropsWidget( rule, layer, style, this, context );
698 
699  scrollArea->setWidget( mPropsWidget );
700  layout->addWidget( buttonBox );
701  this->setWindowTitle( "Edit Rule" );
702 
703  connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererRulePropsDialog::accept );
704  connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
705  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRendererRulePropsDialog::showHelp );
706 
707  QgsSettings settings;
708  restoreGeometry( settings.value( QStringLiteral( "Windows/QgsRendererRulePropsDialog/geometry" ) ).toByteArray() );
709 }
710 
712 {
713  QgsSettings settings;
714  settings.setValue( QStringLiteral( "Windows/QgsRendererRulePropsDialog/geometry" ), saveGeometry() );
715 }
716 
718 {
719  mPropsWidget->testFilter();
720 }
721 
723 {
724  mPropsWidget->buildExpression();
725 }
726 
728 {
729  mPropsWidget->apply();
730  QDialog::accept();
731 }
732 
733 void QgsRendererRulePropsDialog::showHelp()
734 {
735  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-rendering" ) );
736 }
737 
738 
740 {
741  QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
742 
743  // additional scopes
744  Q_FOREACH ( const QgsExpressionContextScope &scope, mContext.additionalExpressionContextScopes() )
745  {
746  context.appendScope( new QgsExpressionContextScope( scope ) );
747  }
748 
749  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
750 
751  if ( dlg.exec() )
752  editFilter->setText( dlg.expressionText() );
753 }
754 
756 {
757  QgsExpression filter( editFilter->text() );
758  if ( filter.hasParserError() )
759  {
760  QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
761  return;
762  }
763 
764  QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
765 
766  // additional scopes
767  Q_FOREACH ( const QgsExpressionContextScope &scope, mContext.additionalExpressionContextScopes() )
768  {
769  context.appendScope( new QgsExpressionContextScope( scope ) );
770  }
771 
772  if ( !filter.prepare( &context ) )
773  {
774  QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
775  return;
776  }
777 
778  QApplication::setOverrideCursor( Qt::WaitCursor );
779 
782  .setFilterExpression( editFilter->text() )
783  .setExpressionContext( context );
784 
785  QgsFeatureIterator fit = mLayer->getFeatures( req );
786 
787  int count = 0;
788  QgsFeature f;
789  while ( fit.nextFeature( f ) )
790  {
791  count++;
792  }
793 
794  QApplication::restoreOverrideCursor();
795 
796  QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
797 }
798 
800 {
801  mRule->setFilterExpression( editFilter->text() );
802  mRule->setLabel( editLabel->text() );
803  mRule->setDescription( editDescription->text() );
804  // caution: rule uses scale denom, scale widget uses true scales
805  mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
806  mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
807  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
808 }
809 
811 {
812  QgsPanelWidget::setDockMode( dockMode );
813  mSymbolSelector->setDockMode( dockMode );
814 }
815 
817 
818 /*
819  setDragEnabled(true);
820  viewport()->setAcceptDrops(true);
821  setDropIndicatorShown(true);
822  setDragDropMode(QAbstractItemView::InternalMove);
823 */
824 
826 
828  : QAbstractItemModel( parent )
829  , mR( renderer )
830 {
831 }
832 
833 Qt::ItemFlags QgsRuleBasedRendererModel::flags( const QModelIndex &index ) const
834 {
835  if ( !index.isValid() )
836  return Qt::ItemIsDropEnabled;
837 
838  // allow drop only at first column
839  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
840 
841  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
842 
843  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
844  Qt::ItemIsEditable | checkable |
845  Qt::ItemIsDragEnabled | drop;
846 }
847 
848 QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) const
849 {
850  if ( !index.isValid() )
851  return QVariant();
852 
853  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index );
854 
855  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
856  {
857  switch ( index.column() )
858  {
859  case 0:
860  return rule->label();
861  case 1:
862  if ( rule->isElse() )
863  {
864  return "ELSE";
865  }
866  else
867  {
868  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
869  }
870  case 2:
871  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
872  case 3:
873  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
874  case 4:
875  if ( mFeatureCountMap.count( rule ) == 1 )
876  {
877  return QVariant( mFeatureCountMap[rule].count );
878  }
879  return QVariant();
880  case 5:
881  if ( mFeatureCountMap.count( rule ) == 1 )
882  {
883  if ( role == Qt::DisplayRole )
884  {
885  return QVariant( mFeatureCountMap[rule].duplicateCount );
886  }
887  else // tooltip - detailed info about duplicates
888  {
889  if ( mFeatureCountMap[rule].duplicateCount > 0 )
890  {
891  QString tip = QStringLiteral( "<p style='margin:0px;'><ul>" );
892  const auto duplicateMap = mFeatureCountMap[rule].duplicateCountMap;
893  for ( auto it = duplicateMap.constBegin(); it != duplicateMap.constEnd(); ++it )
894  {
895  QString label = it.key()->label().replace( '&', QLatin1String( "&amp;" ) ).replace( '>', QLatin1String( "&gt;" ) ).replace( '<', QLatin1String( "&lt;" ) );
896  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( it.value() ).arg( label );
897  }
898  tip += QLatin1String( "</ul>" );
899  return tip;
900  }
901  else
902  {
903  return 0;
904  }
905  }
906  }
907  return QVariant();
908  default:
909  return QVariant();
910  }
911  }
912  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
913  {
914  return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
915  }
916  else if ( role == Qt::TextAlignmentRole )
917  {
918  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
919  }
920  else if ( role == Qt::FontRole && index.column() == 1 )
921  {
922  if ( rule->isElse() )
923  {
924  QFont italicFont;
925  italicFont.setItalic( true );
926  return italicFont;
927  }
928  return QVariant();
929  }
930  else if ( role == Qt::EditRole )
931  {
932  switch ( index.column() )
933  {
934  case 0:
935  return rule->label();
936  case 1:
937  return rule->filterExpression();
938  case 2:
939  return rule->minimumScale();
940  case 3:
941  return rule->maximumScale();
942  default:
943  return QVariant();
944  }
945  }
946  else if ( role == Qt::CheckStateRole )
947  {
948  if ( index.column() != 0 )
949  return QVariant();
950  return rule->active() ? Qt::Checked : Qt::Unchecked;
951  }
952  else
953  return QVariant();
954 }
955 
956 QVariant QgsRuleBasedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
957 {
958  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
959  {
960  QStringList lst;
961  lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Count" ) << tr( "Duplicate count" );
962  return lst[section];
963  }
964  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
965  {
966  if ( section == 4 ) // Count
967  {
968  return tr( "Number of features in this rule." );
969  }
970  else if ( section == 5 ) // Duplicate count
971  {
972  return tr( "Number of features in this rule which are also present in other rule(s)." );
973  }
974  }
975 
976  return QVariant();
977 }
978 
979 int QgsRuleBasedRendererModel::rowCount( const QModelIndex &parent ) const
980 {
981  if ( parent.column() > 0 )
982  return 0;
983 
984  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
985 
986  return parentRule->children().count();
987 }
988 
989 int QgsRuleBasedRendererModel::columnCount( const QModelIndex & ) const
990 {
991  return 6;
992 }
993 
994 QModelIndex QgsRuleBasedRendererModel::index( int row, int column, const QModelIndex &parent ) const
995 {
996  if ( hasIndex( row, column, parent ) )
997  {
998  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
999  QgsRuleBasedRenderer::Rule *childRule = parentRule->children()[row];
1000  return createIndex( row, column, childRule );
1001  }
1002  return QModelIndex();
1003 }
1004 
1005 QModelIndex QgsRuleBasedRendererModel::parent( const QModelIndex &index ) const
1006 {
1007  if ( !index.isValid() )
1008  return QModelIndex();
1009 
1010  QgsRuleBasedRenderer::Rule *childRule = ruleForIndex( index );
1011  QgsRuleBasedRenderer::Rule *parentRule = childRule->parent();
1012 
1013  if ( parentRule == mR->rootRule() )
1014  return QModelIndex();
1015 
1016  // this is right: we need to know row number of our parent (in our grandparent)
1017  int row = parentRule->parent()->children().indexOf( parentRule );
1018 
1019  return createIndex( row, 0, parentRule );
1020 }
1021 
1022 bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
1023 {
1024  if ( !index.isValid() )
1025  return false;
1026 
1027  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index );
1028 
1029  if ( role == Qt::CheckStateRole )
1030  {
1031  rule->setActive( value.toInt() == Qt::Checked );
1032  emit dataChanged( index, index );
1033  return true;
1034  }
1035 
1036  if ( role != Qt::EditRole )
1037  return false;
1038 
1039  switch ( index.column() )
1040  {
1041  case 0: // label
1042  rule->setLabel( value.toString() );
1043  break;
1044  case 1: // filter
1045  rule->setFilterExpression( value.toString() );
1046  break;
1047  case 2: // scale min
1048  rule->setMinimumScale( value.toDouble() );
1049  break;
1050  case 3: // scale max
1051  rule->setMaximumScale( value.toDouble() );
1052  break;
1053  default:
1054  return false;
1055  }
1056 
1057  emit dataChanged( index, index );
1058  return true;
1059 }
1060 
1062 {
1063  return Qt::MoveAction; // | Qt::CopyAction
1064 }
1065 
1067 {
1068  QStringList types;
1069  types << QStringLiteral( "application/vnd.text.list" );
1070  return types;
1071 }
1072 
1073 QMimeData *QgsRuleBasedRendererModel::mimeData( const QModelIndexList &indexes ) const
1074 {
1075  QMimeData *mimeData = new QMimeData();
1076  QByteArray encodedData;
1077 
1078  QDataStream stream( &encodedData, QIODevice::WriteOnly );
1079 
1080  Q_FOREACH ( const QModelIndex &index, indexes )
1081  {
1082  // each item consists of several columns - let's add it with just first one
1083  if ( !index.isValid() || index.column() != 0 )
1084  continue;
1085 
1086  // we use a clone of the existing rule because it has a new unique rule key
1087  // non-unique rule keys would confuse other components using them (e.g. legend)
1088  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index )->clone();
1089  QDomDocument doc;
1090  QgsSymbolMap symbols;
1091 
1092  QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
1093  rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "renderer" ) ); // for determining whether rules are from renderer or labeling
1094  QDomElement rulesElem = rule->save( doc, symbols );
1095  rootElem.appendChild( rulesElem );
1096  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
1097  rootElem.appendChild( symbolsElem );
1098  doc.appendChild( rootElem );
1099 
1100  delete rule;
1101 
1102  stream << doc.toString( -1 );
1103  }
1104 
1105  mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
1106  return mimeData;
1107 }
1108 
1109 
1110 // manipulate DOM before dropping it so that rules are more useful
1111 void _labeling2rendererRules( QDomElement &ruleElem )
1112 {
1113  // labeling rules recognize only "description"
1114  if ( ruleElem.hasAttribute( QStringLiteral( "description" ) ) )
1115  ruleElem.setAttribute( QStringLiteral( "label" ), ruleElem.attribute( QStringLiteral( "description" ) ) );
1116 
1117  // run recursively
1118  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
1119  while ( !childRuleElem.isNull() )
1120  {
1121  _labeling2rendererRules( childRuleElem );
1122  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
1123  }
1124 }
1125 
1126 
1128  Qt::DropAction action, int row, int column, const QModelIndex &parent )
1129 {
1130  Q_UNUSED( column );
1131 
1132  if ( action == Qt::IgnoreAction )
1133  return true;
1134 
1135  if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
1136  return false;
1137 
1138  if ( parent.column() > 0 )
1139  return false;
1140 
1141  QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
1142  QDataStream stream( &encodedData, QIODevice::ReadOnly );
1143  int rows = 0;
1144 
1145  if ( row == -1 )
1146  {
1147  // the item was dropped at a parent - we may decide where to put the items - let's append them
1148  row = rowCount( parent );
1149  }
1150 
1151  while ( !stream.atEnd() )
1152  {
1153  QString text;
1154  stream >> text;
1155 
1156  QDomDocument doc;
1157  if ( !doc.setContent( text ) )
1158  continue;
1159  QDomElement rootElem = doc.documentElement();
1160  if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
1161  continue;
1162  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1163  rootElem.appendChild( doc.createElement( QStringLiteral( "symbols" ) ) );
1164  QDomElement symbolsElem = rootElem.firstChildElement( QStringLiteral( "symbols" ) );
1165  if ( symbolsElem.isNull() )
1166  continue;
1168  QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
1169  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1170  _labeling2rendererRules( ruleElem );
1171  QgsRuleBasedRenderer::Rule *rule = QgsRuleBasedRenderer::Rule::create( ruleElem, symbolMap );
1172 
1173  insertRule( parent, row + rows, rule );
1174 
1175  ++rows;
1176  }
1177  return true;
1178 }
1179 
1181 {
1182  if ( index.isValid() )
1183  return static_cast<QgsRuleBasedRenderer::Rule *>( index.internalPointer() );
1184  return mR->rootRule();
1185 }
1186 
1187 bool QgsRuleBasedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
1188 {
1189  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
1190 
1191  if ( row < 0 || row >= parentRule->children().count() )
1192  return false;
1193 
1194  QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1195 
1196  beginRemoveRows( parent, row, row + count - 1 );
1197 
1198  for ( int i = 0; i < count; i++ )
1199  {
1200  if ( row < parentRule->children().count() )
1201  {
1202  //QgsRuleBasedRenderer::Rule* r = parentRule->children()[row];
1203  parentRule->removeChildAt( row );
1204  //parentRule->takeChildAt( row );
1205  }
1206  else
1207  {
1208  QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
1209  }
1210  }
1211 
1212  endRemoveRows();
1213 
1214  return true;
1215 }
1216 
1217 
1218 void QgsRuleBasedRendererModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule )
1219 {
1220  beginInsertRows( parent, before, before );
1221 
1222  QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1223 
1224  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
1225  parentRule->insertChild( before, newrule );
1226 
1227  endInsertRows();
1228 }
1229 
1230 void QgsRuleBasedRendererModel::updateRule( const QModelIndex &parent, int row )
1231 {
1232  emit dataChanged( index( row, 0, parent ),
1233  index( row, columnCount( parent ), parent ) );
1234 }
1235 
1236 void QgsRuleBasedRendererModel::updateRule( const QModelIndex &idx )
1237 {
1238  emit dataChanged( index( 0, 0, idx ),
1239  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1240 
1241  for ( int i = 0; i < rowCount( idx ); i++ )
1242  {
1243  updateRule( index( i, 0, idx ) );
1244  }
1245 }
1246 
1247 
1249 {
1250  if ( !index.isValid() )
1251  return;
1252 
1253  beginRemoveRows( index.parent(), index.row(), index.row() );
1254 
1255  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index );
1256  rule->parent()->removeChild( rule );
1257 
1258  endRemoveRows();
1259 }
1260 
1261 void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int count )
1262 {
1263  int row = rowCount( parent ); // only consider appending
1264  beginInsertRows( parent, row, row + count - 1 );
1265 }
1266 
1268 {
1269  endInsertRows();
1270 }
1271 
1272 void QgsRuleBasedRendererModel::setFeatureCounts( const QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> &countMap )
1273 {
1274  mFeatureCountMap = countMap;
1275  updateRule( QModelIndex() );
1276 }
1277 
1279 {
1280  mFeatureCountMap.clear();
1281  updateRule( QModelIndex() );
1282 }
QgsSymbolSelectorWidget * mSymbolSelector
QgsFeatureRenderer * renderer() override
return pointer to the renderer (no transfer of ownership)
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)
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)
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
bool dockMode()
Return the dock mode state.
Tree model for the rules:
QgsRuleBasedRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
QMimeData * mimeData(const QModelIndexList &indexes) const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Return a list of attributes required by this renderer.
bool isElse()
Check if this rule is an ELSE rule.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:57
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
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 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.
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()
Return the current set rule.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsRuleBasedRenderer::RuleList & children()
Return all children rules of this rule.
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: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
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 saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
void refineRuleRangesGui()
Opens the dialog for refining a rule using ranges.
QgsFeatureRenderer * renderer() override
return 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.
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsRuleBasedRenderer::RuleList rulesForFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
tell which rules will be used to render the feature
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
return new default symbol for specified geometry type
Definition: qgssymbol.cpp:266
QgsRuleBasedRenderer::RuleList selectedRules()
static QIcon symbolPreviewIcon(QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QgsFields fields() const override
Returns the list of fields of this layer.
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...
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()
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
QgsFeatureRenderer * renderer() override
return pointer to the renderer (no transfer of ownership)
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 ...
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 setFeatureCounts(const QHash< QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount > &countMap)
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
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 apply()
Apply any changes from the widget to the set rule.
virtual QgsSymbol * clone() const =0
Get a deep copy of this symbol.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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.
void contextMenuViewCategories(QPoint p)
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QMap< QString, QgsSymbol *> QgsSymbolMap
Definition: qgsrenderer.h:44
QgsRuleBasedRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:35
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
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 ...
QList< int > QgsAttributeList
Definition: qgsfield.h:27
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)
Set 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...
QgsRuleBasedRenderer::Rule * parent()
The parent rule.