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