QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
Loading...
Searching...
No Matches
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
36QgsExpressionContext QgsLabelingRulePropsWidget::createExpressionContext( QgsMapCanvas *mapCanvas, const QgsMapLayer *layer )
37{
39 if ( mapCanvas )
40 {
41 context = mapCanvas->createExpressionContext();
42 }
43 else
44 {
49 }
50 context << QgsExpressionContextUtils::layerScope( layer );
51 return context;
52}
53
54
56 : QgsPanelWidget( parent )
57 , mLayer( layer )
58 , mCanvas( canvas )
59
60{
61 setupUi( this );
62
63 btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
64 btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
65 btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
66
67 mCopyAction = new QAction( tr( "Copy" ), this );
68 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
69 mPasteAction = new QAction( tr( "Paste" ), this );
70 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
71 mDeleteAction = new QAction( tr( "Remove Rule" ), this );
72 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
73
74 viewRules->addAction( mCopyAction );
75 viewRules->addAction( mPasteAction );
76 viewRules->addAction( mDeleteAction );
77
78 connect( viewRules, &QAbstractItemView::doubleClicked, this, static_cast<void ( QgsRuleBasedLabelingWidget::* )( const QModelIndex & )>( &QgsRuleBasedLabelingWidget::editRule ) );
79
80 connect( btnAddRule, &QAbstractButton::clicked, this, &QgsRuleBasedLabelingWidget::addRule );
81 connect( btnEditRule, &QAbstractButton::clicked, this, static_cast<void ( QgsRuleBasedLabelingWidget::* )()>( &QgsRuleBasedLabelingWidget::editRule ) );
82 connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsRuleBasedLabelingWidget::removeRule );
83 connect( mCopyAction, &QAction::triggered, this, &QgsRuleBasedLabelingWidget::copy );
84 connect( mPasteAction, &QAction::triggered, this, &QgsRuleBasedLabelingWidget::paste );
85 connect( mDeleteAction, &QAction::triggered, this, &QgsRuleBasedLabelingWidget::removeRule );
86
87 if ( mLayer->labeling() && mLayer->labeling()->type() == QLatin1String( "rule-based" ) )
88 {
89 const QgsRuleBasedLabeling *rl = static_cast<const QgsRuleBasedLabeling *>( mLayer->labeling() );
90 mRootRule = rl->rootRule()->clone();
91 }
92 else if ( mLayer->labeling() && mLayer->labeling()->type() == QLatin1String( "simple" ) )
93 {
94 // copy simple label settings to first rule
95 mRootRule = new QgsRuleBasedLabeling::Rule( nullptr );
96 std::unique_ptr< QgsPalLayerSettings > newSettings = std::make_unique< QgsPalLayerSettings >( mLayer->labeling()->settings() );
97 newSettings->drawLabels = true; // otherwise we may be trying to copy a "blocking" setting to a rule - which is confusing for users!
98 mRootRule->appendChild( new QgsRuleBasedLabeling::Rule( newSettings.release() ) );
99 }
100 else
101 {
102 mRootRule = new QgsRuleBasedLabeling::Rule( nullptr );
103 }
104
105 mModel = new QgsRuleBasedLabelingModel( mRootRule );
106 viewRules->setModel( mModel );
107
108 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsRuleBasedLabelingWidget::widgetChanged );
109 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsRuleBasedLabelingWidget::widgetChanged );
110 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsRuleBasedLabelingWidget::widgetChanged );
111}
112
117
119{
120 if ( dockMode )
121 {
122 // when in dock mode, these shortcuts conflict with the main window shortcuts and cannot be used
123 if ( mCopyAction )
124 mCopyAction->setShortcut( QKeySequence() );
125 if ( mPasteAction )
126 mPasteAction->setShortcut( QKeySequence() );
127 if ( mDeleteAction )
128 mDeleteAction->setShortcut( QKeySequence() );
129 }
131}
132
133void QgsRuleBasedLabelingWidget::addRule()
134{
136
137 QgsRuleBasedLabeling::Rule *current = currentRule();
138 if ( current )
139 {
140 // add after this rule
141 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
142 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
143 const QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
144 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
145 }
146 else
147 {
148 // append to root rule
149 const int rows = mModel->rowCount();
150 mModel->insertRule( QModelIndex(), rows, newrule );
151 const QModelIndex newindex = mModel->index( rows, 0 );
152 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
153 }
154 editRule();
155}
156
157void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
158{
159 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
160 widget->apply();
161
162 const QModelIndex index = viewRules->selectionModel()->currentIndex();
163 mModel->updateRule( index.parent(), index.row() );
164}
165
166void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
167{
168 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
169}
170
171
172void QgsRuleBasedLabelingWidget::editRule()
173{
174 editRule( viewRules->selectionModel()->currentIndex() );
175}
176
177void QgsRuleBasedLabelingWidget::editRule( const QModelIndex &index )
178{
179 if ( !index.isValid() )
180 return;
181
182 QgsRuleBasedLabeling::Rule *rule = mModel->ruleForIndex( index );
184
185 if ( panel && panel->dockMode() )
186 {
187 QgsLabelingRulePropsWidget *widget = new QgsLabelingRulePropsWidget( rule, mLayer, this, mCanvas );
188 widget->setPanelTitle( tr( "Edit Rule" ) );
189 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted );
190 connect( widget, &QgsLabelingRulePropsWidget::widgetChanged, this, &QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel );
191 openPanel( widget );
192 return;
193 }
194
195 QgsLabelingRulePropsDialog dlg( rule, mLayer, this, mCanvas );
196 if ( dlg.exec() )
197 {
198 mModel->updateRule( index.parent(), index.row() );
199 emit widgetChanged();
200 }
201}
202
203void QgsRuleBasedLabelingWidget::removeRule()
204{
205 const QItemSelection sel = viewRules->selectionModel()->selection();
206 const auto constSel = sel;
207 for ( const QItemSelectionRange &range : constSel )
208 {
209 if ( range.isValid() )
210 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
211 }
212 // make sure that the selection is gone
213 viewRules->selectionModel()->clear();
214}
215
216void QgsRuleBasedLabelingWidget::copy()
217{
218 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
219
220 if ( indexlist.isEmpty() )
221 return;
222
223 QMimeData *mime = mModel->mimeData( indexlist );
224 QApplication::clipboard()->setMimeData( mime );
225}
226
227void QgsRuleBasedLabelingWidget::paste()
228{
229 const QMimeData *mime = QApplication::clipboard()->mimeData();
230 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
231 QModelIndex index;
232 if ( indexlist.isEmpty() )
233 index = mModel->index( mModel->rowCount(), 0 );
234 else
235 index = indexlist.first();
236 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
237}
238
239QgsRuleBasedLabeling::Rule *QgsRuleBasedLabelingWidget::currentRule()
240{
241 QItemSelectionModel *sel = viewRules->selectionModel();
242 const QModelIndex idx = sel->currentIndex();
243 if ( !idx.isValid() )
244 return nullptr;
245 return mModel->ruleForIndex( idx );
246}
247
248#include "qgsvscrollarea.h"
249
251 : QDialog( parent )
252{
253
254#ifdef Q_OS_MAC
255 setWindowModality( Qt::WindowModal );
256#endif
257
258 QVBoxLayout *layout = new QVBoxLayout( this );
259 QgsVScrollArea *scrollArea = new QgsVScrollArea( this );
260 scrollArea->setFrameShape( QFrame::NoFrame );
261 layout->addWidget( scrollArea );
262
263 buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
264 mPropsWidget = new QgsLabelingRulePropsWidget( rule, layer, this, mapCanvas );
265
266 scrollArea->setWidget( mPropsWidget );
267 layout->addWidget( buttonBox );
268 this->setWindowTitle( "Edit Rule" );
270
271 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsLabelingRulePropsDialog::accept );
272 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
273 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsLabelingRulePropsDialog::showHelp );
274}
275
277{
278 mPropsWidget->testFilter();
279}
280
285
287{
288 mPropsWidget->apply();
289 QDialog::accept();
290}
291
292void QgsLabelingRulePropsDialog::showHelp()
293{
294 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#rule-based-labeling" ) );
295}
296
298
300 : QAbstractItemModel( parent )
301 , mRootRule( rootRule )
302{
303}
304
305Qt::ItemFlags QgsRuleBasedLabelingModel::flags( const QModelIndex &index ) const
306{
307 if ( !index.isValid() )
308 return Qt::ItemIsDropEnabled;
309
310 // allow drop only at first column
311 const Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
312
313 const Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
314
315 return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
316 Qt::ItemIsEditable | checkable |
317 Qt::ItemIsDragEnabled | drop;
318}
319
320QVariant QgsRuleBasedLabelingModel::data( const QModelIndex &index, int role ) const
321{
322 if ( !index.isValid() )
323 return QVariant();
324
326
327 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
328 {
329 switch ( index.column() )
330 {
331 case 0:
332 return rule->description();
333 case 1:
334 if ( rule->isElse() )
335 {
336 return "ELSE";
337 }
338 else
339 {
340 return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
341 }
342 case 2:
343 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
344 case 3:
345 return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
346 case 4:
347 return rule->settings() ? rule->settings()->fieldName : QVariant();
348 default:
349 return QVariant();
350 }
351 }
352 else if ( role == Qt::DecorationRole && index.column() == 0 && rule->settings() )
353 {
354 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
355 return QgsPalLayerSettings::labelSettingsPreviewPixmap( *rule->settings(), QSize( iconSize, iconSize ), QString(), static_cast< int >( iconSize * ICON_PADDING_FACTOR ) );
356 }
357 else if ( role == Qt::TextAlignmentRole )
358 {
359 return ( index.column() == 2 || index.column() == 3 ) ? static_cast<Qt::Alignment::Int>( Qt::AlignRight ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
360 }
361 else if ( role == Qt::FontRole && index.column() == 1 )
362 {
363 if ( rule->isElse() )
364 {
365 QFont italicFont;
366 italicFont.setItalic( true );
367 return italicFont;
368 }
369 return QVariant();
370 }
371 else if ( role == Qt::EditRole )
372 {
373 switch ( index.column() )
374 {
375 case 0:
376 return rule->description();
377 case 1:
378 return rule->filterExpression();
379 case 2:
380 return rule->minimumScale();
381 case 3:
382 return rule->maximumScale();
383 case 4:
384 return rule->settings() ? rule->settings()->fieldName : QVariant();
385 default:
386 return QVariant();
387 }
388 }
389 else if ( role == Qt::CheckStateRole )
390 {
391 if ( index.column() != 0 )
392 return QVariant();
393 return rule->active() ? Qt::Checked : Qt::Unchecked;
394 }
395 else
396 return QVariant();
397}
398
399QVariant QgsRuleBasedLabelingModel::headerData( int section, Qt::Orientation orientation, int role ) const
400{
401 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
402 {
403 QStringList lst;
404 lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Text" ); // << tr( "Count" ) << tr( "Duplicate Count" );
405 return lst[section];
406 }
407
408 return QVariant();
409}
410
411int QgsRuleBasedLabelingModel::rowCount( const QModelIndex &parent ) const
412{
413 if ( parent.column() > 0 )
414 return 0;
415
417
418 return parentRule->children().count();
419}
420
421int QgsRuleBasedLabelingModel::columnCount( const QModelIndex & ) const
422{
423 return 5;
424}
425
426QModelIndex QgsRuleBasedLabelingModel::index( int row, int column, const QModelIndex &parent ) const
427{
428 if ( hasIndex( row, column, parent ) )
429 {
431 QgsRuleBasedLabeling::Rule *childRule = parentRule->children()[row];
432 return createIndex( row, column, childRule );
433 }
434 return QModelIndex();
435}
436
437QModelIndex QgsRuleBasedLabelingModel::parent( const QModelIndex &index ) const
438{
439 if ( !index.isValid() )
440 return QModelIndex();
441
443 QgsRuleBasedLabeling::Rule *parentRule = childRule->parent();
444
445 if ( parentRule == mRootRule )
446 return QModelIndex();
447
448 // this is right: we need to know row number of our parent (in our grandparent)
449 const int row = parentRule->parent()->children().indexOf( parentRule );
450
451 return createIndex( row, 0, parentRule );
452}
453
454bool QgsRuleBasedLabelingModel::setData( const QModelIndex &index, const QVariant &value, int role )
455{
456 if ( !index.isValid() )
457 return false;
458
460
461 if ( role == Qt::CheckStateRole )
462 {
463 rule->setActive( value.toInt() == Qt::Checked );
464 emit dataChanged( index, index );
465 return true;
466 }
467
468 if ( role != Qt::EditRole )
469 return false;
470
471 switch ( index.column() )
472 {
473 case 0: // description
474 rule->setDescription( value.toString() );
475 break;
476 case 1: // filter
477 rule->setFilterExpression( value.toString() );
478 break;
479 case 2: // scale min
480 rule->setMinimumScale( value.toDouble() );
481 break;
482 case 3: // scale max
483 rule->setMaximumScale( value.toDouble() );
484 break;
485 case 4: // label text
486 if ( !rule->settings() )
487 return false;
488 rule->settings()->fieldName = value.toString();
489 break;
490 default:
491 return false;
492 }
493
494 emit dataChanged( index, index );
495 return true;
496}
497
499{
500 return Qt::MoveAction; // | Qt::CopyAction
501}
502
504{
505 QStringList types;
506 types << QStringLiteral( "application/vnd.text.list" );
507 return types;
508}
509
510// manipulate DOM before dropping it so that rules are more useful
511void _renderer2labelingRules( QDomElement &ruleElem )
512{
513 // labeling rules recognize only "description"
514 if ( ruleElem.hasAttribute( QStringLiteral( "label" ) ) )
515 ruleElem.setAttribute( QStringLiteral( "description" ), ruleElem.attribute( QStringLiteral( "label" ) ) );
516
517 // run recursively
518 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
519 while ( !childRuleElem.isNull() )
520 {
521 _renderer2labelingRules( childRuleElem );
522 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
523 }
524}
525
526QMimeData *QgsRuleBasedLabelingModel::mimeData( const QModelIndexList &indexes ) const
527{
528 QMimeData *mimeData = new QMimeData();
529 QByteArray encodedData;
530
531 QDataStream stream( &encodedData, QIODevice::WriteOnly );
532
533 const auto constIndexes = indexes;
534 for ( const QModelIndex &index : constIndexes )
535 {
536 // each item consists of several columns - let's add it with just first one
537 if ( !index.isValid() || index.column() != 0 )
538 continue;
539
540 // we use a clone of the existing rule because it has a new unique rule key
541 // non-unique rule keys would confuse other components using them (e.g. legend)
543 QDomDocument doc;
544
545 QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
546 rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "labeling" ) ); // for determining whether rules are from renderer or labeling
547 const QDomElement rulesElem = rule->save( doc, QgsReadWriteContext() );
548 rootElem.appendChild( rulesElem );
549 doc.appendChild( rootElem );
550
551 delete rule;
552
553 stream << doc.toString( -1 );
554 }
555
556 mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
557 return mimeData;
558}
559
560bool QgsRuleBasedLabelingModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
561{
562 Q_UNUSED( column )
563
564 if ( action == Qt::IgnoreAction )
565 return true;
566
567 if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
568 return false;
569
570 if ( parent.column() > 0 )
571 return false;
572
573 QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
574 QDataStream stream( &encodedData, QIODevice::ReadOnly );
575 int rows = 0;
576
577 if ( row == -1 )
578 {
579 // the item was dropped at a parent - we may decide where to put the items - let's append them
580 row = rowCount( parent );
581 }
582
583 while ( !stream.atEnd() )
584 {
585 QString text;
586 stream >> text;
587
588 QDomDocument doc;
589 if ( !doc.setContent( text ) )
590 continue;
591 const QDomElement rootElem = doc.documentElement();
592 if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
593 continue;
594 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
595 if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "renderer" ) )
596 _renderer2labelingRules( ruleElem ); // do some modifications so that we load the rules more nicely
598
599 insertRule( parent, row + rows, rule );
600
601 ++rows;
602 }
603 return true;
604}
605
606bool QgsRuleBasedLabelingModel::removeRows( int row, int count, const QModelIndex &parent )
607{
609
610 if ( row < 0 || row >= parentRule->children().count() )
611 return false;
612
613 beginRemoveRows( parent, row, row + count - 1 );
614
615 for ( int i = 0; i < count; i++ )
616 {
617 if ( row < parentRule->children().count() )
618 {
619 parentRule->removeChildAt( row );
620 }
621 else
622 {
623 QgsDebugError( QStringLiteral( "trying to remove invalid index - this should not happen!" ) );
624 }
625 }
626
627 endRemoveRows();
628
629 return true;
630}
631
633{
634 if ( index.isValid() )
635 return static_cast<QgsRuleBasedLabeling::Rule *>( index.internalPointer() );
636 return mRootRule;
637}
638
639void QgsRuleBasedLabelingModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedLabeling::Rule *newrule )
640{
641 beginInsertRows( parent, before, before );
642
644 parentRule->insertChild( before, newrule );
645
646 endInsertRows();
647}
648
649void QgsRuleBasedLabelingModel::updateRule( const QModelIndex &parent, int row )
650{
651 emit dataChanged( index( row, 0, parent ),
652 index( row, columnCount( parent ), parent ) );
653}
654
656
658 : QgsPanelWidget( parent )
659 , mRule( rule )
660 , mLayer( layer )
661 , mSettings( nullptr )
662 , mMapCanvas( mapCanvas )
663{
664 setupUi( this );
665
666 QButtonGroup *radioGroup = new QButtonGroup( this );
667 radioGroup->addButton( mFilterRadio );
668 radioGroup->addButton( mElseRadio );
669
670 mElseRadio->setChecked( mRule->isElse() );
671 mFilterRadio->setChecked( !mRule->isElse() );
672 editFilter->setText( mRule->filterExpression() );
673 editFilter->setToolTip( mRule->filterExpression() );
674 editDescription->setText( mRule->description() );
675 editDescription->setToolTip( mRule->description() );
676
677 if ( mRule->dependsOnScale() )
678 {
679 groupScale->setChecked( true );
680 // caution: rule uses scale denom, scale widget uses true scales
681 mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ),
682 std::max( rule->maximumScale(), 0.0 ) );
683 }
684 mScaleRangeWidget->setMapCanvas( mMapCanvas );
685
686 if ( mRule->settings() )
687 {
688 groupSettings->setChecked( true );
689 mSettings = new QgsPalLayerSettings( *mRule->settings() ); // use a clone!
690 }
691 else
692 {
693 groupSettings->setChecked( false );
694 mSettings = new QgsPalLayerSettings;
695 }
696
697 mLabelingGui = new QgsLabelingGui( nullptr, mMapCanvas, *mSettings, this );
698 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
699 QVBoxLayout *l = new QVBoxLayout;
700 l->addWidget( mLabelingGui );
701 groupSettings->setLayout( l );
702
703 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
704 mLabelingGui->setLayer( mLayer );
705
706 connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsLabelingRulePropsWidget::buildExpression );
707 connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsLabelingRulePropsWidget::testFilter );
708 connect( editFilter, &QLineEdit::textEdited, this, &QgsLabelingRulePropsWidget::widgetChanged );
709 connect( editDescription, &QLineEdit::textChanged, this, &QgsLabelingRulePropsWidget::widgetChanged );
710 connect( groupScale, &QGroupBox::toggled, this, &QgsLabelingRulePropsWidget::widgetChanged );
712 connect( groupSettings, &QGroupBox::toggled, this, &QgsLabelingRulePropsWidget::widgetChanged );
714 connect( mFilterRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
715 connect( mElseRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) );} );
716}
717
722
724{
726 mLabelingGui->setDockMode( dockMode );
727}
728
730{
731 if ( !mFilterRadio->isChecked() )
732 return;
733
734 QgsExpression filter( editFilter->text() );
735 if ( filter.hasParserError() )
736 {
737 QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
738 return;
739 }
740
741 QgsExpressionContext context( createExpressionContext( mMapCanvas, mLayer ) );
742
743 if ( !filter.prepare( &context ) )
744 {
745 QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
746 return;
747 }
748
749 QApplication::setOverrideCursor( Qt::WaitCursor );
750
751 QgsFeatureIterator fit = mLayer->getFeatures();
752
753 int count = 0;
754 QgsFeature f;
755 while ( fit.nextFeature( f ) )
756 {
757 context.setFeature( f );
758
759 const QVariant value = filter.evaluate( &context );
760 if ( value.toInt() != 0 )
761 count++;
762 if ( filter.hasEvalError() )
763 break;
764 }
765
766 QApplication::restoreOverrideCursor();
767
768 QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
769}
770
771
773{
774 const QgsExpressionContext context( createExpressionContext( mMapCanvas, mLayer ) );
775
776 QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
777
778 if ( dlg.exec() )
779 editFilter->setText( dlg.expressionText() );
780}
781
783{
784 const QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text().trimmed();
785 mRule->setFilterExpression( filter );
786 mRule->setDescription( editDescription->text() );
787 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
788 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
789 mRule->setSettings( groupSettings->isChecked() ? new QgsPalLayerSettings( mLabelingGui->layerSettings() ) : nullptr );
790}
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.
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:58
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:208
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.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Base class for all map layer types.
Definition qgsmaplayer.h:76
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.
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.
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)
const double ICON_PADDING_FACTOR