41  QVBoxLayout *verticalLayout = 
new QVBoxLayout( 
this );
 
   42  verticalLayout->setSpacing( 3 );
 
   43  verticalLayout->setContentsMargins( 3, 3, 3, 3 );
 
   44  mTreeWidget = 
new QgsVariableEditorTree( 
this );
 
   45  mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
 
   46  verticalLayout->addWidget( mTreeWidget );
 
   47  QHBoxLayout *horizontalLayout = 
new QHBoxLayout();
 
   48  horizontalLayout->setSpacing( 6 );
 
   49  QSpacerItem *horizontalSpacer = 
new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
 
   50  horizontalLayout->addItem( horizontalSpacer );
 
   51  mAddButton = 
new QPushButton();
 
   53  mAddButton->setEnabled( 
false );
 
   54  mAddButton->setToolTip( tr( 
"Add variable" ) );
 
   55  horizontalLayout->addWidget( mAddButton );
 
   56  mRemoveButton = 
new QPushButton();
 
   58  mRemoveButton->setEnabled( 
false );
 
   59  mRemoveButton->setToolTip( tr( 
"Remove variable" ) );
 
   60  horizontalLayout->addWidget( mRemoveButton );
 
   61  verticalLayout->addLayout( horizontalLayout );
 
   62  connect( mRemoveButton, &QAbstractButton::clicked, 
this, &QgsVariableEditorWidget::mRemoveButton_clicked );
 
   63  connect( mAddButton, &QAbstractButton::clicked, 
this, &QgsVariableEditorWidget::mAddButton_clicked );
 
   64  connect( mTreeWidget, &QTreeWidget::itemSelectionChanged, 
this, &QgsVariableEditorWidget::selectionChanged );
 
 
   76  settings.
setValue( saveKey() + 
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
 
 
   91  val = settings.
value( saveKey() + 
"column0width" );
 
   93  const int sectionSize = val.toInt( &ok );
 
   96    mTreeWidget->header()->resizeSection( 0, sectionSize );
 
  100  QWidget::showEvent( event );
 
 
  111  mTreeWidget->resetTree();
 
  112  mTreeWidget->setContext( mContext.get() );
 
  113  mTreeWidget->refreshTree();
 
 
  118  mEditableScopeIndex = scopeIndex;
 
  119  if ( mEditableScopeIndex >= 0 )
 
  121    mAddButton->setEnabled( 
true );
 
  123  mTreeWidget->setEditableScopeIndex( scopeIndex );
 
  124  mTreeWidget->refreshTree();
 
 
  129  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  133  return mContext->scope( mEditableScopeIndex );
 
 
  138  QVariantMap variables;
 
  139  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  146  for ( 
const QString &variable : constVariableNames )
 
  151    variables.insert( variable, scope->
variable( variable ) );
 
 
  157QString QgsVariableEditorWidget::saveKey()
 const 
  161  const QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
 
  162  QString saveKey = 
"/QgsVariableEditorTree/" + setGroup + 
'/';
 
  166void QgsVariableEditorWidget::mAddButton_clicked()
 
  168  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  172  scope->
setVariable( QStringLiteral( 
"new_variable" ), QVariant() );
 
  173  mTreeWidget->refreshTree();
 
  174  QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral( 
"new_variable" ) );
 
  175  const QModelIndex index = mTreeWidget->itemToIndex( item );
 
  176  mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
 
  177  mTreeWidget->editItem( item, 0 );
 
  182void QgsVariableEditorWidget::mRemoveButton_clicked()
 
  184  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  188  const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
 
  190  const auto constSelectedItems = selectedItems;
 
  191  for ( QTreeWidgetItem *item : constSelectedItems )
 
  193    if ( !( item->flags() & Qt::ItemIsEditable ) )
 
  196    const QString name = item->text( 0 );
 
  205    mTreeWidget->removeItem( item );
 
  207  mTreeWidget->refreshTree();
 
  210void QgsVariableEditorWidget::selectionChanged()
 
  212  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  214    mRemoveButton->setEnabled( 
false );
 
  219  const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
 
  221  bool removeEnabled = 
true;
 
  222  const auto constSelectedItems = selectedItems;
 
  223  for ( QTreeWidgetItem *item : constSelectedItems )
 
  225    if ( !( item->flags() & Qt::ItemIsEditable ) )
 
  227      removeEnabled = 
false;
 
  231    const QString name = item->text( 0 );
 
  235      removeEnabled = 
false;
 
  241      removeEnabled = 
false;
 
  245  mRemoveButton->setEnabled( removeEnabled );
 
  254QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
 
  255  : QTreeWidget( parent )
 
  258  if ( mExpandIcon.isNull() )
 
  260    QPixmap pix( 14, 14 );
 
  261    pix.fill( Qt::transparent );
 
  262    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
 
  263    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
 
  264    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
 
  265    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
 
  268  setIconSize( QSize( 18, 18 ) );
 
  270  setHeaderLabels( QStringList() << tr( 
"Variable" ) << tr( 
"Value" ) );
 
  271  setEditTriggers( QAbstractItemView::AllEditTriggers );
 
  272  setRootIsDecorated( 
false );
 
  273  header()->setSectionsMovable( 
false );
 
  274  header()->setSectionResizeMode( QHeaderView::Interactive );
 
  276  mEditorDelegate = 
new VariableEditorDelegate( 
this, 
this );
 
  277  setItemDelegate( mEditorDelegate );
 
  286  const int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
 
  294  else if ( mContext->scopeCount() > contextIndex )
 
  296    return mContext->scope( contextIndex );
 
  306  const int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
 
  307  if ( contextIndex < 0 )
 
  309  return mVariableToItem.value( qMakePair( contextIndex, name ) );
 
  314  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  319  return mContext->scope( mEditableScopeIndex );
 
  322void QgsVariableEditorTree::refreshTree()
 
  324  if ( !mContext || mEditableScopeIndex < 0 )
 
  332  const auto constScopes = mContext->scopes();
 
  335    refreshScopeItems( scope, scopeIndex );
 
  342  const QColor baseColor = rowColor( scopeIndex );
 
  343  const bool isCurrent = scopeIndex == mEditableScopeIndex;
 
  344  QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
 
  347  for ( 
const QString &name : names )
 
  349    QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
 
  352      item = 
new QTreeWidgetItem( scopeItem );
 
  353      mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
 
  356    const bool readOnly = scope->
isReadOnly( name );
 
  357    bool isActive = 
true;
 
  361      activeScope = mContext->activeScopeForVariable( name );
 
  362      isActive = activeScope == scope;
 
  365    item->setFlags( item->flags() | Qt::ItemIsEnabled );
 
  366    item->setText( 0, name );
 
  367    const QVariant value = scope->
variable( name );
 
  369    item->setText( 1, previewString );
 
  370    QFont font = item->font( 0 );
 
  371    if ( readOnly || !isCurrent )
 
  373      font.setItalic( 
true );
 
  374      item->setFlags( item->flags() ^ Qt::ItemIsEditable );
 
  378      font.setItalic( 
false );
 
  379      item->setFlags( item->flags() | Qt::ItemIsEditable );
 
  384      font.setStrikeOut( 
true );
 
  385      const QString toolTip = tr( 
"Overridden by value from %1" ).arg( activeScope->
name() );
 
  386      item->setToolTip( 0, toolTip );
 
  387      item->setToolTip( 1, toolTip );
 
  391      font.setStrikeOut( 
false );
 
  392      item->setToolTip( 0, name );
 
  393      item->setToolTip( 1, previewString );
 
  395    item->setFont( 0, font );
 
  396    item->setFont( 1, font );
 
  397    item->setData( 0, RowBaseColor, baseColor );
 
  398    item->setData( 0, ContextIndex, scopeIndex );
 
  399    item->setFirstColumnSpanned( 
false );
 
  408  const bool isCurrent = scopeIndex == mEditableScopeIndex;
 
  410  QTreeWidgetItem *scopeItem = 
nullptr;
 
  411  if ( mScopeToItem.contains( scopeIndex ) )
 
  414    scopeItem = mScopeToItem.
value( scopeIndex );
 
  419    scopeItem = 
new QTreeWidgetItem();
 
  420    mScopeToItem.insert( scopeIndex, scopeItem );
 
  421    scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
 
  422    scopeItem->setText( 0, scope->
name() );
 
  423    scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
 
  424    scopeItem->setFirstColumnSpanned( 
true );
 
  425    QFont scopeFont = scopeItem->font( 0 );
 
  426    scopeFont .setBold( 
true );
 
  427    scopeItem->setFont( 0, scopeFont );
 
  428    scopeItem->setFirstColumnSpanned( 
true );
 
  430    addTopLevelItem( scopeItem );
 
  433    if ( isCurrent || settings.
value( 
"QgsVariableEditor/" + scopeItem->text( 0 ) + 
"/expanded" ).toBool() )
 
  434      scopeItem->setExpanded( 
true );
 
  436    scopeItem->setIcon( 0, mExpandIcon );
 
  439  refreshScopeVariables( scope, scopeIndex );
 
  442void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
 
  447  mVariableToItem.remove( mVariableToItem.key( item ) );
 
  448  item->parent()->takeChild( item->parent()->indexOfChild( item ) );
 
  453void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item, 
const QString &name )
 
  458  const int contextIndex = mVariableToItem.key( item ).first;
 
  459  mVariableToItem.remove( mVariableToItem.key( item ) );
 
  460  mVariableToItem.insert( qMakePair( contextIndex, name ), item );
 
  461  item->setText( 0, name );
 
  466void QgsVariableEditorTree::resetTree()
 
  468  mVariableToItem.clear();
 
  469  mScopeToItem.clear();
 
  473void QgsVariableEditorTree::emitChanged()
 
  478void QgsVariableEditorTree::drawRow( QPainter *painter, 
const QStyleOptionViewItem &option,
 
  479                                     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,
 
  687    const QStyleOptionViewItem &,
 
  688    const QModelIndex &index )
 const 
  694  if ( !index.parent().isValid() )
 
  697  QTreeWidgetItem *item = mParentTree->indexToItem( index );
 
  699  if ( !item || !scope )
 
  702  const QString variableName = mParentTree->variableNameFromIndex( index );
 
  705  if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
 
  708  QLineEdit *lineEdit = 
new QLineEdit( parent );
 
  709  lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
 
  710  lineEdit->setAutoFillBackground( 
true );
 
  714void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
 
  715    const QStyleOptionViewItem &option,
 
  716    const QModelIndex & )
 const 
  718  editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
 
  721QSize VariableEditorDelegate::sizeHint( 
const QStyleOptionViewItem &option,
 
  722                                        const QModelIndex &index )
 const 
  724  return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
 
  727void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
 
  728    const QModelIndex &index )
 const 
  735  QTreeWidgetItem *item = mParentTree->indexToItem( index );
 
  737  if ( !item || !scope )
 
  740  QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
 
  744  const QString variableName = mParentTree->variableNameFromIndex( index );
 
  745  if ( index.column() == 0 )
 
  748    QString newName = lineEdit->text();
 
  749    newName = newName.trimmed();
 
  750    newName = newName.replace( 
' ', 
'_' );
 
  753    if ( newName == variableName )
 
  760      QMessageBox::warning( mParentTree, tr( 
"Rename Variable" ), tr( 
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
 
  761      newName.append( 
"_1" );
 
  764    const QString value = scope->
variable( variableName ).toString();
 
  765    mParentTree->renameItem( item, newName );
 
  768    mParentTree->emitChanged();
 
  770  else if ( index.column() == 1 )
 
  773    const QString value = lineEdit->text();
 
  774    if ( scope->
variable( variableName ).toString() == value )
 
  779    mParentTree->emitChanged();
 
  781  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.