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