37#include "moc_qgsvariableeditorwidget.cpp"
46 QVBoxLayout *verticalLayout =
new QVBoxLayout(
this );
47 verticalLayout->setSpacing( 3 );
48 verticalLayout->setContentsMargins( 3, 3, 3, 3 );
49 mTreeWidget =
new QgsVariableEditorTree(
this );
50 mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
51 verticalLayout->addWidget( mTreeWidget );
52 QHBoxLayout *horizontalLayout =
new QHBoxLayout();
53 horizontalLayout->setSpacing( 6 );
54 QSpacerItem *horizontalSpacer =
new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
55 horizontalLayout->addItem( horizontalSpacer );
56 mAddButton =
new QPushButton();
58 mAddButton->setEnabled(
false );
59 mAddButton->setToolTip( tr(
"Add variable" ) );
60 horizontalLayout->addWidget( mAddButton );
61 mRemoveButton =
new QPushButton();
63 mRemoveButton->setEnabled(
false );
64 mRemoveButton->setToolTip( tr(
"Remove variable" ) );
65 horizontalLayout->addWidget( mRemoveButton );
66 verticalLayout->addLayout( horizontalLayout );
67 connect( mRemoveButton, &QAbstractButton::clicked,
this, &QgsVariableEditorWidget::mRemoveButton_clicked );
68 connect( mAddButton, &QAbstractButton::clicked,
this, &QgsVariableEditorWidget::mAddButton_clicked );
69 connect( mTreeWidget, &QTreeWidget::itemSelectionChanged,
this, &QgsVariableEditorWidget::selectionChanged );
81 settings.
setValue( saveKey() +
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
96 val = settings.
value( saveKey() +
"column0width" );
98 const int sectionSize = val.toInt( &ok );
101 mTreeWidget->header()->resizeSection( 0, sectionSize );
105 QWidget::showEvent( event );
110 mContext = std::make_unique<QgsExpressionContext>( *
context );
116 mTreeWidget->resetTree();
117 mTreeWidget->setContext( mContext.get() );
118 mTreeWidget->refreshTree();
123 mEditableScopeIndex = scopeIndex;
124 if ( mEditableScopeIndex >= 0 )
126 mAddButton->setEnabled(
true );
128 mTreeWidget->setEditableScopeIndex( scopeIndex );
129 mTreeWidget->refreshTree();
134 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
138 return mContext->scope( mEditableScopeIndex );
143 QVariantMap variables;
144 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
151 for (
const QString &variable : constVariableNames )
156 variables.insert( variable, scope->
variable( variable ) );
162QString QgsVariableEditorWidget::saveKey()
const
166 const QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
167 QString saveKey =
"/QgsVariableEditorTree/" + setGroup +
'/';
171void QgsVariableEditorWidget::mAddButton_clicked()
173 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
176 QgsExpressionContextScope *scope = mContext->scope( mEditableScopeIndex );
177 scope->
setVariable( QStringLiteral(
"new_variable" ), QVariant() );
178 mTreeWidget->refreshTree();
179 QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral(
"new_variable" ) );
180 const QModelIndex index = mTreeWidget->itemToIndex( item );
181 mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
182 mTreeWidget->editItem( item, 0 );
187void QgsVariableEditorWidget::mRemoveButton_clicked()
189 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
192 QgsExpressionContextScope *
editableScope = mContext->scope( mEditableScopeIndex );
193 const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
195 const auto constSelectedItems = selectedItems;
196 for ( QTreeWidgetItem *item : constSelectedItems )
198 if ( !( item->flags() & Qt::ItemIsEditable ) )
201 const QString name = item->text( 0 );
202 QgsExpressionContextScope *itemScope = mTreeWidget->scopeFromItem( item );
210 mTreeWidget->removeItem( item );
212 mTreeWidget->refreshTree();
215void QgsVariableEditorWidget::selectionChanged()
217 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
219 mRemoveButton->setEnabled(
false );
223 QgsExpressionContextScope *
editableScope = mContext->scope( mEditableScopeIndex );
224 const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
226 bool removeEnabled =
true;
227 const auto constSelectedItems = selectedItems;
228 for ( QTreeWidgetItem *item : constSelectedItems )
230 if ( !( item->flags() & Qt::ItemIsEditable ) )
232 removeEnabled =
false;
236 const QString name = item->text( 0 );
237 QgsExpressionContextScope *itemScope = mTreeWidget->scopeFromItem( item );
240 removeEnabled =
false;
246 removeEnabled =
false;
250 mRemoveButton->setEnabled( removeEnabled );
259QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
260 : QTreeWidget( parent )
263 if ( mExpandIcon.isNull() )
265 QPixmap pix( 14, 14 );
266 pix.fill( Qt::transparent );
267 mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
268 mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
269 mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
270 mExpandIcon.addPixmap( QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
273 setIconSize( QSize( 18, 18 ) );
275 setHeaderLabels( QStringList() << tr(
"Variable" ) << tr(
"Value" ) );
276 setEditTriggers( QAbstractItemView::AllEditTriggers );
277 setRootIsDecorated(
false );
278 header()->setSectionsMovable(
false );
279 header()->setSectionResizeMode( QHeaderView::Interactive );
281 mEditorDelegate =
new VariableEditorDelegate(
this,
this );
282 setItemDelegate( mEditorDelegate );
291 const int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
299 else if ( mContext->scopeCount() > contextIndex )
301 return mContext->scope( contextIndex );
311 const int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
312 if ( contextIndex < 0 )
314 return mVariableToItem.value( qMakePair( contextIndex, name ) );
319 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
324 return mContext->scope( mEditableScopeIndex );
327void QgsVariableEditorTree::refreshTree()
329 if ( !mContext || mEditableScopeIndex < 0 )
337 const auto constScopes = mContext->scopes();
340 refreshScopeItems( scope, scopeIndex );
347 const QColor baseColor = rowColor( scopeIndex );
348 const bool isCurrent = scopeIndex == mEditableScopeIndex;
349 QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
352 for (
const QString &name : names )
354 QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
357 item =
new QTreeWidgetItem( scopeItem );
358 mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
361 const bool readOnly = scope->
isReadOnly( name );
362 bool isActive =
true;
366 activeScope = mContext->activeScopeForVariable( name );
367 isActive = activeScope == scope;
370 item->setFlags( item->flags() | Qt::ItemIsEnabled );
371 item->setText( 0, name );
372 const QVariant value = scope->
variable( name );
374 item->setText( 1, previewString );
375 QFont font = item->font( 0 );
376 if ( readOnly || !isCurrent )
378 font.setItalic(
true );
379 item->setFlags( item->flags() ^ Qt::ItemIsEditable );
383 font.setItalic(
false );
384 item->setFlags( item->flags() | Qt::ItemIsEditable );
389 font.setStrikeOut(
true );
390 const QString toolTip = tr(
"Overridden by value from %1" ).arg( activeScope->
name() );
391 item->setToolTip( 0, toolTip );
392 item->setToolTip( 1, toolTip );
396 font.setStrikeOut(
false );
397 item->setToolTip( 0, name );
398 item->setToolTip( 1, previewString );
400 item->setFont( 0, font );
401 item->setFont( 1, font );
402 item->setData( 0, RowBaseColor, baseColor );
403 item->setData( 0, ContextIndex, scopeIndex );
404 item->setFirstColumnSpanned(
false );
413 const bool isCurrent = scopeIndex == mEditableScopeIndex;
415 QTreeWidgetItem *scopeItem =
nullptr;
416 if ( mScopeToItem.contains( scopeIndex ) )
419 scopeItem = mScopeToItem.value( scopeIndex );
424 scopeItem =
new QTreeWidgetItem();
425 mScopeToItem.insert( scopeIndex, scopeItem );
426 scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
427 scopeItem->setText( 0, scope->
name() );
428 scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
429 scopeItem->setFirstColumnSpanned(
true );
430 QFont scopeFont = scopeItem->font( 0 );
431 scopeFont.setBold(
true );
432 scopeItem->setFont( 0, scopeFont );
433 scopeItem->setFirstColumnSpanned(
true );
435 addTopLevelItem( scopeItem );
438 if ( isCurrent || settings.
value(
"QgsVariableEditor/" + scopeItem->text( 0 ) +
"/expanded" ).toBool() )
439 scopeItem->setExpanded(
true );
441 scopeItem->setIcon( 0, mExpandIcon );
444 refreshScopeVariables( scope, scopeIndex );
447void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
452 mVariableToItem.remove( mVariableToItem.key( item ) );
453 item->parent()->takeChild( item->parent()->indexOfChild( item ) );
458void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item,
const QString &name )
463 const int contextIndex = mVariableToItem.key( item ).first;
464 mVariableToItem.remove( mVariableToItem.key( item ) );
465 mVariableToItem.insert( qMakePair( contextIndex, name ), item );
466 item->setText( 0, name );
471void QgsVariableEditorTree::resetTree()
473 mVariableToItem.clear();
474 mScopeToItem.clear();
478void QgsVariableEditorTree::emitChanged()
483void QgsVariableEditorTree::drawRow( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
485 QStyleOptionViewItem opt = option;
486 QTreeWidgetItem *item = itemFromIndex( index );
487 if ( index.parent().isValid() )
490 QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
491 if ( index.row() % 2 == 1 )
493 baseColor.setAlpha( 59 );
495 painter->fillRect( option.rect, baseColor );
497 QTreeWidget::drawRow( painter, opt, index );
498 const QColor color =
static_cast<QRgb
>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
500 painter->setPen( QPen( color ) );
501 painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
504QColor QgsVariableEditorTree::rowColor(
int index )
const
507 const int colorIdx = index % 6;
511 return QColor( 255, 163, 0, 89 );
513 return QColor( 255, 255, 77, 89 );
515 return QColor( 0, 255, 77, 89 );
517 return QColor( 0, 255, 255, 89 );
519 return QColor( 196, 125, 255, 89 );
522 return QColor( 255, 125, 225, 89 );
526void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
531 item->setExpanded( !item->isExpanded() );
535 settings.
setValue(
"QgsVariableEditor/" + item->text( 0 ) +
"/expanded", item->isExpanded() );
538void QgsVariableEditorTree::editNext(
const QModelIndex &index )
540 if ( !index.isValid() )
543 if ( index.column() == 0 )
546 const QModelIndex nextIndex = index.sibling( index.row(), 1 );
547 if ( nextIndex.isValid() )
549 setCurrentIndex( nextIndex );
555 const QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
556 if ( nextIndex.isValid() )
559 setCurrentIndex( nextIndex );
569QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
571 if ( cursorAction == QAbstractItemView::MoveNext )
573 const QModelIndex index = currentIndex();
574 if ( index.isValid() )
576 if ( index.column() + 1 < model()->columnCount() )
577 return index.sibling( index.row(), index.column() + 1 );
578 else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
579 return index.sibling( index.row() + 1, 0 );
581 return QModelIndex();
584 else if ( cursorAction == QAbstractItemView::MovePrevious )
586 const QModelIndex index = currentIndex();
587 if ( index.isValid() )
589 if ( index.column() >= 1 )
590 return index.sibling( index.row(), index.column() - 1 );
591 else if ( index.row() >= 1 )
592 return index.sibling( index.row() - 1, model()->columnCount() - 1 );
594 return QModelIndex();
598 return QTreeWidget::moveCursor( cursorAction, modifiers );
601void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
603 switch ( event->key() )
609 QTreeWidgetItem *item = currentItem();
610 if ( item && !item->parent() )
613 toggleContextExpanded( item );
616 else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
619 editNext( currentIndex() );
628 if ( event == QKeySequence::Copy )
630 const QList<QTreeWidgetItem *> selected = selectedItems();
631 if ( selected.size() > 0 )
633 QString text = selected.at( 0 )->text( 0 );
634 const QString varName = variableNameFromItem( selected.at( 0 ) );
636 if ( !varName.isEmpty() && scope )
637 text = scope->
variable( varName ).toString();
639 QClipboard *clipboard = QApplication::clipboard();
640 clipboard->setText( text );
646 QTreeWidget::keyPressEvent( event );
649void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
651 QTreeWidget::mousePressEvent( event );
652 QTreeWidgetItem *item = itemAt( event->pos() );
656 if ( item->parent() )
662 if ( event->pos().x() + header()->offset() > 20 )
668 if ( event->modifiers() & Qt::ShiftModifier )
671 if ( !item->isExpanded() )
682 toggleContextExpanded( item );
690QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex &index )
const
696 if ( !index.parent().isValid() )
699 QTreeWidgetItem *item = mParentTree->indexToItem( index );
701 if ( !item || !scope )
704 const QString variableName = mParentTree->variableNameFromIndex( index );
707 if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
710 QLineEdit *lineEdit =
new QLineEdit( parent );
711 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
712 lineEdit->setAutoFillBackground(
true );
716void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex & )
const
718 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
721QSize VariableEditorDelegate::sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
723 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
726void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
const QModelIndex &index )
const
733 QTreeWidgetItem *item = mParentTree->indexToItem( index );
735 if ( !item || !scope )
738 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( widget );
742 const QString variableName = mParentTree->variableNameFromIndex( index );
743 if ( index.column() == 0 )
746 QString newName = lineEdit->text();
747 newName = newName.trimmed();
748 newName = newName.replace(
' ',
'_' );
751 if ( newName == variableName )
758 QMessageBox::warning( mParentTree, tr(
"Rename Variable" ), tr(
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
759 newName.append(
"_1" );
762 const QString value = scope->
variable( variableName ).toString();
763 mParentTree->renameItem( item, newName );
766 mParentTree->emitChanged();
768 else if ( index.column() == 1 )
771 const QString value = lineEdit->text();
772 if ( scope->
variable( variableName ).toString() == value )
777 mParentTree->emitChanged();
779 mParentTree->refreshTree();
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.
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.