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