QGIS API Documentation 3.41.0-Master (cea29feecf2)
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, 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, const QStyleOptionViewItem &, const QModelIndex &index ) const
687{
688 if ( !mParentTree )
689 return nullptr;
690
691 //no editing for top level items
692 if ( !index.parent().isValid() )
693 return nullptr;
694
695 QTreeWidgetItem *item = mParentTree->indexToItem( index );
696 QgsExpressionContextScope *scope = mParentTree->scopeFromItem( item );
697 if ( !item || !scope )
698 return nullptr;
699
700 const QString variableName = mParentTree->variableNameFromIndex( index );
701
702 //no editing inherited or read-only variables
703 if ( scope != mParentTree->editableScope() || scope->isReadOnly( variableName ) )
704 return nullptr;
705
706 QLineEdit *lineEdit = new QLineEdit( parent );
707 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
708 lineEdit->setAutoFillBackground( true );
709 return lineEdit;
710}
711
712void VariableEditorDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & ) const
713{
714 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
715}
716
717QSize VariableEditorDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
718{
719 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
720}
721
722void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model, const QModelIndex &index ) const
723{
724 Q_UNUSED( model )
725
726 if ( !mParentTree )
727 return;
728
729 QTreeWidgetItem *item = mParentTree->indexToItem( index );
730 QgsExpressionContextScope *scope = mParentTree->scopeFromItem( item );
731 if ( !item || !scope )
732 return;
733
734 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( widget );
735 if ( !lineEdit )
736 return;
737
738 const QString variableName = mParentTree->variableNameFromIndex( index );
739 if ( index.column() == 0 )
740 {
741 //edited variable name
742 QString newName = lineEdit->text();
743 newName = newName.trimmed();
744 newName = newName.replace( ' ', '_' );
745
746 //test for validity
747 if ( newName == variableName )
748 {
749 return;
750 }
751 if ( scope->hasVariable( newName ) )
752 {
753 //existing name
754 QMessageBox::warning( mParentTree, tr( "Rename Variable" ), tr( "A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
755 newName.append( "_1" );
756 }
757
758 const QString value = scope->variable( variableName ).toString();
759 mParentTree->renameItem( item, newName );
760 scope->removeVariable( variableName );
761 scope->setVariable( newName, value );
762 mParentTree->emitChanged();
763 }
764 else if ( index.column() == 1 )
765 {
766 //edited variable value
767 const QString value = lineEdit->text();
768 if ( scope->variable( variableName ).toString() == value )
769 {
770 return;
771 }
772 scope->setVariable( variableName, value );
773 mParentTree->emitChanged();
774 }
775 mParentTree->refreshTree();
776}
777
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.