QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
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
79void 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 ) );
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
157QString 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
166void 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
182void 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
210void 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
254QgsVariableEditorTree::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
280QgsExpressionContextScope *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
304QTreeWidgetItem *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
312QgsExpressionContextScope *QgsVariableEditorTree::editableScope()
313{
314 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
315 {
316 return nullptr;
317 }
318
319 return mContext->scope( mEditableScopeIndex );
320}
321
322void 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
340void 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
403void 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
442void 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
453void 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
466void QgsVariableEditorTree::resetTree()
467{
468 mVariableToItem.clear();
469 mScopeToItem.clear();
470 clear();
471}
472
473void QgsVariableEditorTree::emitChanged()
474{
475 emit scopeChanged();
476}
477
478void 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
500QColor 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
522void 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
534void 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
565QModelIndex 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
597void 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
645void 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
686QWidget *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
714void 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
721QSize VariableEditorDelegate::sizeHint( const QStyleOptionViewItem &option,
722 const QModelIndex &index ) const
723{
724 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
725}
726
727void 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
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Single scope for storing variables and functions for use within a QgsExpressionContext.
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in the scope.
QVariant variable(const QString &name) const
Retrieves a variable's value from the scope.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
QString name() const
Returns the friendly display name of the context scope.
QStringList filteredVariableNames() const
Returns a filtered and sorted list of variable names contained within 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.
QStringList variableNames() const
Returns a list of variable names contained within the scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
Scoped object for saving and restoring a QPainter object's state.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void scopeChanged()
Emitted when the user has modified a scope using the widget.
QgsExpressionContext * context() const
Returns the current expression context for the widget.
QVariantMap variablesInActiveScope() const
Returns a map variables set within the editable scope.
void reloadContext()
Reloads all scopes from the editor's current context.
void showEvent(QShowEvent *event) override
void setEditableScopeIndex(int scopeIndex)
Sets the editable scope for the widget.
QgsVariableEditorWidget(QWidget *parent=nullptr)
Constructor for QgsVariableEditorWidget.
void setContext(QgsExpressionContext *context)
Overwrites the QgsExpressionContext for the widget.
QgsExpressionContextScope * editableScope() const
Returns the current editable scope for the widget.