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