QGIS API Documentation 3.99.0-Master (d270888f95f)
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]() {
462 setSymbolLevels( widget->symbolLevels(), widget->usingLevels() );
463 } );
464 panel->openPanel( widget );
465 }
466 else
467 {
468 QgsSymbolLevelsDialog dlg( mRenderer.get(), true, panel );
469 dlg.setForceOrderingEnabled( true );
470 if ( dlg.exec() )
471 {
473 }
474 }
475}
476
477void QgsRuleBasedRendererWidget::saveSectionWidth( int section, int oldSize, int newSize )
478{
479 Q_UNUSED( oldSize )
480 // skip last section, as it stretches
481 if ( section == 5 )
482 return;
483 QgsSettings settings;
484 QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
485 settings.setValue( path, newSize );
486}
487
489{
490 QgsSettings settings;
491 QString path = u"/Windows/RuleBasedTree/sectionWidth/"_s;
492 QHeaderView *head = viewRules->header();
493 head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
494 head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
495 head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
496 head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
497 head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
498 head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
499}
500
502{
503 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
504 QgsDebugMsgLevel( QString::number( indexlist.count() ), 2 );
505
506 if ( indexlist.isEmpty() )
507 return;
508
509 QMimeData *mime = mModel->mimeData( indexlist );
510 QApplication::clipboard()->setMimeData( mime );
511}
512
514{
515 const QMimeData *mime = QApplication::clipboard()->mimeData();
516 if ( !mime )
517 return;
518
519 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
520 QModelIndex index;
521 if ( indexlist.isEmpty() )
522 index = mModel->index( mModel->rowCount(), 0 );
523 else
524 index = indexlist.first();
525 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
526}
527
529{
530 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
531 if ( !tempSymbol )
532 return;
533
534 const QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
535 for ( const QModelIndex &index : indexList )
536 {
537 if ( QgsRuleBasedRenderer::Rule *rule = mModel->ruleForIndex( index ) )
538 {
539 if ( !rule->symbol() || rule->symbol()->type() != tempSymbol->type() )
540 continue;
541
542 mModel->setSymbol( index, tempSymbol->clone() );
543 }
544 }
545 emit widgetChanged();
546}
547
548void QgsRuleBasedRendererWidget::refineRuleCategoriesAccepted( QgsPanelWidget *panel )
549{
550 QgsCategorizedSymbolRendererWidget *w = qobject_cast<QgsCategorizedSymbolRendererWidget *>( panel );
551
552 // create new rules
554 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
555 const auto constIndexList = indexList;
556 for ( const QModelIndex &index : constIndexList )
557 {
558 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
559 mModel->willAddRules( index, r->categories().count() );
561 }
562 mModel->finishedAddingRules();
563}
564
565void QgsRuleBasedRendererWidget::refineRuleRangesAccepted( QgsPanelWidget *panel )
566{
567 QgsGraduatedSymbolRendererWidget *w = qobject_cast<QgsGraduatedSymbolRendererWidget *>( panel );
568 // create new rules
569 QgsGraduatedSymbolRenderer *r = static_cast<QgsGraduatedSymbolRenderer *>( w->renderer() );
570 QModelIndexList indexList = viewRules->selectionModel()->selectedRows();
571 const auto constIndexList = indexList;
572 for ( const QModelIndex &index : constIndexList )
573 {
574 QgsRuleBasedRenderer::Rule *initialRule = mModel->ruleForIndex( index );
575 mModel->willAddRules( index, r->ranges().count() );
577 }
578 mModel->finishedAddingRules();
579}
580
581void QgsRuleBasedRendererWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
582{
583 QgsRendererRulePropsWidget *widget = qobject_cast<QgsRendererRulePropsWidget *>( panel );
584 if ( !widget )
585 return;
586
587 widget->apply();
588
589 // model should know about the change and emit dataChanged signal for the view
590 QModelIndex index = viewRules->selectionModel()->currentIndex();
591 mModel->updateRule( index.parent(), index.row() );
592 mModel->clearFeatureCounts();
593}
594
595void QgsRuleBasedRendererWidget::liveUpdateRuleFromPanel()
596{
597 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
598}
599
600void QgsRuleBasedRendererWidget::showContextMenu( QPoint )
601{
602 mContextMenu->clear();
603 mContextMenu->addAction( mCopyAction );
604 mContextMenu->addAction( mPasteAction );
605
606 const QList<QAction *> actions = contextMenu->actions();
607 for ( QAction *act : actions )
608 {
609 mContextMenu->addAction( act );
610 }
611
612 mContextMenu->addMenu( mRefineMenu );
613
614 mContextMenu->exec( QCursor::pos() );
615}
616
617
619{
620 if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
621 {
622 return;
623 }
624 QHash<QgsRuleBasedRenderer::Rule *, QgsRuleBasedRendererCount> countMap;
625
626 QgsRuleBasedRenderer::RuleList ruleList = mRenderer->rootRule()->descendants();
627 // insert all so that we have counts 0
628 const auto constRuleList = ruleList;
629 for ( QgsRuleBasedRenderer::Rule *rule : constRuleList )
630 {
631 countMap[rule].count = 0;
632 countMap[rule].duplicateCount = 0;
633 }
634
635 QgsRenderContext renderContext;
636 renderContext.setRendererScale( 0 ); // ignore scale
637
638 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
639
640 // additional scopes
641 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
642 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
643 {
644 context.appendScope( new QgsExpressionContextScope( scope ) );
645 }
646
647 renderContext.setExpressionContext( context );
648
649 mRenderer->startRender( renderContext, mLayer->fields() );
650 // QgsRuleBasedRenderer::filter must be called after startRender
653 req.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mLayer->fields() );
654 QgsFeatureIterator fit = mLayer->getFeatures( req );
655
656 long long nFeatures = mLayer->featureCount();
657 QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, 100 );
658 p.setWindowModality( Qt::WindowModal );
659 long long featuresCounted = 0;
660
661 QgsFeature f;
662 while ( fit.nextFeature( f ) )
663 {
664 renderContext.expressionContext().setFeature( f );
665 QgsRuleBasedRenderer::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
666
667 const auto constFeatureRuleList = featureRuleList;
668 for ( QgsRuleBasedRenderer::Rule *rule : constFeatureRuleList )
669 {
670 countMap[rule].count++;
671 if ( featureRuleList.size() > 1 )
672 {
673 countMap[rule].duplicateCount++;
674 }
675 const auto constFeatureRuleList = featureRuleList;
676 for ( QgsRuleBasedRenderer::Rule *duplicateRule : constFeatureRuleList )
677 {
678 if ( duplicateRule == rule )
679 continue;
680 countMap[rule].duplicateCountMap[duplicateRule] += 1;
681 }
682 }
683 ++featuresCounted;
684 if ( featuresCounted % 50 == 0 )
685 {
686 if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
687 {
688 p.setMaximum( 0 );
689 }
690 p.setValue( static_cast<double>( featuresCounted ) / nFeatures * 100.0 );
691 if ( p.wasCanceled() )
692 {
693 return;
694 }
695 }
696 }
697 p.setValue( nFeatures );
698
699 mRenderer->stopRender( renderContext );
700
701#ifdef QGISDEBUG
702 const auto constKeys = countMap.keys();
703 for ( QgsRuleBasedRenderer::Rule *rule : constKeys )
704 {
705 QgsDebugMsgLevel( u"rule: %1 count %2"_s.arg( rule->label() ).arg( countMap[rule].count ), 2 );
706 }
707#endif
708
709 mModel->setFeatureCounts( countMap );
710}
711
713{
714 bool enabled = !viewRules->selectionModel()->selectedIndexes().isEmpty();
715 btnRefineRule->setEnabled( enabled );
716 btnRemoveRule->setEnabled( enabled );
717}
718
720
722 : QgsPanelWidget( parent )
723 , mRule( rule )
724 , mLayer( layer )
725 , mContext( context )
726{
727 setupUi( this );
728 layout()->setContentsMargins( 0, 0, 0, 0 );
729
730 mElseRadio->setChecked( mRule->isElse() );
731 mFilterRadio->setChecked( !mRule->isElse() );
732 editFilter->setText( mRule->filterExpression() );
733 editFilter->setToolTip( mRule->filterExpression() );
734 editLabel->setText( mRule->label() );
735 editDescription->setText( mRule->description() );
736 editDescription->setToolTip( mRule->description() );
737
738 if ( mRule->dependsOnScale() )
739 {
740 groupScale->setChecked( true );
741 mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ), std::max( rule->maximumScale(), 0.0 ) );
742 }
743 mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );
744
745 if ( mRule->symbol() )
746 {
747 groupSymbol->setChecked( true );
748 mSymbol = mRule->symbol()->clone(); // use a clone!
749 }
750 else
751 {
752 groupSymbol->setChecked( false );
753 mSymbol = QgsSymbol::defaultSymbol( mLayer->geometryType() );
754 }
755
757 mSymbolSelector->setContext( mContext );
760
761 QVBoxLayout *l = new QVBoxLayout;
762 l->addWidget( mSymbolSelector );
763 groupSymbol->setLayout( l );
764
765 connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::buildExpression );
766 connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsRendererRulePropsWidget::testFilter );
767 connect( editFilter, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
768 connect( editLabel, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
769 connect( editDescription, &QLineEdit::editingFinished, this, &QgsPanelWidget::widgetChanged );
770 connect( groupSymbol, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
771 connect( groupScale, &QGroupBox::toggled, this, &QgsPanelWidget::widgetChanged );
772 connect( mScaleRangeWidget, &QgsScaleRangeWidget::rangeChanged, this, &QgsPanelWidget::widgetChanged );
773 connect( mFilterRadio, &QRadioButton::toggled, this, [this]( bool toggled ) { filterFrame->setEnabled( toggled ); } );
774 connect( mElseRadio, &QRadioButton::toggled, this, [this]( bool toggled ) { if ( toggled ) editFilter->setText( u"ELSE"_s ); } );
775}
776
777#include "qgsvscrollarea.h"
778
780 : QDialog( parent )
781{
782#ifdef Q_OS_MAC
783 setWindowModality( Qt::WindowModal );
784#endif
785
786 QVBoxLayout *layout = new QVBoxLayout( this );
787 QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
788 scrollArea->setFrameShape( QFrame::NoFrame );
789 layout->addWidget( scrollArea );
790
791 buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
792 mPropsWidget = new QgsRendererRulePropsWidget( rule, layer, style, this, context );
793
794 scrollArea->setWidget( mPropsWidget );
795 layout->addWidget( buttonBox );
796 this->setWindowTitle( "Edit Rule" );
798
799 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererRulePropsDialog::accept );
800 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
801 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRendererRulePropsDialog::showHelp );
802}
803
805{
806 mPropsWidget->testFilter();
807}
808
810{
811 mPropsWidget->buildExpression();
812}
813
815{
816 mPropsWidget->apply();
817 QDialog::accept();
818}
819
820void QgsRendererRulePropsDialog::showHelp()
821{
822 QgsHelp::openHelp( u"working_with_vector/vector_properties.html#rule-based-rendering"_s );
823}
824
825
827{
828 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
829
830 // additional scopes
831 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
832 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
833 {
834 context.appendScope( new QgsExpressionContextScope( scope ) );
835 }
836
837 QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, u"generic"_s, context );
838
839 if ( dlg.exec() )
840 editFilter->setText( dlg.expressionText() );
841}
842
844{
845 if ( !mFilterRadio->isChecked() )
846 return;
847
848 QgsExpression filter( editFilter->text() );
849 if ( filter.hasParserError() )
850 {
851 QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
852 return;
853 }
854
855 QgsExpressionContext context( mContext.globalProjectAtlasMapLayerScopes( mLayer ) );
856
857 // additional scopes
858 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
859 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
860 {
861 context.appendScope( new QgsExpressionContextScope( scope ) );
862 }
863
864 if ( !filter.prepare( &context ) )
865 {
866 QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
867 return;
868 }
869
870 QApplication::setOverrideCursor( Qt::WaitCursor );
871
873
874 QgsFeatureIterator fit = mLayer->getFeatures( req );
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() ? u"ELSE"_s : 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
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 , mScreen( screen )
921{
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:2254
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:231
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:353
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
A QScrollArea subclass which only displays a vertical scrollbar and fits the width to the contents.
Represents a vector layer which manages a vector based dataset.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:52
void _labeling2rendererRules(QDomElement &ruleElem)