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