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,
480 const QModelIndex &index )
const
482 QStyleOptionViewItem opt = option;
483 QTreeWidgetItem *item = itemFromIndex( index );
484 if ( index.parent().isValid() )
487 QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
488 if ( index.row() % 2 == 1 )
490 baseColor.setAlpha( 59 );
492 painter->fillRect( option.rect, baseColor );
494 QTreeWidget::drawRow( painter, opt, index );
495 const QColor color =
static_cast<QRgb
>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
497 painter->setPen( QPen( color ) );
498 painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
501QColor QgsVariableEditorTree::rowColor(
int index )
const
504 const int colorIdx = index % 6;
508 return QColor( 255, 163, 0, 89 );
510 return QColor( 255, 255, 77, 89 );
512 return QColor( 0, 255, 77, 89 );
514 return QColor( 0, 255, 255, 89 );
516 return QColor( 196, 125, 255, 89 );
519 return QColor( 255, 125, 225, 89 );
523void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
528 item->setExpanded( !item->isExpanded() );
532 settings.
setValue(
"QgsVariableEditor/" + item->text( 0 ) +
"/expanded", item->isExpanded() );
535void QgsVariableEditorTree::editNext(
const QModelIndex &index )
537 if ( !index.isValid() )
540 if ( index.column() == 0 )
543 const QModelIndex nextIndex = index.sibling( index.row(), 1 );
544 if ( nextIndex.isValid() )
546 setCurrentIndex( nextIndex );
552 const QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
553 if ( nextIndex.isValid() )
556 setCurrentIndex( nextIndex );
566QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
568 if ( cursorAction == QAbstractItemView::MoveNext )
570 const QModelIndex index = currentIndex();
571 if ( index.isValid() )
573 if ( index.column() + 1 < model()->columnCount() )
574 return index.sibling( index.row(), index.column() + 1 );
575 else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
576 return index.sibling( index.row() + 1, 0 );
578 return QModelIndex();
581 else if ( cursorAction == QAbstractItemView::MovePrevious )
583 const QModelIndex index = currentIndex();
584 if ( index.isValid() )
586 if ( index.column() >= 1 )
587 return index.sibling( index.row(), index.column() - 1 );
588 else if ( index.row() >= 1 )
589 return index.sibling( index.row() - 1, model()->columnCount() - 1 );
591 return QModelIndex();
595 return QTreeWidget::moveCursor( cursorAction, modifiers );
598void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
600 switch ( event->key() )
606 QTreeWidgetItem *item = currentItem();
607 if ( item && !item->parent() )
610 toggleContextExpanded( item );
613 else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
616 editNext( currentIndex() );
625 if ( event == QKeySequence::Copy )
627 const QList<QTreeWidgetItem *> selected = selectedItems();
628 if ( selected.size() > 0 )
630 QString text = selected.at( 0 )->text( 0 );
631 const QString varName = variableNameFromItem( selected.at( 0 ) );
633 if ( !varName.isEmpty() && scope )
634 text = scope->
variable( varName ).toString();
636 QClipboard *clipboard = QApplication::clipboard();
637 clipboard->setText( text );
643 QTreeWidget::keyPressEvent( event );
646void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
648 QTreeWidget::mousePressEvent( event );
649 QTreeWidgetItem *item = itemAt( event->pos() );
653 if ( item->parent() )
659 if ( event->pos().x() + header()->offset() > 20 )
665 if ( event->modifiers() & Qt::ShiftModifier )
668 if ( !item->isExpanded() )
679 toggleContextExpanded( item );
687QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
688 const QStyleOptionViewItem &,
689 const QModelIndex &index )
const
695 if ( !index.parent().isValid() )
698 QTreeWidgetItem *item = mParentTree->indexToItem( index );
700 if ( !item || !scope )
703 const QString variableName = mParentTree->variableNameFromIndex( index );
706 if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
709 QLineEdit *lineEdit =
new QLineEdit( parent );
710 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
711 lineEdit->setAutoFillBackground(
true );
715void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
716 const QStyleOptionViewItem &option,
717 const QModelIndex & )
const
719 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
722QSize VariableEditorDelegate::sizeHint(
const QStyleOptionViewItem &option,
723 const QModelIndex &index )
const
725 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
728void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
729 const QModelIndex &index )
const
736 QTreeWidgetItem *item = mParentTree->indexToItem( index );
738 if ( !item || !scope )
741 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
745 const QString variableName = mParentTree->variableNameFromIndex( index );
746 if ( index.column() == 0 )
749 QString newName = lineEdit->text();
750 newName = newName.trimmed();
751 newName = newName.replace(
' ',
'_' );
754 if ( newName == variableName )
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" );
765 const QString value = scope->
variable( variableName ).toString();
766 mParentTree->renameItem( item, newName );
769 mParentTree->emitChanged();
771 else if ( index.column() == 1 )
774 const QString value = lineEdit->text();
775 if ( scope->
variable( variableName ).toString() == value )
780 mParentTree->emitChanged();
782 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.