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