QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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"
24 #include "qgsrulebasedlabeling.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsvectorlayerlabeling.h"
27 #include "qgslogger.h"
29 
30 #include <QAction>
31 #include <QClipboard>
32 #include <QMessageBox>
33 
34 
35 static QList<QgsExpressionContextScope *> _globalProjectAtlasMapLayerScopes( QgsMapCanvas *mapCanvas, const QgsMapLayer *layer )
36 {
37  QList<QgsExpressionContextScope *> scopes;
41  if ( mapCanvas )
42  {
45  }
46  else
47  {
49  }
50  scopes << QgsExpressionContextUtils::layerScope( layer );
51  return scopes;
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 = qgis::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 
114 {
115  delete mRootRule;
116 }
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  }
130  QgsPanelWidget::setDockMode( dockMode );
131 }
132 
133 void QgsRuleBasedLabelingWidget::addRule()
134 {
135 
137 
138  QgsRuleBasedLabeling::Rule *current = currentRule();
139  if ( current )
140  {
141  // add after this rule
142  QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
143  mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
144  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  int rows = mModel->rowCount();
151  mModel->insertRule( QModelIndex(), rows, newrule );
152  QModelIndex newindex = mModel->index( rows, 0 );
153  viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
154  }
155  editRule();
156 }
157 
158 void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted( QgsPanelWidget *panel )
159 {
160  QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
161  widget->apply();
162 
163  QModelIndex index = viewRules->selectionModel()->currentIndex();
164  mModel->updateRule( index.parent(), index.row() );
165 }
166 
167 void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
168 {
169  ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
170 }
171 
172 
173 void QgsRuleBasedLabelingWidget::editRule()
174 {
175  editRule( viewRules->selectionModel()->currentIndex() );
176 }
177 
178 void QgsRuleBasedLabelingWidget::editRule( const QModelIndex &index )
179 {
180  if ( !index.isValid() )
181  return;
182 
183  QgsRuleBasedLabeling::Rule *rule = mModel->ruleForIndex( index );
184 
185  QgsLabelingRulePropsWidget *widget = new QgsLabelingRulePropsWidget( rule, mLayer, this, mCanvas );
186  widget->setPanelTitle( tr( "Edit Rule" ) );
187  connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted );
188  connect( widget, &QgsLabelingRulePropsWidget::widgetChanged, this, &QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel );
189  openPanel( widget );
190 }
191 
192 void QgsRuleBasedLabelingWidget::removeRule()
193 {
194  QItemSelection sel = viewRules->selectionModel()->selection();
195  const auto constSel = sel;
196  for ( const QItemSelectionRange &range : constSel )
197  {
198  if ( range.isValid() )
199  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
200  }
201  // make sure that the selection is gone
202  viewRules->selectionModel()->clear();
203 }
204 
205 void QgsRuleBasedLabelingWidget::copy()
206 {
207  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
208 
209  if ( indexlist.isEmpty() )
210  return;
211 
212  QMimeData *mime = mModel->mimeData( indexlist );
213  QApplication::clipboard()->setMimeData( mime );
214 }
215 
216 void QgsRuleBasedLabelingWidget::paste()
217 {
218  const QMimeData *mime = QApplication::clipboard()->mimeData();
219  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
220  QModelIndex index;
221  if ( indexlist.isEmpty() )
222  index = mModel->index( mModel->rowCount(), 0 );
223  else
224  index = indexlist.first();
225  mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
226 }
227 
228 QgsRuleBasedLabeling::Rule *QgsRuleBasedLabelingWidget::currentRule()
229 {
230  QItemSelectionModel *sel = viewRules->selectionModel();
231  QModelIndex idx = sel->currentIndex();
232  if ( !idx.isValid() )
233  return nullptr;
234  return mModel->ruleForIndex( idx );
235 }
236 
238 
240  : QAbstractItemModel( parent )
241  , mRootRule( rootRule )
242 {
243 }
244 
245 Qt::ItemFlags QgsRuleBasedLabelingModel::flags( const QModelIndex &index ) const
246 {
247  if ( !index.isValid() )
248  return Qt::ItemIsDropEnabled;
249 
250  // allow drop only at first column
251  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
252 
253  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
254 
255  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
256  Qt::ItemIsEditable | checkable |
257  Qt::ItemIsDragEnabled | drop;
258 }
259 
260 QVariant QgsRuleBasedLabelingModel::data( const QModelIndex &index, int role ) const
261 {
262  if ( !index.isValid() )
263  return QVariant();
264 
266 
267  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
268  {
269  switch ( index.column() )
270  {
271  case 0:
272  return rule->description();
273  case 1:
274  if ( rule->isElse() )
275  {
276  return "ELSE";
277  }
278  else
279  {
280  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
281  }
282  case 2:
283  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
284  case 3:
285  return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
286  case 4:
287  return rule->settings() ? rule->settings()->fieldName : QVariant();
288  default:
289  return QVariant();
290  }
291  }
292  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->settings() )
293  {
294  // TODO return QgsSymbolLayerUtils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
295  return QVariant();
296  }
297  else if ( role == Qt::TextAlignmentRole )
298  {
299  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
300  }
301  else if ( role == Qt::FontRole && index.column() == 1 )
302  {
303  if ( rule->isElse() )
304  {
305  QFont italicFont;
306  italicFont.setItalic( true );
307  return italicFont;
308  }
309  return QVariant();
310  }
311  else if ( role == Qt::EditRole )
312  {
313  switch ( index.column() )
314  {
315  case 0:
316  return rule->description();
317  case 1:
318  return rule->filterExpression();
319  case 2:
320  return rule->minimumScale();
321  case 3:
322  return rule->maximumScale();
323  case 4:
324  return rule->settings() ? rule->settings()->fieldName : QVariant();
325  default:
326  return QVariant();
327  }
328  }
329  else if ( role == Qt::CheckStateRole )
330  {
331  if ( index.column() != 0 )
332  return QVariant();
333  return rule->active() ? Qt::Checked : Qt::Unchecked;
334  }
335  else
336  return QVariant();
337 }
338 
339 QVariant QgsRuleBasedLabelingModel::headerData( int section, Qt::Orientation orientation, int role ) const
340 {
341  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
342  {
343  QStringList lst;
344  lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. Scale" ) << tr( "Max. Scale" ) << tr( "Text" ); // << tr( "Count" ) << tr( "Duplicate Count" );
345  return lst[section];
346  }
347 
348  return QVariant();
349 }
350 
351 int QgsRuleBasedLabelingModel::rowCount( const QModelIndex &parent ) const
352 {
353  if ( parent.column() > 0 )
354  return 0;
355 
357 
358  return parentRule->children().count();
359 }
360 
361 int QgsRuleBasedLabelingModel::columnCount( const QModelIndex & ) const
362 {
363  return 5;
364 }
365 
366 QModelIndex QgsRuleBasedLabelingModel::index( int row, int column, const QModelIndex &parent ) const
367 {
368  if ( hasIndex( row, column, parent ) )
369  {
371  QgsRuleBasedLabeling::Rule *childRule = parentRule->children()[row];
372  return createIndex( row, column, childRule );
373  }
374  return QModelIndex();
375 }
376 
377 QModelIndex QgsRuleBasedLabelingModel::parent( const QModelIndex &index ) const
378 {
379  if ( !index.isValid() )
380  return QModelIndex();
381 
383  QgsRuleBasedLabeling::Rule *parentRule = childRule->parent();
384 
385  if ( parentRule == mRootRule )
386  return QModelIndex();
387 
388  // this is right: we need to know row number of our parent (in our grandparent)
389  int row = parentRule->parent()->children().indexOf( parentRule );
390 
391  return createIndex( row, 0, parentRule );
392 }
393 
394 bool QgsRuleBasedLabelingModel::setData( const QModelIndex &index, const QVariant &value, int role )
395 {
396  if ( !index.isValid() )
397  return false;
398 
400 
401  if ( role == Qt::CheckStateRole )
402  {
403  rule->setActive( value.toInt() == Qt::Checked );
404  emit dataChanged( index, index );
405  return true;
406  }
407 
408  if ( role != Qt::EditRole )
409  return false;
410 
411  switch ( index.column() )
412  {
413  case 0: // description
414  rule->setDescription( value.toString() );
415  break;
416  case 1: // filter
417  rule->setFilterExpression( value.toString() );
418  break;
419  case 2: // scale min
420  rule->setMinimumScale( value.toDouble() );
421  break;
422  case 3: // scale max
423  rule->setMaximumScale( value.toDouble() );
424  break;
425  case 4: // label text
426  if ( !rule->settings() )
427  return false;
428  rule->settings()->fieldName = value.toString();
429  break;
430  default:
431  return false;
432  }
433 
434  emit dataChanged( index, index );
435  return true;
436 }
437 
439 {
440  return Qt::MoveAction; // | Qt::CopyAction
441 }
442 
444 {
445  QStringList types;
446  types << QStringLiteral( "application/vnd.text.list" );
447  return types;
448 }
449 
450 // manipulate DOM before dropping it so that rules are more useful
451 void _renderer2labelingRules( QDomElement &ruleElem )
452 {
453  // labeling rules recognize only "description"
454  if ( ruleElem.hasAttribute( QStringLiteral( "label" ) ) )
455  ruleElem.setAttribute( QStringLiteral( "description" ), ruleElem.attribute( QStringLiteral( "label" ) ) );
456 
457  // run recursively
458  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
459  while ( !childRuleElem.isNull() )
460  {
461  _renderer2labelingRules( childRuleElem );
462  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
463  }
464 }
465 
466 QMimeData *QgsRuleBasedLabelingModel::mimeData( const QModelIndexList &indexes ) const
467 {
468  QMimeData *mimeData = new QMimeData();
469  QByteArray encodedData;
470 
471  QDataStream stream( &encodedData, QIODevice::WriteOnly );
472 
473  const auto constIndexes = indexes;
474  for ( const QModelIndex &index : constIndexes )
475  {
476  // each item consists of several columns - let's add it with just first one
477  if ( !index.isValid() || index.column() != 0 )
478  continue;
479 
480  // we use a clone of the existing rule because it has a new unique rule key
481  // non-unique rule keys would confuse other components using them (e.g. legend)
483  QDomDocument doc;
484 
485  QDomElement rootElem = doc.createElement( QStringLiteral( "rule_mime" ) );
486  rootElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "labeling" ) ); // for determining whether rules are from renderer or labeling
487  QDomElement rulesElem = rule->save( doc, QgsReadWriteContext() );
488  rootElem.appendChild( rulesElem );
489  doc.appendChild( rootElem );
490 
491  delete rule;
492 
493  stream << doc.toString( -1 );
494  }
495 
496  mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
497  return mimeData;
498 }
499 
500 bool QgsRuleBasedLabelingModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
501 {
502  Q_UNUSED( column )
503 
504  if ( action == Qt::IgnoreAction )
505  return true;
506 
507  if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
508  return false;
509 
510  if ( parent.column() > 0 )
511  return false;
512 
513  QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
514  QDataStream stream( &encodedData, QIODevice::ReadOnly );
515  int rows = 0;
516 
517  if ( row == -1 )
518  {
519  // the item was dropped at a parent - we may decide where to put the items - let's append them
520  row = rowCount( parent );
521  }
522 
523  while ( !stream.atEnd() )
524  {
525  QString text;
526  stream >> text;
527 
528  QDomDocument doc;
529  if ( !doc.setContent( text ) )
530  continue;
531  QDomElement rootElem = doc.documentElement();
532  if ( rootElem.tagName() != QLatin1String( "rule_mime" ) )
533  continue;
534  QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral( "rule" ) );
535  if ( rootElem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "renderer" ) )
536  _renderer2labelingRules( ruleElem ); // do some modifications so that we load the rules more nicely
538 
539  insertRule( parent, row + rows, rule );
540 
541  ++rows;
542  }
543  return true;
544 }
545 
546 bool QgsRuleBasedLabelingModel::removeRows( int row, int count, const QModelIndex &parent )
547 {
549 
550  if ( row < 0 || row >= parentRule->children().count() )
551  return false;
552 
553  beginRemoveRows( parent, row, row + count - 1 );
554 
555  for ( int i = 0; i < count; i++ )
556  {
557  if ( row < parentRule->children().count() )
558  {
559  parentRule->removeChildAt( row );
560  }
561  else
562  {
563  QgsDebugMsg( QStringLiteral( "trying to remove invalid index - this should not happen!" ) );
564  }
565  }
566 
567  endRemoveRows();
568 
569  return true;
570 }
571 
573 {
574  if ( index.isValid() )
575  return static_cast<QgsRuleBasedLabeling::Rule *>( index.internalPointer() );
576  return mRootRule;
577 }
578 
579 void QgsRuleBasedLabelingModel::insertRule( const QModelIndex &parent, int before, QgsRuleBasedLabeling::Rule *newrule )
580 {
581  beginInsertRows( parent, before, before );
582 
584  parentRule->insertChild( before, newrule );
585 
586  endInsertRows();
587 }
588 
589 void QgsRuleBasedLabelingModel::updateRule( const QModelIndex &parent, int row )
590 {
591  emit dataChanged( index( row, 0, parent ),
592  index( row, columnCount( parent ), parent ) );
593 }
594 
596 
598  : QgsPanelWidget( parent )
599  , mRule( rule )
600  , mLayer( layer )
601  , mSettings( nullptr )
602  , mMapCanvas( mapCanvas )
603 {
604  setupUi( this );
605 
606  mElseRadio->setChecked( mRule->isElse() );
607  mFilterRadio->setChecked( !mRule->isElse() );
608  editFilter->setText( mRule->filterExpression() );
609  editFilter->setToolTip( mRule->filterExpression() );
610  editDescription->setText( mRule->description() );
611  editDescription->setToolTip( mRule->description() );
612 
613  if ( mRule->dependsOnScale() )
614  {
615  groupScale->setChecked( true );
616  // caution: rule uses scale denom, scale widget uses true scales
617  mScaleRangeWidget->setScaleRange( std::max( rule->minimumScale(), 0.0 ),
618  std::max( rule->maximumScale(), 0.0 ) );
619  }
620  mScaleRangeWidget->setMapCanvas( mMapCanvas );
621 
622  if ( mRule->settings() )
623  {
624  groupSettings->setChecked( true );
625  mSettings = new QgsPalLayerSettings( *mRule->settings() ); // use a clone!
626  }
627  else
628  {
629  groupSettings->setChecked( false );
630  mSettings = new QgsPalLayerSettings;
631  }
632 
633  mLabelingGui = new QgsLabelingGui( nullptr, mMapCanvas, *mSettings, this );
634  mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
635  QVBoxLayout *l = new QVBoxLayout;
636  l->addWidget( mLabelingGui );
637  groupSettings->setLayout( l );
638 
639  mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
640  mLabelingGui->setLayer( mLayer );
641 
642  connect( btnExpressionBuilder, &QAbstractButton::clicked, this, &QgsLabelingRulePropsWidget::buildExpression );
643  connect( btnTestFilter, &QAbstractButton::clicked, this, &QgsLabelingRulePropsWidget::testFilter );
644  connect( editFilter, &QLineEdit::textEdited, this, &QgsLabelingRulePropsWidget::widgetChanged );
645  connect( editDescription, &QLineEdit::textChanged, this, &QgsLabelingRulePropsWidget::widgetChanged );
646  connect( groupScale, &QGroupBox::toggled, this, &QgsLabelingRulePropsWidget::widgetChanged );
648  connect( groupSettings, &QGroupBox::toggled, this, &QgsLabelingRulePropsWidget::widgetChanged );
650  connect( mFilterRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
651  connect( mElseRadio, &QRadioButton::toggled, this, [ = ]( bool toggled ) { if ( toggled ) editFilter->setText( QStringLiteral( "ELSE" ) );} );
652 }
653 
655 {
656  delete mSettings;
657 }
658 
660 {
661  QgsPanelWidget::setDockMode( dockMode );
662  mLabelingGui->setDockMode( dockMode );
663 }
664 
665 void QgsLabelingRulePropsWidget::testFilter()
666 {
667  if ( !mFilterRadio->isChecked() )
668  return;
669 
670  QgsExpression filter( editFilter->text() );
671  if ( filter.hasParserError() )
672  {
673  QMessageBox::critical( this, tr( "Test Filter" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
674  return;
675  }
676 
677  QgsExpressionContext context( _globalProjectAtlasMapLayerScopes( mMapCanvas, mLayer ) );
678 
679  if ( !filter.prepare( &context ) )
680  {
681  QMessageBox::critical( this, tr( "Test Filter" ), filter.evalErrorString() );
682  return;
683  }
684 
685  QApplication::setOverrideCursor( Qt::WaitCursor );
686 
687  QgsFeatureIterator fit = mLayer->getFeatures();
688 
689  int count = 0;
690  QgsFeature f;
691  while ( fit.nextFeature( f ) )
692  {
693  context.setFeature( f );
694 
695  QVariant value = filter.evaluate( &context );
696  if ( value.toInt() != 0 )
697  count++;
698  if ( filter.hasEvalError() )
699  break;
700  }
701 
702  QApplication::restoreOverrideCursor();
703 
704  QMessageBox::information( this, tr( "Test Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
705 }
706 
707 
708 void QgsLabelingRulePropsWidget::buildExpression()
709 {
710  QgsExpressionContext context( _globalProjectAtlasMapLayerScopes( mMapCanvas, mLayer ) );
711 
712  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, QStringLiteral( "generic" ), context );
713 
714  if ( dlg.exec() )
715  editFilter->setText( dlg.expressionText() );
716 }
717 
719 {
720  QString filter = mElseRadio->isChecked() ? QStringLiteral( "ELSE" ) : editFilter->text();
721  mRule->setFilterExpression( filter );
722  mRule->setDescription( editDescription->text() );
723  mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
724  mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
725  mRule->setSettings( groupSettings->isChecked() ? new QgsPalLayerSettings( mLabelingGui->layerSettings() ) : nullptr );
726 }
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:369
qgsexpressioncontextutils.h
QgsRuleBasedLabeling::rootRule
QgsRuleBasedLabeling::Rule * rootRule()
Definition: qgsrulebasedlabeling.cpp:447
QgsRuleBasedLabeling::Rule
Definition: qgsrulebasedlabeling.h:52
QgsRuleBasedLabeling::Rule::setMinimumScale
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
Definition: qgsrulebasedlabeling.h:144
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:33
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
qgsmapcanvas.h
QgsPanelWidget::setDockMode
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
Definition: qgspanelwidget.cpp:44
QgsPalLayerSettings
Definition: qgspallabeling.h:205
QgsLabelingRulePropsWidget::QgsLabelingRulePropsWidget
QgsLabelingRulePropsWidget(QgsRuleBasedLabeling::Rule *rule, QgsVectorLayer *layer, QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr)
constructor
Definition: qgsrulebasedlabelingwidget.cpp:597
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:390
qgsreadwritecontext.h
QgsRuleBasedLabelingModel::columnCount
int columnCount(const QModelIndex &=QModelIndex()) const override
Definition: qgsrulebasedlabelingwidget.cpp:361
QgsRuleBasedLabeling::Rule::setActive
void setActive(bool state)
Sets if this rule is active.
Definition: qgsrulebasedlabeling.h:173
QgsRuleBasedLabelingModel::rowCount
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Definition: qgsrulebasedlabelingwidget.cpp:351
qgsfeatureiterator.h
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:264
QgsRuleBasedLabelingModel::index
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
provide model index for parent's child item
Definition: qgsrulebasedlabelingwidget.cpp:366
QgsRuleBasedLabelingModel::dropMimeData
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Definition: qgsrulebasedlabelingwidget.cpp:500
QgsMapCanvas
Definition: qgsmapcanvas.h:83
QgsRuleBasedLabelingWidget::setDockMode
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
Definition: qgsrulebasedlabelingwidget.cpp:118
QgsRuleBasedLabelingModel::parent
QModelIndex parent(const QModelIndex &index) const override
provide parent model index
Definition: qgsrulebasedlabelingwidget.cpp:377
QgsScaleComboBox::toString
static QString toString(double scale)
Helper function to convert a scale double to scale string.
Definition: qgsscalecombobox.cpp:203
QgsExpressionContextUtils::mapSettingsScope
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
Definition: qgsexpressioncontextutils.cpp:356
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:458
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsApplication::iconPath
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Definition: qgsapplication.cpp:594
QgsRuleBasedLabelingModel::updateRule
void updateRule(const QModelIndex &parent, int row)
Updates the rule at the specified position.
Definition: qgsrulebasedlabelingwidget.cpp:589
QgsAbstractVectorLayerLabeling::type
virtual QString type() const =0
Unique type string of the labeling configuration implementation.
QgsRuleBasedLabeling
Definition: qgsrulebasedlabeling.h:40
QgsRuleBasedLabeling::Rule::setDescription
void setDescription(const QString &description)
Set a human readable description for this rule.
Definition: qgsrulebasedlabeling.h:167
QgsRuleBasedLabeling::Rule::filterExpression
QString filterExpression() const
A filter that will check if this rule applies.
Definition: qgsrulebasedlabeling.h:108
QgsRuleBasedLabeling::Rule::removeChildAt
void removeChildAt(int i)
delete child rule
Definition: qgsrulebasedlabeling.cpp:190
QgsRuleBasedLabeling::Rule::active
bool active() const
Returns if this rule is active.
Definition: qgsrulebasedlabeling.h:122
qgsapplication.h
QgsRuleBasedLabeling::Rule::setMaximumScale
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
Definition: qgsrulebasedlabeling.h:153
QgsRuleBasedLabeling::Rule::parent
const QgsRuleBasedLabeling::Rule * parent() const
The parent rule.
Definition: qgsrulebasedlabeling.h:213
QgsRuleBasedLabelingModel::insertRule
void insertRule(const QModelIndex &parent, int before, QgsRuleBasedLabeling::Rule *newrule)
Inserts a new rule at the specified position.
Definition: qgsrulebasedlabelingwidget.cpp:579
QgsRuleBasedLabelingModel::removeRows
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Definition: qgsrulebasedlabelingwidget.cpp:546
QgsRuleBasedLabeling::Rule::settings
QgsPalLayerSettings * settings() const
Returns the labeling settings.
Definition: qgsrulebasedlabeling.h:75
QgsExpressionContextUtils::projectScope
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Definition: qgsexpressioncontextutils.cpp:221
QgsLabelingRulePropsWidget::~QgsLabelingRulePropsWidget
~QgsLabelingRulePropsWidget() override
Definition: qgsrulebasedlabelingwidget.cpp:654
qgsrulebasedlabeling.h
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsRuleBasedLabelingModel::mimeTypes
QStringList mimeTypes() const override
Definition: qgsrulebasedlabelingwidget.cpp:443
QgsRuleBasedLabelingModel::mRootRule
QgsRuleBasedLabeling::Rule * mRootRule
Definition: qgsrulebasedlabelingwidget.h:95
QgsRuleBasedLabeling::Rule::save
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
store labeling info to XML element
Definition: qgsrulebasedlabeling.cpp:278
QgsPanelWidget::panelAccepted
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QgsRuleBasedLabeling::Rule::maximumScale
double maximumScale() const
Returns the maximum map scale (i.e.
Definition: qgsrulebasedlabeling.h:92
QgsVectorLayer::labeling
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
Definition: qgsvectorlayer.h:1598
QgsExpressionBuilderDialog
Definition: qgsexpressionbuilderdialog.h:30
QgsRuleBasedLabelingWidget::QgsRuleBasedLabelingWidget
QgsRuleBasedLabelingWidget(QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
constructor
Definition: qgsrulebasedlabelingwidget.cpp:55
QgsRuleBasedLabeling::Rule::insertChild
void insertChild(int i, QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
Definition: qgsrulebasedlabeling.cpp:183
QgsMapCanvas::expressionContextScope
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:623
QgsRuleBasedLabelingWidget::~QgsRuleBasedLabelingWidget
~QgsRuleBasedLabelingWidget() override
Definition: qgsrulebasedlabelingwidget.cpp:113
QgsRuleBasedLabelingModel::headerData
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Definition: qgsrulebasedlabelingwidget.cpp:339
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
QgsRuleBasedLabeling::Rule::setFilterExpression
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
Definition: qgsrulebasedlabeling.h:160
QgsRuleBasedLabelingModel::ruleForIndex
QgsRuleBasedLabeling::Rule * ruleForIndex(const QModelIndex &index) const
Returns the rule at the specified index.
Definition: qgsrulebasedlabelingwidget.cpp:572
QgsLabelingRulePropsWidget::setDockMode
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
Definition: qgsrulebasedlabelingwidget.cpp:659
QgsRuleBasedLabelingModel::mimeData
QMimeData * mimeData(const QModelIndexList &indexes) const override
Definition: qgsrulebasedlabelingwidget.cpp:466
QgsScaleRangeWidget::rangeChanged
void rangeChanged(double min, double max)
Emitted when the scale range set in the widget is changed.
QgsLabelingRulePropsWidget::apply
void apply()
Apply any changes from the widget to the set rule.
Definition: qgsrulebasedlabelingwidget.cpp:718
QgsRuleBasedLabelingModel::setData
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Definition: qgsrulebasedlabelingwidget.cpp:394
QgsRuleBasedLabeling::Rule::isElse
bool isElse() const
Check if this rule is an ELSE rule.
Definition: qgsrulebasedlabeling.h:129
QgsRuleBasedLabeling::Rule::dependsOnScale
bool dependsOnScale() const
Determines if scale based labeling is active.
Definition: qgsrulebasedlabeling.h:82
QgsRuleBasedLabelingModel::QgsRuleBasedLabelingModel
QgsRuleBasedLabelingModel(QgsRuleBasedLabeling::Rule *rootRule, QObject *parent=nullptr)
constructor
Definition: qgsrulebasedlabelingwidget.cpp:239
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext....
Definition: qgsexpressioncontext.h:111
qgsvectorlayer.h
QgsLabelingRulePropsWidget::rule
QgsRuleBasedLabeling::Rule * rule()
Returns the rule being edited.
Definition: qgsrulebasedlabelingwidget.h:171
QgsPalLayerSettings::fieldName
QString fieldName
Name of field (or an expression) to use for label text.
Definition: qgspallabeling.h:531
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:373
QgsRuleBasedLabeling::Rule::clone
QgsRuleBasedLabeling::Rule * clone() const
clone this rule, return new instance
Definition: qgsrulebasedlabeling.cpp:227
QgsRuleBasedLabeling::Rule::children
const QgsRuleBasedLabeling::RuleList & children() const
Returns all children rules of this rule.
Definition: qgsrulebasedlabeling.h:192
QgsTextFormatWidget::widgetChanged
void widgetChanged()
Emitted when the text format defined by the widget changes.
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsRuleBasedLabeling::Rule::appendChild
void appendChild(QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
Definition: qgsrulebasedlabeling.cpp:176
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsRuleBasedLabeling::Rule::description
QString description() const
A human readable description for this rule.
Definition: qgsrulebasedlabeling.h:115
QgsRuleBasedLabelingModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: qgsrulebasedlabelingwidget.cpp:260
QgsRuleBasedLabeling::Rule::create
static QgsRuleBasedLabeling::Rule * create(const QDomElement &ruleElem, const QgsReadWriteContext &context)
Create a rule from an XML definition.
Definition: qgsrulebasedlabeling.cpp:238
QgsExpressionContextUtils::atlasScope
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
Definition: qgsexpressioncontextutils.cpp:570
qgsrulebasedlabelingwidget.h
QgsFeature
Definition: qgsfeature.h:55
qgsvectorlayerlabeling.h
QgsRuleBasedLabelingModel
Definition: qgsrulebasedlabelingwidget.h:41
qgslogger.h
QgsRuleBasedLabeling::Rule::minimumScale
double minimumScale() const
Returns the minimum map scale (i.e.
Definition: qgsrulebasedlabeling.h:102
QgsExpression
Definition: qgsexpression.h:113
QgsMapSettings
Definition: qgsmapsettings.h:86
QgsFeatureIterator
Definition: qgsfeatureiterator.h:263
QgsRuleBasedLabelingModel::flags
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: qgsrulebasedlabelingwidget.cpp:245
_renderer2labelingRules
void _renderer2labelingRules(QDomElement &ruleElem)
Definition: qgsrulebasedlabelingwidget.cpp:451
QgsRuleBasedLabelingModel::supportedDropActions
Qt::DropActions supportedDropActions() const override
Definition: qgsrulebasedlabelingwidget.cpp:438
qgsproject.h
qgslabelinggui.h
QgsAbstractVectorLayerLabeling::settings
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
qgsexpressionbuilderdialog.h