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