21 #include <QVBoxLayout> 22 #include <QTreeWidget> 25 #include <QMouseEvent> 27 #include <QPushButton> 28 #include <QHeaderView> 29 #include <QMessageBox> 39 QVBoxLayout *verticalLayout =
new QVBoxLayout(
this );
40 verticalLayout->setSpacing( 3 );
41 verticalLayout->setContentsMargins( 3, 3, 3, 3 );
42 mTreeWidget =
new QgsVariableEditorTree(
this );
43 mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
44 verticalLayout->addWidget( mTreeWidget );
45 QHBoxLayout *horizontalLayout =
new QHBoxLayout();
46 horizontalLayout->setSpacing( 6 );
47 QSpacerItem *horizontalSpacer =
new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
48 horizontalLayout->addItem( horizontalSpacer );
49 mAddButton =
new QPushButton();
51 mAddButton->setEnabled(
false );
52 mAddButton->setToolTip( tr(
"Add variable" ) );
53 horizontalLayout->addWidget( mAddButton );
54 mRemoveButton =
new QPushButton();
56 mRemoveButton->setEnabled(
false );
57 mRemoveButton->setToolTip( tr(
"Remove variable" ) );
58 horizontalLayout->addWidget( mRemoveButton );
59 verticalLayout->addLayout( horizontalLayout );
60 connect( mRemoveButton, &QAbstractButton::clicked,
this, &QgsVariableEditorWidget::mRemoveButton_clicked );
61 connect( mAddButton, &QAbstractButton::clicked,
this, &QgsVariableEditorWidget::mAddButton_clicked );
62 connect( mTreeWidget, &QTreeWidget::itemSelectionChanged,
this, &QgsVariableEditorWidget::selectionChanged );
74 settings.
setValue( saveKey() +
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
89 val = settings.
value( saveKey() +
"column0width" );
91 int sectionSize = val.toInt( &ok );
94 mTreeWidget->header()->resizeSection( 0, sectionSize );
98 QWidget::showEvent( event );
109 mTreeWidget->resetTree();
110 mTreeWidget->setContext( mContext.get() );
111 mTreeWidget->refreshTree();
116 mEditableScopeIndex = scopeIndex;
117 if ( mEditableScopeIndex >= 0 )
119 mAddButton->setEnabled(
true );
121 mTreeWidget->setEditableScopeIndex( scopeIndex );
122 mTreeWidget->refreshTree();
127 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
131 return mContext->scope( mEditableScopeIndex );
136 QVariantMap variables;
137 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
143 Q_FOREACH (
const QString &variable, scope->
variableNames() )
148 variables.insert( variable, scope->
variable( variable ) );
154 QString QgsVariableEditorWidget::saveKey()
const 158 QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
159 QString saveKey =
"/QgsVariableEditorTree/" + setGroup +
'/';
163 void QgsVariableEditorWidget::mAddButton_clicked()
165 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
169 scope->
setVariable( QStringLiteral(
"new_variable" ), QVariant() );
170 mTreeWidget->refreshTree();
171 QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral(
"new_variable" ) );
172 QModelIndex index = mTreeWidget->itemToIndex( item );
173 mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
174 mTreeWidget->editItem( item, 0 );
179 void QgsVariableEditorWidget::mRemoveButton_clicked()
181 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
185 QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
187 Q_FOREACH ( QTreeWidgetItem *item, selectedItems )
189 if ( !( item->flags() & Qt::ItemIsEditable ) )
192 QString name = item->text( 0 );
194 if ( itemScope != editableScope )
201 mTreeWidget->removeItem( item );
203 mTreeWidget->refreshTree();
206 void QgsVariableEditorWidget::selectionChanged()
208 if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
210 mRemoveButton->setEnabled(
false );
215 QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
217 bool removeEnabled =
true;
218 Q_FOREACH ( QTreeWidgetItem *item, selectedItems )
220 if ( !( item->flags() & Qt::ItemIsEditable ) )
222 removeEnabled =
false;
226 QString name = item->text( 0 );
228 if ( itemScope != editableScope )
230 removeEnabled =
false;
236 removeEnabled =
false;
240 mRemoveButton->setEnabled( removeEnabled );
249 QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
250 : QTreeWidget( parent )
253 if ( mExpandIcon.isNull() )
255 QPixmap pix( 14, 14 );
256 pix.fill( Qt::transparent );
257 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
258 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
259 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
260 mExpandIcon.addPixmap(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
263 setIconSize( QSize( 18, 18 ) );
265 setHeaderLabels( QStringList() << tr(
"Variable" ) << tr(
"Value" ) );
266 setEditTriggers( QAbstractItemView::AllEditTriggers );
267 setRootIsDecorated(
false );
268 header()->setSectionsMovable(
false );
269 header()->setSectionResizeMode( QHeaderView::Interactive );
271 mEditorDelegate =
new VariableEditorDelegate(
this,
this );
272 setItemDelegate( mEditorDelegate );
281 int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
289 else if ( mContext->scopeCount() > contextIndex )
291 return mContext->scope( contextIndex );
301 int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
302 if ( contextIndex < 0 )
304 return mVariableToItem.value( qMakePair( contextIndex, name ) );
309 if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
314 return mContext->scope( mEditableScopeIndex );
317 void QgsVariableEditorTree::refreshTree()
319 if ( !mContext || mEditableScopeIndex < 0 )
329 refreshScopeItems( scope, scopeIndex );
336 QColor baseColor = rowColor( scopeIndex );
337 bool isCurrent = scopeIndex == mEditableScopeIndex;
338 QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
341 for (
const QString &name : names )
343 QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
346 item =
new QTreeWidgetItem( scopeItem );
347 mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
351 bool isActive =
true;
355 activeScope = mContext->activeScopeForVariable( name );
356 isActive = activeScope == scope;
359 item->setFlags( item->flags() | Qt::ItemIsEnabled );
360 item->setText( 0, name );
361 const QVariant value = scope->
variable( name );
363 item->setText( 1, previewString );
364 QFont font = item->font( 0 );
365 if ( readOnly || !isCurrent )
367 font.setItalic(
true );
368 item->setFlags( item->flags() ^ Qt::ItemIsEditable );
372 font.setItalic(
false );
373 item->setFlags( item->flags() | Qt::ItemIsEditable );
378 font.setStrikeOut(
true );
379 QString toolTip = tr(
"Overridden by value from %1" ).arg( activeScope->
name() );
380 item->setToolTip( 0, toolTip );
381 item->setToolTip( 1, toolTip );
385 font.setStrikeOut(
false );
386 item->setToolTip( 0, name );
387 item->setToolTip( 1, previewString );
389 item->setFont( 0, font );
390 item->setFont( 1, font );
391 item->setData( 0, RowBaseColor, baseColor );
392 item->setData( 0, ContextIndex, scopeIndex );
393 item->setFirstColumnSpanned(
false );
402 bool isCurrent = scopeIndex == mEditableScopeIndex;
404 QTreeWidgetItem *scopeItem =
nullptr;
405 if ( mScopeToItem.contains( scopeIndex ) )
408 scopeItem = mScopeToItem.value( scopeIndex );
413 scopeItem =
new QTreeWidgetItem();
414 mScopeToItem.insert( scopeIndex, scopeItem );
415 scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
416 scopeItem->setText( 0, scope->
name() );
417 scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
418 scopeItem->setFirstColumnSpanned(
true );
419 QFont scopeFont = scopeItem->font( 0 );
420 scopeFont .setBold(
true );
421 scopeItem->setFont( 0, scopeFont );
422 scopeItem->setFirstColumnSpanned(
true );
424 addTopLevelItem( scopeItem );
427 if ( isCurrent || settings.
value(
"QgsVariableEditor/" + scopeItem->text( 0 ) +
"/expanded" ).toBool() )
428 scopeItem->setExpanded(
true );
430 scopeItem->setIcon( 0, mExpandIcon );
433 refreshScopeVariables( scope, scopeIndex );
436 void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
441 mVariableToItem.remove( mVariableToItem.key( item ) );
442 item->parent()->takeChild( item->parent()->indexOfChild( item ) );
447 void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item,
const QString &name )
452 int contextIndex = mVariableToItem.key( item ).first;
453 mVariableToItem.remove( mVariableToItem.key( item ) );
454 mVariableToItem.insert( qMakePair( contextIndex, name ), item );
455 item->setText( 0, name );
460 void QgsVariableEditorTree::resetTree()
462 mVariableToItem.clear();
463 mScopeToItem.clear();
467 void QgsVariableEditorTree::emitChanged()
472 void QgsVariableEditorTree::drawRow( QPainter *painter,
const QStyleOptionViewItem &option,
473 const QModelIndex &index )
const 475 QStyleOptionViewItem opt = option;
476 QTreeWidgetItem *item = itemFromIndex( index );
477 if ( index.parent().isValid() )
480 QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
481 if ( index.row() % 2 == 1 )
483 baseColor = baseColor.lighter( 110 );
485 painter->fillRect( option.rect, baseColor );
488 QPalette
pal = opt.palette;
489 pal.setColor( QPalette::Active, QPalette::Text, Qt::black );
490 pal.setColor( QPalette::Inactive, QPalette::Text, Qt::black );
491 pal.setColor( QPalette::Disabled, QPalette::Text, Qt::gray );
494 QTreeWidget::drawRow( painter, opt, index );
495 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() );
502 QColor QgsVariableEditorTree::rowColor(
int index )
const 505 int colorIdx = index % 6;
509 return QColor( 255, 220, 167 );
511 return QColor( 255, 255, 191 );
513 return QColor( 191, 255, 191 );
515 return QColor( 199, 255, 255 );
517 return QColor( 234, 191, 255 );
520 return QColor( 255, 191, 239 );
524 void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
529 item->setExpanded( !item->isExpanded() );
533 settings.
setValue(
"QgsVariableEditor/" + item->text( 0 ) +
"/expanded", item->isExpanded() );
536 void QgsVariableEditorTree::editNext(
const QModelIndex &index )
538 if ( !index.isValid() )
541 if ( index.column() == 0 )
544 QModelIndex nextIndex = index.sibling( index.row(), 1 );
545 if ( nextIndex.isValid() )
547 setCurrentIndex( nextIndex );
553 QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
554 if ( nextIndex.isValid() )
557 setCurrentIndex( nextIndex );
567 QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
569 if ( cursorAction == QAbstractItemView::MoveNext )
571 QModelIndex index = currentIndex();
572 if ( index.isValid() )
574 if ( index.column() + 1 < model()->columnCount() )
575 return index.sibling( index.row(), index.column() + 1 );
576 else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
577 return index.sibling( index.row() + 1, 0 );
579 return QModelIndex();
582 else if ( cursorAction == QAbstractItemView::MovePrevious )
584 QModelIndex index = currentIndex();
585 if ( index.isValid() )
587 if ( index.column() >= 1 )
588 return index.sibling( index.row(), index.column() - 1 );
589 else if ( index.row() >= 1 )
590 return index.sibling( index.row() - 1, model()->columnCount() - 1 );
592 return QModelIndex();
596 return QTreeWidget::moveCursor( cursorAction, modifiers );
599 void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
601 switch ( event->key() )
607 QTreeWidgetItem *item = currentItem();
608 if ( item && !item->parent() )
611 toggleContextExpanded( item );
614 else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
617 editNext( currentIndex() );
625 QTreeWidget::keyPressEvent( event );
628 void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
630 QTreeWidget::mousePressEvent( event );
631 QTreeWidgetItem *item = itemAt( event->pos() );
635 if ( item->parent() )
641 if ( event->pos().x() + header()->offset() > 20 )
647 if ( event->modifiers() & Qt::ShiftModifier )
650 if ( !item->isExpanded() )
661 toggleContextExpanded( item );
669 QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
670 const QStyleOptionViewItem &,
671 const QModelIndex &index )
const 677 if ( !index.parent().isValid() )
680 QTreeWidgetItem *item = mParentTree->indexToItem( index );
682 if ( !item || !scope )
685 QString variableName = mParentTree->variableNameFromIndex( index );
688 if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
691 QLineEdit *lineEdit =
new QLineEdit( parent );
692 lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
693 lineEdit->setAutoFillBackground(
true );
697 void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
698 const QStyleOptionViewItem &option,
699 const QModelIndex & )
const 701 editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
704 QSize VariableEditorDelegate::sizeHint(
const QStyleOptionViewItem &option,
705 const QModelIndex &index )
const 707 return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
710 void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
711 const QModelIndex &index )
const 718 QTreeWidgetItem *item = mParentTree->indexToItem( index );
720 if ( !item || !scope )
723 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
727 QString variableName = mParentTree->variableNameFromIndex( index );
728 if ( index.column() == 0 )
731 QString newName = lineEdit->text();
732 newName = newName.trimmed();
733 newName = newName.replace(
' ',
'_' );
736 if ( newName == variableName )
743 QMessageBox::warning( mParentTree, tr(
"Rename Variable" ), tr(
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
744 newName.append(
"_1" );
747 QString value = scope->
variable( variableName ).toString();
748 mParentTree->renameItem( item, newName );
751 mParentTree->emitChanged();
753 else if ( index.column() == 1 )
756 QString value = lineEdit->text();
757 if ( scope->
variable( variableName ).toString() == value )
762 mParentTree->emitChanged();
764 mParentTree->refreshTree();
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.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in 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.
QString name() const
Returns the friendly display name of the context scope.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
QStringList filteredVariableNames() const
Returns a filtered and sorted list of variable names contained within the scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Single scope for storing variables and functions for use within a QgsExpressionContext.
QVariant variable(const QString &name) const
Retrieves a variable's value from the scope.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
QStringList variableNames() const
Returns a list of variable names contained within the scope.