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