22 #include <QVBoxLayout> 23 #include <QTreeWidget> 26 #include <QMouseEvent> 28 #include <QPushButton> 29 #include <QHeaderView> 30 #include <QMessageBox> 40 QVBoxLayout *verticalLayout =
new QVBoxLayout(
this );
41 verticalLayout->setSpacing( 3 );
42 verticalLayout->setContentsMargins( 3, 3, 3, 3 );
43 mTreeWidget =
new QgsVariableEditorTree(
this );
44 mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
45 verticalLayout->addWidget( mTreeWidget );
46 QHBoxLayout *horizontalLayout =
new QHBoxLayout();
47 horizontalLayout->setSpacing( 6 );
48 QSpacerItem *horizontalSpacer =
new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
49 horizontalLayout->addItem( horizontalSpacer );
50 mAddButton =
new QPushButton();
52 mAddButton->setEnabled(
false );
53 mAddButton->setToolTip( tr(
"Add variable" ) );
54 horizontalLayout->addWidget( mAddButton );
55 mRemoveButton =
new QPushButton();
57 mRemoveButton->setEnabled(
false );
58 mRemoveButton->setToolTip( tr(
"Remove variable" ) );
59 horizontalLayout->addWidget( mRemoveButton );
60 verticalLayout->addLayout( horizontalLayout );
61 connect( mRemoveButton, &QAbstractButton::clicked,
this, &QgsVariableEditorWidget::mRemoveButton_clicked );
62 connect( mAddButton, &QAbstractButton::clicked,
this, &QgsVariableEditorWidget::mAddButton_clicked );
63 connect( mTreeWidget, &QTreeWidget::itemSelectionChanged,
this, &QgsVariableEditorWidget::selectionChanged );
75 settings.
setValue( saveKey() +
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
90 val = settings.
value( saveKey() +
"column0width" );
92 int sectionSize = val.toInt( &ok );
95 mTreeWidget->header()->resizeSection( 0, sectionSize );
99 QWidget::showEvent( event );
110 mTreeWidget->resetTree();
111 mTreeWidget->setContext( mContext.get() );
112 mTreeWidget->refreshTree();
117 mEditableScopeIndex = scopeIndex;
118 if ( mEditableScopeIndex >= 0 )
120 mAddButton->setEnabled(
true );
122 mTreeWidget->setEditableScopeIndex( scopeIndex );
123 mTreeWidget->refreshTree();
128 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
132 return mContext->scope( mEditableScopeIndex );
137 QVariantMap variables;
138 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
145 for (
const QString &variable : constVariableNames )
150 variables.insert( variable, scope->
variable( variable ) );
156 QString QgsVariableEditorWidget::saveKey()
const 160 QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
161 QString saveKey =
"/QgsVariableEditorTree/" + setGroup +
'/';
165 void QgsVariableEditorWidget::mAddButton_clicked()
167 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
171 scope->
setVariable( QStringLiteral(
"new_variable" ), QVariant() );
172 mTreeWidget->refreshTree();
173 QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral(
"new_variable" ) );
174 QModelIndex index = mTreeWidget->itemToIndex( item );
175 mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
176 mTreeWidget->editItem( item, 0 );
181 void QgsVariableEditorWidget::mRemoveButton_clicked()
183 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
187 QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
189 const auto constSelectedItems = selectedItems;
190 for ( QTreeWidgetItem *item : constSelectedItems )
192 if ( !( item->flags() & Qt::ItemIsEditable ) )
195 QString name = item->text( 0 );
197 if ( itemScope != editableScope )
204 mTreeWidget->removeItem( item );
206 mTreeWidget->refreshTree();
209 void QgsVariableEditorWidget::selectionChanged()
211 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
213 mRemoveButton->setEnabled(
false );
218 QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
220 bool removeEnabled =
true;
221 const auto constSelectedItems = selectedItems;
222 for ( QTreeWidgetItem *item : constSelectedItems )
224 if ( !( item->flags() & Qt::ItemIsEditable ) )
226 removeEnabled =
false;
230 QString name = item->text( 0 );
232 if ( itemScope != editableScope )
234 removeEnabled =
false;
240 removeEnabled =
false;
244 mRemoveButton->setEnabled( removeEnabled );
253 QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
254 : QTreeWidget( parent )
257 if ( mExpandIcon.isNull() )
259 QPixmap pix( 14, 14 );
260 pix.fill( Qt::transparent );
261 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
262 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
263 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
264 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
267 setIconSize( QSize( 18, 18 ) );
269 setHeaderLabels( QStringList() << tr(
"Variable" ) << tr(
"Value" ) );
270 setEditTriggers( QAbstractItemView::AllEditTriggers );
271 setRootIsDecorated(
false );
272 header()->setSectionsMovable(
false );
273 header()->setSectionResizeMode( QHeaderView::Interactive );
275 mEditorDelegate =
new VariableEditorDelegate(
this,
this );
276 setItemDelegate( mEditorDelegate );
285 int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
293 else if ( mContext->scopeCount() > contextIndex )
295 return mContext->scope( contextIndex );
305 int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
306 if ( contextIndex < 0 )
308 return mVariableToItem.value( qMakePair( contextIndex, name ) );
313 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
318 return mContext->scope( mEditableScopeIndex );
321 void QgsVariableEditorTree::refreshTree()
323 if ( !mContext || mEditableScopeIndex < 0 )
331 const auto constScopes = mContext->scopes();
334 refreshScopeItems( scope, scopeIndex );
341 QColor baseColor = rowColor( scopeIndex );
342 bool isCurrent = scopeIndex == mEditableScopeIndex;
343 QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
346 for (
const QString &name : names )
348 QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
351 item =
new QTreeWidgetItem( scopeItem );
352 mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
356 bool isActive =
true;
360 activeScope = mContext->activeScopeForVariable( name );
361 isActive = activeScope == scope;
364 item->setFlags( item->flags() | Qt::ItemIsEnabled );
365 item->setText( 0, name );
366 const QVariant value = scope->
variable( name );
368 item->setText( 1, previewString );
369 QFont font = item->font( 0 );
370 if ( readOnly || !isCurrent )
372 font.setItalic(
true );
373 item->setFlags( item->flags() ^ Qt::ItemIsEditable );
377 font.setItalic(
false );
378 item->setFlags( item->flags() | Qt::ItemIsEditable );
383 font.setStrikeOut(
true );
384 QString toolTip = tr(
"Overridden by value from %1" ).arg( activeScope->
name() );
385 item->setToolTip( 0, toolTip );
386 item->setToolTip( 1, toolTip );
390 font.setStrikeOut(
false );
391 item->setToolTip( 0, name );
392 item->setToolTip( 1, previewString );
394 item->setFont( 0, font );
395 item->setFont( 1, font );
396 item->setData( 0, RowBaseColor, baseColor );
397 item->setData( 0, ContextIndex, scopeIndex );
398 item->setFirstColumnSpanned(
false );
407 bool isCurrent = scopeIndex == mEditableScopeIndex;
409 QTreeWidgetItem *scopeItem =
nullptr;
410 if ( mScopeToItem.contains( scopeIndex ) )
413 scopeItem = mScopeToItem.value( scopeIndex );
418 scopeItem =
new QTreeWidgetItem();
419 mScopeToItem.insert( scopeIndex, scopeItem );
420 scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
421 scopeItem->setText( 0, scope->
name() );
422 scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
423 scopeItem->setFirstColumnSpanned(
true );
424 QFont scopeFont = scopeItem->font( 0 );
425 scopeFont .setBold(
true );
426 scopeItem->setFont( 0, scopeFont );
427 scopeItem->setFirstColumnSpanned(
true );
429 addTopLevelItem( scopeItem );
432 if ( isCurrent || settings.
value(
"QgsVariableEditor/" + scopeItem->text( 0 ) +
"/expanded" ).toBool() )
433 scopeItem->setExpanded(
true );
435 scopeItem->setIcon( 0, mExpandIcon );
438 refreshScopeVariables( scope, scopeIndex );
441 void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
446 mVariableToItem.remove( mVariableToItem.key( item ) );
447 item->parent()->takeChild( item->parent()->indexOfChild( item ) );
452 void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item,
const QString &name )
457 int contextIndex = mVariableToItem.key( item ).first;
458 mVariableToItem.remove( mVariableToItem.key( item ) );
459 mVariableToItem.insert( qMakePair( contextIndex, name ), item );
460 item->setText( 0, name );
465 void QgsVariableEditorTree::resetTree()
467 mVariableToItem.clear();
468 mScopeToItem.clear();
472 void QgsVariableEditorTree::emitChanged()
477 void QgsVariableEditorTree::drawRow( QPainter *painter,
const QStyleOptionViewItem &option,
478 const QModelIndex &index )
const 480 QStyleOptionViewItem opt = option;
481 QTreeWidgetItem *item = itemFromIndex( index );
482 if ( index.parent().isValid() )
485 QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
486 if ( index.row() % 2 == 1 )
488 baseColor = baseColor.lighter( 110 );
490 painter->fillRect( option.rect, baseColor );
493 QPalette
pal = opt.palette;
494 pal.setColor( QPalette::Active, QPalette::Text, Qt::black );
495 pal.setColor( QPalette::Inactive, QPalette::Text, Qt::black );
496 pal.setColor( QPalette::Disabled, QPalette::Text, Qt::gray );
499 QTreeWidget::drawRow( painter, opt, index );
500 QColor color =
static_cast<QRgb
>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
502 painter->setPen( QPen( color ) );
503 painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
507 QColor QgsVariableEditorTree::rowColor(
int index )
const 510 int colorIdx = index % 6;
514 return QColor( 255, 220, 167 );
516 return QColor( 255, 255, 191 );
518 return QColor( 191, 255, 191 );
520 return QColor( 199, 255, 255 );
522 return QColor( 234, 191, 255 );
525 return QColor( 255, 191, 239 );
529 void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
534 item->setExpanded( !item->isExpanded() );
538 settings.
setValue(
"QgsVariableEditor/" + item->text( 0 ) +
"/expanded", item->isExpanded() );
541 void QgsVariableEditorTree::editNext(
const QModelIndex &index )
543 if ( !index.isValid() )
546 if ( index.column() == 0 )
549 QModelIndex nextIndex = index.sibling( index.row(), 1 );
550 if ( nextIndex.isValid() )
552 setCurrentIndex( nextIndex );
558 QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
559 if ( nextIndex.isValid() )
562 setCurrentIndex( nextIndex );
572 QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
574 if ( cursorAction == QAbstractItemView::MoveNext )
576 QModelIndex index = currentIndex();
577 if ( index.isValid() )
579 if ( index.column() + 1 < model()->columnCount() )
580 return index.sibling( index.row(), index.column() + 1 );
581 else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
582 return index.sibling( index.row() + 1, 0 );
584 return QModelIndex();
587 else if ( cursorAction == QAbstractItemView::MovePrevious )
589 QModelIndex index = currentIndex();
590 if ( index.isValid() )
592 if ( index.column() >= 1 )
593 return index.sibling( index.row(), index.column() - 1 );
594 else if ( index.row() >= 1 )
595 return index.sibling( index.row() - 1, model()->columnCount() - 1 );
597 return QModelIndex();
601 return QTreeWidget::moveCursor( cursorAction, modifiers );
604 void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
606 switch ( event->key() )
612 QTreeWidgetItem *item = currentItem();
613 if ( item && !item->parent() )
616 toggleContextExpanded( item );
619 else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
622 editNext( currentIndex() );
630 QTreeWidget::keyPressEvent( event );
633 void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
635 QTreeWidget::mousePressEvent( event );
636 QTreeWidgetItem *item = itemAt( event->pos() );
640 if ( item->parent() )
646 if ( event->pos().x() + header()->offset() > 20 )
652 if ( event->modifiers() & Qt::ShiftModifier )
655 if ( !item->isExpanded() )
666 toggleContextExpanded( item );
674 QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
675 const QStyleOptionViewItem &,
676 const QModelIndex &index )
const 682 if ( !index.parent().isValid() )
685 QTreeWidgetItem *item = mParentTree->indexToItem( index );
687 if ( !item || !scope )
690 QString variableName = mParentTree->variableNameFromIndex( index );
693 if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
696 QLineEdit *lineEdit =
new QLineEdit( parent );
697 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
698 lineEdit->setAutoFillBackground(
true );
702 void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
703 const QStyleOptionViewItem &option,
704 const QModelIndex & )
const 706 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
709 QSize VariableEditorDelegate::sizeHint(
const QStyleOptionViewItem &option,
710 const QModelIndex &index )
const 712 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
715 void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
716 const QModelIndex &index )
const 723 QTreeWidgetItem *item = mParentTree->indexToItem( index );
725 if ( !item || !scope )
728 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
732 QString variableName = mParentTree->variableNameFromIndex( index );
733 if ( index.column() == 0 )
736 QString newName = lineEdit->text();
737 newName = newName.trimmed();
738 newName = newName.replace(
' ',
'_' );
741 if ( newName == variableName )
748 QMessageBox::warning( mParentTree, tr(
"Rename Variable" ), tr(
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
749 newName.append(
"_1" );
752 QString value = scope->
variable( variableName ).toString();
753 mParentTree->renameItem( item, newName );
756 mParentTree->emitChanged();
758 else if ( index.column() == 1 )
761 QString value = lineEdit->text();
762 if ( scope->
variable( variableName ).toString() == value )
767 mParentTree->emitChanged();
769 mParentTree->refreshTree();
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in the scope.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user.
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.
QStringList variableNames() const
Returns a list of variable names contained within the scope.
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
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.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QVariant variable(const QString &name) const
Retrieves a variable's value from the scope.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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.