QGIS API Documentation  3.13.0-Master (740be229cb)
qgsvariableeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvariableeditorwidget.cpp
3  ---------------------------
4  Date : April 2015
5  Copyright : (C) 2015 by Nyall Dawson
6  Email : nyall dot dawson 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  ***************************************************************************/
15 
17 #include "qgsexpressioncontext.h"
18 #include "qgsapplication.h"
19 #include "qgssettings.h"
20 #include "qgsexpression.h"
21 
22 #include <QVBoxLayout>
23 #include <QTreeWidget>
24 #include <QPainter>
25 #include <QKeyEvent>
26 #include <QMouseEvent>
27 #include <QLineEdit>
28 #include <QPushButton>
29 #include <QHeaderView>
30 #include <QMessageBox>
31 
32 
33 //
34 // QgsVariableEditorWidget
35 //
36 
38  : QWidget( parent )
39 {
40  QVBoxLayout *verticalLayout = new QVBoxLayout( this );
41  verticalLayout->setSpacing( 3 );
42  verticalLayout->setContentsMargins( 3, 3, 3, 3 );
43  mTreeWidget = new QgsVariableEditorTree( this );
44  mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
45  verticalLayout->addWidget( mTreeWidget );
46  QHBoxLayout *horizontalLayout = new QHBoxLayout();
47  horizontalLayout->setSpacing( 6 );
48  QSpacerItem *horizontalSpacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
49  horizontalLayout->addItem( horizontalSpacer );
50  mAddButton = new QPushButton();
51  mAddButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
52  mAddButton->setEnabled( false );
53  mAddButton->setToolTip( tr( "Add variable" ) );
54  horizontalLayout->addWidget( mAddButton );
55  mRemoveButton = new QPushButton();
56  mRemoveButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyRemove.svg" ) ) );
57  mRemoveButton->setEnabled( false );
58  mRemoveButton->setToolTip( tr( "Remove variable" ) );
59  horizontalLayout->addWidget( mRemoveButton );
60  verticalLayout->addLayout( horizontalLayout );
61  connect( mRemoveButton, &QAbstractButton::clicked, this, &QgsVariableEditorWidget::mRemoveButton_clicked );
62  connect( mAddButton, &QAbstractButton::clicked, this, &QgsVariableEditorWidget::mAddButton_clicked );
63  connect( mTreeWidget, &QTreeWidget::itemSelectionChanged, this, &QgsVariableEditorWidget::selectionChanged );
64  connect( mTreeWidget, &QgsVariableEditorTree::scopeChanged, this, &QgsVariableEditorWidget::scopeChanged );
65 
66  //setContext clones context
68  setContext( context );
69  delete context;
70 }
71 
73 {
74  QgsSettings settings;
75  settings.setValue( saveKey() + "column0width", mTreeWidget->header()->sectionSize( 0 ) );
76 }
77 
78 void QgsVariableEditorWidget::showEvent( QShowEvent *event )
79 {
80  // initialize widget on first show event only
81  if ( mShown )
82  {
83  event->accept();
84  return;
85  }
86 
87  //restore split size
88  QgsSettings settings;
89  QVariant val;
90  val = settings.value( saveKey() + "column0width" );
91  bool ok;
92  int sectionSize = val.toInt( &ok );
93  if ( ok )
94  {
95  mTreeWidget->header()->resizeSection( 0, sectionSize );
96  }
97  mShown = true;
98 
99  QWidget::showEvent( event );
100 }
101 
103 {
104  mContext.reset( new QgsExpressionContext( *context ) );
105  reloadContext();
106 }
107 
109 {
110  mTreeWidget->resetTree();
111  mTreeWidget->setContext( mContext.get() );
112  mTreeWidget->refreshTree();
113 }
114 
116 {
117  mEditableScopeIndex = scopeIndex;
118  if ( mEditableScopeIndex >= 0 )
119  {
120  mAddButton->setEnabled( true );
121  }
122  mTreeWidget->setEditableScopeIndex( scopeIndex );
123  mTreeWidget->refreshTree();
124 }
125 
127 {
128  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
129  {
130  return nullptr;
131  }
132  return mContext->scope( mEditableScopeIndex );
133 }
134 
136 {
137  QVariantMap variables;
138  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
139  {
140  return variables;
141  }
142 
143  QgsExpressionContextScope *scope = mContext->scope( mEditableScopeIndex );
144  const auto constVariableNames = scope->variableNames();
145  for ( const QString &variable : constVariableNames )
146  {
147  if ( scope->isReadOnly( variable ) )
148  continue;
149 
150  variables.insert( variable, scope->variable( variable ) );
151  }
152 
153  return variables;
154 }
155 
156 QString QgsVariableEditorWidget::saveKey() const
157 {
158  // save key for load/save state
159  // currently QgsVariableEditorTree/window()/object
160  QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
161  QString saveKey = "/QgsVariableEditorTree/" + setGroup + '/';
162  return saveKey;
163 }
164 
165 void QgsVariableEditorWidget::mAddButton_clicked()
166 {
167  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
168  return;
169 
170  QgsExpressionContextScope *scope = mContext->scope( mEditableScopeIndex );
171  scope->setVariable( QStringLiteral( "new_variable" ), QVariant() );
172  mTreeWidget->refreshTree();
173  QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral( "new_variable" ) );
174  QModelIndex index = mTreeWidget->itemToIndex( item );
175  mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
176  mTreeWidget->editItem( item, 0 );
177 
178  emit scopeChanged();
179 }
180 
181 void QgsVariableEditorWidget::mRemoveButton_clicked()
182 {
183  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
184  return;
185 
186  QgsExpressionContextScope *editableScope = mContext->scope( mEditableScopeIndex );
187  QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
188 
189  const auto constSelectedItems = selectedItems;
190  for ( QTreeWidgetItem *item : constSelectedItems )
191  {
192  if ( !( item->flags() & Qt::ItemIsEditable ) )
193  continue;
194 
195  QString name = item->text( 0 );
196  QgsExpressionContextScope *itemScope = mTreeWidget->scopeFromItem( item );
197  if ( itemScope != editableScope )
198  continue;
199 
200  if ( itemScope->isReadOnly( name ) )
201  continue;
202 
203  itemScope->removeVariable( name );
204  mTreeWidget->removeItem( item );
205  }
206  mTreeWidget->refreshTree();
207 }
208 
209 void QgsVariableEditorWidget::selectionChanged()
210 {
211  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
212  {
213  mRemoveButton->setEnabled( false );
214  return;
215  }
216 
217  QgsExpressionContextScope *editableScope = mContext->scope( mEditableScopeIndex );
218  QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
219 
220  bool removeEnabled = true;
221  const auto constSelectedItems = selectedItems;
222  for ( QTreeWidgetItem *item : constSelectedItems )
223  {
224  if ( !( item->flags() & Qt::ItemIsEditable ) )
225  {
226  removeEnabled = false;
227  break;
228  }
229 
230  QString name = item->text( 0 );
231  QgsExpressionContextScope *itemScope = mTreeWidget->scopeFromItem( item );
232  if ( itemScope != editableScope )
233  {
234  removeEnabled = false;
235  break;
236  }
237 
238  if ( editableScope->isReadOnly( name ) )
239  {
240  removeEnabled = false;
241  break;
242  }
243  }
244  mRemoveButton->setEnabled( removeEnabled );
245 }
246 
247 
249 //
250 // VariableEditorTree
251 //
252 
253 QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
254  : QTreeWidget( parent )
255 {
256  // init icons
257  if ( mExpandIcon.isNull() )
258  {
259  QPixmap pix( 14, 14 );
260  pix.fill( Qt::transparent );
261  mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
262  mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
263  mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral( "/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
264  mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral( "/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
265  }
266 
267  setIconSize( QSize( 18, 18 ) );
268  setColumnCount( 2 );
269  setHeaderLabels( QStringList() << tr( "Variable" ) << tr( "Value" ) );
270  setEditTriggers( QAbstractItemView::AllEditTriggers );
271  setRootIsDecorated( false );
272  header()->setSectionsMovable( false );
273  header()->setSectionResizeMode( QHeaderView::Interactive );
274 
275  mEditorDelegate = new VariableEditorDelegate( this, this );
276  setItemDelegate( mEditorDelegate );
277 }
278 
279 QgsExpressionContextScope *QgsVariableEditorTree::scopeFromItem( QTreeWidgetItem *item ) const
280 {
281  if ( !item )
282  return nullptr;
283 
284  bool ok;
285  int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
286  if ( !ok )
287  return nullptr;
288 
289  if ( !mContext )
290  {
291  return nullptr;
292  }
293  else if ( mContext->scopeCount() > contextIndex )
294  {
295  return mContext->scope( contextIndex );
296  }
297  else
298  {
299  return nullptr;
300  }
301 }
302 
303 QTreeWidgetItem *QgsVariableEditorTree::itemFromVariable( QgsExpressionContextScope *scope, const QString &name ) const
304 {
305  int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
306  if ( contextIndex < 0 )
307  return nullptr;
308  return mVariableToItem.value( qMakePair( contextIndex, name ) );
309 }
310 
311 QgsExpressionContextScope *QgsVariableEditorTree::editableScope()
312 {
313  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
314  {
315  return nullptr;
316  }
317 
318  return mContext->scope( mEditableScopeIndex );
319 }
320 
321 void QgsVariableEditorTree::refreshTree()
322 {
323  if ( !mContext || mEditableScopeIndex < 0 )
324  {
325  clear();
326  return;
327  }
328 
329  //add all scopes from the context
330  int scopeIndex = 0;
331  const auto constScopes = mContext->scopes();
332  for ( QgsExpressionContextScope *scope : constScopes )
333  {
334  refreshScopeItems( scope, scopeIndex );
335  scopeIndex++;
336  }
337 }
338 
339 void QgsVariableEditorTree::refreshScopeVariables( QgsExpressionContextScope *scope, int scopeIndex )
340 {
341  QColor baseColor = rowColor( scopeIndex );
342  bool isCurrent = scopeIndex == mEditableScopeIndex;
343  QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
344 
345  const QStringList names = scope->filteredVariableNames();
346  for ( const QString &name : names )
347  {
348  QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
349  if ( !item )
350  {
351  item = new QTreeWidgetItem( scopeItem );
352  mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
353  }
354 
355  bool readOnly = scope->isReadOnly( name );
356  bool isActive = true;
357  QgsExpressionContextScope *activeScope = nullptr;
358  if ( mContext )
359  {
360  activeScope = mContext->activeScopeForVariable( name );
361  isActive = activeScope == scope;
362  }
363 
364  item->setFlags( item->flags() | Qt::ItemIsEnabled );
365  item->setText( 0, name );
366  const QVariant value = scope->variable( name );
367  const QString previewString = QgsExpression::formatPreviewString( value, false );
368  item->setText( 1, previewString );
369  QFont font = item->font( 0 );
370  if ( readOnly || !isCurrent )
371  {
372  font.setItalic( true );
373  item->setFlags( item->flags() ^ Qt::ItemIsEditable );
374  }
375  else
376  {
377  font.setItalic( false );
378  item->setFlags( item->flags() | Qt::ItemIsEditable );
379  }
380  if ( !isActive )
381  {
382  //overridden
383  font.setStrikeOut( true );
384  QString toolTip = tr( "Overridden by value from %1" ).arg( activeScope->name() );
385  item->setToolTip( 0, toolTip );
386  item->setToolTip( 1, toolTip );
387  }
388  else
389  {
390  font.setStrikeOut( false );
391  item->setToolTip( 0, name );
392  item->setToolTip( 1, previewString );
393  }
394  item->setFont( 0, font );
395  item->setFont( 1, font );
396  item->setData( 0, RowBaseColor, baseColor );
397  item->setData( 0, ContextIndex, scopeIndex );
398  item->setFirstColumnSpanned( false );
399  }
400 }
401 
402 void QgsVariableEditorTree::refreshScopeItems( QgsExpressionContextScope *scope, int scopeIndex )
403 {
404  QgsSettings settings;
405 
406  //add top level item
407  bool isCurrent = scopeIndex == mEditableScopeIndex;
408 
409  QTreeWidgetItem *scopeItem = nullptr;
410  if ( mScopeToItem.contains( scopeIndex ) )
411  {
412  //retrieve existing item
413  scopeItem = mScopeToItem.value( scopeIndex );
414  }
415  else
416  {
417  //create new top-level item
418  scopeItem = new QTreeWidgetItem();
419  mScopeToItem.insert( scopeIndex, scopeItem );
420  scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
421  scopeItem->setText( 0, scope->name() );
422  scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
423  scopeItem->setFirstColumnSpanned( true );
424  QFont scopeFont = scopeItem->font( 0 );
425  scopeFont .setBold( true );
426  scopeItem->setFont( 0, scopeFont );
427  scopeItem->setFirstColumnSpanned( true );
428 
429  addTopLevelItem( scopeItem );
430 
431  //expand by default if current context or context was previously expanded
432  if ( isCurrent || settings.value( "QgsVariableEditor/" + scopeItem->text( 0 ) + "/expanded" ).toBool() )
433  scopeItem->setExpanded( true );
434 
435  scopeItem->setIcon( 0, mExpandIcon );
436  }
437 
438  refreshScopeVariables( scope, scopeIndex );
439 }
440 
441 void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
442 {
443  if ( !item )
444  return;
445 
446  mVariableToItem.remove( mVariableToItem.key( item ) );
447  item->parent()->takeChild( item->parent()->indexOfChild( item ) );
448 
449  emit scopeChanged();
450 }
451 
452 void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item, const QString &name )
453 {
454  if ( !item )
455  return;
456 
457  int contextIndex = mVariableToItem.key( item ).first;
458  mVariableToItem.remove( mVariableToItem.key( item ) );
459  mVariableToItem.insert( qMakePair( contextIndex, name ), item );
460  item->setText( 0, name );
461 
462  emit scopeChanged();
463 }
464 
465 void QgsVariableEditorTree::resetTree()
466 {
467  mVariableToItem.clear();
468  mScopeToItem.clear();
469  clear();
470 }
471 
472 void QgsVariableEditorTree::emitChanged()
473 {
474  emit scopeChanged();
475 }
476 
477 void QgsVariableEditorTree::drawRow( QPainter *painter, const QStyleOptionViewItem &option,
478  const QModelIndex &index ) const
479 {
480  QStyleOptionViewItem opt = option;
481  QTreeWidgetItem *item = itemFromIndex( index );
482  if ( index.parent().isValid() )
483  {
484  //not a top-level item, so shade row background by context
485  QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
486  if ( index.row() % 2 == 1 )
487  {
488  baseColor = baseColor.lighter( 110 );
489  }
490  painter->fillRect( option.rect, baseColor );
491 
492  //declare custom text color since we've overwritten default background color
493  QPalette pal = opt.palette;
494  pal.setColor( QPalette::Active, QPalette::Text, Qt::black );
495  pal.setColor( QPalette::Inactive, QPalette::Text, Qt::black );
496  pal.setColor( QPalette::Disabled, QPalette::Text, Qt::gray );
497  opt.palette = pal;
498  }
499  QTreeWidget::drawRow( painter, opt, index );
500  QColor color = static_cast<QRgb>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
501  painter->save();
502  painter->setPen( QPen( color ) );
503  painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
504  painter->restore();
505 }
506 
507 QColor QgsVariableEditorTree::rowColor( int index ) const
508 {
509  //return some nice (inspired by Qt Designer) background row colors
510  int colorIdx = index % 6;
511  switch ( colorIdx )
512  {
513  case 0:
514  return QColor( 255, 220, 167 );
515  case 1:
516  return QColor( 255, 255, 191 );
517  case 2:
518  return QColor( 191, 255, 191 );
519  case 3:
520  return QColor( 199, 255, 255 );
521  case 4:
522  return QColor( 234, 191, 255 );
523  case 5:
524  default:
525  return QColor( 255, 191, 239 );
526  }
527 }
528 
529 void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
530 {
531  if ( !item )
532  return;
533 
534  item->setExpanded( !item->isExpanded() );
535 
536  //save expanded state
537  QgsSettings settings;
538  settings.setValue( "QgsVariableEditor/" + item->text( 0 ) + "/expanded", item->isExpanded() );
539 }
540 
541 void QgsVariableEditorTree::editNext( const QModelIndex &index )
542 {
543  if ( !index.isValid() )
544  return;
545 
546  if ( index.column() == 0 )
547  {
548  //switch to next column
549  QModelIndex nextIndex = index.sibling( index.row(), 1 );
550  if ( nextIndex.isValid() )
551  {
552  setCurrentIndex( nextIndex );
553  edit( nextIndex );
554  }
555  }
556  else
557  {
558  QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
559  if ( nextIndex.isValid() )
560  {
561  //start editing next row
562  setCurrentIndex( nextIndex );
563  edit( nextIndex );
564  }
565  else
566  {
567  edit( index );
568  }
569  }
570 }
571 
572 QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
573 {
574  if ( cursorAction == QAbstractItemView::MoveNext )
575  {
576  QModelIndex index = currentIndex();
577  if ( index.isValid() )
578  {
579  if ( index.column() + 1 < model()->columnCount() )
580  return index.sibling( index.row(), index.column() + 1 );
581  else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
582  return index.sibling( index.row() + 1, 0 );
583  else
584  return QModelIndex();
585  }
586  }
587  else if ( cursorAction == QAbstractItemView::MovePrevious )
588  {
589  QModelIndex index = currentIndex();
590  if ( index.isValid() )
591  {
592  if ( index.column() >= 1 )
593  return index.sibling( index.row(), index.column() - 1 );
594  else if ( index.row() >= 1 )
595  return index.sibling( index.row() - 1, model()->columnCount() - 1 );
596  else
597  return QModelIndex();
598  }
599  }
600 
601  return QTreeWidget::moveCursor( cursorAction, modifiers );
602 }
603 
604 void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
605 {
606  switch ( event->key() )
607  {
608  case Qt::Key_Return:
609  case Qt::Key_Enter:
610  case Qt::Key_Space:
611  {
612  QTreeWidgetItem *item = currentItem();
613  if ( item && !item->parent() )
614  {
615  event->accept();
616  toggleContextExpanded( item );
617  return;
618  }
619  else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
620  {
621  event->accept();
622  editNext( currentIndex() );
623  return;
624  }
625  break;
626  }
627  default:
628  break;
629  }
630  QTreeWidget::keyPressEvent( event );
631 }
632 
633 void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
634 {
635  QTreeWidget::mousePressEvent( event );
636  QTreeWidgetItem *item = itemAt( event->pos() );
637  if ( !item )
638  return;
639 
640  if ( item->parent() )
641  {
642  //not a top-level item
643  return;
644  }
645 
646  if ( event->pos().x() + header()->offset() > 20 )
647  {
648  //not clicking on expand icon
649  return;
650  }
651 
652  if ( event->modifiers() & Qt::ShiftModifier )
653  {
654  //shift modifier expands all
655  if ( !item->isExpanded() )
656  {
657  expandAll();
658  }
659  else
660  {
661  collapseAll();
662  }
663  }
664  else
665  {
666  toggleContextExpanded( item );
667  }
668 }
669 
670 //
671 // VariableEditorDelegate
672 //
673 
674 QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
675  const QStyleOptionViewItem &,
676  const QModelIndex &index ) const
677 {
678  if ( !mParentTree )
679  return nullptr;
680 
681  //no editing for top level items
682  if ( !index.parent().isValid() )
683  return nullptr;
684 
685  QTreeWidgetItem *item = mParentTree->indexToItem( index );
686  QgsExpressionContextScope *scope = mParentTree->scopeFromItem( item );
687  if ( !item || !scope )
688  return nullptr;
689 
690  QString variableName = mParentTree->variableNameFromIndex( index );
691 
692  //no editing inherited or read-only variables
693  if ( scope != mParentTree->editableScope() || scope->isReadOnly( variableName ) )
694  return nullptr;
695 
696  QLineEdit *lineEdit = new QLineEdit( parent );
697  lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
698  lineEdit->setAutoFillBackground( true );
699  return lineEdit;
700 }
701 
702 void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
703  const QStyleOptionViewItem &option,
704  const QModelIndex & ) const
705 {
706  editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
707 }
708 
709 QSize VariableEditorDelegate::sizeHint( const QStyleOptionViewItem &option,
710  const QModelIndex &index ) const
711 {
712  return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
713 }
714 
715 void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
716  const QModelIndex &index ) const
717 {
718  Q_UNUSED( model )
719 
720  if ( !mParentTree )
721  return;
722 
723  QTreeWidgetItem *item = mParentTree->indexToItem( index );
724  QgsExpressionContextScope *scope = mParentTree->scopeFromItem( item );
725  if ( !item || !scope )
726  return;
727 
728  QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
729  if ( !lineEdit )
730  return;
731 
732  QString variableName = mParentTree->variableNameFromIndex( index );
733  if ( index.column() == 0 )
734  {
735  //edited variable name
736  QString newName = lineEdit->text();
737  newName = newName.trimmed();
738  newName = newName.replace( ' ', '_' );
739 
740  //test for validity
741  if ( newName == variableName )
742  {
743  return;
744  }
745  if ( scope->hasVariable( newName ) )
746  {
747  //existing name
748  QMessageBox::warning( mParentTree, tr( "Rename Variable" ), tr( "A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
749  newName.append( "_1" );
750  }
751 
752  QString value = scope->variable( variableName ).toString();
753  mParentTree->renameItem( item, newName );
754  scope->removeVariable( variableName );
755  scope->setVariable( newName, value );
756  mParentTree->emitChanged();
757  }
758  else if ( index.column() == 1 )
759  {
760  //edited variable value
761  QString value = lineEdit->text();
762  if ( scope->variable( variableName ).toString() == value )
763  {
764  return;
765  }
766  scope->setVariable( variableName, value );
767  mParentTree->emitChanged();
768  }
769  mParentTree->refreshTree();
770 }
771 
void setEditableScopeIndex(int scopeIndex)
Sets the editable scope for the widget.
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in the scope.
QgsExpressionContext * context() const
Returns the current expression context for the widget.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user.
void showEvent(QShowEvent *event) override
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QStringList variableNames() const
Returns a list of variable names contained within the scope.
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QgsExpressionContextScope * editableScope() const
Returns the current editable scope for the widget.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
QgsVariableEditorWidget(QWidget *parent=nullptr)
Constructor for QgsVariableEditorWidget.
void scopeChanged()
Emitted when the user has modified a scope using the widget.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QVariant variable(const QString &name) const
Retrieves a variable&#39;s value from the scope.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void reloadContext()
Reloads all scopes from the editor&#39;s current context.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void setContext(QgsExpressionContext *context)
Overwrites the QgsExpressionContext for the widget.
QString name() const
Returns the friendly display name of the context scope.
QVariantMap variablesInActiveScope() const
Returns a map variables set within the editable scope.
QStringList filteredVariableNames() const
Returns a filtered and sorted list of variable names contained within the scope.