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