QGIS API Documentation 3.41.0-Master (d5b93354e9c)
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#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 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
513 QModelIndex index;
514 if ( indexlist.isEmpty() )
515 index = mModel->index( mModel->rowCount(), 0 );
516 else
517 index = indexlist.first();
518 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
519}
520
522{
523 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
524 if ( !tempSymbol )
525 return;
526
527 const QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
528 for ( const QModelIndex &index : indexList )
529 {
530 if ( QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index ) )
531 {
532 if ( !rule->symbol() || rule->symbol()->type() != tempSymbol->type() )
533 continue;
534
535 mModel->setSymbol( index, tempSymbol->clone() );
536 }
537 }
538 emit widgetChanged();
539}
540
541void QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted( QgsPanelWidget *panel )
542{
543 QgsCategorizedSymbolRendererWidget *w = qobject_cast<QgsCategorizedSymbolRendererWidget *>( panel );
544
545 // create new rules
547 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
548 const auto constIndexList = indexList;
549 for ( const QModelIndex &index : constIndexList )
550 {
551 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
552 mModel->willAddRules( index, r->categories().count() );
554 }
556}
557
558void QgsRuleBasedRendererWidget::refineRuleRangesAccepted( QgsPanelWidget *panel )
559{
560 QgsGraduatedSymbolRendererWidget *w = qobject_cast<QgsGraduatedSymbolRendererWidget *>( panel );
561 // create new rules
563 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
564 const auto constIndexList = indexList;
565 for ( const QModelIndex &index : constIndexList )
566 {
567 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
568 mModel->willAddRules( index, r->ranges().count() );
570 }
572}
573
574void QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
575{
576 QgsRendererRulePropsWidget *widget = qobject_cast<QgsRendererRulePropsWidget *>( panel );
577 if ( !widget )
578 return;
579
580 widget->apply();
581
582 // model should know about the change and emit dataChanged signal for the view
583 QModelIndex index = viewRules->selectionModel()->currentIndex();
584 mModel->updateRule( index.parent(), index.row() );
586}
587
588void QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel()
589{
590 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
591}
592
593void QgsRuleBasedRendererWidget::showContextMenu( QPoint )
594{
595 mContextMenu->clear();
596 mContextMenu->addAction( mCopyAction );
597 mContextMenu->addAction( mPasteAction );
598
599 const QList<QAction *> actions = contextMenu->actions();
600 for ( QAction *act : actions )
601 {
602 mContextMenu->addAction( act );
603 }
604
605 mContextMenu->addMenu( mRefineMenu );
606
607 mContextMenu->exec( QCursor::pos() );
608}
609
610
612{
613 if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
614 {
615 return;
616 }
617 QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> countMap;
618
619 QgsRuleBasedRenderer::RuleList ruleList = mRenderer->rootRule()->descendants();
620 // insert all so that we have counts 0
621 const auto constRuleList = ruleList;
622 for ( QgsRuleBasedRenderer::Rule *rule : constRuleList )
623 {
624 countMap[rule].count = 0;
625 countMap[rule].duplicateCount = 0;
626 }
627
628 QgsRenderContext renderContext;
629 renderContext.setRendererScale( 0 ); // ignore scale
630
632
633 // additional scopes
634 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
635 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
636 {
637 context.appendScope( new QgsExpressionContextScope( scope ) );
638 }
639
640 renderContext.setExpressionContext( context );
641
642 mRenderer->startRender( renderContext, mLayer->fields() );
643 // QgsRuleBasedRenderer::filter must be called after startRender
646 req.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mLayer->fields() );
648
649 long long nFeatures = mLayer->featureCount();
650 QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, 100 );
651 p.setWindowModality( Qt::WindowModal );
652 long long featuresCounted = 0;
653
654 QgsFeature f;
655 while ( fit.nextFeature( f ) )
656 {
657 renderContext.expressionContext().setFeature( f );
658 QgsRuleBasedRenderer::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
659
660 const auto constFeatureRuleList = featureRuleList;
661 for ( QgsRuleBasedRenderer::Rule *rule : constFeatureRuleList )
662 {
663 countMap[rule].count++;
664 if ( featureRuleList.size() > 1 )
665 {
666 countMap[rule].duplicateCount++;
667 }
668 const auto constFeatureRuleList = featureRuleList;
669 for ( QgsRuleBasedRenderer::Rule *duplicateRule : constFeatureRuleList )
670 {
671 if ( duplicateRule == rule )
672 continue;
673 countMap[rule].duplicateCountMap[duplicateRule] += 1;
674 }
675 }
676 ++featuresCounted;
677 if ( featuresCounted % 50 == 0 )
678 {
679 if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
680 {
681 p.setMaximum( 0 );
682 }
683 p.setValue( static_cast<double>( featuresCounted ) / nFeatures * 100.0 );
684 if ( p.wasCanceled() )
685 {
686 return;
687 }
688 }
689 }
690 p.setValue( nFeatures );
691
692 mRenderer->stopRender( renderContext );
693
694#ifdef QGISDEBUG
695 const auto constKeys = countMap.keys();
696 for ( QgsRuleBasedRenderer::Rule *rule : constKeys )
697 {
698 QgsDebugMsgLevel( QStringLiteral( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ), 2 );
699 }
700#endif
701
702 mModel->setFeatureCounts( countMap );
703}
704
706{
707 bool enabled = !viewRules->selectionModel()->selectedIndexes().isEmpty();
708 btnRefineRule->setEnabled( enabled );
709 btnRemoveRule->setEnabled( enabled );
710}
711
713
715 : QgsPanelWidget( parent )
716 , mRule( rule )
717 , mLayer( layer )
718 , mContext( context )
719{
720 setupUi( this );
721 layout()->setContentsMargins( 0, 0, 0, 0 );
722
723 mElseRadio->setChecked( mRule->isElse() );
724 mFilterRadio->setChecked( !mRule->isElse() );
725 editFilter->setText( mRule->filterExpression() );
726 editFilter->setToolTip( mRule->filterExpression() );
727 editLabel->setText( mRule->label() );
728 editDescription->setText( mRule->description() );
729 editDescription->setToolTip( mRule->description() );
730
731 if ( mRule->dependsOnScale() )
732 {
733 groupScale->setChecked( true );
734 mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ), std::max( rule->maximumScale(), 0.0 ) );
735 }
736 mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
737
738 if ( mRule->symbol() )
739 {
740 groupSymbol->setChecked( true );
741 mSymbol = mRule->symbol()->clone(); // use a clone!
742 }
743 else
744 {
745 groupSymbol->setChecked( false );
747 }
748
753
754 QVBoxLayout *l = new QVBoxLayout;
755 l->addWidget( mSymbolSelector );
756 groupSymbol->setLayout( l );
757
758 connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
759 connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
760 connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
761 connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
762 connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
763 connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
764 connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
765 connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
766 connect( mFilterRadio, &QRadioButton::toggled, this, [=]( bool toggled ) { filterFrame->setEnabled( toggled ); } );
767 connect( mElseRadio, &QRadioButton::toggled, this, [=]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) ); } );
768}
769
770#include "qgsvscrollarea.h"
771
773 : QDialog( parent )
774{
775#ifdef Q_OS_MAC
776 setWindowModality( Qt::WindowModal );
777#endif
778
779 QVBoxLayout *layout = new QVBoxLayout( this );
780 QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
781 scrollArea->setFrameShape( QFrame::NoFrame );
782 layout->addWidget( scrollArea );
783
784 buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
785 mPropsWidget = new QgsRendererRulePropsWidget( rule, layer, style, this, context );
786
787 scrollArea->setWidget( mPropsWidget );
788 layout->addWidget( buttonBox );
789 this->setWindowTitle( "Edit Rule" );
791
792 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererRulePropsDialog::accept );
793 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
794 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRendererRulePropsDialog::showHelp );
795}
796
798{
799 mPropsWidget->testFilter();
800}
801
806
808{
809 mPropsWidget->apply();
810 QDialog::accept();
811}
812
813void QgsRendererRulePropsDialog::showHelp()
814{
815 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-rendering" ) );
816}
817
818
820{
822
823 // additional scopes
824 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
825 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
826 {
827 context.appendScope( new QgsExpressionContextScope( scope ) );
828 }
829
830 QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
831
832 if ( dlg.exec() )
833 editFilter->setText( dlg.expressionText() );
834}
835
837{
838 if ( !mFilterRadio->isChecked() )
839 return;
840
841 QgsExpression filter( editFilter->text() );
842 if ( filter.hasParserError() )
843 {
844 QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
845 return;
846 }
847
849
850 // additional scopes
851 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
852 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
853 {
854 context.appendScope( new QgsExpressionContextScope( scope ) );
855 }
856
857 if ( !filter.prepare( &context ) )
858 {
859 QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
860 return;
861 }
862
863 QApplication::setOverrideCursor( Qt::WaitCursor );
864
866
868
869 int count = 0;
870 QgsFeature f;
871 while ( fit.nextFeature( f ) )
872 {
873 count++;
874 }
875
876 QApplication::restoreOverrideCursor();
877
878 QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
879}
880
882{
883 QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text();
884 mRule->setFilterExpression( filter );
885 mRule->setLabel( editLabel->text() );
886 mRule->setDescription( editDescription->text() );
887 // caution: rule uses scale denom, scale widget uses true scales
888 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
889 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
890 mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
891}
892
898
900
901/*
902 setDragEnabled(true);
903 viewport()->setAcceptDrops(true);
904 setDropIndicatorShown(true);
905 setDragDropMode(QAbstractItemView::InternalMove);
906*/
907
909
911 : QAbstractItemModel( parent )
912 , mR( renderer )
913 , mScreen( screen )
914{
915}
916
917Qt::ItemFlags QgsRuleBasedRendererModel::flags( const QModelIndex &index ) const
918{
919 if ( !index.isValid() )
920 return Qt::ItemIsDropEnabled;
921
922 // allow drop only at first column
923 Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
924
925 Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
926
927 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | checkable | Qt::ItemIsDragEnabled | drop;
928}
929
930QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) const
931{
932 if ( !index.isValid() )
933 return QVariant();
934
936
937 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
938 {
939 switch ( index.column() )
940 {
941 case 0:
942 return rule->label();
943 case 1:
944 if ( rule->isElse() )
945 {
946 return "ELSE";
947 }
948 else
949 {
950 return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
951 }
952 case 2:
953 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
954 case 3:
955 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
956 case 4:
957 if ( mFeatureCountMap.count( rule ) == 1 )
958 {
959 return QVariant( mFeatureCountMap[rule].count );
960 }
961 return QVariant();
962 case 5:
963 if ( mFeatureCountMap.count( rule ) == 1 )
964 {
965 if ( role == Qt::DisplayRole )
966 {
967 return QVariant( mFeatureCountMap[rule].duplicateCount );
968 }
969 else // tooltip - detailed info about duplicates
970 {
971 if ( mFeatureCountMap[rule].duplicateCount > 0 )
972 {
973 QString tip = QStringLiteral( "<p style='margin:0px;'><ul>" );
974 const auto duplicateMap = mFeatureCountMap[rule].duplicateCountMap;
975 for ( auto it = duplicateMap.constBegin(); it != duplicateMap.constEnd(); ++it )
976 {
977 QString label = it.key()->label().replace( '&', QLatin1String( "&amp;" ) ).replace( '>', QLatin1String( "&gt;" ) ).replace( '<', QLatin1String( "&lt;" ) );
978 tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( it.value() ).arg( label );
979 }
980 tip += QLatin1String( "</ul>" );
981 return tip;
982 }
983 else
984 {
985 return 0;
986 }
987 }
988 }
989 return QVariant();
990 default:
991 return QVariant();
992 }
993 }
994 else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
995 {
996 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
997 return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( iconSize, iconSize ), 0, nullptr, QgsScreenProperties( mScreen.data() ) );
998 }
999 else if ( role == Qt::TextAlignmentRole )
1000 {
1001 return ( index.column() == 2 || index.column() == 3 ) ? static_cast<Qt::Alignment::Int>( Qt::AlignRight ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
1002 }
1003 else if ( role == Qt::FontRole && index.column() == 1 )
1004 {
1005 if ( rule->isElse() )
1006 {
1007 QFont italicFont;
1008 italicFont.setItalic( true );
1009 return italicFont;
1010 }
1011 return QVariant();
1012 }
1013 else if ( role == Qt::EditRole )
1014 {
1015 switch ( index.column() )
1016 {
1017 case 0:
1018 return rule->label();
1019 case 1:
1020 return rule->filterExpression();
1021 case 2:
1022 return rule->minimumScale();
1023 case 3:
1024 return rule->maximumScale();
1025 default:
1026 return QVariant();
1027 }
1028 }
1029 else if ( role == Qt::CheckStateRole )
1030 {
1031 if ( index.column() != 0 )
1032 return QVariant();
1033 return rule->active() ? Qt::Checked : Qt::Unchecked;
1034 }
1035 else
1036 return QVariant();
1037}
1038
1039QVariant QgsRuleBasedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
1040{
1041 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
1042 {
1043 QStringList lst;
1044 lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Count" ) << tr( "Duplicate Count" );
1045 return lst[section];
1046 }
1047 else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
1048 {
1049 if ( section == 4 ) // Count
1050 {
1051 return tr( "Number of features in this rule." );
1052 }
1053 else if ( section == 5 ) // Duplicate count
1054 {
1055 return tr( "Number of features in this rule which are also present in other rule(s)." );
1056 }
1057 }
1058
1059 return QVariant();
1060}
1061
1062int QgsRuleBasedRendererModel::rowCount( const QModelIndex &parent ) const
1063{
1064 if ( parent.column() > 0 )
1065 return 0;
1066
1068
1069 return parentRule->children().count();
1070}
1071
1072int QgsRuleBasedRendererModel::columnCount( const QModelIndex & ) const
1073{
1074 return 6;
1075}
1076
1077QModelIndex QgsRuleBasedRendererModel::index( int row, int column, const QModelIndex &parent ) const
1078{
1079 if ( hasIndex( row, column, parent ) )
1080 {
1082 QgsRuleBasedRenderer::Rule *childRule = parentRule->children()[row];
1083 return createIndex( row, column, childRule );
1084 }
1085 return QModelIndex();
1086}
1087
1088QModelIndex QgsRuleBasedRendererModel::parent( const QModelIndex &index ) const
1089{
1090 if ( !index.isValid() )
1091 return QModelIndex();
1092
1094 QgsRuleBasedRenderer::Rule *parentRule = childRule->parent();
1095
1096 if ( parentRule == mR->rootRule() )
1097 return QModelIndex();
1098
1099 // this is right: we need to know row number of our parent (in our grandparent)
1100 int row = parentRule->parent()->children().indexOf( parentRule );
1101
1102 return createIndex( row, 0, parentRule );
1103}
1104
1105bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
1106{
1107 if ( !index.isValid() )
1108 return false;
1109
1111
1112 if ( role == Qt::CheckStateRole )
1113 {
1114 rule->setActive( value.toInt() == Qt::Checked );
1115 emit dataChanged( index, index );
1116 return true;
1117 }
1118
1119 if ( role != Qt::EditRole )
1120 return false;
1121
1122 switch ( index.column() )
1123 {
1124 case 0: // label
1125 rule->setLabel( value.toString() );
1126 break;
1127 case 1: // filter
1128 rule->setFilterExpression( value.toString() );
1129 break;
1130 case 2: // scale min
1131 rule->setMinimumScale( value.toDouble() );
1132 break;
1133 case 3: // scale max
1134 rule->setMaximumScale( value.toDouble() );
1135 break;
1136 default:
1137 return false;
1138 }
1139
1140 emit dataChanged( index, index );
1141 return true;
1142}
1143
1145{
1146 return Qt::MoveAction; // | Qt::CopyAction
1147}
1148
1150{
1151 QStringList types;
1152 types << QStringLiteral( "application/vnd.text.list" );
1153 return types;
1154}
1155
1156QMimeData *QgsRuleBasedRendererModel::mimeData( const QModelIndexList &indexes ) const
1157{
1158 QMimeData *mimeData = new QMimeData();
1159 QByteArray encodedData;
1160
1161 QDataStream stream( &encodedData, QIODevice::WriteOnly );
1162
1163 const auto constIndexes = indexes;
1164 for ( const QModelIndex &index : constIndexes )
1165 {
1166 // each item consists of several columns - let's add it with just first one
1167 if ( !index.isValid() || index.column() != 0 )
1168 continue;
1169
1170 // we use a clone of the existing rule because it has a new unique rule key
1171 // non-unique rule keys would confuse other components using them (e.g. legend)
1173 QDomDocument doc;
1174 QgsSymbolMap symbols;
1175
1176 QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
1177 rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "renderer" ) ); // for determining whether rules are from renderer or labeling
1178 QDomElement rulesElem = rule->save( doc, symbols );
1179 rootElem.appendChild( rulesElem );
1180 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
1181 rootElem.appendChild( symbolsElem );
1182 doc.appendChild( rootElem );
1183
1184 delete rule;
1185
1186 stream << doc.toString( -1 );
1187 }
1188
1189 mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
1190 return mimeData;
1191}
1192
1193
1194// manipulate DOM before dropping it so that rules are more useful
1195void _labeling2rendererRules( QDomElement &ruleElem )
1196{
1197 // labeling rules recognize only "description"
1198 if ( ruleElem.hasAttribute( QStringLiteral( "description" ) ) )
1199 ruleElem.setAttribute( QStringLiteral( "label" ), ruleElem.attribute( QStringLiteral( "description" ) ) );
1200
1201 // run recursively
1202 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
1203 while ( !childRuleElem.isNull() )
1204 {
1205 _labeling2rendererRules( childRuleElem );
1206 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
1207 }
1208}
1209
1210
1211bool QgsRuleBasedRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1212{
1213 Q_UNUSED( column )
1214
1215 if ( action == Qt::IgnoreAction )
1216 return true;
1217
1218 if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
1219 return false;
1220
1221 if ( parent.column() > 0 )
1222 return false;
1223
1224 QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
1225 QDataStream stream( &encodedData, QIODevice::ReadOnly );
1226 int rows = 0;
1227
1228 if ( row == -1 )
1229 {
1230 // the item was dropped at a parent - we may decide where to put the items - let's append them
1231 row = rowCount( parent );
1232 }
1233
1234 while ( !stream.atEnd() )
1235 {
1236 QString text;
1237 stream >> text;
1238
1239 QDomDocument doc;
1240 if ( !doc.setContent( text ) )
1241 continue;
1242 QDomElement rootElem = doc.documentElement();
1243 if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
1244 continue;
1245 if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1246 rootElem.appendChild( doc.createElement( QStringLiteral( "symbols" ) ) );
1247 QDomElement symbolsElem = rootElem.firstChildElement( QStringLiteral( "symbols" ) );
1248 if ( symbolsElem.isNull() )
1249 continue;
1251 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
1252 if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "labeling" ) )
1253 _labeling2rendererRules( ruleElem );
1254 QgsRuleBasedRenderer::Rule *rule = QgsRuleBasedRenderer::Rule::create( ruleElem, symbolMap, false );
1255
1256 insertRule( parent, row + rows, rule );
1257
1258 ++rows;
1259 }
1260 return true;
1261}
1262
1264{
1265 if ( index.isValid() )
1266 return static_cast<QgsRuleBasedRenderer::Rule *>( index.internalPointer() );
1267 return mR->rootRule();
1268}
1269
1270bool QgsRuleBasedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
1271{
1273
1274 if ( row < 0 || row >= parentRule->children().count() )
1275 return false;
1276
1277 QgsDebugMsgLevel( QStringLiteral( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ), 2 );
1278
1279 beginRemoveRows( parent, row, row + count - 1 );
1280
1281 for ( int i = 0; i < count; i++ )
1282 {
1283 if ( row < parentRule->children().count() )
1284 {
1285 //QgsRuleBasedRenderer::Rule* r = parentRule->children()[row];
1286 parentRule->removeChildAt( row );
1287 //parentRule->takeChildAt( row );
1288 }
1289 else
1290 {
1291 QgsDebugError( QStringLiteral( "trying to remove invalid index - this should not happen!" ) );
1292 }
1293 }
1294
1295 endRemoveRows();
1296
1297 return true;
1298}
1299
1300
1301void QgsRuleBasedRendererModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule )
1302{
1303 beginInsertRows( parent, before, before );
1304
1305 QgsDebugMsgLevel( QStringLiteral( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ), 2 );
1306
1308 parentRule->insertChild( before, newrule );
1309
1310 endInsertRows();
1311}
1312
1313void QgsRuleBasedRendererModel::updateRule( const QModelIndex &parent, int row )
1314{
1315 emit dataChanged( index( row, 0, parent ), index( row, columnCount( parent ), parent ) );
1316}
1317
1318void QgsRuleBasedRendererModel::updateRule( const QModelIndex &idx )
1319{
1320 emit dataChanged( index( 0, 0, idx ), index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1321
1322 for ( int i = 0; i < rowCount( idx ); i++ )
1323 {
1324 updateRule( index( i, 0, idx ) );
1325 }
1326}
1327
1328
1329void QgsRuleBasedRendererModel::removeRule( const QModelIndex &index )
1330{
1331 if ( !index.isValid() )
1332 return;
1333
1334 beginRemoveRows( index.parent(), index.row(), index.row() );
1335
1337 rule->parent()->removeChild( rule );
1338
1339 endRemoveRows();
1340}
1341
1342void QgsRuleBasedRendererModel::setSymbol( const QModelIndex &index, QgsSymbol *symbol )
1343{
1345 rule->setSymbol( symbol );
1346 emit dataChanged( index, index );
1347}
1348
1349void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int count )
1350{
1351 int row = rowCount( parent ); // only consider appending
1352 beginInsertRows( parent, row, row + count - 1 );
1353}
1354
1356{
1357 endInsertRows();
1358}
1359
1360void QgsRuleBasedRendererModel::setFeatureCounts( const QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> &countMap )
1361{
1362 mFeatureCountMap = countMap;
1363 updateRule( QModelIndex() );
1364}
1365
1367{
1368 mFeatureCountMap.clear();
1369 updateRule( QModelIndex() );
1370}
@ 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.
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
A generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString evalErrorString() const
Returns evaluation error.
QString parserErrorString() const
Returns parser error.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
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.
This class 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
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
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Base class for any widget that can be shown as a inline panel.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void widgetChanged()
Emitted when the widget state changes.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
bool dockMode()
Returns the dock mode state.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
QgsRuleBasedRenderer::Rule * rule()
QgsRendererRulePropsDialog(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Constructor for QgsRendererRulePropsDialog.
void buildExpression()
Open the expression builder widget to check if the.
void setDockMode(bool dockMode) override
Set the widget in dock mode.
QgsRendererRulePropsWidget(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Widget to edit the details of a rule based renderer rule.
QgsSymbolSelectorWidget * mSymbolSelector
void apply()
Apply any changes from the widget to the set rule.
QgsRuleBasedRenderer::Rule * mRule
void testFilter()
Test the filter that is set in the widget.
QgsRuleBasedRenderer::Rule * rule()
Returns the current set rule.
Base class for renderer settings widgets.
QAction * mPasteSymbolAction
Paste symbol action.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsSymbolWidgetContext mContext
Context in which widget is shown.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
QgsVectorLayer * mLayer
QgsRuleBasedRenderer::Rule * ruleForIndex(const QModelIndex &index) const
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void updateRule(const QModelIndex &parent, int row)
void willAddRules(const QModelIndex &parent, int count)
Qt::ItemFlags flags(const QModelIndex &index) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setSymbol(const QModelIndex &index, QgsSymbol *symbol)
Sets the symbol for the rule at the specified index.
QStringList mimeTypes() const override
void removeRule(const QModelIndex &index)
int columnCount(const QModelIndex &=QModelIndex()) const override
Qt::DropActions supportedDropActions() const override
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)
This class keeps data about a rules for rule-based renderer.
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
void setDescription(const QString &description)
Set a human readable description for this rule.
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.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
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.
static 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:352
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
QgsVScrollArea is a QScrollArea subclass which only displays a vertical scrollbar and fits the width ...
Represents a vector layer which manages a vector based data sets.
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:39
#define QgsDebugError(str)
Definition qgslogger.h:38
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:48
void _labeling2rendererRules(QDomElement &ruleElem)