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