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