QGIS API Documentation  3.2.0-Bonn (bc43194)
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  const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
346  for ( int row = range.top(); row <= range.bottom(); row++ )
347  {
348  symbolList.append( children.at( 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  const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
364  for ( int row = range.top(); row <= range.bottom(); row++ )
365  {
366  rl.append( children.at( 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  mElseRadio->setChecked( mRule->isElse() );
639  mFilterRadio->setChecked( !mRule->isElse() );
640  editFilter->setText( mRule->filterExpression() );
641  editFilter->setToolTip( mRule->filterExpression() );
642  editLabel->setText( mRule->label() );
643  editDescription->setText( mRule->description() );
644  editDescription->setToolTip( mRule->description() );
645 
646  if ( mRule->dependsOnScale() )
647  {
648  groupScale->setChecked( true );
649  mScaleRangeWidget->setMaximumScale( std::max( rule->maximumScale(), 0.0 ) );
650  mScaleRangeWidget->setMinimumScale( std::max( rule->minimumScale(), 0.0 ) );
651  }
652  mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
653 
654  if ( mRule->symbol() )
655  {
656  groupSymbol->setChecked( true );
657  mSymbol = mRule->symbol()->clone(); // use a clone!
658  }
659  else
660  {
661  groupSymbol->setChecked( false );
663  }
664 
665  mSymbolSelector = new QgsSymbolSelectorWidget( mSymbol, style, mLayer, this );
666  mSymbolSelector->setContext( mContext );
667  connect( mSymbolSelector, &QgsPanelWidget::widgetChanged, this, &QgsPanelWidget::widgetChanged );
668  connect( mSymbolSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
669 
670  QVBoxLayout *l = new QVBoxLayout;
671  l->addWidget( mSymbolSelector );
672  groupSymbol->setLayout( l );
673 
674  connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
675  connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
676  connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
677  connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
678  connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
679  connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
680  connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
681  connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
682  connect( mFilterRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
683  connect( mElseRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) );} );
684 }
685 
686 #include "qgsvscrollarea.h"
687 
689  : QDialog( parent )
690 {
691 
692 #ifdef Q_OS_MAC
693  setWindowModality( Qt::WindowModal );
694 #endif
695 
696  QVBoxLayout *layout = new QVBoxLayout( this );
697  QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
698  layout->addWidget( scrollArea );
699 
700  buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
701  mPropsWidget = new QgsRendererRulePropsWidget( rule, layer, style, this, context );
702 
703  scrollArea->setWidget( mPropsWidget );
704  layout->addWidget( buttonBox );
705  this->setWindowTitle( "Edit Rule" );
706 
707  connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererRulePropsDialog::accept );
708  connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
709  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRendererRulePropsDialog::showHelp );
710 
711  QgsSettings settings;
712  restoreGeometry( settings.value( QStringLiteral( "Windows/QgsRendererRulePropsDialog/geometry" ) ).toByteArray() );
713 }
714 
716 {
717  QgsSettings settings;
718  settings.setValue( QStringLiteral( "Windows/QgsRendererRulePropsDialog/geometry" ), saveGeometry() );
719 }
720 
722 {
723  mPropsWidget->testFilter();
724 }
725 
727 {
728  mPropsWidget->buildExpression();
729 }
730 
732 {
733  mPropsWidget->apply();
734  QDialog::accept();
735 }
736 
737 void QgsRendererRulePropsDialog::showHelp()
738 {
739  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-rendering" ) );
740 }
741 
742 
744 {
745  QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
746 
747  // additional scopes
748  Q_FOREACH ( const QgsExpressionContextScope &scope, mContext.additionalExpressionContextScopes() )
749  {
750  context.appendScope( new QgsExpressionContextScope( scope ) );
751  }
752 
753  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
754 
755  if ( dlg.exec() )
756  editFilter->setText( dlg.expressionText() );
757 }
758 
760 {
761  if ( !mFilterRadio->isChecked() )
762  return;
763 
764  QgsExpression filter( editFilter->text() );
765  if ( filter.hasParserError() )
766  {
767  QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
768  return;
769  }
770 
771  QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
772 
773  // additional scopes
774  Q_FOREACH ( const QgsExpressionContextScope &scope, mContext.additionalExpressionContextScopes() )
775  {
776  context.appendScope( new QgsExpressionContextScope( scope ) );
777  }
778 
779  if ( !filter.prepare( &context ) )
780  {
781  QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
782  return;
783  }
784 
785  QApplication::setOverrideCursor( Qt::WaitCursor );
786 
789  .setFilterExpression( editFilter->text() )
790  .setExpressionContext( context );
791 
792  QgsFeatureIterator fit = mLayer->getFeatures( req );
793 
794  int count = 0;
795  QgsFeature f;
796  while ( fit.nextFeature( f ) )
797  {
798  count++;
799  }
800 
801  QApplication::restoreOverrideCursor();
802 
803  QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
804 }
805 
807 {
808  QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text();
809  mRule->setFilterExpression( filter );
810  mRule->setLabel( editLabel->text() );
811  mRule->setDescription( editDescription->text() );
812  // caution: rule uses scale denom, scale widget uses true scales
813  mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
814  mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
815  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
816 }
817 
819 {
820  QgsPanelWidget::setDockMode( dockMode );
821  mSymbolSelector->setDockMode( dockMode );
822 }
823 
825 
826 /*
827  setDragEnabled(true);
828  viewport()->setAcceptDrops(true);
829  setDropIndicatorShown(true);
830  setDragDropMode(QAbstractItemView::InternalMove);
831 */
832 
834 
836  : QAbstractItemModel( parent )
837  , mR( renderer )
838 {
839 }
840 
841 Qt::ItemFlags QgsRuleBasedRendererModel::flags( const QModelIndex &index ) const
842 {
843  if ( !index.isValid() )
844  return Qt::ItemIsDropEnabled;
845 
846  // allow drop only at first column
847  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
848 
849  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
850 
851  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
852  Qt::ItemIsEditable | checkable |
853  Qt::ItemIsDragEnabled | drop;
854 }
855 
856 QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) const
857 {
858  if ( !index.isValid() )
859  return QVariant();
860 
861  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index );
862 
863  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
864  {
865  switch ( index.column() )
866  {
867  case 0:
868  return rule->label();
869  case 1:
870  if ( rule->isElse() )
871  {
872  return "ELSE";
873  }
874  else
875  {
876  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
877  }
878  case 2:
879  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
880  case 3:
881  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
882  case 4:
883  if ( mFeatureCountMap.count( rule ) == 1 )
884  {
885  return QVariant( mFeatureCountMap[rule].count );
886  }
887  return QVariant();
888  case 5:
889  if ( mFeatureCountMap.count( rule ) == 1 )
890  {
891  if ( role == Qt::DisplayRole )
892  {
893  return QVariant( mFeatureCountMap[rule].duplicateCount );
894  }
895  else // tooltip - detailed info about duplicates
896  {
897  if ( mFeatureCountMap[rule].duplicateCount > 0 )
898  {
899  QString tip = QStringLiteral( "<p style='margin:0px;'><ul>" );
900  const auto duplicateMap = mFeatureCountMap[rule].duplicateCountMap;
901  for ( auto it = duplicateMap.constBegin(); it != duplicateMap.constEnd(); ++it )
902  {
903  QString label = it.key()->label().replace( '&', QLatin1String( "&amp;" ) ).replace( '>', QLatin1String( "&gt;" ) ).replace( '<', QLatin1String( "&lt;" ) );
904  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( it.value() ).arg( label );
905  }
906  tip += QLatin1String( "</ul>" );
907  return tip;
908  }
909  else
910  {
911  return 0;
912  }
913  }
914  }
915  return QVariant();
916  default:
917  return QVariant();
918  }
919  }
920  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
921  {
922  return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
923  }
924  else if ( role == Qt::TextAlignmentRole )
925  {
926  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
927  }
928  else if ( role == Qt::FontRole && index.column() == 1 )
929  {
930  if ( rule->isElse() )
931  {
932  QFont italicFont;
933  italicFont.setItalic( true );
934  return italicFont;
935  }
936  return QVariant();
937  }
938  else if ( role == Qt::EditRole )
939  {
940  switch ( index.column() )
941  {
942  case 0:
943  return rule->label();
944  case 1:
945  return rule->filterExpression();
946  case 2:
947  return rule->minimumScale();
948  case 3:
949  return rule->maximumScale();
950  default:
951  return QVariant();
952  }
953  }
954  else if ( role == Qt::CheckStateRole )
955  {
956  if ( index.column() != 0 )
957  return QVariant();
958  return rule->active() ? Qt::Checked : Qt::Unchecked;
959  }
960  else
961  return QVariant();
962 }
963 
964 QVariant QgsRuleBasedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
965 {
966  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
967  {
968  QStringList lst;
969  lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Count" ) << tr( "Duplicate count" );
970  return lst[section];
971  }
972  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
973  {
974  if ( section == 4 ) // Count
975  {
976  return tr( "Number of features in this rule." );
977  }
978  else if ( section == 5 ) // Duplicate count
979  {
980  return tr( "Number of features in this rule which are also present in other rule(s)." );
981  }
982  }
983 
984  return QVariant();
985 }
986 
987 int QgsRuleBasedRendererModel::rowCount( const QModelIndex &parent ) const
988 {
989  if ( parent.column() > 0 )
990  return 0;
991 
992  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
993 
994  return parentRule->children().count();
995 }
996 
997 int QgsRuleBasedRendererModel::columnCount( const QModelIndex & ) const
998 {
999  return 6;
1000 }
1001 
1002 QModelIndex QgsRuleBasedRendererModel::index( int row, int column, const QModelIndex &parent ) const
1003 {
1004  if ( hasIndex( row, column, parent ) )
1005  {
1006  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
1007  QgsRuleBasedRenderer::Rule *childRule = parentRule->children()[row];
1008  return createIndex( row, column, childRule );
1009  }
1010  return QModelIndex();
1011 }
1012 
1013 QModelIndex QgsRuleBasedRendererModel::parent( const QModelIndex &index ) const
1014 {
1015  if ( !index.isValid() )
1016  return QModelIndex();
1017 
1018  QgsRuleBasedRenderer::Rule *childRule = ruleForIndex( index );
1019  QgsRuleBasedRenderer::Rule *parentRule = childRule->parent();
1020 
1021  if ( parentRule == mR->rootRule() )
1022  return QModelIndex();
1023 
1024  // this is right: we need to know row number of our parent (in our grandparent)
1025  int row = parentRule->parent()->children().indexOf( parentRule );
1026 
1027  return createIndex( row, 0, parentRule );
1028 }
1029 
1030 bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
1031 {
1032  if ( !index.isValid() )
1033  return false;
1034 
1035  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index );
1036 
1037  if ( role == Qt::CheckStateRole )
1038  {
1039  rule->setActive( value.toInt() == Qt::Checked );
1040  emit dataChanged( index, index );
1041  return true;
1042  }
1043 
1044  if ( role != Qt::EditRole )
1045  return false;
1046 
1047  switch ( index.column() )
1048  {
1049  case 0: // label
1050  rule->setLabel( value.toString() );
1051  break;
1052  case 1: // filter
1053  rule->setFilterExpression( value.toString() );
1054  break;
1055  case 2: // scale min
1056  rule->setMinimumScale( value.toDouble() );
1057  break;
1058  case 3: // scale max
1059  rule->setMaximumScale( value.toDouble() );
1060  break;
1061  default:
1062  return false;
1063  }
1064 
1065  emit dataChanged( index, index );
1066  return true;
1067 }
1068 
1070 {
1071  return Qt::MoveAction; // | Qt::CopyAction
1072 }
1073 
1075 {
1076  QStringList types;
1077  types << QStringLiteral( "application/vnd.text.list" );
1078  return types;
1079 }
1080 
1081 QMimeData *QgsRuleBasedRendererModel::mimeData( const QModelIndexList &indexes ) const
1082 {
1083  QMimeData *mimeData = new QMimeData();
1084  QByteArray encodedData;
1085 
1086  QDataStream stream( &encodedData, QIODevice::WriteOnly );
1087 
1088  Q_FOREACH ( const QModelIndex &index, indexes )
1089  {
1090  // each item consists of several columns - let's add it with just first one
1091  if ( !index.isValid() || index.column() != 0 )
1092  continue;
1093 
1094  // we use a clone of the existing rule because it has a new unique rule key
1095  // non-unique rule keys would confuse other components using them (e.g. legend)
1096  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index )->clone();
1097  QDomDocument doc;
1098  QgsSymbolMap symbols;
1099 
1100  QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
1101  rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "renderer" ) ); // for determining whether rules are from renderer or labeling
1102  QDomElement rulesElem = rule->save( doc, symbols );
1103  rootElem.appendChild( rulesElem );
1104  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
1105  rootElem.appendChild( symbolsElem );
1106  doc.appendChild( rootElem );
1107 
1108  delete rule;
1109 
1110  stream << doc.toString( -1 );
1111  }
1112 
1113  mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
1114  return mimeData;
1115 }
1116 
1117 
1118 // manipulate DOM before dropping it so that rules are more useful
1119 void _labeling2rendererRules( QDomElement &ruleElem )
1120 {
1121  // labeling rules recognize only "description"
1122  if ( ruleElem.hasAttribute( QStringLiteral( "description" ) ) )
1123  ruleElem.setAttribute( QStringLiteral( "label" ), ruleElem.attribute( QStringLiteral( "description" ) ) );
1124 
1125  // run recursively
1126  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
1127  while ( !childRuleElem.isNull() )
1128  {
1129  _labeling2rendererRules( childRuleElem );
1130  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
1131  }
1132 }
1133 
1134 
1136  Qt::DropAction action, int row, int column, const QModelIndex &parent )
1137 {
1138  Q_UNUSED( column );
1139 
1140  if ( action == Qt::IgnoreAction )
1141  return true;
1142 
1143  if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
1144  return false;
1145 
1146  if ( parent.column() > 0 )
1147  return false;
1148 
1149  QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
1150  QDataStream stream( &encodedData, QIODevice::ReadOnly );
1151  int rows = 0;
1152 
1153  if ( row == -1 )
1154  {
1155  // the item was dropped at a parent - we may decide where to put the items - let's append them
1156  row = rowCount( parent );
1157  }
1158 
1159  while ( !stream.atEnd() )
1160  {
1161  QString text;
1162  stream >> text;
1163 
1164  QDomDocument doc;
1165  if ( !doc.setContent( text ) )
1166  continue;
1167  QDomElement rootElem = doc.documentElement();
1168  if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
1169  continue;
1170  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1171  rootElem.appendChild( doc.createElement( QStringLiteral( "symbols" ) ) );
1172  QDomElement symbolsElem = rootElem.firstChildElement( QStringLiteral( "symbols" ) );
1173  if ( symbolsElem.isNull() )
1174  continue;
1176  QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
1177  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1178  _labeling2rendererRules( ruleElem );
1179  QgsRuleBasedRenderer::Rule *rule = QgsRuleBasedRenderer::Rule::create( ruleElem, symbolMap );
1180 
1181  insertRule( parent, row + rows, rule );
1182 
1183  ++rows;
1184  }
1185  return true;
1186 }
1187 
1189 {
1190  if ( index.isValid() )
1191  return static_cast<QgsRuleBasedRenderer::Rule *>( index.internalPointer() );
1192  return mR->rootRule();
1193 }
1194 
1195 bool QgsRuleBasedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
1196 {
1197  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
1198 
1199  if ( row < 0 || row >= parentRule->children().count() )
1200  return false;
1201 
1202  QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1203 
1204  beginRemoveRows( parent, row, row + count - 1 );
1205 
1206  for ( int i = 0; i < count; i++ )
1207  {
1208  if ( row < parentRule->children().count() )
1209  {
1210  //QgsRuleBasedRenderer::Rule* r = parentRule->children()[row];
1211  parentRule->removeChildAt( row );
1212  //parentRule->takeChildAt( row );
1213  }
1214  else
1215  {
1216  QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
1217  }
1218  }
1219 
1220  endRemoveRows();
1221 
1222  return true;
1223 }
1224 
1225 
1226 void QgsRuleBasedRendererModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule )
1227 {
1228  beginInsertRows( parent, before, before );
1229 
1230  QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1231 
1232  QgsRuleBasedRenderer::Rule *parentRule = ruleForIndex( parent );
1233  parentRule->insertChild( before, newrule );
1234 
1235  endInsertRows();
1236 }
1237 
1238 void QgsRuleBasedRendererModel::updateRule( const QModelIndex &parent, int row )
1239 {
1240  emit dataChanged( index( row, 0, parent ),
1241  index( row, columnCount( parent ), parent ) );
1242 }
1243 
1244 void QgsRuleBasedRendererModel::updateRule( const QModelIndex &idx )
1245 {
1246  emit dataChanged( index( 0, 0, idx ),
1247  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1248 
1249  for ( int i = 0; i < rowCount( idx ); i++ )
1250  {
1251  updateRule( index( i, 0, idx ) );
1252  }
1253 }
1254 
1255 
1257 {
1258  if ( !index.isValid() )
1259  return;
1260 
1261  beginRemoveRows( index.parent(), index.row(), index.row() );
1262 
1263  QgsRuleBasedRenderer::Rule *rule = ruleForIndex( index );
1264  rule->parent()->removeChild( rule );
1265 
1266  endRemoveRows();
1267 }
1268 
1269 void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int count )
1270 {
1271  int row = rowCount( parent ); // only consider appending
1272  beginInsertRows( parent, row, row + count - 1 );
1273 }
1274 
1276 {
1277  endInsertRows();
1278 }
1279 
1280 void QgsRuleBasedRendererModel::setFeatureCounts( const QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> &countMap )
1281 {
1282  mFeatureCountMap = countMap;
1283  updateRule( QModelIndex() );
1284 }
1285 
1287 {
1288  mFeatureCountMap.clear();
1289  updateRule( QModelIndex() );
1290 }
QgsSymbolSelectorWidget * mSymbolSelector
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
Class for parsing and evaluation of expressions (formerly called "search strings").
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
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()
Returns 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
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.
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()
Returns the current set rule.
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: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 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
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.
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.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns new default symbol for specified geometry type.
Definition: qgssymbol.cpp:267
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...
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.
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
Returns 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 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
Gets 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.
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)
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...
QgsRuleBasedRenderer::Rule * parent()
The parent rule.