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