QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsrulebasedlabelingwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrulebasedlabelingwidget.cpp
3 ---------------------
4 begin : September 2015
5 copyright : (C) 2015 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 ***************************************************************************/
16
17#include "qgsapplication.h"
19#include "qgsfeatureiterator.h"
20#include "qgslabelinggui.h"
21#include "qgsmapcanvas.h"
22#include "qgsproject.h"
23#include "qgsreadwritecontext.h"
25#include "qgsvectorlayer.h"
27#include "qgslogger.h"
29
30#include <QAction>
31#include <QClipboard>
32#include <QMessageBox>
33
34const double ICON_PADDING_FACTOR = 0.16;
35
36static QList<QgsExpressionContextScope *> _globalProjectAtlasMapLayerScopes( QgsMapCanvas *mapCanvas, const QgsMapLayer *layer )
37{
38 QList<QgsExpressionContextScope *> scopes;
42 if ( mapCanvas )
43 {
46 }
47 else
48 {
50 }
52 return scopes;
53}
54
55
57 : QgsPanelWidget( parent )
58 , mLayer( layer )
59 , mCanvas( canvas )
60
61{
62 setupUi( this );
63
64 btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
65 btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
66 btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
67
68 mCopyAction = new QAction( tr( "Copy" ), this );
69 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
70 mPasteAction = new QAction( tr( "Paste" ), this );
71 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
72 mDeleteAction = new QAction( tr( "Remove Rule" ), this );
73 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
74
75 viewRules->addAction( mCopyAction );
76 viewRules->addAction( mPasteAction );
77 viewRules->addAction( mDeleteAction );
78
79 connect( viewRules, &QAbstractItemView::doubleClicked, this, static_cast<void ( QgsRuleBasedLabelingWidget::* )( const QModelIndex & )>( &QgsRuleBasedLabelingWidget::editRule ) );
80
81 connect( btnAddRule, &QAbstractButton::clicked, this, &QgsRuleBasedLabelingWidget::addRule );
82 connect( btnEditRule, &QAbstractButton::clicked, this, static_cast<void ( QgsRuleBasedLabelingWidget::* )()>( &QgsRuleBasedLabelingWidget::editRule ) );
83 connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsRuleBasedLabelingWidget::removeRule );
84 connect( mCopyAction, &QAction::triggered, this, &QgsRuleBasedLabelingWidget::copy );
85 connect( mPasteAction, &QAction::triggered, this, &QgsRuleBasedLabelingWidget::paste );
86 connect( mDeleteAction, &QAction::triggered, this, &QgsRuleBasedLabelingWidget::removeRule );
87
88 if ( mLayer->labeling() && mLayer->labeling()->type() == QLatin1String( "rule-based" ) )
89 {
90 const QgsRuleBasedLabeling *rl = static_cast<const QgsRuleBasedLabeling *>( mLayer->labeling() );
91 mRootRule = rl->rootRule()->clone();
92 }
93 else if ( mLayer->labeling() && mLayer->labeling()->type() == QLatin1String( "simple" ) )
94 {
95 // copy simple label settings to first rule
96 mRootRule = new QgsRuleBasedLabeling::Rule( nullptr );
97 std::unique_ptr< QgsPalLayerSettings > newSettings = std::make_unique< QgsPalLayerSettings >( mLayer->labeling()->settings() );
98 newSettings->drawLabels = true; // otherwise we may be trying to copy a "blocking" setting to a rule - which is confusing for users!
99 mRootRule->appendChild( new QgsRuleBasedLabeling::Rule( newSettings.release() ) );
100 }
101 else
102 {
103 mRootRule = new QgsRuleBasedLabeling::Rule( nullptr );
104 }
105
106 mModel = new QgsRuleBasedLabelingModel( mRootRule );
107 viewRules->setModel( mModel );
108
109 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsRuleBasedLabelingWidget::widgetChanged );
110 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsRuleBasedLabelingWidget::widgetChanged );
111 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsRuleBasedLabelingWidget::widgetChanged );
112}
113
115{
116 delete mRootRule;
117}
118
120{
121 if ( dockMode )
122 {
123 // when in dock mode, these shortcuts conflict with the main window shortcuts and cannot be used
124 if ( mCopyAction )
125 mCopyAction->setShortcut( QKeySequence() );
126 if ( mPasteAction )
127 mPasteAction->setShortcut( QKeySequence() );
128 if ( mDeleteAction )
129 mDeleteAction->setShortcut( QKeySequence() );
130 }
132}
133
134void QgsRuleBasedLabelingWidget::addRule()
135{
137
138 QgsRuleBasedLabeling::Rule *current = currentRule();
139 if ( current )
140 {
141 // add after this rule
142 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
143 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
144 const QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
145 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
146 }
147 else
148 {
149 // append to root rule
150 const int rows = mModel->rowCount();
151 mModel->insertRule( QModelIndex(), rows, newrule );
152 const QModelIndex newindex = mModel->index( rows, 0 );
153 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
154 }
155 editRule();
156}
157
158void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
159{
160 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
161 widget->apply();
162
163 const QModelIndex index = viewRules->selectionModel()->currentIndex();
164 mModel->updateRule( index.parent(), index.row() );
165}
166
167void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
168{
169 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
170}
171
172
173void QgsRuleBasedLabelingWidget::editRule()
174{
175 editRule( viewRules->selectionModel()->currentIndex() );
176}
177
178void QgsRuleBasedLabelingWidget::editRule( const QModelIndex &index )
179{
180 if ( !index.isValid() )
181 return;
182
183 QgsRuleBasedLabeling::Rule *rule = mModel->ruleForIndex( index );
185
186 if ( panel && panel->dockMode() )
187 {
188 QgsLabelingRulePropsWidget *widget = new QgsLabelingRulePropsWidget( rule, mLayer, this, mCanvas );
189 widget->setPanelTitle( tr( "Edit Rule" ) );
190 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted );
191 connect( widget, &QgsLabelingRulePropsWidget::widgetChanged, this, &QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel );
192 openPanel( widget );
193 return;
194 }
195
196 QgsLabelingRulePropsDialog dlg( rule, mLayer, this, mCanvas );
197 if ( dlg.exec() )
198 {
199 mModel->updateRule( index.parent(), index.row() );
200 emit widgetChanged();
201 }
202}
203
204void QgsRuleBasedLabelingWidget::removeRule()
205{
206 const QItemSelection sel = viewRules->selectionModel()->selection();
207 const auto constSel = sel;
208 for ( const QItemSelectionRange &range : constSel )
209 {
210 if ( range.isValid() )
211 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
212 }
213 // make sure that the selection is gone
214 viewRules->selectionModel()->clear();
215}
216
217void QgsRuleBasedLabelingWidget::copy()
218{
219 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
220
221 if ( indexlist.isEmpty() )
222 return;
223
224 QMimeData *mime = mModel->mimeData( indexlist );
225 QApplication::clipboard()->setMimeData( mime );
226}
227
228void QgsRuleBasedLabelingWidget::paste()
229{
230 const QMimeData *mime = QApplication::clipboard()->mimeData();
231 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
232 QModelIndex index;
233 if ( indexlist.isEmpty() )
234 index = mModel->index( mModel->rowCount(), 0 );
235 else
236 index = indexlist.first();
237 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
238}
239
240QgsRuleBasedLabeling::Rule *QgsRuleBasedLabelingWidget::currentRule()
241{
242 QItemSelectionModel *sel = viewRules->selectionModel();
243 const QModelIndex idx = sel->currentIndex();
244 if ( !idx.isValid() )
245 return nullptr;
246 return mModel->ruleForIndex( idx );
247}
248
249#include "qgsvscrollarea.h"
250
252 : QDialog( parent )
253{
254
255#ifdef Q_OS_MAC
256 setWindowModality( Qt::WindowModal );
257#endif
258
259 QVBoxLayout *layout = new QVBoxLayout( this );
260 QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
261 scrollArea->setFrameShape( QFrame::NoFrame );
262 layout->addWidget( scrollArea );
263
264 buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
265 mPropsWidget = new QgsLabelingRulePropsWidget( rule, layer, this, mapCanvas );
266
267 scrollArea->setWidget( mPropsWidget );
268 layout->addWidget( buttonBox );
269 this->setWindowTitle( "Edit Rule" );
271
272 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsLabelingRulePropsDialog::accept );
273 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
274 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsLabelingRulePropsDialog::showHelp );
275}
276
278{
279 mPropsWidget->testFilter();
280}
281
283{
284 mPropsWidget->buildExpression();
285}
286
288{
289 mPropsWidget->apply();
290 QDialog::accept();
291}
292
293void QgsLabelingRulePropsDialog::showHelp()
294{
295 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-labeling" ) );
296}
297
299
301 : QAbstractItemModel( parent )
302 , mRootRule( rootRule )
303{
304}
305
306Qt::ItemFlags QgsRuleBasedLabelingModel::flags( const QModelIndex &index ) const
307{
308 if ( !index.isValid() )
309 return Qt::ItemIsDropEnabled;
310
311 // allow drop only at first column
312 const Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
313
314 const Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
315
316 return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
317 Qt::ItemIsEditable | checkable |
318 Qt::ItemIsDragEnabled | drop;
319}
320
321QVariant QgsRuleBasedLabelingModel::data( const QModelIndex &index, int role ) const
322{
323 if ( !index.isValid() )
324 return QVariant();
325
327
328 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
329 {
330 switch ( index.column() )
331 {
332 case 0:
333 return rule->description();
334 case 1:
335 if ( rule->isElse() )
336 {
337 return "ELSE";
338 }
339 else
340 {
341 return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
342 }
343 case 2:
344 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
345 case 3:
346 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
347 case 4:
348 return rule->settings() ? rule->settings()->fieldName : QVariant();
349 default:
350 return QVariant();
351 }
352 }
353 else if ( role == Qt::DecorationRole && index.column() == 0 && rule->settings() )
354 {
355 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
356 return QgsPalLayerSettings::labelSettingsPreviewPixmap( *rule->settings(), QSize( iconSize, iconSize ), QString(), static_cast< int >( iconSize * ICON_PADDING_FACTOR ) );
357 }
358 else if ( role == Qt::TextAlignmentRole )
359 {
360 return ( index.column() == 2 || index.column() == 3 ) ? static_cast<Qt::Alignment::Int>( Qt::AlignRight ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
361 }
362 else if ( role == Qt::FontRole && index.column() == 1 )
363 {
364 if ( rule->isElse() )
365 {
366 QFont italicFont;
367 italicFont.setItalic( true );
368 return italicFont;
369 }
370 return QVariant();
371 }
372 else if ( role == Qt::EditRole )
373 {
374 switch ( index.column() )
375 {
376 case 0:
377 return rule->description();
378 case 1:
379 return rule->filterExpression();
380 case 2:
381 return rule->minimumScale();
382 case 3:
383 return rule->maximumScale();
384 case 4:
385 return rule->settings() ? rule->settings()->fieldName : QVariant();
386 default:
387 return QVariant();
388 }
389 }
390 else if ( role == Qt::CheckStateRole )
391 {
392 if ( index.column() != 0 )
393 return QVariant();
394 return rule->active() ? Qt::Checked : Qt::Unchecked;
395 }
396 else
397 return QVariant();
398}
399
400QVariant QgsRuleBasedLabelingModel::headerData( int section, Qt::Orientation orientation, int role ) const
401{
402 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
403 {
404 QStringList lst;
405 lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Text" ); // << tr( "Count" ) << tr( "Duplicate Count" );
406 return lst[section];
407 }
408
409 return QVariant();
410}
411
412int QgsRuleBasedLabelingModel::rowCount( const QModelIndex &parent ) const
413{
414 if ( parent.column() > 0 )
415 return 0;
416
418
419 return parentRule->children().count();
420}
421
422int QgsRuleBasedLabelingModel::columnCount( const QModelIndex & ) const
423{
424 return 5;
425}
426
427QModelIndex QgsRuleBasedLabelingModel::index( int row, int column, const QModelIndex &parent ) const
428{
429 if ( hasIndex( row, column, parent ) )
430 {
432 QgsRuleBasedLabeling::Rule *childRule = parentRule->children()[row];
433 return createIndex( row, column, childRule );
434 }
435 return QModelIndex();
436}
437
438QModelIndex QgsRuleBasedLabelingModel::parent( const QModelIndex &index ) const
439{
440 if ( !index.isValid() )
441 return QModelIndex();
442
444 QgsRuleBasedLabeling::Rule *parentRule = childRule->parent();
445
446 if ( parentRule == mRootRule )
447 return QModelIndex();
448
449 // this is right: we need to know row number of our parent (in our grandparent)
450 const int row = parentRule->parent()->children().indexOf( parentRule );
451
452 return createIndex( row, 0, parentRule );
453}
454
455bool QgsRuleBasedLabelingModel::setData( const QModelIndex &index, const QVariant &value, int role )
456{
457 if ( !index.isValid() )
458 return false;
459
461
462 if ( role == Qt::CheckStateRole )
463 {
464 rule->setActive( value.toInt() == Qt::Checked );
465 emit dataChanged( index, index );
466 return true;
467 }
468
469 if ( role != Qt::EditRole )
470 return false;
471
472 switch ( index.column() )
473 {
474 case 0: // description
475 rule->setDescription( value.toString() );
476 break;
477 case 1: // filter
478 rule->setFilterExpression( value.toString() );
479 break;
480 case 2: // scale min
481 rule->setMinimumScale( value.toDouble() );
482 break;
483 case 3: // scale max
484 rule->setMaximumScale( value.toDouble() );
485 break;
486 case 4: // label text
487 if ( !rule->settings() )
488 return false;
489 rule->settings()->fieldName = value.toString();
490 break;
491 default:
492 return false;
493 }
494
495 emit dataChanged( index, index );
496 return true;
497}
498
500{
501 return Qt::MoveAction; // | Qt::CopyAction
502}
503
505{
506 QStringList types;
507 types << QStringLiteral( "application/vnd.text.list" );
508 return types;
509}
510
511// manipulate DOM before dropping it so that rules are more useful
512void _renderer2labelingRules( QDomElement &ruleElem )
513{
514 // labeling rules recognize only "description"
515 if ( ruleElem.hasAttribute( QStringLiteral( "label" ) ) )
516 ruleElem.setAttribute( QStringLiteral( "description" ), ruleElem.attribute( QStringLiteral( "label" ) ) );
517
518 // run recursively
519 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
520 while ( !childRuleElem.isNull() )
521 {
522 _renderer2labelingRules( childRuleElem );
523 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
524 }
525}
526
527QMimeData *QgsRuleBasedLabelingModel::mimeData( const QModelIndexList &indexes ) const
528{
529 QMimeData *mimeData = new QMimeData();
530 QByteArray encodedData;
531
532 QDataStream stream( &encodedData, QIODevice::WriteOnly );
533
534 const auto constIndexes = indexes;
535 for ( const QModelIndex &index : constIndexes )
536 {
537 // each item consists of several columns - let's add it with just first one
538 if ( !index.isValid() || index.column() != 0 )
539 continue;
540
541 // we use a clone of the existing rule because it has a new unique rule key
542 // non-unique rule keys would confuse other components using them (e.g. legend)
544 QDomDocument doc;
545
546 QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
547 rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "labeling" ) ); // for determining whether rules are from renderer or labeling
548 const QDomElement rulesElem = rule->save( doc, QgsReadWriteContext() );
549 rootElem.appendChild( rulesElem );
550 doc.appendChild( rootElem );
551
552 delete rule;
553
554 stream << doc.toString( -1 );
555 }
556
557 mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
558 return mimeData;
559}
560
561bool QgsRuleBasedLabelingModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
562{
563 Q_UNUSED( column )
564
565 if ( action == Qt::IgnoreAction )
566 return true;
567
568 if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
569 return false;
570
571 if ( parent.column() > 0 )
572 return false;
573
574 QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
575 QDataStream stream( &encodedData, QIODevice::ReadOnly );
576 int rows = 0;
577
578 if ( row == -1 )
579 {
580 // the item was dropped at a parent - we may decide where to put the items - let's append them
581 row = rowCount( parent );
582 }
583
584 while ( !stream.atEnd() )
585 {
586 QString text;
587 stream >> text;
588
589 QDomDocument doc;
590 if ( !doc.setContent( text ) )
591 continue;
592 const QDomElement rootElem = doc.documentElement();
593 if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
594 continue;
595 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
596 if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "renderer" ) )
597 _renderer2labelingRules( ruleElem ); // do some modifications so that we load the rules more nicely
599
600 insertRule( parent, row + rows, rule );
601
602 ++rows;
603 }
604 return true;
605}
606
607bool QgsRuleBasedLabelingModel::removeRows( int row, int count, const QModelIndex &parent )
608{
610
611 if ( row < 0 || row >= parentRule->children().count() )
612 return false;
613
614 beginRemoveRows( parent, row, row + count - 1 );
615
616 for ( int i = 0; i < count; i++ )
617 {
618 if ( row < parentRule->children().count() )
619 {
620 parentRule->removeChildAt( row );
621 }
622 else
623 {
624 QgsDebugError( QStringLiteral( "trying to remove invalid index - this should not happen!" ) );
625 }
626 }
627
628 endRemoveRows();
629
630 return true;
631}
632
634{
635 if ( index.isValid() )
636 return static_cast<QgsRuleBasedLabeling::Rule *>( index.internalPointer() );
637 return mRootRule;
638}
639
640void QgsRuleBasedLabelingModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedLabeling::Rule *newrule )
641{
642 beginInsertRows( parent, before, before );
643
645 parentRule->insertChild( before, newrule );
646
647 endInsertRows();
648}
649
650void QgsRuleBasedLabelingModel::updateRule( const QModelIndex &parent, int row )
651{
652 emit dataChanged( index( row, 0, parent ),
653 index( row, columnCount( parent ), parent ) );
654}
655
657
659 : QgsPanelWidget( parent )
660 , mRule( rule )
661 , mLayer( layer )
662 , mSettings( nullptr )
663 , mMapCanvas( mapCanvas )
664{
665 setupUi( this );
666
667 QButtonGroup *radioGroup = new QButtonGroup( this );
668 radioGroup->addButton( mFilterRadio );
669 radioGroup->addButton( mElseRadio );
670
671 mElseRadio->setChecked( mRule->isElse() );
672 mFilterRadio->setChecked( !mRule->isElse() );
673 editFilter->setText( mRule->filterExpression() );
674 editFilter->setToolTip( mRule->filterExpression() );
675 editDescription->setText( mRule->description() );
676 editDescription->setToolTip( mRule->description() );
677
678 if ( mRule->dependsOnScale() )
679 {
680 groupScale->setChecked( true );
681 // caution: rule uses scale denom, scale widget uses true scales
682 mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ),
683 std::max( rule->maximumScale(), 0.0 ) );
684 }
685 mScaleRangeWidget->setMapCanvas( mMapCanvas );
686
687 if ( mRule->settings() )
688 {
689 groupSettings->setChecked( true );
690 mSettings = new QgsPalLayerSettings( *mRule->settings() ); // use a clone!
691 }
692 else
693 {
694 groupSettings->setChecked( false );
695 mSettings = new QgsPalLayerSettings;
696 }
697
698 mLabelingGui = new QgsLabelingGui( nullptr, mMapCanvas, *mSettings, this );
699 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
700 QVBoxLayout *l = new QVBoxLayout;
701 l->addWidget( mLabelingGui );
702 groupSettings->setLayout( l );
703
704 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
705 mLabelingGui->setLayer( mLayer );
706
707 connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsLabelingRulePropsWidget::buildExpression );
708 connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsLabelingRulePropsWidget::testFilter );
709 connect( editFilter, &QLineEdit::textEdited, this, &QgsLabelingRulePropsWidget::widgetChanged );
710 connect( editDescription, &QLineEdit::textChanged, this, &QgsLabelingRulePropsWidget::widgetChanged );
711 connect( groupScale, &QGroupBox::toggled, this, &QgsLabelingRulePropsWidget::widgetChanged );
713 connect( groupSettings, &QGroupBox::toggled, this, &QgsLabelingRulePropsWidget::widgetChanged );
715 connect( mFilterRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
716 connect( mElseRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) );} );
717}
718
720{
721 delete mSettings;
722}
723
725{
727 mLabelingGui->setDockMode( dockMode );
728}
729
731{
732 if ( !mFilterRadio->isChecked() )
733 return;
734
735 QgsExpression filter( editFilter->text() );
736 if ( filter.hasParserError() )
737 {
738 QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
739 return;
740 }
741
742 QgsExpressionContext context( _globalProjectAtlasMapLayerScopes( mMapCanvas, mLayer ) );
743
744 if ( !filter.prepare( &context ) )
745 {
746 QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
747 return;
748 }
749
750 QApplication::setOverrideCursor( Qt::WaitCursor );
751
752 QgsFeatureIterator fit = mLayer->getFeatures();
753
754 int count = 0;
755 QgsFeature f;
756 while ( fit.nextFeature( f ) )
757 {
758 context.setFeature( f );
759
760 const QVariant value = filter.evaluate( &context );
761 if ( value.toInt() != 0 )
762 count++;
763 if ( filter.hasEvalError() )
764 break;
765 }
766
767 QApplication::restoreOverrideCursor();
768
769 QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
770}
771
772
774{
775 const QgsExpressionContext context( _globalProjectAtlasMapLayerScopes( mMapCanvas, mLayer ) );
776
777 QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
778
779 if ( dlg.exec() )
780 editFilter->setText( dlg.expressionText() );
781}
782
784{
785 const QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text().trimmed();
786 mRule->setFilterExpression( filter );
787 mRule->setDescription( editDescription->text() );
788 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
789 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
790 mRule->setSettings( groupSettings->isChecked() ? new QgsPalLayerSettings( mLabelingGui->layerSettings() ) : nullptr );
791}
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
static QgsPalLayerSettings defaultSettingsForLayer(const QgsVectorLayer *layer)
Returns the default layer settings to use for the specified vector layer.
virtual QString type() const =0
Unique type string of the labeling configuration implementation.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
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.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
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.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
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:194
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:39
Dialog for editing labeling rule.
void buildExpression()
Open the expression builder widget.
QgsLabelingRulePropsDialog(QgsRuleBasedLabeling::Rule *rule, QgsVectorLayer *layer, QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr)
Constructor for QgsLabelingRulePropsDialog.
void testFilter()
Test the filter that is set in the widget.
QgsRuleBasedLabeling::Rule * rule()
Returns the current set rule.
void accept() override
Apply any changes from the widget to the set rule.
Widget for editing a labeling rule.
void setDockMode(bool dockMode) override
Set the widget in dock mode.
QgsRuleBasedLabeling::Rule * rule()
Returns the rule being edited.
void apply()
Apply any changes from the widget to the set rule.
void testFilter()
Test the filter that is set in the widget.
void buildExpression()
Open the expression builder widget.
QgsLabelingRulePropsWidget(QgsRuleBasedLabeling::Rule *rule, QgsVectorLayer *layer, QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr)
constructor
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:93
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:734
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
The QgsMapSettings class contains configuration for rendering of the map.
Contains settings for how a map layer will be labeled.
QString fieldName
Name of field (or an expression) to use for label text.
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for label settings.
Base class for any widget that can be shown as a inline panel.
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.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
The class is used as a container of context for various read/write operations on other objects.
Model for rule based rendering rules view.
QgsRuleBasedLabelingModel(QgsRuleBasedLabeling::Rule *rootRule, QObject *parent=nullptr)
constructor
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QStringList mimeTypes() const override
QgsRuleBasedLabeling::Rule * mRootRule
Qt::DropActions supportedDropActions() const override
void insertRule(const QModelIndex &parent, int before, QgsRuleBasedLabeling::Rule *newrule)
Inserts a new rule at the specified position.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
provide model index for parent's child item
QgsRuleBasedLabeling::Rule * ruleForIndex(const QModelIndex &index) const
Returns the rule at the specified index.
void updateRule(const QModelIndex &parent, int row)
Updates the rule at the specified position.
QMimeData * mimeData(const QModelIndexList &indexes) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex parent(const QModelIndex &index) const override
provide parent model index
Qt::ItemFlags flags(const QModelIndex &index) const override
int columnCount(const QModelIndex &=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Widget for configuring rule based labeling.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsRuleBasedLabelingWidget(QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
constructor
A child rule for QgsRuleBasedLabeling.
QgsRuleBasedLabeling::Rule * clone() const
clone this rule, return new instance
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
double maximumScale() const
Returns the maximum map scale (i.e.
void setDescription(const QString &description)
Set a human readable description for this rule.
bool dependsOnScale() const
Determines if scale based labeling is active.
const QgsRuleBasedLabeling::RuleList & children() const
Returns all children rules of this rule.
QString filterExpression() const
A filter that will check if this rule applies.
const QgsRuleBasedLabeling::Rule * parent() const
The parent rule.
bool active() const
Returns if this rule is active.
QgsPalLayerSettings * settings() const
Returns the labeling settings.
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
static QgsRuleBasedLabeling::Rule * create(const QDomElement &ruleElem, const QgsReadWriteContext &context, bool reuseId=true)
Create a rule from an XML definition.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
store labeling info to XML element
void setSettings(QgsPalLayerSettings *settings)
Sets new settings (or nullptr). Deletes old settings if any.
bool isElse() const
Check if this rule is an ELSE rule.
void insertChild(int i, QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
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.
double minimumScale() const
Returns the minimum map scale (i.e.
void appendChild(QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
QString description() const
A human readable description for this rule.
Rule based labeling for a vector layer.
QgsRuleBasedLabeling::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.
void widgetChanged()
Emitted when the text format defined by the widget changes.
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugError(str)
Definition: qgslogger.h:38
const double ICON_PADDING_FACTOR
void _renderer2labelingRules(QDomElement &ruleElem)