QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
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 "qgsapplication.h"
19#include "qgsexpression.h"
20#include "qgsfeatureiterator.h"
21#include "qgsguiutils.h"
22#include "qgslogger.h"
23#include "qgsmapcanvas.h"
24#include "qgspanelwidget.h"
25#include "qgsreadwritecontext.h"
27#include "qgssettings.h"
28#include "qgssymbol.h"
29#include "qgssymbollayerutils.h"
31#include "qgsvectorlayer.h"
32
33#include <QClipboard>
34#include <QKeyEvent>
35#include <QMenu>
36#include <QMessageBox>
37#include <QPointer>
38#include <QProgressDialog>
39#include <QScreen>
40#include <QTreeWidgetItem>
41#include <QVBoxLayout>
42
43#include "moc_qgsrulebasedrendererwidget.cpp"
44
45#ifdef ENABLE_MODELTEST
46#include "modeltest.h"
47#endif
48
49
54
56 : QgsRendererWidget( layer, style )
57 , mContextMenu( new QMenu( this ) )
58{
59 mRenderer = nullptr;
60 // try to recognize the previous renderer
61 // (null renderer means "no previous renderer")
62
63
64 if ( renderer )
65 {
67 }
68 if ( !mRenderer )
69 {
70 // some default options
71 QgsSymbol *symbol = QgsSymbol::defaultSymbol( mLayer->geometryType() );
72
73 mRenderer = std::make_unique<QgsRuleBasedRenderer>( symbol );
74 if ( renderer )
75 renderer->copyRendererData( mRenderer.get() );
76 }
77
78 setupUi( this );
79 this->layout()->setContentsMargins( 0, 0, 0, 0 );
80
81 mModel = new QgsRuleBasedRendererModel( mRenderer.get(), viewRules, screen() );
82#ifdef ENABLE_MODELTEST
83 new ModelTest( mModel, this ); // for model validity checking
84#endif
85 viewRules->setModel( mModel );
86
87 mDeleteAction = new QAction( tr( "Remove Rule" ), this );
88 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
89
90 viewRules->addAction( mCopyAction );
91 viewRules->addAction( mPasteAction );
92 viewRules->addAction( mDeleteAction );
93
94 mRefineMenu = new QMenu( tr( "Refine Current Rule" ), btnRefineRule );
95 mRefineMenu->addAction( tr( "Add Scales to Rule" ), this, &QgsRuleBasedRendererWidget::refineRuleScales );
96 mRefineMenu->addAction( tr( "Add Categories to Rule" ), this, &QgsRuleBasedRendererWidget::refineRuleCategories );
97 mRefineMenu->addAction( tr( "Add Ranges to Rule" ), this, &QgsRuleBasedRendererWidget::refineRuleRanges );
98 btnRefineRule->setMenu( mRefineMenu );
99
100 btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
101 btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
102 btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
103
104 connect( viewRules, &QAbstractItemView::doubleClicked, this, static_cast<void ( QgsRuleBasedRendererWidget::* )( const QModelIndex &index )>( &QgsRuleBasedRendererWidget::editRule ) );
105
106 // support for context menu (now handled generically)
107 connect( viewRules, &QWidget::customContextMenuRequested, this, &QgsRuleBasedRendererWidget::showContextMenu );
108
109 connect( viewRules->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsRuleBasedRendererWidget::currentRuleChanged );
110 connect( viewRules->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsRuleBasedRendererWidget::selectedRulesChanged );
111
112 connect( btnAddRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::addRule );
113 connect( btnEditRule, &QAbstractButton::clicked, this, static_cast<void ( QgsRuleBasedRendererWidget::* )()>( &QgsRuleBasedRendererWidget::editRule ) );
114 connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::removeRule );
115 connect( mDeleteAction, &QAction::triggered, this, &QgsRuleBasedRendererWidget::removeRule );
116 connect( btnCountFeatures, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::countFeatures );
117
118 connect( btnRenderingOrder, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::setRenderingOrder );
119
120 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
121 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsPanelWidget::widgetChanged );
122 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsPanelWidget::widgetChanged );
123
126
127 // store/restore header section widths
128 connect( viewRules->header(), &QHeaderView::sectionResized, this, &QgsRuleBasedRendererWidget::saveSectionWidth );
129
131
132 connect( mContextMenu, &QMenu::aboutToShow, this, [this] {
133 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
134 mPasteSymbolAction->setEnabled( static_cast<bool>( tempSymbol ) );
135 } );
136}
137
142
147
149{
150 if ( dockMode )
151 {
152 // when in dock mode, these shortcuts conflict with the main window shortcuts and cannot be used
153 if ( mDeleteAction )
154 mDeleteAction->setShortcut( QKeySequence() );
155 }
157}
158
160{
161 QgsSymbol *s = QgsSymbol::defaultSymbol( mLayer->geometryType() );
163
165 if ( current )
166 {
167 // add after this rule
168 QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
169 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
170 QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
171 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
172 }
173 else
174 {
175 // append to root rule
176 int rows = mModel->rowCount();
177 mModel->insertRule( QModelIndex(), rows, newrule );
178 QModelIndex newindex = mModel->index( rows, 0 );
179 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
180 }
181 editRule();
182}
183
185{
186 QItemSelectionModel *sel = viewRules->selectionModel();
187 QModelIndex idx = sel->currentIndex();
188 if ( !idx.isValid() )
189 return nullptr;
190 return mModel->ruleForIndex( idx );
191}
192
194{
195 editRule( viewRules->selectionModel()->currentIndex() );
196}
197
198void QgsRuleBasedRendererWidget::editRule( const QModelIndex &index )
199{
200 if ( !index.isValid() )
201 return;
202
203 QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index );
205
206 if ( panel && panel->dockMode() )
207 {
208 QgsRendererRulePropsWidget *widget = new QgsRendererRulePropsWidget( rule, mLayer, mStyle, this, mContext ); //panel?
209 widget->setPanelTitle( tr( "Edit Rule" ) );
210 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted );
211 connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel );
212 openPanel( widget );
213 return;
214 }
215
217 if ( dlg.exec() )
218 {
219 mModel->updateRule( index.parent(), index.row() );
220 mModel->clearFeatureCounts();
221 emit widgetChanged();
222 }
223}
224
226{
227 QItemSelection sel = viewRules->selectionModel()->selection();
228 QgsDebugMsgLevel( QStringLiteral( "REMOVE RULES!!! ranges: %1" ).arg( sel.count() ), 2 );
229 const auto constSel = sel;
230 for ( const QItemSelectionRange &range : constSel )
231 {
232 QgsDebugMsgLevel( QStringLiteral( "RANGE: r %1 - %2" ).arg( range.top() ).arg( range.bottom() ), 2 );
233 if ( range.isValid() )
234 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
235 }
236 // make sure that the selection is gone
237 viewRules->selectionModel()->clear();
238 mModel->clearFeatureCounts();
239}
240
241void QgsRuleBasedRendererWidget::currentRuleChanged( const QModelIndex &current, const QModelIndex &previous )
242{
243 Q_UNUSED( previous )
244 btnEditRule->setEnabled( current.isValid() );
245}
246
247
253#include <QDialogButtonBox>
254#include <QInputDialog>
255#include <QClipboard>
256
258{
259 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
260
261 if ( indexlist.isEmpty() )
262 return;
263
264
265 if ( type == 0 ) // categories
267 else if ( type == 1 ) // ranges
269 else // scales
270 refineRuleScalesGui( indexlist );
271
272 // TODO: set initial rule's symbol to NULL (?)
273
274 // show the newly added rules
275 const auto constIndexlist = indexlist;
276 for ( const QModelIndex &index : constIndexlist )
277 viewRules->expand( index );
278}
279
284
289
294
296{
298 w->setPanelTitle( tr( "Add Categories to Rules" ) );
299 connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted );
300 w->setContext( mContext );
301 openPanel( w );
302}
303
305{
307 w->setPanelTitle( tr( "Add Ranges to Rules" ) );
308 connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleRangesAccepted );
309 w->setContext( mContext );
310 openPanel( w );
311}
312
313void QgsRuleBasedRendererWidget::refineRuleScalesGui( const QModelIndexList &indexList )
314{
315 for ( const QModelIndex &index : indexList )
316 {
317 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
318
319 // If any of the rules don't have a symbol let the user know and exit.
320 if ( !initialRule->symbol() )
321 {
322 QMessageBox::warning( this, tr( "Scale Refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
323 return;
324 }
325 }
326
327 QString txt = QInputDialog::getText( this, tr( "Scale Refinement" ), tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
328 if ( txt.isEmpty() )
329 return;
330
331 QList<int> scales;
332 bool ok;
333 const auto constSplit = txt.split( ',' );
334 for ( const QString &item : constSplit )
335 {
336 int scale = item.toInt( &ok );
337 if ( ok )
338 scales.append( scale );
339 else
340 QMessageBox::information( this, tr( "Scale Refinement" ), tr( "\"%1\" is not valid scale denominator, ignoring it." ).arg( item ) );
341 }
342
343 for ( const QModelIndex &index : indexList )
344 {
345 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
346 mModel->willAddRules( index, scales.count() + 1 );
347 QgsRuleBasedRenderer::refineRuleScales( initialRule, scales );
348 }
349 mModel->finishedAddingRules();
350}
351
352void QgsRuleBasedRendererWidget::setSymbolLevels( const QList<QgsLegendSymbolItem> &levels, bool )
353{
354 if ( !mRenderer )
355 return;
356
357 for ( const QgsLegendSymbolItem &legendSymbol : std::as_const( levels ) )
358 {
359 QgsSymbol *sym = legendSymbol.symbol();
360 for ( int layer = 0; layer < sym->symbolLayerCount(); layer++ )
361 {
362 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->clone() );
363 }
364 }
365
366 emit widgetChanged();
367}
368
370{
371 QList<QgsSymbol *> symbolList;
372
373 if ( !mRenderer )
374 {
375 return symbolList;
376 }
377
378 QItemSelection sel = viewRules->selectionModel()->selection();
379 const auto constSel = sel;
380 for ( const QItemSelectionRange &range : constSel )
381 {
382 QModelIndex parent = range.parent();
383 QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
384 const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
385 for ( int row = range.top(); row <= range.bottom(); row++ )
386 {
387 symbolList.append( children.at( row )->symbol() );
388 }
389 }
390
391 return symbolList;
392}
393
395{
397 QItemSelection sel = viewRules->selectionModel()->selection();
398 const auto constSel = sel;
399 for ( const QItemSelectionRange &range : constSel )
400 {
401 QModelIndex parent = range.parent();
402 QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
403 const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
404 for ( int row = range.top(); row <= range.bottom(); row++ )
405 {
406 rl.append( children.at( row )->clone() );
407 }
408 }
409 return rl;
410}
411
413{
414 // TODO: model/view
415 /*
416 if ( treeRules )
417 {
418 treeRules->populateRules();
419 }
420 */
421 emit widgetChanged();
422}
423
425{
426 if ( !event )
427 {
428 return;
429 }
430
431 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
432 {
433 qDeleteAll( mCopyBuffer );
434 mCopyBuffer.clear();
436 }
437 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
438 {
439 QgsRuleBasedRenderer::RuleList::const_iterator rIt = mCopyBuffer.constBegin();
440 for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
441 {
442 int rows = mModel->rowCount();
443 mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
444 }
445 }
446}
447
449
451{
453 if ( panel && panel->dockMode() )
454 {
455 QgsSymbolLevelsWidget *widget = new QgsSymbolLevelsWidget( mRenderer.get(), true, panel );
456 widget->setForceOrderingEnabled( true );
457 widget->setPanelTitle( tr( "Symbol Levels" ) );
458 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget]() {
459 setSymbolLevels( widget->symbolLevels(), widget->usingLevels() );
460 } );
461 panel->openPanel( widget );
462 }
463 else
464 {
465 QgsSymbolLevelsDialog dlg( mRenderer.get(), true, panel );
466 dlg.setForceOrderingEnabled( true );
467 if ( dlg.exec() )
468 {
470 }
471 }
472}
473
474void QgsRuleBasedRendererWidget::saveSectionWidth( int section, int oldSize, int newSize )
475{
476 Q_UNUSED( oldSize )
477 // skip last section, as it stretches
478 if ( section == 5 )
479 return;
480 QgsSettings settings;
481 QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
482 settings.setValue( path, newSize );
483}
484
486{
487 QgsSettings settings;
488 QString path = QStringLiteral( "/Windows/RuleBasedTree/sectionWidth/" );
489 QHeaderView *head = viewRules->header();
490 head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
491 head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
492 head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
493 head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
494 head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
495 head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
496}
497
499{
500 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
501 QgsDebugMsgLevel( QString::number( indexlist.count() ), 2 );
502
503 if ( indexlist.isEmpty() )
504 return;
505
506 QMimeData *mime = mModel->mimeData( indexlist );
507 QApplication::clipboard()->setMimeData( mime );
508}
509
511{
512 const QMimeData *mime = QApplication::clipboard()->mimeData();
513 if ( !mime )
514 return;
515
516 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
517 QModelIndex index;
518 if ( indexlist.isEmpty() )
519 index = mModel->index( mModel->rowCount(), 0 );
520 else
521 index = indexlist.first();
522 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
523}
524
526{
527 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
528 if ( !tempSymbol )
529 return;
530
531 const QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
532 for ( const QModelIndex &index : indexList )
533 {
534 if ( QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index ) )
535 {
536 if ( !rule->symbol() || rule->symbol()->type() != tempSymbol->type() )
537 continue;
538
539 mModel->setSymbol( index, tempSymbol->clone() );
540 }
541 }
542 emit widgetChanged();
543}
544
545void QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted( QgsPanelWidget *panel )
546{
547 QgsCategorizedSymbolRendererWidget *w = qobject_cast<QgsCategorizedSymbolRendererWidget *>( panel );
548
549 // create new rules
551 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
552 const auto constIndexList = indexList;
553 for ( const QModelIndex &index : constIndexList )
554 {
555 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
556 mModel->willAddRules( index, r->categories().count() );
558 }
559 mModel->finishedAddingRules();
560}
561
562void QgsRuleBasedRendererWidget::refineRuleRangesAccepted( QgsPanelWidget *panel )
563{
564 QgsGraduatedSymbolRendererWidget *w = qobject_cast<QgsGraduatedSymbolRendererWidget *>( panel );
565 // create new rules
566 QgsGraduatedSymbolRenderer *r = static_cast<QgsGraduatedSymbolRenderer *>( w->renderer() );
567 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
568 const auto constIndexList = indexList;
569 for ( const QModelIndex &index : constIndexList )
570 {
571 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
572 mModel->willAddRules( index, r->ranges().count() );
574 }
575 mModel->finishedAddingRules();
576}
577
578void QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
579{
580 QgsRendererRulePropsWidget *widget = qobject_cast<QgsRendererRulePropsWidget *>( panel );
581 if ( !widget )
582 return;
583
584 widget->apply();
585
586 // model should know about the change and emit dataChanged signal for the view
587 QModelIndex index = viewRules->selectionModel()->currentIndex();
588 mModel->updateRule( index.parent(), index.row() );
589 mModel->clearFeatureCounts();
590}
591
592void QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel()
593{
594 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
595}
596
597void QgsRuleBasedRendererWidget::showContextMenu( QPoint )
598{
599 mContextMenu->clear();
600 mContextMenu->addAction( mCopyAction );
601 mContextMenu->addAction( mPasteAction );
602
603 const QList<QAction *> actions = contextMenu->actions();
604 for ( QAction *act : actions )
605 {
606 mContextMenu->addAction( act );
607 }
608
609 mContextMenu->addMenu( mRefineMenu );
610
611 mContextMenu->exec( QCursor::pos() );
612}
613
614
616{
617 if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
618 {
619 return;
620 }
621 QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> countMap;
622
623 QgsRuleBasedRenderer::RuleList ruleList = mRenderer->rootRule()->descendants();
624 // insert all so that we have counts 0
625 const auto constRuleList = ruleList;
626 for ( QgsRuleBasedRenderer::Rule *rule : constRuleList )
627 {
628 countMap[rule].count = 0;
629 countMap[rule].duplicateCount = 0;
630 }
631
632 QgsRenderContext renderContext;
633 renderContext.setRendererScale( 0 ); // ignore scale
634
635 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
636
637 // additional scopes
638 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
639 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
640 {
641 context.appendScope( new QgsExpressionContextScope( scope ) );
642 }
643
644 renderContext.setExpressionContext( context );
645
646 mRenderer->startRender( renderContext, mLayer->fields() );
647 // QgsRuleBasedRenderer::filter must be called after startRender
650 req.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mLayer->fields() );
651 QgsFeatureIterator fit = mLayer->getFeatures( req );
652
653 long long nFeatures = mLayer->featureCount();
654 QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, 100 );
655 p.setWindowModality( Qt::WindowModal );
656 long long featuresCounted = 0;
657
658 QgsFeature f;
659 while ( fit.nextFeature( f ) )
660 {
661 renderContext.expressionContext().setFeature( f );
662 QgsRuleBasedRenderer::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
663
664 const auto constFeatureRuleList = featureRuleList;
665 for ( QgsRuleBasedRenderer::Rule *rule : constFeatureRuleList )
666 {
667 countMap[rule].count++;
668 if ( featureRuleList.size() > 1 )
669 {
670 countMap[rule].duplicateCount++;
671 }
672 const auto constFeatureRuleList = featureRuleList;
673 for ( QgsRuleBasedRenderer::Rule *duplicateRule : constFeatureRuleList )
674 {
675 if ( duplicateRule == rule )
676 continue;
677 countMap[rule].duplicateCountMap[duplicateRule] += 1;
678 }
679 }
680 ++featuresCounted;
681 if ( featuresCounted % 50 == 0 )
682 {
683 if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
684 {
685 p.setMaximum( 0 );
686 }
687 p.setValue( static_cast<double>( featuresCounted ) / nFeatures * 100.0 );
688 if ( p.wasCanceled() )
689 {
690 return;
691 }
692 }
693 }
694 p.setValue( nFeatures );
695
696 mRenderer->stopRender( renderContext );
697
698#ifdef QGISDEBUG
699 const auto constKeys = countMap.keys();
700 for ( QgsRuleBasedRenderer::Rule *rule : constKeys )
701 {
702 QgsDebugMsgLevel( QStringLiteral( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ), 2 );
703 }
704#endif
705
706 mModel->setFeatureCounts( countMap );
707}
708
710{
711 bool enabled = !viewRules->selectionModel()->selectedIndexes().isEmpty();
712 btnRefineRule->setEnabled( enabled );
713 btnRemoveRule->setEnabled( enabled );
714}
715
717
719 : QgsPanelWidget( parent )
720 , mRule( rule )
721 , mLayer( layer )
722 , mContext( context )
723{
724 setupUi( this );
725 layout()->setContentsMargins( 0, 0, 0, 0 );
726
727 mElseRadio->setChecked( mRule->isElse() );
728 mFilterRadio->setChecked( !mRule->isElse() );
729 editFilter->setText( mRule->filterExpression() );
730 editFilter->setToolTip( mRule->filterExpression() );
731 editLabel->setText( mRule->label() );
732 editDescription->setText( mRule->description() );
733 editDescription->setToolTip( mRule->description() );
734
735 if ( mRule->dependsOnScale() )
736 {
737 groupScale->setChecked( true );
738 mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ), std::max( rule->maximumScale(), 0.0 ) );
739 }
740 mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
741
742 if ( mRule->symbol() )
743 {
744 groupSymbol->setChecked( true );
745 mSymbol = mRule->symbol()->clone(); // use a clone!
746 }
747 else
748 {
749 groupSymbol->setChecked( false );
750 mSymbol = QgsSymbol::defaultSymbol( mLayer->geometryType() );
751 }
752
754 mSymbolSelector->setContext( mContext );
757
758 QVBoxLayout *l = new QVBoxLayout;
759 l->addWidget( mSymbolSelector );
760 groupSymbol->setLayout( l );
761
762 connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
763 connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
764 connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
765 connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
766 connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
767 connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
768 connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
769 connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
770 connect( mFilterRadio, &QRadioButton::toggled, this, [this]( bool toggled ) { filterFrame->setEnabled( toggled ); } );
771 connect( mElseRadio, &QRadioButton::toggled, this, [this]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) ); } );
772}
773
774#include "qgsvscrollarea.h"
775
777 : QDialog( parent )
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
817void QgsRendererRulePropsDialog::showHelp()
818{
819 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-rendering" ) );
820}
821
822
824{
825 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
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
852 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
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
870
871 QgsFeatureIterator fit = mLayer->getFeatures( req );
872
873 int count = 0;
874 QgsFeature f;
875 while ( fit.nextFeature( f ) )
876 {
877 count++;
878 }
879
880 QApplication::restoreOverrideCursor();
881
882 QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
883}
884
886{
887 QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text();
888 mRule->setFilterExpression( filter );
889 mRule->setLabel( editLabel->text() );
890 mRule->setDescription( editDescription->text() );
891 // caution: rule uses scale denom, scale widget uses true scales
892 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
893 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
894 mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
895}
896
902
904
905/*
906 setDragEnabled(true);
907 viewport()->setAcceptDrops(true);
908 setDropIndicatorShown(true);
909 setDragDropMode(QAbstractItemView::InternalMove);
910*/
911
913
915 : QAbstractItemModel( parent )
916 , mR( renderer )
917 , mScreen( screen )
918{
919}
920
921Qt::ItemFlags QgsRuleBasedRendererModel::flags( const QModelIndex &index ) const
922{
923 if ( !index.isValid() )
924 return Qt::ItemIsDropEnabled;
925
926 // allow drop only at first column
927 Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
928
929 Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
930
931 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | checkable | Qt::ItemIsDragEnabled | drop;
932}
933
934QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) const
935{
936 if ( !index.isValid() )
937 return QVariant();
938
940
941 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
942 {
943 switch ( index.column() )
944 {
945 case 0:
946 return rule->label();
947 case 1:
948 if ( rule->isElse() )
949 {
950 return "ELSE";
951 }
952 else
953 {
954 return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
955 }
956 case 2:
957 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
958 case 3:
959 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
960 case 4:
961 if ( mFeatureCountMap.count( rule ) == 1 )
962 {
963 return QVariant( mFeatureCountMap[rule].count );
964 }
965 return QVariant();
966 case 5:
967 if ( mFeatureCountMap.count( rule ) == 1 )
968 {
969 if ( role == Qt::DisplayRole )
970 {
971 return QVariant( mFeatureCountMap[rule].duplicateCount );
972 }
973 else // tooltip - detailed info about duplicates
974 {
975 if ( mFeatureCountMap[rule].duplicateCount > 0 )
976 {
977 QString tip = QStringLiteral( "<p style='margin:0px;'><ul>" );
978 const auto duplicateMap = mFeatureCountMap[rule].duplicateCountMap;
979 for ( auto it = duplicateMap.constBegin(); it != duplicateMap.constEnd(); ++it )
980 {
981 QString label = it.key()->label().replace( '&', QLatin1String( "&amp;" ) ).replace( '>', QLatin1String( "&gt;" ) ).replace( '<', QLatin1String( "&lt;" ) );
982 tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( it.value() ).arg( label );
983 }
984 tip += QLatin1String( "</ul>" );
985 return tip;
986 }
987 else
988 {
989 return 0;
990 }
991 }
992 }
993 return QVariant();
994 default:
995 return QVariant();
996 }
997 }
998 else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
999 {
1000 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
1001 return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( iconSize, iconSize ), 0, nullptr, QgsScreenProperties( mScreen.data() ) );
1002 }
1003 else if ( role == Qt::TextAlignmentRole )
1004 {
1005 return ( index.column() == 2 || index.column() == 3 ) ? static_cast<Qt::Alignment::Int>( Qt::AlignRight ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
1006 }
1007 else if ( role == Qt::FontRole && index.column() == 1 )
1008 {
1009 if ( rule->isElse() )
1010 {
1011 QFont italicFont;
1012 italicFont.setItalic( true );
1013 return italicFont;
1014 }
1015 return QVariant();
1016 }
1017 else if ( role == Qt::EditRole )
1018 {
1019 switch ( index.column() )
1020 {
1021 case 0:
1022 return rule->label();
1023 case 1:
1024 return rule->filterExpression();
1025 case 2:
1026 return rule->minimumScale();
1027 case 3:
1028 return rule->maximumScale();
1029 default:
1030 return QVariant();
1031 }
1032 }
1033 else if ( role == Qt::CheckStateRole )
1034 {
1035 if ( index.column() != 0 )
1036 return QVariant();
1037 return rule->active() ? Qt::Checked : Qt::Unchecked;
1038 }
1039 else
1040 return QVariant();
1041}
1042
1043QVariant QgsRuleBasedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
1044{
1045 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
1046 {
1047 QStringList lst;
1048 lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Count" ) << tr( "Duplicate Count" );
1049 return lst[section];
1050 }
1051 else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
1052 {
1053 if ( section == 4 ) // Count
1054 {
1055 return tr( "Number of features in this rule." );
1056 }
1057 else if ( section == 5 ) // Duplicate count
1058 {
1059 return tr( "Number of features in this rule which are also present in other rule(s)." );
1060 }
1061 }
1062
1063 return QVariant();
1064}
1065
1066int QgsRuleBasedRendererModel::rowCount( const QModelIndex &parent ) const
1067{
1068 if ( parent.column() > 0 )
1069 return 0;
1070
1072
1073 return parentRule->children().count();
1074}
1075
1076int QgsRuleBasedRendererModel::columnCount( const QModelIndex & ) const
1077{
1078 return 6;
1079}
1080
1081QModelIndex QgsRuleBasedRendererModel::index( int row, int column, const QModelIndex &parent ) const
1082{
1083 if ( hasIndex( row, column, parent ) )
1084 {
1086 QgsRuleBasedRenderer::Rule *childRule = parentRule->children()[row];
1087 return createIndex( row, column, childRule );
1088 }
1089 return QModelIndex();
1090}
1091
1092QModelIndex QgsRuleBasedRendererModel::parent( const QModelIndex &index ) const
1093{
1094 if ( !index.isValid() )
1095 return QModelIndex();
1096
1098 QgsRuleBasedRenderer::Rule *parentRule = childRule->parent();
1099
1100 if ( parentRule == mR->rootRule() )
1101 return QModelIndex();
1102
1103 // this is right: we need to know row number of our parent (in our grandparent)
1104 int row = parentRule->parent()->children().indexOf( parentRule );
1105
1106 return createIndex( row, 0, parentRule );
1107}
1108
1109bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
1110{
1111 if ( !index.isValid() )
1112 return false;
1113
1115
1116 if ( role == Qt::CheckStateRole )
1117 {
1118 rule->setActive( value.toInt() == Qt::Checked );
1119 emit dataChanged( index, index );
1120 return true;
1121 }
1122
1123 if ( role != Qt::EditRole )
1124 return false;
1125
1126 switch ( index.column() )
1127 {
1128 case 0: // label
1129 rule->setLabel( value.toString() );
1130 break;
1131 case 1: // filter
1132 rule->setFilterExpression( value.toString() );
1133 break;
1134 case 2: // scale min
1135 rule->setMinimumScale( value.toDouble() );
1136 break;
1137 case 3: // scale max
1138 rule->setMaximumScale( value.toDouble() );
1139 break;
1140 default:
1141 return false;
1142 }
1143
1144 emit dataChanged( index, index );
1145 return true;
1146}
1147
1149{
1150 return Qt::MoveAction; // | Qt::CopyAction
1151}
1152
1154{
1155 QStringList types;
1156 types << QStringLiteral( "application/vnd.text.list" );
1157 return types;
1158}
1159
1160QMimeData *QgsRuleBasedRendererModel::mimeData( const QModelIndexList &indexes ) const
1161{
1162 QMimeData *mimeData = new QMimeData();
1163 QByteArray encodedData;
1164
1165 QDataStream stream( &encodedData, QIODevice::WriteOnly );
1166
1167 const auto constIndexes = indexes;
1168 for ( const QModelIndex &index : constIndexes )
1169 {
1170 // each item consists of several columns - let's add it with just first one
1171 if ( !index.isValid() || index.column() != 0 )
1172 continue;
1173
1174 // we use a clone of the existing rule because it has a new unique rule key
1175 // non-unique rule keys would confuse other components using them (e.g. legend)
1177 QDomDocument doc;
1178 QgsSymbolMap symbols;
1179
1180 QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
1181 rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "renderer" ) ); // for determining whether rules are from renderer or labeling
1182 QDomElement rulesElem = rule->save( doc, symbols );
1183 rootElem.appendChild( rulesElem );
1184 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
1185 rootElem.appendChild( symbolsElem );
1186 doc.appendChild( rootElem );
1187
1188 delete rule;
1189
1190 stream << doc.toString( -1 );
1191 }
1192
1193 mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
1194 return mimeData;
1195}
1196
1197
1198// manipulate DOM before dropping it so that rules are more useful
1199void _labeling2rendererRules( QDomElement &ruleElem )
1200{
1201 // labeling rules recognize only "description"
1202 if ( ruleElem.hasAttribute( QStringLiteral( "description" ) ) )
1203 ruleElem.setAttribute( QStringLiteral( "label" ), ruleElem.attribute( QStringLiteral( "description" ) ) );
1204
1205 // run recursively
1206 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
1207 while ( !childRuleElem.isNull() )
1208 {
1209 _labeling2rendererRules( childRuleElem );
1210 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
1211 }
1212}
1213
1214
1215bool QgsRuleBasedRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1216{
1217 Q_UNUSED( column )
1218
1219 if ( action == Qt::IgnoreAction )
1220 return true;
1221
1222 if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
1223 return false;
1224
1225 if ( parent.column() > 0 )
1226 return false;
1227
1228 QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
1229 QDataStream stream( &encodedData, QIODevice::ReadOnly );
1230 int rows = 0;
1231
1232 if ( row == -1 )
1233 {
1234 // the item was dropped at a parent - we may decide where to put the items - let's append them
1235 row = rowCount( parent );
1236 }
1237
1238 while ( !stream.atEnd() )
1239 {
1240 QString text;
1241 stream >> text;
1242
1243 QDomDocument doc;
1244 if ( !doc.setContent( text ) )
1245 continue;
1246 QDomElement rootElem = doc.documentElement();
1247 if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
1248 continue;
1249 if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1250 rootElem.appendChild( doc.createElement( QStringLiteral( "symbols" ) ) );
1251 QDomElement symbolsElem = rootElem.firstChildElement( QStringLiteral( "symbols" ) );
1252 if ( symbolsElem.isNull() )
1253 continue;
1255 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
1256 if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1257 _labeling2rendererRules( ruleElem );
1258 QgsRuleBasedRenderer::Rule *rule = QgsRuleBasedRenderer::Rule::create( ruleElem, symbolMap, false );
1259
1260 insertRule( parent, row + rows, rule );
1261
1262 ++rows;
1263 }
1264 return true;
1265}
1266
1268{
1269 if ( index.isValid() )
1270 return static_cast<QgsRuleBasedRenderer::Rule *>( index.internalPointer() );
1271 return mR->rootRule();
1272}
1273
1274bool QgsRuleBasedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
1275{
1277
1278 if ( row < 0 || row >= parentRule->children().count() )
1279 return false;
1280
1281 QgsDebugMsgLevel( QStringLiteral( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ), 2 );
1282
1283 beginRemoveRows( parent, row, row + count - 1 );
1284
1285 for ( int i = 0; i < count; i++ )
1286 {
1287 if ( row < parentRule->children().count() )
1288 {
1289 //QgsRuleBasedRenderer::Rule* r = parentRule->children()[row];
1290 parentRule->removeChildAt( row );
1291 //parentRule->takeChildAt( row );
1292 }
1293 else
1294 {
1295 QgsDebugError( QStringLiteral( "trying to remove invalid index - this should not happen!" ) );
1296 }
1297 }
1298
1299 endRemoveRows();
1300
1301 return true;
1302}
1303
1304
1305void QgsRuleBasedRendererModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule )
1306{
1307 beginInsertRows( parent, before, before );
1308
1309 QgsDebugMsgLevel( QStringLiteral( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ), 2 );
1310
1312 parentRule->insertChild( before, newrule );
1313
1314 endInsertRows();
1315}
1316
1317void QgsRuleBasedRendererModel::updateRule( const QModelIndex &parent, int row )
1318{
1319 emit dataChanged( index( row, 0, parent ), index( row, columnCount( parent ), parent ) );
1320}
1321
1322void QgsRuleBasedRendererModel::updateRule( const QModelIndex &idx )
1323{
1324 emit dataChanged( index( 0, 0, idx ), index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1325
1326 for ( int i = 0; i < rowCount( idx ); i++ )
1327 {
1328 updateRule( index( i, 0, idx ) );
1329 }
1330}
1331
1332
1334{
1335 if ( !index.isValid() )
1336 return;
1337
1338 beginRemoveRows( index.parent(), index.row(), index.row() );
1339
1341 rule->parent()->removeChild( rule );
1342
1343 endRemoveRows();
1344}
1345
1346void QgsRuleBasedRendererModel::setSymbol( const QModelIndex &index, QgsSymbol *symbol )
1347{
1349 rule->setSymbol( symbol );
1350 emit dataChanged( index, index );
1351}
1352
1353void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int count )
1354{
1355 int row = rowCount( parent ); // only consider appending
1356 beginInsertRows( parent, row, row + count - 1 );
1357}
1358
1360{
1361 endInsertRows();
1362}
1363
1364void QgsRuleBasedRendererModel::setFeatureCounts( const QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> &countMap )
1365{
1366 mFeatureCountMap = countMap;
1367 updateRule( QModelIndex() );
1368}
1369
1371{
1372 mFeatureCountMap.clear();
1373 updateRule( QModelIndex() );
1374}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2196
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A widget for configuring a QgsCategorizedSymbolRenderer.
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...
A feature renderer which represents features using a list of renderer categories.
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.
Handles 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)
Fetch next feature and stores in f, returns true on success.
Abstract base class for all 2D vector feature renderers.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags 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.
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:58
A widget for configuring a QgsGraduatedSymbolRenderer.
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
Returns a list of all ranges used in the classification.
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:221
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:38
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
Base class for any widget that can be shown as an 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 ...
bool dockMode() const
Returns the dock mode state.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
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.
A container for the context for various read/write operations on 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.
A dialog for editing the details of a rule based renderer rule.
QgsRuleBasedRenderer::Rule * rule()
QgsRendererRulePropsDialog(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Constructor for QgsRendererRulePropsDialog.
A widget for editing the details of a rule based renderer rule.
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.
QAction * mPasteSymbolAction
Paste symbol action.
QgsRendererWidget(QgsVectorLayer *layer, QgsStyle *style)
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
Tree model for a rule-based renderer's rules.
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
QgsRuleBasedRendererModel(QgsRuleBasedRenderer *renderer, QObject *parent, QScreen *screen=nullptr)
Constructor for QgsRuleBasedRendererModel, for the specified renderer.
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
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)
std::unique_ptr< QgsRuleBasedRenderer > mRenderer
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.
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)
Represents an individual rule for a 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
const QgsRuleBasedRenderer::RuleList & children() const
Returns all children rules of this rule.
double maximumScale() const
Returns the maximum map scale (i.e.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
bool isElse() const
Check if this rule is an ELSE rule.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
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.
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
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.
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId=true)
Create a rule from an XML definition.
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
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, QgsScaleComboBox::RatioMode mode=QgsScaleComboBox::RatioMode::ForceUnitNumerator)
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.
Stores properties relating to a screen.
Stores settings for use within QGIS.
Definition qgssettings.h:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:88
static std::unique_ptr< QgsSymbol > symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
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.
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns an icon preview for a color ramp.
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.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:353
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
A QScrollArea subclass which only displays a vertical scrollbar and fits the width to the contents.
Represents a vector layer which manages a vector based dataset.
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:61
#define QgsDebugError(str)
Definition qgslogger.h:57
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:50
void _labeling2rendererRules(QDomElement &ruleElem)