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