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