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