17#include "moc_qgsvariableeditorwidget.cpp"
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();
54 mAddButton->setEnabled(
false );
55 mAddButton->setToolTip( tr(
"Add variable" ) );
56 horizontalLayout->addWidget( mAddButton );
57 mRemoveButton =
new QPushButton();
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 );
77 settings.
setValue( saveKey() +
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
92 val = settings.
value( saveKey() +
"column0width" );
94 const int sectionSize = val.toInt( &ok );
97 mTreeWidget->header()->resizeSection( 0, sectionSize );
101 QWidget::showEvent( event );
112 mTreeWidget->resetTree();
113 mTreeWidget->setContext( mContext.get() );
114 mTreeWidget->refreshTree();
119 mEditableScopeIndex = scopeIndex;
120 if ( mEditableScopeIndex >= 0 )
122 mAddButton->setEnabled(
true );
124 mTreeWidget->setEditableScopeIndex( scopeIndex );
125 mTreeWidget->refreshTree();
130 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
134 return mContext->scope( mEditableScopeIndex );
139 QVariantMap variables;
140 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
147 for (
const QString &variable : constVariableNames )
152 variables.insert( variable, scope->
variable( variable ) );
158QString QgsVariableEditorWidget::saveKey()
const
162 const QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
163 QString saveKey =
"/QgsVariableEditorTree/" + setGroup +
'/';
167void QgsVariableEditorWidget::mAddButton_clicked()
169 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
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 );
183void QgsVariableEditorWidget::mRemoveButton_clicked()
185 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
189 const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
191 const auto constSelectedItems = selectedItems;
192 for ( QTreeWidgetItem *item : constSelectedItems )
194 if ( !( item->flags() & Qt::ItemIsEditable ) )
197 const QString name = item->text( 0 );
206 mTreeWidget->removeItem( item );
208 mTreeWidget->refreshTree();
211void QgsVariableEditorWidget::selectionChanged()
213 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
215 mRemoveButton->setEnabled(
false );
220 const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
222 bool removeEnabled =
true;
223 const auto constSelectedItems = selectedItems;
224 for ( QTreeWidgetItem *item : constSelectedItems )
226 if ( !( item->flags() & Qt::ItemIsEditable ) )
228 removeEnabled =
false;
232 const QString name = item->text( 0 );
236 removeEnabled =
false;
242 removeEnabled =
false;
246 mRemoveButton->setEnabled( removeEnabled );
255QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
256 : QTreeWidget( parent )
259 if ( mExpandIcon.isNull() )
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 );
269 setIconSize( QSize( 18, 18 ) );
271 setHeaderLabels( QStringList() << tr(
"Variable" ) << tr(
"Value" ) );
272 setEditTriggers( QAbstractItemView::AllEditTriggers );
273 setRootIsDecorated(
false );
274 header()->setSectionsMovable(
false );
275 header()->setSectionResizeMode( QHeaderView::Interactive );
277 mEditorDelegate =
new VariableEditorDelegate(
this,
this );
278 setItemDelegate( mEditorDelegate );
287 const int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
295 else if ( mContext->scopeCount() > contextIndex )
297 return mContext->scope( contextIndex );
307 const int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
308 if ( contextIndex < 0 )
310 return mVariableToItem.value( qMakePair( contextIndex, name ) );
315 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
320 return mContext->scope( mEditableScopeIndex );
323void QgsVariableEditorTree::refreshTree()
325 if ( !mContext || mEditableScopeIndex < 0 )
333 const auto constScopes = mContext->scopes();
336 refreshScopeItems( scope, scopeIndex );
343 const QColor baseColor = rowColor( scopeIndex );
344 const bool isCurrent = scopeIndex == mEditableScopeIndex;
345 QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
348 for (
const QString &name : names )
350 QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
353 item =
new QTreeWidgetItem( scopeItem );
354 mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
357 const bool readOnly = scope->
isReadOnly( name );
358 bool isActive =
true;
362 activeScope = mContext->activeScopeForVariable( name );
363 isActive = activeScope == scope;
366 item->setFlags( item->flags() | Qt::ItemIsEnabled );
367 item->setText( 0, name );
368 const QVariant value = scope->
variable( name );
370 item->setText( 1, previewString );
371 QFont font = item->font( 0 );
372 if ( readOnly || !isCurrent )
374 font.setItalic(
true );
375 item->setFlags( item->flags() ^ Qt::ItemIsEditable );
379 font.setItalic(
false );
380 item->setFlags( item->flags() | Qt::ItemIsEditable );
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 );
392 font.setStrikeOut(
false );
393 item->setToolTip( 0, name );
394 item->setToolTip( 1, previewString );
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 );
409 const bool isCurrent = scopeIndex == mEditableScopeIndex;
411 QTreeWidgetItem *scopeItem =
nullptr;
412 if ( mScopeToItem.contains( scopeIndex ) )
415 scopeItem = mScopeToItem.
value( scopeIndex );
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 );
431 addTopLevelItem( scopeItem );
434 if ( isCurrent || settings.
value(
"QgsVariableEditor/" + scopeItem->text( 0 ) +
"/expanded" ).toBool() )
435 scopeItem->setExpanded(
true );
437 scopeItem->setIcon( 0, mExpandIcon );
440 refreshScopeVariables( scope, scopeIndex );
443void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
448 mVariableToItem.remove( mVariableToItem.key( item ) );
449 item->parent()->takeChild( item->parent()->indexOfChild( item ) );
454void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item,
const QString &name )
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 );
467void QgsVariableEditorTree::resetTree()
469 mVariableToItem.clear();
470 mScopeToItem.clear();
474void QgsVariableEditorTree::emitChanged()
479void QgsVariableEditorTree::drawRow( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
481 QStyleOptionViewItem opt = option;
482 QTreeWidgetItem *item = itemFromIndex( index );
483 if ( index.parent().isValid() )
486 QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
487 if ( index.row() % 2 == 1 )
489 baseColor.setAlpha( 59 );
491 painter->fillRect( option.rect, baseColor );
493 QTreeWidget::drawRow( painter, opt, index );
494 const QColor color =
static_cast<QRgb
>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
496 painter->setPen( QPen( color ) );
497 painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
500QColor QgsVariableEditorTree::rowColor(
int index )
const
503 const int colorIdx = index % 6;
507 return QColor( 255, 163, 0, 89 );
509 return QColor( 255, 255, 77, 89 );
511 return QColor( 0, 255, 77, 89 );
513 return QColor( 0, 255, 255, 89 );
515 return QColor( 196, 125, 255, 89 );
518 return QColor( 255, 125, 225, 89 );
522void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
527 item->setExpanded( !item->isExpanded() );
531 settings.
setValue(
"QgsVariableEditor/" + item->text( 0 ) +
"/expanded", item->isExpanded() );
534void QgsVariableEditorTree::editNext(
const QModelIndex &index )
536 if ( !index.isValid() )
539 if ( index.column() == 0 )
542 const QModelIndex nextIndex = index.sibling( index.row(), 1 );
543 if ( nextIndex.isValid() )
545 setCurrentIndex( nextIndex );
551 const QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
552 if ( nextIndex.isValid() )
555 setCurrentIndex( nextIndex );
565QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
567 if ( cursorAction == QAbstractItemView::MoveNext )
569 const QModelIndex index = currentIndex();
570 if ( index.isValid() )
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 );
577 return QModelIndex();
580 else if ( cursorAction == QAbstractItemView::MovePrevious )
582 const QModelIndex index = currentIndex();
583 if ( index.isValid() )
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 );
590 return QModelIndex();
594 return QTreeWidget::moveCursor( cursorAction, modifiers );
597void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
599 switch ( event->key() )
605 QTreeWidgetItem *item = currentItem();
606 if ( item && !item->parent() )
609 toggleContextExpanded( item );
612 else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
615 editNext( currentIndex() );
624 if ( event == QKeySequence::Copy )
626 const QList<QTreeWidgetItem *> selected = selectedItems();
627 if ( selected.size() > 0 )
629 QString text = selected.at( 0 )->text( 0 );
630 const QString varName = variableNameFromItem( selected.at( 0 ) );
632 if ( !varName.isEmpty() && scope )
633 text = scope->
variable( varName ).toString();
635 QClipboard *clipboard = QApplication::clipboard();
636 clipboard->setText( text );
642 QTreeWidget::keyPressEvent( event );
645void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
647 QTreeWidget::mousePressEvent( event );
648 QTreeWidgetItem *item = itemAt( event->pos() );
652 if ( item->parent() )
658 if ( event->pos().x() + header()->offset() > 20 )
664 if ( event->modifiers() & Qt::ShiftModifier )
667 if ( !item->isExpanded() )
678 toggleContextExpanded( item );
686QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex &index )
const
692 if ( !index.parent().isValid() )
695 QTreeWidgetItem *item = mParentTree->indexToItem( index );
697 if ( !item || !scope )
700 const QString variableName = mParentTree->variableNameFromIndex( index );
703 if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
706 QLineEdit *lineEdit =
new QLineEdit( parent );
707 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
708 lineEdit->setAutoFillBackground(
true );
712void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex & )
const
714 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
717QSize VariableEditorDelegate::sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
719 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
722void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
const QModelIndex &index )
const
729 QTreeWidgetItem *item = mParentTree->indexToItem( index );
731 if ( !item || !scope )
734 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( widget );
738 const QString variableName = mParentTree->variableNameFromIndex( index );
739 if ( index.column() == 0 )
742 QString newName = lineEdit->text();
743 newName = newName.trimmed();
744 newName = newName.replace(
' ',
'_' );
747 if ( newName == variableName )
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" );
758 const QString value = scope->
variable( variableName ).toString();
759 mParentTree->renameItem( item, newName );
762 mParentTree->emitChanged();
764 else if ( index.column() == 1 )
767 const QString value = lineEdit->text();
768 if ( scope->
variable( variableName ).toString() == value )
773 mParentTree->emitChanged();
775 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.
This class is a composition of two QSettings instances:
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.