QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsrulebasedrendererwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrulebasedrendererwidget.cpp - Settings widget for rule-based renderer
3 ---------------------
4 begin : May 2010
5 copyright : (C) 2010 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsapplication.h"
19#include "qgsexpression.h"
20#include "qgsfeatureiterator.h"
21#include "qgsguiutils.h"
22#include "qgslogger.h"
23#include "qgsmapcanvas.h"
24#include "qgspanelwidget.h"
25#include "qgsreadwritecontext.h"
27#include "qgssettings.h"
28#include "qgssymbol.h"
29#include "qgssymbollayerutils.h"
31#include "qgsvectorlayer.h"
32
33#include <QClipboard>
34#include <QKeyEvent>
35#include <QMenu>
36#include <QMessageBox>
37#include <QPointer>
38#include <QProgressDialog>
39#include <QScreen>
40#include <QString>
41#include <QTreeWidgetItem>
42#include <QVBoxLayout>
43
44#include "moc_qgsrulebasedrendererwidget.cpp"
45
46using namespace Qt::StringLiterals;
47
48#ifdef ENABLE_MODELTEST
49#include "modeltest.h"
50#endif
51
52
57
59 : QgsRendererWidget( layer, style )
60 , mContextMenu( new QMenu( this ) )
61{
62 mRenderer = nullptr;
63 // try to recognize the previous renderer
64 // (null renderer means "no previous renderer")
65
66
67 if ( renderer )
68 {
70 }
71 if ( !mRenderer )
72 {
73 // some default options
74 QgsSymbol *symbol = QgsSymbol::defaultSymbol( mLayer->geometryType() );
75
76 mRenderer = std::make_unique<QgsRuleBasedRenderer>( symbol );
77 if ( renderer )
78 renderer->copyRendererData( mRenderer.get() );
79 }
80
81 setupUi( this );
82 this->layout()->setContentsMargins( 0, 0, 0, 0 );
83
84 mModel = new QgsRuleBasedRendererModel( mRenderer.get(), viewRules, screen() );
85#ifdef ENABLE_MODELTEST
86 new ModelTest( mModel, this ); // for model validity checking
87#endif
88 viewRules->setModel( mModel );
89
90 mDeleteAction = new QAction( tr( "Remove Rule" ), this );
91 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
92
93 viewRules->addAction( mCopyAction );
94 viewRules->addAction( mPasteAction );
95 viewRules->addAction( mDeleteAction );
96
97 mRefineMenu = new QMenu( tr( "Refine Current Rule" ), btnRefineRule );
98 mRefineMenu->addAction( tr( "Add Scales to Rule" ), this, &QgsRuleBasedRendererWidget::refineRuleScales );
99 mRefineMenu->addAction( tr( "Add Categories to Rule" ), this, &QgsRuleBasedRendererWidget::refineRuleCategories );
100 mRefineMenu->addAction( tr( "Add Ranges to Rule" ), this, &QgsRuleBasedRendererWidget::refineRuleRanges );
101 btnRefineRule->setMenu( mRefineMenu );
102
103 btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
104 btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
105 btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
106
107 connect( viewRules, &QAbstractItemView::doubleClicked, this, static_cast<void ( QgsRuleBasedRendererWidget::* )( const QModelIndex &index )>( &QgsRuleBasedRendererWidget::editRule ) );
108
109 // support for context menu (now handled generically)
110 connect( viewRules, &QWidget::customContextMenuRequested, this, &QgsRuleBasedRendererWidget::showContextMenu );
111
112 connect( viewRules->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsRuleBasedRendererWidget::currentRuleChanged );
113 connect( viewRules->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsRuleBasedRendererWidget::selectedRulesChanged );
114
115 connect( btnAddRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::addRule );
116 connect( btnEditRule, &QAbstractButton::clicked, this, static_cast<void ( QgsRuleBasedRendererWidget::* )()>( &QgsRuleBasedRendererWidget::editRule ) );
117 connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::removeRule );
118 connect( mDeleteAction, &QAction::triggered, this, &QgsRuleBasedRendererWidget::removeRule );
119 connect( btnCountFeatures, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::countFeatures );
120
121 connect( btnRenderingOrder, &QAbstractButton::clicked, this, &QgsRuleBasedRendererWidget::setRenderingOrder );
122
123 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
124 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsPanelWidget::widgetChanged );
125 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsPanelWidget::widgetChanged );
126
129
130 // store/restore header section widths
131 connect( viewRules->header(), &QHeaderView::sectionResized, this, &QgsRuleBasedRendererWidget::saveSectionWidth );
132
134
135 connect( mContextMenu, &QMenu::aboutToShow, this, [this] {
136 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
137 mPasteSymbolAction->setEnabled( static_cast<bool>( tempSymbol ) );
138 } );
139}
140
145
150
152{
153 if ( dockMode )
154 {
155 // when in dock mode, these shortcuts conflict with the main window shortcuts and cannot be used
156 if ( mDeleteAction )
157 mDeleteAction->setShortcut( QKeySequence() );
158 }
160}
161
163{
164 QgsSymbol *s = QgsSymbol::defaultSymbol( mLayer->geometryType() );
166
168 if ( current )
169 {
170 // add after this rule
171 QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
172 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
173 QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
174 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
175 }
176 else
177 {
178 // append to root rule
179 int rows = mModel->rowCount();
180 mModel->insertRule( QModelIndex(), rows, newrule );
181 QModelIndex newindex = mModel->index( rows, 0 );
182 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
183 }
184 editRule();
185}
186
188{
189 QItemSelectionModel *sel = viewRules->selectionModel();
190 QModelIndex idx = sel->currentIndex();
191 if ( !idx.isValid() )
192 return nullptr;
193 return mModel->ruleForIndex( idx );
194}
195
197{
198 editRule( viewRules->selectionModel()->currentIndex() );
199}
200
201void QgsRuleBasedRendererWidget::editRule( const QModelIndex &index )
202{
203 if ( !index.isValid() )
204 return;
205
206 QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index );
208
209 if ( panel && panel->dockMode() )
210 {
211 QgsRendererRulePropsWidget *widget = new QgsRendererRulePropsWidget( rule, mLayer, mStyle, this, mContext ); //panel?
212 widget->setPanelTitle( tr( "Edit Rule" ) );
213 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted );
214 connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel );
215 openPanel( widget );
216 return;
217 }
218
220 if ( dlg.exec() )
221 {
222 mModel->updateRule( index.parent(), index.row() );
223 mModel->clearFeatureCounts();
224 emit widgetChanged();
225 }
226}
227
229{
230 QItemSelection sel = viewRules->selectionModel()->selection();
231 QgsDebugMsgLevel( u"REMOVE RULES!!! ranges: %1"_s.arg( sel.count() ), 2 );
232 const auto constSel = sel;
233 for ( const QItemSelectionRange &range : constSel )
234 {
235 QgsDebugMsgLevel( u"RANGE: r %1 - %2"_s.arg( range.top() ).arg( range.bottom() ), 2 );
236 if ( range.isValid() )
237 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
238 }
239 // make sure that the selection is gone
240 viewRules->selectionModel()->clear();
241 mModel->clearFeatureCounts();
242}
243
244void QgsRuleBasedRendererWidget::currentRuleChanged( const QModelIndex &current, const QModelIndex &previous )
245{
246 Q_UNUSED( previous )
247 btnEditRule->setEnabled( current.isValid() );
248}
249
250
256#include <QDialogButtonBox>
257#include <QInputDialog>
258#include <QClipboard>
259
261{
262 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
263
264 if ( indexlist.isEmpty() )
265 return;
266
267
268 if ( type == 0 ) // categories
270 else if ( type == 1 ) // ranges
272 else // scales
273 refineRuleScalesGui( indexlist );
274
275 // TODO: set initial rule's symbol to NULL (?)
276
277 // show the newly added rules
278 const auto constIndexlist = indexlist;
279 for ( const QModelIndex &index : constIndexlist )
280 viewRules->expand( index );
281}
282
287
292
297
299{
301 w->setPanelTitle( tr( "Add Categories to Rules" ) );
302 connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted );
303 w->setContext( mContext );
304 openPanel( w );
305}
306
308{
310 w->setPanelTitle( tr( "Add Ranges to Rules" ) );
311 connect( w, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedRendererWidget::refineRuleRangesAccepted );
312 w->setContext( mContext );
313 openPanel( w );
314}
315
316void QgsRuleBasedRendererWidget::refineRuleScalesGui( const QModelIndexList &indexList )
317{
318 for ( const QModelIndex &index : indexList )
319 {
320 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
321
322 // If any of the rules don't have a symbol let the user know and exit.
323 if ( !initialRule->symbol() )
324 {
325 QMessageBox::warning( this, tr( "Scale Refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
326 return;
327 }
328 }
329
330 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):" ) );
331 if ( txt.isEmpty() )
332 return;
333
334 QList<int> scales;
335 bool ok;
336 const auto constSplit = txt.split( ',' );
337 for ( const QString &item : constSplit )
338 {
339 int scale = item.toInt( &ok );
340 if ( ok )
341 scales.append( scale );
342 else
343 QMessageBox::information( this, tr( "Scale Refinement" ), tr( "\"%1\" is not valid scale denominator, ignoring it." ).arg( item ) );
344 }
345
346 for ( const QModelIndex &index : indexList )
347 {
348 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
349 mModel->willAddRules( index, scales.count() + 1 );
350 QgsRuleBasedRenderer::refineRuleScales( initialRule, scales );
351 }
352 mModel->finishedAddingRules();
353}
354
355void QgsRuleBasedRendererWidget::setSymbolLevels( const QList<QgsLegendSymbolItem> &levels, bool )
356{
357 if ( !mRenderer )
358 return;
359
360 for ( const QgsLegendSymbolItem &legendSymbol : std::as_const( levels ) )
361 {
362 QgsSymbol *sym = legendSymbol.symbol();
363 for ( int layer = 0; layer < sym->symbolLayerCount(); layer++ )
364 {
365 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->clone() );
366 }
367 }
368
369 emit widgetChanged();
370}
371
373{
374 QList<QgsSymbol *> symbolList;
375
376 if ( !mRenderer )
377 {
378 return symbolList;
379 }
380
381 QItemSelection sel = viewRules->selectionModel()->selection();
382 const auto constSel = sel;
383 for ( const QItemSelectionRange &range : constSel )
384 {
385 QModelIndex parent = range.parent();
386 QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
387 const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
388 for ( int row = range.top(); row <= range.bottom(); row++ )
389 {
390 symbolList.append( children.at( row )->symbol() );
391 }
392 }
393
394 return symbolList;
395}
396
398{
400 QItemSelection sel = viewRules->selectionModel()->selection();
401 const auto constSel = sel;
402 for ( const QItemSelectionRange &range : constSel )
403 {
404 QModelIndex parent = range.parent();
405 QgsRuleBasedRenderer::Rule *parentRule = mModel->ruleForIndex( parent );
406 const QgsRuleBasedRenderer::RuleList &children = parentRule->children();
407 for ( int row = range.top(); row <= range.bottom(); row++ )
408 {
409 rl.append( children.at( row )->clone() );
410 }
411 }
412 return rl;
413}
414
416{
417 // TODO: model/view
418 /*
419 if ( treeRules )
420 {
421 treeRules->populateRules();
422 }
423 */
424 emit widgetChanged();
425}
426
428{
429 if ( !event )
430 {
431 return;
432 }
433
434 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
435 {
436 qDeleteAll( mCopyBuffer );
437 mCopyBuffer.clear();
439 }
440 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
441 {
442 QgsRuleBasedRenderer::RuleList::const_iterator rIt = mCopyBuffer.constBegin();
443 for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
444 {
445 int rows = mModel->rowCount();
446 mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
447 }
448 }
449}
450
452
454{
456 if ( panel && panel->dockMode() )
457 {
458 QgsSymbolLevelsWidget *widget = new QgsSymbolLevelsWidget( mRenderer.get(), true, panel );
459 widget->setForceOrderingEnabled( true );
460 widget->setPanelTitle( tr( "Symbol Levels" ) );
461 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget]() { setSymbolLevels( widget->symbolLevels(), widget->usingLevels() ); } );
462 panel->openPanel( widget );
463 }
464 else
465 {
466 QgsSymbolLevelsDialog dlg( mRenderer.get(), true, panel );
467 dlg.setForceOrderingEnabled( true );
468 if ( dlg.exec() )
469 {
471 }
472 }
473}
474
475void QgsRuleBasedRendererWidget::saveSectionWidth( int section, int oldSize, int newSize )
476{
477 Q_UNUSED( oldSize )
478 // skip last section, as it stretches
479 if ( section == 5 )
480 return;
481 QgsSettings settings;
482 QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
483 settings.setValue( path, newSize );
484}
485
487{
488 QgsSettings settings;
489 QString path = u"/Windows/RuleBasedTree/sectionWidth/"_s;
490 QHeaderView *head = viewRules->header();
491 head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
492 head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
493 head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
494 head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
495 head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
496 head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
497}
498
500{
501 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
502 QgsDebugMsgLevel( QString::number( indexlist.count() ), 2 );
503
504 if ( indexlist.isEmpty() )
505 return;
506
507 QMimeData *mime = mModel->mimeData( indexlist );
508 QApplication::clipboard()->setMimeData( mime );
509}
510
512{
513 const QMimeData *mime = QApplication::clipboard()->mimeData();
514 if ( !mime )
515 return;
516
517 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
518 QModelIndex index;
519 if ( indexlist.isEmpty() )
520 index = mModel->index( mModel->rowCount(), 0 );
521 else
522 index = indexlist.first();
523 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
524}
525
527{
528 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
529 if ( !tempSymbol )
530 return;
531
532 const QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
533 for ( const QModelIndex &index : indexList )
534 {
535 if ( QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index ) )
536 {
537 if ( !rule->symbol() || rule->symbol()->type() != tempSymbol->type() )
538 continue;
539
540 mModel->setSymbol( index, tempSymbol->clone() );
541 }
542 }
543 emit widgetChanged();
544}
545
546void QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted( QgsPanelWidget *panel )
547{
548 QgsCategorizedSymbolRendererWidget *w = qobject_cast<QgsCategorizedSymbolRendererWidget *>( panel );
549
550 // create new rules
552 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
553 const auto constIndexList = indexList;
554 for ( const QModelIndex &index : constIndexList )
555 {
556 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
557 mModel->willAddRules( index, r->categories().count() );
559 }
560 mModel->finishedAddingRules();
561}
562
563void QgsRuleBasedRendererWidget::refineRuleRangesAccepted( QgsPanelWidget *panel )
564{
565 QgsGraduatedSymbolRendererWidget *w = qobject_cast<QgsGraduatedSymbolRendererWidget *>( panel );
566 // create new rules
567 QgsGraduatedSymbolRenderer *r = static_cast<QgsGraduatedSymbolRenderer *>( w->renderer() );
568 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
569 const auto constIndexList = indexList;
570 for ( const QModelIndex &index : constIndexList )
571 {
572 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
573 mModel->willAddRules( index, r->ranges().count() );
575 }
576 mModel->finishedAddingRules();
577}
578
579void QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
580{
581 QgsRendererRulePropsWidget *widget = qobject_cast<QgsRendererRulePropsWidget *>( panel );
582 if ( !widget )
583 return;
584
585 widget->apply();
586
587 // model should know about the change and emit dataChanged signal for the view
588 QModelIndex index = viewRules->selectionModel()->currentIndex();
589 mModel->updateRule( index.parent(), index.row() );
590 mModel->clearFeatureCounts();
591}
592
593void QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel()
594{
595 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
596}
597
598void QgsRuleBasedRendererWidget::showContextMenu( QPoint )
599{
600 mContextMenu->clear();
601 mContextMenu->addAction( mCopyAction );
602 mContextMenu->addAction( mPasteAction );
603
604 const QList<QAction *> actions = contextMenu->actions();
605 for ( QAction *act : actions )
606 {
607 mContextMenu->addAction( act );
608 }
609
610 mContextMenu->addMenu( mRefineMenu );
611
612 mContextMenu->exec( QCursor::pos() );
613}
614
615
617{
618 if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
619 {
620 return;
621 }
622 QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> countMap;
623
624 QgsRuleBasedRenderer::RuleList ruleList = mRenderer->rootRule()->descendants();
625 // insert all so that we have counts 0
626 const auto constRuleList = ruleList;
627 for ( QgsRuleBasedRenderer::Rule *rule : constRuleList )
628 {
629 countMap[rule].count = 0;
630 countMap[rule].duplicateCount = 0;
631 }
632
633 QgsRenderContext renderContext;
634 renderContext.setRendererScale( 0 ); // ignore scale
635
636 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
637
638 // additional scopes
639 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
640 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
641 {
642 context.appendScope( new QgsExpressionContextScope( scope ) );
643 }
644
645 renderContext.setExpressionContext( context );
646
647 mRenderer->startRender( renderContext, mLayer->fields() );
648 // QgsRuleBasedRenderer::filter must be called after startRender
651 req.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mLayer->fields() );
652 QgsFeatureIterator fit = mLayer->getFeatures( req );
653
654 long long nFeatures = mLayer->featureCount();
655 QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, 100 );
656 p.setWindowModality( Qt::WindowModal );
657 long long featuresCounted = 0;
658
659 QgsFeature f;
660 while ( fit.nextFeature( f ) )
661 {
662 renderContext.expressionContext().setFeature( f );
663 QgsRuleBasedRenderer::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
664
665 const auto constFeatureRuleList = featureRuleList;
666 for ( QgsRuleBasedRenderer::Rule *rule : constFeatureRuleList )
667 {
668 countMap[rule].count++;
669 if ( featureRuleList.size() > 1 )
670 {
671 countMap[rule].duplicateCount++;
672 }
673 const auto constFeatureRuleList = featureRuleList;
674 for ( QgsRuleBasedRenderer::Rule *duplicateRule : constFeatureRuleList )
675 {
676 if ( duplicateRule == rule )
677 continue;
678 countMap[rule].duplicateCountMap[duplicateRule] += 1;
679 }
680 }
681 ++featuresCounted;
682 if ( featuresCounted % 50 == 0 )
683 {
684 if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
685 {
686 p.setMaximum( 0 );
687 }
688 p.setValue( static_cast<double>( featuresCounted ) / nFeatures * 100.0 );
689 if ( p.wasCanceled() )
690 {
691 return;
692 }
693 }
694 }
695 p.setValue( nFeatures );
696
697 mRenderer->stopRender( renderContext );
698
699#ifdef QGISDEBUG
700 const auto constKeys = countMap.keys();
701 for ( QgsRuleBasedRenderer::Rule *rule : constKeys )
702 {
703 QgsDebugMsgLevel( u"rule: %1 count %2"_s.arg( rule->label() ).arg( countMap[rule].count ), 2 );
704 }
705#endif
706
707 mModel->setFeatureCounts( countMap );
708}
709
711{
712 bool enabled = !viewRules->selectionModel()->selectedIndexes().isEmpty();
713 btnRefineRule->setEnabled( enabled );
714 btnRemoveRule->setEnabled( enabled );
715}
716
718
720 : QgsPanelWidget( parent )
721 , mRule( rule )
722 , mLayer( layer )
723 , mContext( context )
724{
725 setupUi( this );
726 layout()->setContentsMargins( 0, 0, 0, 0 );
727
728 mElseRadio->setChecked( mRule->isElse() );
729 mFilterRadio->setChecked( !mRule->isElse() );
730 editFilter->setText( mRule->filterExpression() );
731 editFilter->setToolTip( mRule->filterExpression() );
732 editLabel->setText( mRule->label() );
733 editDescription->setText( mRule->description() );
734 editDescription->setToolTip( mRule->description() );
735
736 if ( mRule->dependsOnScale() )
737 {
738 groupScale->setChecked( true );
739 mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ), std::max( rule->maximumScale(), 0.0 ) );
740 }
741 mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
742
743 if ( mRule->symbol() )
744 {
745 groupSymbol->setChecked( true );
746 mSymbol = mRule->symbol()->clone(); // use a clone!
747 }
748 else
749 {
750 groupSymbol->setChecked( false );
751 mSymbol = QgsSymbol::defaultSymbol( mLayer->geometryType() );
752 }
753
755 mSymbolSelector->setContext( mContext );
758
759 QVBoxLayout *l = new QVBoxLayout;
760 l->addWidget( mSymbolSelector );
761 groupSymbol->setLayout( l );
762
763 connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
764 connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
765 connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
766 connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
767 connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
768 connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
769 connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
770 connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
771 connect( mFilterRadio, &QRadioButton::toggled, this, [this]( bool toggled ) { filterFrame->setEnabled( toggled ); } );
772 connect( mElseRadio, &QRadioButton::toggled, this, [this]( bool toggled ) {
773 if ( toggled )
774 editFilter->setText( u"ELSE"_s );
775 } );
776}
777
778#include "qgsvscrollarea.h"
779
781 : QDialog( parent )
782{
783#ifdef Q_OS_MAC
784 setWindowModality( Qt::WindowModal );
785#endif
786
787 QVBoxLayout *layout = new QVBoxLayout( this );
788 QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
789 scrollArea->setFrameShape( QFrame::NoFrame );
790 layout->addWidget( scrollArea );
791
792 buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
793 mPropsWidget = new QgsRendererRulePropsWidget( rule, layer, style, this, context );
794
795 scrollArea->setWidget( mPropsWidget );
796 layout->addWidget( buttonBox );
797 this->setWindowTitle( "Edit Rule" );
799
800 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererRulePropsDialog::accept );
801 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
802 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRendererRulePropsDialog::showHelp );
803}
804
806{
807 mPropsWidget->testFilter();
808}
809
811{
812 mPropsWidget->buildExpression();
813}
814
816{
817 mPropsWidget->apply();
818 QDialog::accept();
819}
820
821void QgsRendererRulePropsDialog::showHelp()
822{
823 QgsHelp::openHelp( u"working_with_vector/vector_properties.html#rule-based-rendering"_s );
824}
825
826
828{
829 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
830
831 // additional scopes
832 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
833 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
834 {
835 context.appendScope( new QgsExpressionContextScope( scope ) );
836 }
837
838 QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, u"generic"_s, context );
839
840 if ( dlg.exec() )
841 editFilter->setText( dlg.expressionText() );
842}
843
845{
846 if ( !mFilterRadio->isChecked() )
847 return;
848
849 QgsExpression filter( editFilter->text() );
850 if ( filter.hasParserError() )
851 {
852 QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
853 return;
854 }
855
856 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
857
858 // additional scopes
859 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
860 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
861 {
862 context.appendScope( new QgsExpressionContextScope( scope ) );
863 }
864
865 if ( !filter.prepare( &context ) )
866 {
867 QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
868 return;
869 }
870
871 QApplication::setOverrideCursor( Qt::WaitCursor );
872
874
875 QgsFeatureIterator fit = mLayer->getFeatures( req );
876
877 int count = 0;
878 QgsFeature f;
879 while ( fit.nextFeature( f ) )
880 {
881 count++;
882 }
883
884 QApplication::restoreOverrideCursor();
885
886 QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
887}
888
890{
891 QString filter = mElseRadio->isChecked() ? u"ELSE"_s : editFilter->text();
892 mRule->setFilterExpression( filter );
893 mRule->setLabel( editLabel->text() );
894 mRule->setDescription( editDescription->text() );
895 // caution: rule uses scale denom, scale widget uses true scales
896 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
897 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
898 mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
899}
900
906
908
909/*
910 setDragEnabled(true);
911 viewport()->setAcceptDrops(true);
912 setDropIndicatorShown(true);
913 setDragDropMode(QAbstractItemView::InternalMove);
914*/
915
917
919 : QAbstractItemModel( parent )
920 , mR( renderer )
921 , mScreen( screen )
922{}
923
924Qt::ItemFlags QgsRuleBasedRendererModel::flags( const QModelIndex &index ) const
925{
926 if ( !index.isValid() )
927 return Qt::ItemIsDropEnabled;
928
929 // allow drop only at first column
930 Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
931
932 Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
933
934 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | checkable | Qt::ItemIsDragEnabled | drop;
935}
936
937QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) const
938{
939 if ( !index.isValid() )
940 return QVariant();
941
943
944 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
945 {
946 switch ( index.column() )
947 {
948 case 0:
949 return rule->label();
950 case 1:
951 if ( rule->isElse() )
952 {
953 return "ELSE";
954 }
955 else
956 {
957 return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
958 }
959 case 2:
960 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
961 case 3:
962 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
963 case 4:
964 if ( mFeatureCountMap.count( rule ) == 1 )
965 {
966 return QVariant( mFeatureCountMap[rule].count );
967 }
968 return QVariant();
969 case 5:
970 if ( mFeatureCountMap.count( rule ) == 1 )
971 {
972 if ( role == Qt::DisplayRole )
973 {
974 return QVariant( mFeatureCountMap[rule].duplicateCount );
975 }
976 else // tooltip - detailed info about duplicates
977 {
978 if ( mFeatureCountMap[rule].duplicateCount > 0 )
979 {
980 QString tip = u"<p style='margin:0px;'><ul>"_s;
981 const auto duplicateMap = mFeatureCountMap[rule].duplicateCountMap;
982 for ( auto it = duplicateMap.constBegin(); it != duplicateMap.constEnd(); ++it )
983 {
984 QString label = it.key()->label().replace( '&', "&amp;"_L1 ).replace( '>', "&gt;"_L1 ).replace( '<', "&lt;"_L1 );
985 tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( it.value() ).arg( label );
986 }
987 tip += "</ul>"_L1;
988 return tip;
989 }
990 else
991 {
992 return 0;
993 }
994 }
995 }
996 return QVariant();
997 default:
998 return QVariant();
999 }
1000 }
1001 else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
1002 {
1003 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
1004 return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( iconSize, iconSize ), 0, nullptr, QgsScreenProperties( mScreen.data() ) );
1005 }
1006 else if ( role == Qt::TextAlignmentRole )
1007 {
1008 return ( index.column() == 2 || index.column() == 3 ) ? static_cast<Qt::Alignment::Int>( Qt::AlignRight ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
1009 }
1010 else if ( role == Qt::FontRole && index.column() == 1 )
1011 {
1012 if ( rule->isElse() )
1013 {
1014 QFont italicFont;
1015 italicFont.setItalic( true );
1016 return italicFont;
1017 }
1018 return QVariant();
1019 }
1020 else if ( role == Qt::EditRole )
1021 {
1022 switch ( index.column() )
1023 {
1024 case 0:
1025 return rule->label();
1026 case 1:
1027 return rule->filterExpression();
1028 case 2:
1029 return rule->minimumScale();
1030 case 3:
1031 return rule->maximumScale();
1032 default:
1033 return QVariant();
1034 }
1035 }
1036 else if ( role == Qt::CheckStateRole )
1037 {
1038 if ( index.column() != 0 )
1039 return QVariant();
1040 return rule->active() ? Qt::Checked : Qt::Unchecked;
1041 }
1042 else
1043 return QVariant();
1044}
1045
1046QVariant QgsRuleBasedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
1047{
1048 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
1049 {
1050 QStringList lst;
1051 lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Count" ) << tr( "Duplicate Count" );
1052 return lst[section];
1053 }
1054 else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
1055 {
1056 if ( section == 4 ) // Count
1057 {
1058 return tr( "Number of features in this rule." );
1059 }
1060 else if ( section == 5 ) // Duplicate count
1061 {
1062 return tr( "Number of features in this rule which are also present in other rule(s)." );
1063 }
1064 }
1065
1066 return QVariant();
1067}
1068
1069int QgsRuleBasedRendererModel::rowCount( const QModelIndex &parent ) const
1070{
1071 if ( parent.column() > 0 )
1072 return 0;
1073
1075
1076 return parentRule->children().count();
1077}
1078
1079int QgsRuleBasedRendererModel::columnCount( const QModelIndex & ) const
1080{
1081 return 6;
1082}
1083
1084QModelIndex QgsRuleBasedRendererModel::index( int row, int column, const QModelIndex &parent ) const
1085{
1086 if ( hasIndex( row, column, parent ) )
1087 {
1089 QgsRuleBasedRenderer::Rule *childRule = parentRule->children()[row];
1090 return createIndex( row, column, childRule );
1091 }
1092 return QModelIndex();
1093}
1094
1095QModelIndex QgsRuleBasedRendererModel::parent( const QModelIndex &index ) const
1096{
1097 if ( !index.isValid() )
1098 return QModelIndex();
1099
1101 QgsRuleBasedRenderer::Rule *parentRule = childRule->parent();
1102
1103 if ( parentRule == mR->rootRule() )
1104 return QModelIndex();
1105
1106 // this is right: we need to know row number of our parent (in our grandparent)
1107 int row = parentRule->parent()->children().indexOf( parentRule );
1108
1109 return createIndex( row, 0, parentRule );
1110}
1111
1112bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
1113{
1114 if ( !index.isValid() )
1115 return false;
1116
1118
1119 if ( role == Qt::CheckStateRole )
1120 {
1121 rule->setActive( value.toInt() == Qt::Checked );
1122 emit dataChanged( index, index );
1123 return true;
1124 }
1125
1126 if ( role != Qt::EditRole )
1127 return false;
1128
1129 switch ( index.column() )
1130 {
1131 case 0: // label
1132 rule->setLabel( value.toString() );
1133 break;
1134 case 1: // filter
1135 rule->setFilterExpression( value.toString() );
1136 break;
1137 case 2: // scale min
1138 rule->setMinimumScale( value.toDouble() );
1139 break;
1140 case 3: // scale max
1141 rule->setMaximumScale( value.toDouble() );
1142 break;
1143 default:
1144 return false;
1145 }
1146
1147 emit dataChanged( index, index );
1148 return true;
1149}
1150
1152{
1153 return Qt::MoveAction; // | Qt::CopyAction
1154}
1155
1157{
1158 QStringList types;
1159 types << u"application/vnd.text.list"_s;
1160 return types;
1161}
1162
1163QMimeData *QgsRuleBasedRendererModel::mimeData( const QModelIndexList &indexes ) const
1164{
1165 QMimeData *mimeData = new QMimeData();
1166 QByteArray encodedData;
1167
1168 QDataStream stream( &encodedData, QIODevice::WriteOnly );
1169
1170 const auto constIndexes = indexes;
1171 for ( const QModelIndex &index : constIndexes )
1172 {
1173 // each item consists of several columns - let's add it with just first one
1174 if ( !index.isValid() || index.column() != 0 )
1175 continue;
1176
1177 // we use a clone of the existing rule because it has a new unique rule key
1178 // non-unique rule keys would confuse other components using them (e.g. legend)
1180 QDomDocument doc;
1181 QgsSymbolMap symbols;
1182
1183 QDomElement rootElem = doc.createElement( u"rule_mime"_s );
1184 rootElem.setAttribute( u"type"_s, u"renderer"_s ); // for determining whether rules are from renderer or labeling
1185 QDomElement rulesElem = rule->save( doc, symbols );
1186 rootElem.appendChild( rulesElem );
1187 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, u"symbols"_s, doc, QgsReadWriteContext() );
1188 rootElem.appendChild( symbolsElem );
1189 doc.appendChild( rootElem );
1190
1191 delete rule;
1192
1193 stream << doc.toString( -1 );
1194 }
1195
1196 mimeData->setData( u"application/vnd.text.list"_s, encodedData );
1197 return mimeData;
1198}
1199
1200
1201// manipulate DOM before dropping it so that rules are more useful
1202void _labeling2rendererRules( QDomElement &ruleElem )
1203{
1204 // labeling rules recognize only "description"
1205 if ( ruleElem.hasAttribute( u"description"_s ) )
1206 ruleElem.setAttribute( u"label"_s, ruleElem.attribute( u"description"_s ) );
1207
1208 // run recursively
1209 QDomElement childRuleElem = ruleElem.firstChildElement( u"rule"_s );
1210 while ( !childRuleElem.isNull() )
1211 {
1212 _labeling2rendererRules( childRuleElem );
1213 childRuleElem = childRuleElem.nextSiblingElement( u"rule"_s );
1214 }
1215}
1216
1217
1218bool QgsRuleBasedRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1219{
1220 Q_UNUSED( column )
1221
1222 if ( action == Qt::IgnoreAction )
1223 return true;
1224
1225 if ( !data->hasFormat( u"application/vnd.text.list"_s ) )
1226 return false;
1227
1228 if ( parent.column() > 0 )
1229 return false;
1230
1231 QByteArray encodedData = data->data( u"application/vnd.text.list"_s );
1232 QDataStream stream( &encodedData, QIODevice::ReadOnly );
1233 int rows = 0;
1234
1235 if ( row == -1 )
1236 {
1237 // the item was dropped at a parent - we may decide where to put the items - let's append them
1238 row = rowCount( parent );
1239 }
1240
1241 while ( !stream.atEnd() )
1242 {
1243 QString text;
1244 stream >> text;
1245
1246 QDomDocument doc;
1247 if ( !doc.setContent( text ) )
1248 continue;
1249 QDomElement rootElem = doc.documentElement();
1250 if ( rootElem.tagName() != "rule_mime"_L1 )
1251 continue;
1252 if ( rootElem.attribute( u"type"_s ) == "labeling"_L1 )
1253 rootElem.appendChild( doc.createElement( u"symbols"_s ) );
1254 QDomElement symbolsElem = rootElem.firstChildElement( u"symbols"_s );
1255 if ( symbolsElem.isNull() )
1256 continue;
1258 QDomElement ruleElem = rootElem.firstChildElement( u"rule"_s );
1259 if ( rootElem.attribute( u"type"_s ) == "labeling"_L1 )
1260 _labeling2rendererRules( ruleElem );
1261 QgsRuleBasedRenderer::Rule *rule = QgsRuleBasedRenderer::Rule::create( ruleElem, symbolMap, false );
1262
1263 insertRule( parent, row + rows, rule );
1264
1265 ++rows;
1266 }
1267 return true;
1268}
1269
1271{
1272 if ( index.isValid() )
1273 return static_cast<QgsRuleBasedRenderer::Rule *>( index.internalPointer() );
1274 return mR->rootRule();
1275}
1276
1277bool QgsRuleBasedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
1278{
1280
1281 if ( row < 0 || row >= parentRule->children().count() )
1282 return false;
1283
1284 QgsDebugMsgLevel( u"Called: row %1 count %2 parent ~~%3~~"_s.arg( row ).arg( count ).arg( parentRule->dump() ), 2 );
1285
1286 beginRemoveRows( parent, row, row + count - 1 );
1287
1288 for ( int i = 0; i < count; i++ )
1289 {
1290 if ( row < parentRule->children().count() )
1291 {
1292 //QgsRuleBasedRenderer::Rule* r = parentRule->children()[row];
1293 parentRule->removeChildAt( row );
1294 //parentRule->takeChildAt( row );
1295 }
1296 else
1297 {
1298 QgsDebugError( u"trying to remove invalid index - this should not happen!"_s );
1299 }
1300 }
1301
1302 endRemoveRows();
1303
1304 return true;
1305}
1306
1307
1308void QgsRuleBasedRendererModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule )
1309{
1310 beginInsertRows( parent, before, before );
1311
1312 QgsDebugMsgLevel( u"insert before %1 rule: %2"_s.arg( before ).arg( newrule->dump() ), 2 );
1313
1315 parentRule->insertChild( before, newrule );
1316
1317 endInsertRows();
1318}
1319
1320void QgsRuleBasedRendererModel::updateRule( const QModelIndex &parent, int row )
1321{
1322 emit dataChanged( index( row, 0, parent ), index( row, columnCount( parent ), parent ) );
1323}
1324
1325void QgsRuleBasedRendererModel::updateRule( const QModelIndex &idx )
1326{
1327 emit dataChanged( index( 0, 0, idx ), index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1328
1329 for ( int i = 0; i < rowCount( idx ); i++ )
1330 {
1331 updateRule( index( i, 0, idx ) );
1332 }
1333}
1334
1335
1337{
1338 if ( !index.isValid() )
1339 return;
1340
1341 beginRemoveRows( index.parent(), index.row(), index.row() );
1342
1344 rule->parent()->removeChild( rule );
1345
1346 endRemoveRows();
1347}
1348
1349void QgsRuleBasedRendererModel::setSymbol( const QModelIndex &index, QgsSymbol *symbol )
1350{
1352 rule->setSymbol( symbol );
1353 emit dataChanged( index, index );
1354}
1355
1356void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int count )
1357{
1358 int row = rowCount( parent ); // only consider appending
1359 beginInsertRows( parent, row, row + count - 1 );
1360}
1361
1363{
1364 endInsertRows();
1365}
1366
1367void QgsRuleBasedRendererModel::setFeatureCounts( const QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> &countMap )
1368{
1369 mFeatureCountMap = countMap;
1370 updateRule( QModelIndex() );
1371}
1372
1374{
1375 mFeatureCountMap.clear();
1376 updateRule( QModelIndex() );
1377}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2276
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A widget for configuring a QgsCategorizedSymbolRenderer.
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership).
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
A feature renderer which represents features using a list of renderer categories.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
A generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString evalErrorString() const
Returns evaluation error.
QString parserErrorString() const
Returns parser error.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Abstract base class for all 2D vector feature renderers.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
A widget for configuring a QgsGraduatedSymbolRenderer.
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership).
const QgsRangeList & ranges() const
Returns a list of all ranges used in the classification.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:224
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:41
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
Base class for any widget that can be shown as an inline panel.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool dockMode() const
Returns the dock mode state.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
void widgetChanged()
Emitted when the widget state changes.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
A container for the context for various read/write operations on objects.
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
A dialog for editing the details of a rule based renderer rule.
QgsRuleBasedRenderer::Rule * rule()
QgsRendererRulePropsDialog(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Constructor for QgsRendererRulePropsDialog.
A widget for editing the details of a rule based renderer rule.
void buildExpression()
Open the expression builder widget to check if the.
void setDockMode(bool dockMode) override
Set the widget in dock mode.
QgsRendererRulePropsWidget(QgsRuleBasedRenderer::Rule *rule, QgsVectorLayer *layer, QgsStyle *style, QWidget *parent=nullptr, const QgsSymbolWidgetContext &context=QgsSymbolWidgetContext())
Widget to edit the details of a rule based renderer rule.
QgsSymbolSelectorWidget * mSymbolSelector
void apply()
Apply any changes from the widget to the set rule.
QgsRuleBasedRenderer::Rule * mRule
void testFilter()
Test the filter that is set in the widget.
QgsRuleBasedRenderer::Rule * rule()
Returns the current set rule.
QAction * mPasteSymbolAction
Paste symbol action.
QgsRendererWidget(QgsVectorLayer *layer, QgsStyle *style)
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsSymbolWidgetContext mContext
Context in which widget is shown.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
QgsVectorLayer * mLayer
Tree model for a rule-based renderer's rules.
QgsRuleBasedRenderer::Rule * ruleForIndex(const QModelIndex &index) const
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void updateRule(const QModelIndex &parent, int row)
void willAddRules(const QModelIndex &parent, int count)
Qt::ItemFlags flags(const QModelIndex &index) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setSymbol(const QModelIndex &index, QgsSymbol *symbol)
Sets the symbol for the rule at the specified index.
QStringList mimeTypes() const override
void removeRule(const QModelIndex &index)
int columnCount(const QModelIndex &=QModelIndex()) const override
Qt::DropActions supportedDropActions() const override
QgsRuleBasedRendererModel(QgsRuleBasedRenderer *renderer, QObject *parent, QScreen *screen=nullptr)
Constructor for QgsRuleBasedRendererModel, for the specified renderer.
void setFeatureCounts(const QHash< QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount > &countMap)
QModelIndex parent(const QModelIndex &index) const override
QHash< QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount > mFeatureCountMap
QMimeData * mimeData(const QModelIndexList &indexes) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void insertRule(const QModelIndex &parent, int before, QgsRuleBasedRenderer::Rule *newrule)
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
void refineRuleCategoriesGui()
Opens the dialog for refining a rule using categories.
QgsRuleBasedRenderer::RuleList selectedRules()
void refineRuleScalesGui(const QModelIndexList &index)
void refineRuleRangesGui()
Opens the dialog for refining a rule using ranges.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
std::unique_ptr< QgsRuleBasedRenderer > mRenderer
QList< QgsSymbol * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
QgsRuleBasedRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
QgsRuleBasedRenderer::RuleList mCopyBuffer
void currentRuleChanged(const QModelIndex &current=QModelIndex(), const QModelIndex &previous=QModelIndex())
QgsRuleBasedRendererModel * mModel
void setSymbolLevels(const QList< QgsLegendSymbolItem > &levels, bool enabled) override
Sets the symbol levels for the renderer defined in the widget.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership).
void keyPressEvent(QKeyEvent *event) override
QgsRuleBasedRenderer::Rule * currentRule()
void saveSectionWidth(int section, int oldSize, int newSize)
Represents an individual rule for a rule-based renderer.
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId=true, const QgsReadWriteContext &context=QgsReadWriteContext())
Create a rule from an XML definition.
const QgsRuleBasedRenderer::RuleList & children() const
Returns all children rules of this rule.
double maximumScale() const
Returns the maximum map scale (i.e.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
bool isElse() const
Check if this rule is an ELSE rule.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
void removeChildAt(int i)
delete child rule
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
void setActive(bool state)
Sets if this rule is active.
QDomElement save(QDomDocument &doc, QgsSymbolMap &symbolMap) const
double minimumScale() const
Returns the minimum map scale (i.e.
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
void setLabel(const QString &label)
QString dump(int indent=0) const
Dump for debug purpose.
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
Rule based renderer.
static void refineRuleCategories(QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer
static void refineRuleRanges(QgsRuleBasedRenderer::Rule *initialRule, QgsGraduatedSymbolRenderer *r)
take a rule and create a list of new rules based on the ranges from graduated symbol renderer
static QgsRuleBasedRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsRuleBasedRenderer from an existing renderer.
static void refineRuleScales(QgsRuleBasedRenderer::Rule *initialRule, QList< int > scales)
take a rule and create a list of new rules with intervals of scales given by the passed scale denomin...
QList< QgsRuleBasedRenderer::Rule * > RuleList
static QString toString(double scale, QgsScaleComboBox::RatioMode mode=QgsScaleComboBox::RatioMode::ForceUnitNumerator)
Helper function to convert a scale double to scale string.
void rangeChanged(double min, double max)
Emitted when the scale range set in the widget is changed.
Stores properties relating to a screen.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:89
static std::unique_ptr< QgsSymbol > symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns an icon preview for a color ramp.
A dialog which allows the user to modify the rendering order of symbol layers.
QgsLegendSymbolList symbolLevels() const
Returns the current legend symbols with rendering passes set, as defined in the widget.
bool usingLevels() const
Returns whether the level ordering is enabled.
void setForceOrderingEnabled(bool enabled)
A widget which allows the user to modify the rendering order of symbol layers.
void setForceOrderingEnabled(bool enabled)
Sets whether the level ordering is always forced on and hide the checkbox (used by rule-based rendere...
bool usingLevels() const
Returns whether the level ordering is enabled.
QgsLegendSymbolList symbolLevels() const
Returns the current legend symbols with rendering passes set, as defined in the widget.
Symbol selector widget that can be used to select and build a symbol.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
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:357
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
A QScrollArea subclass which only displays a vertical scrollbar and fits the width to the contents.
Represents a vector layer which manages a vector based dataset.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:52
void _labeling2rendererRules(QDomElement &ruleElem)