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() )
144 Q_FOREACH (
const QString &variable, scope->
variableNames() )
149 variables.insert( variable, scope->
variable( variable ) );
155 QString QgsVariableEditorWidget::saveKey()
const 159 QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
160 QString saveKey =
"/QgsVariableEditorTree/" + setGroup +
'/';
164 void QgsVariableEditorWidget::mAddButton_clicked()
166 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
170 scope->
setVariable( QStringLiteral(
"new_variable" ), QVariant() );
171 mTreeWidget->refreshTree();
172 QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral(
"new_variable" ) );
173 QModelIndex index = mTreeWidget->itemToIndex( item );
174 mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
175 mTreeWidget->editItem( item, 0 );
180 void QgsVariableEditorWidget::mRemoveButton_clicked()
182 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
186 QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
188 Q_FOREACH ( QTreeWidgetItem *item, selectedItems )
190 if ( !( item->flags() & Qt::ItemIsEditable ) )
193 QString name = item->text( 0 );
195 if ( itemScope != editableScope )
202 mTreeWidget->removeItem( item );
204 mTreeWidget->refreshTree();
207 void QgsVariableEditorWidget::selectionChanged()
209 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
211 mRemoveButton->setEnabled(
false );
216 QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
218 bool removeEnabled =
true;
219 Q_FOREACH ( QTreeWidgetItem *item, selectedItems )
221 if ( !( item->flags() & Qt::ItemIsEditable ) )
223 removeEnabled =
false;
227 QString name = item->text( 0 );
229 if ( itemScope != editableScope )
231 removeEnabled =
false;
237 removeEnabled =
false;
241 mRemoveButton->setEnabled( removeEnabled );
250 QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
251 : QTreeWidget( parent )
254 if ( mExpandIcon.isNull() )
256 QPixmap pix( 14, 14 );
257 pix.fill( Qt::transparent );
258 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
259 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
260 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
261 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
264 setIconSize( QSize( 18, 18 ) );
266 setHeaderLabels( QStringList() << tr(
"Variable" ) << tr(
"Value" ) );
267 setEditTriggers( QAbstractItemView::AllEditTriggers );
268 setRootIsDecorated(
false );
269 header()->setSectionsMovable(
false );
270 header()->setSectionResizeMode( QHeaderView::Interactive );
272 mEditorDelegate =
new VariableEditorDelegate(
this,
this );
273 setItemDelegate( mEditorDelegate );
282 int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
290 else if ( mContext->scopeCount() > contextIndex )
292 return mContext->scope( contextIndex );
302 int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
303 if ( contextIndex < 0 )
305 return mVariableToItem.value( qMakePair( contextIndex, name ) );
310 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
315 return mContext->scope( mEditableScopeIndex );
318 void QgsVariableEditorTree::refreshTree()
320 if ( !mContext || mEditableScopeIndex < 0 )
330 refreshScopeItems( scope, scopeIndex );
337 QColor baseColor = rowColor( scopeIndex );
338 bool isCurrent = scopeIndex == mEditableScopeIndex;
339 QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
342 for (
const QString &name : names )
344 QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
347 item =
new QTreeWidgetItem( scopeItem );
348 mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
352 bool isActive =
true;
356 activeScope = mContext->activeScopeForVariable( name );
357 isActive = activeScope == scope;
360 item->setFlags( item->flags() | Qt::ItemIsEnabled );
361 item->setText( 0, name );
362 const QVariant value = scope->
variable( name );
364 item->setText( 1, previewString );
365 QFont font = item->font( 0 );
366 if ( readOnly || !isCurrent )
368 font.setItalic(
true );
369 item->setFlags( item->flags() ^ Qt::ItemIsEditable );
373 font.setItalic(
false );
374 item->setFlags( item->flags() | Qt::ItemIsEditable );
379 font.setStrikeOut(
true );
380 QString toolTip = tr(
"Overridden by value from %1" ).arg( activeScope->
name() );
381 item->setToolTip( 0, toolTip );
382 item->setToolTip( 1, toolTip );
386 font.setStrikeOut(
false );
387 item->setToolTip( 0, name );
388 item->setToolTip( 1, previewString );
390 item->setFont( 0, font );
391 item->setFont( 1, font );
392 item->setData( 0, RowBaseColor, baseColor );
393 item->setData( 0, ContextIndex, scopeIndex );
394 item->setFirstColumnSpanned(
false );
403 bool isCurrent = scopeIndex == mEditableScopeIndex;
405 QTreeWidgetItem *scopeItem =
nullptr;
406 if ( mScopeToItem.contains( scopeIndex ) )
409 scopeItem = mScopeToItem.value( scopeIndex );
414 scopeItem =
new QTreeWidgetItem();
415 mScopeToItem.insert( scopeIndex, scopeItem );
416 scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
417 scopeItem->setText( 0, scope->
name() );
418 scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
419 scopeItem->setFirstColumnSpanned(
true );
420 QFont scopeFont = scopeItem->font( 0 );
421 scopeFont .setBold(
true );
422 scopeItem->setFont( 0, scopeFont );
423 scopeItem->setFirstColumnSpanned(
true );
425 addTopLevelItem( scopeItem );
428 if ( isCurrent || settings.
value(
"QgsVariableEditor/" + scopeItem->text( 0 ) +
"/expanded" ).toBool() )
429 scopeItem->setExpanded(
true );
431 scopeItem->setIcon( 0, mExpandIcon );
434 refreshScopeVariables( scope, scopeIndex );
437 void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
442 mVariableToItem.remove( mVariableToItem.key( item ) );
443 item->parent()->takeChild( item->parent()->indexOfChild( item ) );
448 void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item,
const QString &name )
453 int contextIndex = mVariableToItem.key( item ).first;
454 mVariableToItem.remove( mVariableToItem.key( item ) );
455 mVariableToItem.insert( qMakePair( contextIndex, name ), item );
456 item->setText( 0, name );
461 void QgsVariableEditorTree::resetTree()
463 mVariableToItem.clear();
464 mScopeToItem.clear();
468 void QgsVariableEditorTree::emitChanged()
473 void QgsVariableEditorTree::drawRow( QPainter *painter,
const QStyleOptionViewItem &option,
474 const QModelIndex &index )
const 476 QStyleOptionViewItem opt = option;
477 QTreeWidgetItem *item = itemFromIndex( index );
478 if ( index.parent().isValid() )
481 QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
482 if ( index.row() % 2 == 1 )
484 baseColor = baseColor.lighter( 110 );
486 painter->fillRect( option.rect, baseColor );
489 QPalette
pal = opt.palette;
490 pal.setColor( QPalette::Active, QPalette::Text, Qt::black );
491 pal.setColor( QPalette::Inactive, QPalette::Text, Qt::black );
492 pal.setColor( QPalette::Disabled, QPalette::Text, Qt::gray );
495 QTreeWidget::drawRow( painter, opt, index );
496 QColor color =
static_cast<QRgb
>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
498 painter->setPen( QPen( color ) );
499 painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
503 QColor QgsVariableEditorTree::rowColor(
int index )
const 506 int colorIdx = index % 6;
510 return QColor( 255, 220, 167 );
512 return QColor( 255, 255, 191 );
514 return QColor( 191, 255, 191 );
516 return QColor( 199, 255, 255 );
518 return QColor( 234, 191, 255 );
521 return QColor( 255, 191, 239 );
525 void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
530 item->setExpanded( !item->isExpanded() );
534 settings.
setValue(
"QgsVariableEditor/" + item->text( 0 ) +
"/expanded", item->isExpanded() );
537 void QgsVariableEditorTree::editNext(
const QModelIndex &index )
539 if ( !index.isValid() )
542 if ( index.column() == 0 )
545 QModelIndex nextIndex = index.sibling( index.row(), 1 );
546 if ( nextIndex.isValid() )
548 setCurrentIndex( nextIndex );
554 QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
555 if ( nextIndex.isValid() )
558 setCurrentIndex( nextIndex );
568 QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
570 if ( cursorAction == QAbstractItemView::MoveNext )
572 QModelIndex index = currentIndex();
573 if ( index.isValid() )
575 if ( index.column() + 1 < model()->columnCount() )
576 return index.sibling( index.row(), index.column() + 1 );
577 else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
578 return index.sibling( index.row() + 1, 0 );
580 return QModelIndex();
583 else if ( cursorAction == QAbstractItemView::MovePrevious )
585 QModelIndex index = currentIndex();
586 if ( index.isValid() )
588 if ( index.column() >= 1 )
589 return index.sibling( index.row(), index.column() - 1 );
590 else if ( index.row() >= 1 )
591 return index.sibling( index.row() - 1, model()->columnCount() - 1 );
593 return QModelIndex();
597 return QTreeWidget::moveCursor( cursorAction, modifiers );
600 void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
602 switch ( event->key() )
608 QTreeWidgetItem *item = currentItem();
609 if ( item && !item->parent() )
612 toggleContextExpanded( item );
615 else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
618 editNext( currentIndex() );
626 QTreeWidget::keyPressEvent( event );
629 void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
631 QTreeWidget::mousePressEvent( event );
632 QTreeWidgetItem *item = itemAt( event->pos() );
636 if ( item->parent() )
642 if ( event->pos().x() + header()->offset() > 20 )
648 if ( event->modifiers() & Qt::ShiftModifier )
651 if ( !item->isExpanded() )
662 toggleContextExpanded( item );
670 QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
671 const QStyleOptionViewItem &,
672 const QModelIndex &index )
const 678 if ( !index.parent().isValid() )
681 QTreeWidgetItem *item = mParentTree->indexToItem( index );
683 if ( !item || !scope )
686 QString variableName = mParentTree->variableNameFromIndex( index );
689 if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
692 QLineEdit *lineEdit =
new QLineEdit( parent );
693 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
694 lineEdit->setAutoFillBackground(
true );
698 void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
699 const QStyleOptionViewItem &option,
700 const QModelIndex & )
const 702 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
705 QSize VariableEditorDelegate::sizeHint(
const QStyleOptionViewItem &option,
706 const QModelIndex &index )
const 708 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
711 void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
712 const QModelIndex &index )
const 719 QTreeWidgetItem *item = mParentTree->indexToItem( index );
721 if ( !item || !scope )
724 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
728 QString variableName = mParentTree->variableNameFromIndex( index );
729 if ( index.column() == 0 )
732 QString newName = lineEdit->text();
733 newName = newName.trimmed();
734 newName = newName.replace(
' ',
'_' );
737 if ( newName == variableName )
744 QMessageBox::warning( mParentTree, tr(
"Rename Variable" ), tr(
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
745 newName.append(
"_1" );
748 QString value = scope->
variable( variableName ).toString();
749 mParentTree->renameItem( item, newName );
752 mParentTree->emitChanged();
754 else if ( index.column() == 1 )
757 QString value = lineEdit->text();
758 if ( scope->
variable( variableName ).toString() == value )
763 mParentTree->emitChanged();
765 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.