Add editor widget for editing QgsExpressionContextScope variables

This commit is contained in:
Nyall Dawson 2015-08-12 16:10:32 +10:00
parent 05b15dc3c5
commit 1c7a5b2dba
14 changed files with 1451 additions and 51 deletions

View File

@ -314,6 +314,7 @@
<file>themes/default/mIconClear.svg</file>
<file>themes/default/mIconClose.png</file>
<file>themes/default/mIconCollapse.png</file>
<file>themes/default/mIconCollapseSmall.svg</file>
<file>themes/default/mIconColorBox.svg</file>
<file>themes/default/mIconColorPicker.svg</file>
<file>themes/default/mIconColorSwatches.svg</file>
@ -333,6 +334,7 @@
<file>themes/default/mIconEditable.png</file>
<file>themes/default/mIconEditableEdits.png</file>
<file>themes/default/mIconExpand.png</file>
<file>themes/default/mIconExpandSmall.svg</file>
<file>themes/default/mIconExpression.svg</file>
<file>themes/default/mIconExpressionEditorOpen.svg</file>
<file>themes/default/mIconExpressionFilter.svg</file>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="14"
height="14"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="New document 1">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.627417"
inkscape:cx="9.8576035"
inkscape:cy="9.6322253"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showguides="false"
inkscape:snap-global="false"
inkscape:window-width="1114"
inkscape:window-height="630"
inkscape:window-x="190"
inkscape:window-y="52"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1038.3622)">
<path
style="fill:#4d4d4d;stroke:none"
d="m 2.032932,1042.3744 10.032077,0 0,0.9722 -4.9939412,6.0546 -5.0381358,-6.0546 z"
id="path2998"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:#808080;stroke:none"
d="m 3.4029514,1043.3908 7.3804266,0 -3.7123102,4.4636 z"
id="path3768"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="14"
height="14"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="mIconCollapseSmall.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.627417"
inkscape:cx="9.8576035"
inkscape:cy="9.6322253"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showguides="false"
inkscape:snap-global="false"
inkscape:window-width="1114"
inkscape:window-height="630"
inkscape:window-x="190"
inkscape:window-y="52"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1038.3622)">
<path
style="fill:#4d4d4d;stroke:none"
d="m 3.9775122,1050.3735 0,-10.032 0.9722,0 6.0546008,4.9939 -6.0546008,5.0381 z"
id="path2998"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:#808080;stroke:none"
d="m 4.9939122,1049.0035 0,-7.3804 4.4636,3.7123 z"
id="path3768"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -118,6 +118,7 @@
%Include qgssvgannotationitem.sip
%Include qgstextannotationitem.sip
%Include qgsuserinputdockwidget.sip
%Include qgsvariableeditorwidget.sip
%Include qgsvectorlayertools.sip
%Include qgsvertexmarker.sip

View File

@ -0,0 +1,90 @@
/** \ingroup gui
* \class QgsVariableEditorWidget
* A tree based widget for editing expression context scope variables. The widget allows editing
* variables from a QgsExpressionContextScope, and can optionally also show inherited
* variables from a QgsExpressionContext.
* \note added in QGIS 2.12
*/
class QgsVariableEditorWidget : QWidget
{
%TypeHeaderCode
#include <qgsvariableeditorwidget.h>
%End
public:
/** Constructor for QgsVariableEditorWidget.
* @param parent parent widget
*/
QgsVariableEditorWidget( QWidget *parent /TransferThis/ = 0 );
~QgsVariableEditorWidget();
/** Overwrites the QgsExpressionContext for the widget. Setting a context
* allows the widget to show all inherited variables for the context,
* and highlight any overriden variables within scopes.
* @param context expression context
* @see context()
*/
void setContext( QgsExpressionContext* context );
/** Returns the current expression context for the widget. QgsVariableEditorWidget widgets
* are created with an empty context by default.
* @see setContext()
*/
QgsExpressionContext* context() const;
/** Reloads all scopes from the editor's current context. This method should be called
* after adding or removing scopes from the attached context.
* @see context()
*/
void reloadContext();
/** Sets the editable scope for the widget. Only variables from the editable scope can
* be modified by users.
* @param scopeIndex index of current editable scope. Set to -1 to disable
* editing and make the widget read-only.
* @see editableScope()
*/
void setEditableScopeIndex( int scopeIndex );
/** Returns the current editable scope for the widget.
* @returns editable scope, or 0 if no editable scope is set
* @see setEditableScopeIndex()
*/
QgsExpressionContextScope* editableScope() const;
/** Sets the setting group for the widget. QgsVariableEditorWidget widgets with
* the same setting group will synchronise their settings, eg the size
* of columns in the tree widget.
* @param group setting group
* @see settingGroup()
*/
void setSettingGroup( const QString &group );
/** Returns the setting group for the widget. QgsVariableEditorWidget widgets with
* the same setting group will synchronise their settings, eg the size
* of columns in the tree widget.
* @returns setting group name
* @see setSettingGroup()
*/
QString settingGroup() const;
/** Returns a map variables set within the editable scope. Read only variables are not
* returned. This method can be used to retrieve the variables edited an added by
* users via the widget.
*/
QgsStringMap variablesInActiveScope() const;
signals:
/** Emitted when the user has modified a scope using the widget.
*/
void scopeChanged();
protected:
void showEvent( QShowEvent *event );
};

View File

@ -39,6 +39,7 @@
#include "qgscolorschemeregistry.h"
#include "qgssymbollayerv2utils.h"
#include "qgscolordialog.h"
#include "qgsexpressioncontext.h"
#include <QInputDialog>
#include <QFileDialog>
@ -838,6 +839,10 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) :
// load gdal driver list only when gdal tab is first opened
mLoadedGdalDriverList = false;
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() );
mVariableEditor->reloadContext();
mVariableEditor->setEditableScopeIndex( 0 );
// restore window and widget geometry/state
restoreOptionsBaseUi();
}
@ -1333,6 +1338,9 @@ void QgsOptions::saveOptions()
// TODO[MD] QgisApp::instance()->legend()->updateLegendItemSymbologies();
}
//save variables
QgsExpressionContextUtils::setGlobalVariables( mVariableEditor->variablesInActiveScope() );
// save app stylesheet last (in case reset becomes necessary)
if ( mStyleSheetNewOpts != mStyleSheetOldOpts )
{

View File

@ -29,6 +29,8 @@
#include <QList>
class QgsExpressionContext;
/**
* \class QgsOptions
* \brief Set user options and preferences

View File

@ -48,6 +48,7 @@
#include "qgscolorschemeregistry.h"
#include "qgssymbollayerv2utils.h"
#include "qgscolordialog.h"
#include "qgsexpressioncontext.h"
//qt includes
#include <QInputDialog>
@ -524,6 +525,11 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa
on_cbxProjectionEnabled_toggled( myProjectionEnabled );
}
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() );
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::projectScope() );
mVariableEditor->reloadContext();
mVariableEditor->setEditableScopeIndex( 1 );
projectionSelectorInitialized();
restoreOptionsBaseUi();
restoreState();
@ -919,6 +925,9 @@ void QgsProjectProperties::apply()
QgsProject::instance()->relationManager()->setRelations( mRelationManagerDlg->relations() );
//save variables
QgsExpressionContextUtils::setProjectVariables( mVariableEditor->variablesInActiveScope() );
emit refresh();
}

View File

@ -26,6 +26,7 @@
class QgsMapCanvas;
class QgsRelationManagerDialog;
class QgsStyleV2;
class QgsExpressionContext;
/** Dialog to set project level properties

View File

@ -234,6 +234,7 @@ SET(QGIS_GUI_SRCS
qgssvgannotationitem.cpp
qgstextannotationitem.cpp
qgsuserinputdockwidget.cpp
qgsvariableeditorwidget.cpp
qgsvertexmarker.cpp
qgsunitselectionwidget.cpp
)
@ -351,6 +352,7 @@ SET(QGIS_GUI_MOC_HDRS
qgssearchquerybuilder.h
qgsslider.h
qgssublayersdialog.h
qgsvariableeditorwidget.h
qgsunitselectionwidget.h
qgsuserinputdockwidget.h

View File

@ -0,0 +1,753 @@
/***************************************************************************
qgsvariableeditorwidget.cpp
---------------------------
Date : April 2015
Copyright : (C) 2015 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsvariableeditorwidget.h"
#include "qgsexpressioncontext.h"
#include "qgsfeature.h"
#include "qgsapplication.h"
#include <QVBoxLayout>
#include <QTreeWidget>
#include <QPainter>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QLineEdit>
#include <QPushButton>
#include <QHeaderView>
#include <QMessageBox>
#include <QSettings>
/// @cond
class VariableEditorDelegate : public QItemDelegate
{
public:
VariableEditorDelegate( QObject *parent = 0, QgsVariableEditorTree *tree = 0 )
: QItemDelegate( parent )
, mParentTree( tree )
{}
QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index ) const override;
void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index ) const override;
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
void setModelData( QWidget* widget, QAbstractItemModel* model,
const QModelIndex & index ) const override;
void setEditorData( QWidget *, const QModelIndex & ) const override {}
private:
QgsVariableEditorTree *mParentTree;
};
QIcon QgsVariableEditorTree::mExpandIcon;
/// @endcond
//
// QgsVariableEditorWidget
//
QgsVariableEditorWidget::QgsVariableEditorWidget( QWidget *parent )
: QWidget( parent )
, mContext( 0 )
, mEditableScopeIndex( -1 )
, mShown( false )
{
QVBoxLayout* verticalLayout = new QVBoxLayout( this );
verticalLayout->setSpacing( 3 );
verticalLayout->setContentsMargins( 3, 3, 3, 3 );
mTreeWidget = new QgsVariableEditorTree( this );
mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
verticalLayout->addWidget( mTreeWidget );
QHBoxLayout* horizontalLayout = new QHBoxLayout();
horizontalLayout->setSpacing( 6 );
QSpacerItem* horizontalSpacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
horizontalLayout->addItem( horizontalSpacer );
mAddButton = new QPushButton();
mAddButton->setIcon( QgsApplication::getThemeIcon( "/mActionSignPlus.png" ) );
mAddButton->setEnabled( false );
horizontalLayout->addWidget( mAddButton );
mRemoveButton = new QPushButton();
mRemoveButton->setIcon( QgsApplication::getThemeIcon( "/symbologyRemove.png" ) );
mRemoveButton->setEnabled( false );
horizontalLayout->addWidget( mRemoveButton );
verticalLayout->addLayout( horizontalLayout );
connect( mRemoveButton, SIGNAL( clicked() ), this, SLOT( on_mRemoveButton_clicked() ) );
connect( mAddButton, SIGNAL( clicked() ), this, SLOT( on_mAddButton_clicked() ) );
connect( mTreeWidget, SIGNAL( itemSelectionChanged() ), this, SLOT( selectionChanged() ) );
connect( mTreeWidget, SIGNAL( scopeChanged() ), this, SIGNAL( scopeChanged() ) );
setContext( new QgsExpressionContext() );
}
QgsVariableEditorWidget::~QgsVariableEditorWidget()
{
QSettings settings;
settings.setValue( saveKey() + "column0width", mTreeWidget->header()->sectionSize( 0 ) );
}
void QgsVariableEditorWidget::showEvent( QShowEvent * event )
{
// initialise widget on first show event only
if ( mShown )
{
event->accept();
return;
}
//restore split size
QSettings settings;
QVariant val;
val = settings.value( saveKey() + "column0width" );
bool ok;
int sectionSize = val.toInt( &ok );
if ( ok )
{
mTreeWidget->header()->resizeSection( 0, sectionSize );
}
mShown = true;
QWidget::showEvent( event );
}
void QgsVariableEditorWidget::setContext( QgsExpressionContext* context )
{
mContext.reset( new QgsExpressionContext( *context ) );
reloadContext();
}
void QgsVariableEditorWidget::reloadContext()
{
mTreeWidget->resetTree();
mTreeWidget->setContext( mContext.data() );
mTreeWidget->refreshTree();
}
void QgsVariableEditorWidget::setEditableScopeIndex( int scopeIndex )
{
mEditableScopeIndex = scopeIndex;
if ( mEditableScopeIndex >= 0 )
{
mAddButton->setEnabled( true );
}
mTreeWidget->setEditableScopeIndex( scopeIndex );
mTreeWidget->refreshTree();
}
QgsExpressionContextScope* QgsVariableEditorWidget::editableScope() const
{
if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
{
return 0;
}
return mContext->scope( mEditableScopeIndex );
}
QgsStringMap QgsVariableEditorWidget::variablesInActiveScope() const
{
QgsStringMap variables;
if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
{
return variables;
}
QgsExpressionContextScope* scope = mContext->scope( mEditableScopeIndex );
Q_FOREACH ( QString variable, scope->variableNames() )
{
if ( scope->isReadOnly( variable ) )
continue;
variables.insert( variable, scope->variable( variable ).toString() );
}
return variables;
}
QString QgsVariableEditorWidget::saveKey() const
{
// save key for load/save state
// currently QgsVariableEditorTree/window()/object
QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
QString saveKey = "/QgsVariableEditorTree/" + setGroup + "/";
return saveKey;
}
void QgsVariableEditorWidget::on_mAddButton_clicked()
{
if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
return;
QgsExpressionContextScope* scope = mContext->scope( mEditableScopeIndex );
scope->setVariable( "new_variable", QVariant() );
mTreeWidget->refreshTree();
QTreeWidgetItem* item = mTreeWidget->itemFromVariable( scope, "new_variable" );
QModelIndex index = mTreeWidget->itemToIndex( item );
mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
mTreeWidget->editItem( item, 0 );
emit scopeChanged();
}
void QgsVariableEditorWidget::on_mRemoveButton_clicked()
{
if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
return;
QgsExpressionContextScope* editableScope = mContext->scope( mEditableScopeIndex );
QList<QTreeWidgetItem*> selectedItems = mTreeWidget->selectedItems();
foreach ( QTreeWidgetItem* item, selectedItems )
{
if ( !( item->flags() & Qt::ItemIsEditable ) )
continue;
QString name = item->text( 0 );
QgsExpressionContextScope* itemScope = mTreeWidget->scopeFromItem( item );
if ( itemScope != editableScope )
continue;
if ( itemScope->isReadOnly( name ) )
continue;
itemScope->removeVariable( name );
mTreeWidget->removeItem( item );
}
mTreeWidget->refreshTree();
}
void QgsVariableEditorWidget::selectionChanged()
{
if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
{
mRemoveButton->setEnabled( false );
return;
}
QgsExpressionContextScope* editableScope = mContext->scope( mEditableScopeIndex );
QList<QTreeWidgetItem*> selectedItems = mTreeWidget->selectedItems();
bool removeEnabled = true;
foreach ( QTreeWidgetItem* item, selectedItems )
{
if ( !( item->flags() & Qt::ItemIsEditable ) )
{
removeEnabled = false;
break;
}
QString name = item->text( 0 );
QgsExpressionContextScope* itemScope = mTreeWidget->scopeFromItem( item );
if ( itemScope != editableScope )
{
removeEnabled = false;
break;
}
if ( editableScope->isReadOnly( name ) )
{
removeEnabled = false;
break;
}
}
mRemoveButton->setEnabled( removeEnabled );
}
//
// VariableEditorTree
//
QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
: QTreeWidget( parent )
, mEditorDelegate( 0 )
, mEditableScopeIndex( -1 )
, mContext( 0 )
{
// init icons
if ( mExpandIcon.isNull() )
{
QPixmap pix( 14, 14 );
pix.fill( Qt::transparent );
mExpandIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconExpandSmall.svg" ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
mExpandIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconExpandSmall.svg" ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
mExpandIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconCollapseSmall.svg" ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
mExpandIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconCollapseSmall.svg" ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
}
setIconSize( QSize( 18, 18 ) );
setColumnCount( 2 );
setHeaderLabels( QStringList() << tr( "Variable" ) << tr( "Value" ) );
setAlternatingRowColors( true );
setEditTriggers( QAbstractItemView::AllEditTriggers );
setRootIsDecorated( false );
header()->setMovable( false );
header()->setResizeMode( QHeaderView::Interactive );
mEditorDelegate = new VariableEditorDelegate( this, this );
setItemDelegate( mEditorDelegate );
}
QgsExpressionContextScope* QgsVariableEditorTree::scopeFromItem( QTreeWidgetItem *item ) const
{
if ( !item )
return 0;
bool ok;
int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
if ( !ok )
return 0;
if ( !mContext )
{
return 0;
}
else if ( mContext->scopeCount() > contextIndex )
{
return mContext->scope( contextIndex );
}
else
{
return 0;
}
}
QTreeWidgetItem* QgsVariableEditorTree::itemFromVariable( QgsExpressionContextScope *scope, const QString &name ) const
{
int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
if ( contextIndex < 0 )
return 0;
return mVariableToItem.value( qMakePair( contextIndex, name ) );
}
QgsExpressionContextScope* QgsVariableEditorTree::editableScope()
{
if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
{
return 0;
}
return mContext->scope( mEditableScopeIndex );
}
void QgsVariableEditorTree::refreshTree()
{
if ( !mContext || mEditableScopeIndex < 0 )
{
clear();
return;
}
//add all scopes from the context
int scopeIndex = 0;
Q_FOREACH ( QgsExpressionContextScope* scope, mContext->scopes() )
{
refreshScopeItems( scope, scopeIndex );
scopeIndex++;
}
}
void QgsVariableEditorTree::refreshScopeVariables( QgsExpressionContextScope* scope, int scopeIndex )
{
QColor baseColor = rowColor( scopeIndex );
bool isCurrent = scopeIndex == mEditableScopeIndex;
QTreeWidgetItem* scopeItem = mScopeToItem.value( scopeIndex );
foreach ( QString name, scope->variableNames() )
{
QTreeWidgetItem* item;
if ( mVariableToItem.contains( qMakePair( scopeIndex, name ) ) )
{
item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
}
else
{
item = new QTreeWidgetItem( scopeItem );
mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
}
bool readOnly = scope->isReadOnly( name );
bool isActive = true;
QgsExpressionContextScope* activeScope = 0;
if ( mContext )
{
activeScope = mContext->activeScopeForVariable( name );
isActive = activeScope == scope;
}
item->setFlags( item->flags() | Qt::ItemIsEnabled );
item->setText( 0, name );
QString value = scope->variable( name ).toString();
item->setText( 1, value );
QFont font = item->font( 0 );
if ( readOnly || !isCurrent )
{
font.setItalic( true );
item->setFlags( item->flags() ^ Qt::ItemIsEditable );
}
else
{
font.setItalic( false );
item->setFlags( item->flags() | Qt::ItemIsEditable );
}
if ( !isActive )
{
//overridden
font.setStrikeOut( true );
QString toolTip = tr( "Overriden by value from %1" ).arg( activeScope->name() );
item->setToolTip( 0, toolTip );
item->setToolTip( 1, toolTip );
}
else
{
font.setStrikeOut( false );
item->setToolTip( 0, name );
item->setToolTip( 1, value );
}
item->setFont( 0, font );
item->setFont( 1, font );
item->setData( 0, RowBaseColor, baseColor );
item->setData( 0, ContextIndex, scopeIndex );
item->setFirstColumnSpanned( false );
}
}
void QgsVariableEditorTree::refreshScopeItems( QgsExpressionContextScope* scope, int scopeIndex )
{
QSettings settings;
//add top level item
bool isCurrent = scopeIndex == mEditableScopeIndex;
QTreeWidgetItem* scopeItem;
if ( mScopeToItem.contains( scopeIndex ) )
{
//retrieve existing item
scopeItem = mScopeToItem.value( scopeIndex );
}
else
{
//create new top-level item
scopeItem = new QTreeWidgetItem();
mScopeToItem.insert( scopeIndex, scopeItem );
scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
scopeItem->setText( 0, scope->name() );
scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
scopeItem->setFirstColumnSpanned( true );
QFont scopeFont = scopeItem->font( 0 );
scopeFont .setBold( true );
scopeItem->setFont( 0, scopeFont );
scopeItem->setFirstColumnSpanned( true );
addTopLevelItem( scopeItem );
//expand by default if current context or context was previously expanded
if ( isCurrent || settings.value( "QgsVariableEditor/" + scopeItem->text( 0 ) + "/expanded" ).toBool() )
scopeItem->setExpanded( true );
scopeItem->setIcon( 0, mExpandIcon );
}
refreshScopeVariables( scope, scopeIndex );
}
void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
{
if ( !item )
return;
mVariableToItem.remove( mVariableToItem.key( item ) );
item->parent()->takeChild( item->parent()->indexOfChild( item ) );
emit scopeChanged();
}
void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item, const QString& name )
{
if ( !item )
return;
int contextIndex = mVariableToItem.key( item ).first;
mVariableToItem.remove( mVariableToItem.key( item ) );
mVariableToItem.insert( qMakePair( contextIndex, name ), item );
item->setText( 0, name );
emit scopeChanged();
}
void QgsVariableEditorTree::resetTree()
{
mVariableToItem.clear();
mScopeToItem.clear();
clear();
}
void QgsVariableEditorTree::emitChanged()
{
emit scopeChanged();
}
void QgsVariableEditorTree::drawRow( QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index ) const
{
QStyleOptionViewItemV3 opt = option;
QTreeWidgetItem* item = itemFromIndex( index );
if ( index.parent().isValid() )
{
//not a top-level item, so shade row background by context
const QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
painter->fillRect( option.rect, baseColor );
opt.palette.setColor( QPalette::AlternateBase, baseColor.lighter( 110 ) );
}
QTreeWidget::drawRow( painter, opt, index );
QColor color = static_cast<QRgb>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
painter->save();
painter->setPen( QPen( color ) );
painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
painter->restore();
}
QColor QgsVariableEditorTree::rowColor( int index ) const
{
//return some nice (inspired by Qt Designer) background row colors
int colorIdx = index % 6;
switch ( colorIdx )
{
case 0:
return QColor( 255, 220, 167 );
case 1:
return QColor( 255, 255, 191 );
case 2:
return QColor( 191, 255, 191 );
case 3:
return QColor( 199, 255, 255 );
case 4:
return QColor( 234, 191, 255 );
case 5:
default:
return QColor( 255, 191, 239 );
}
}
void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem* item )
{
if ( !item )
return;
item->setExpanded( !item->isExpanded() );
//save expanded state
QSettings settings;
settings.setValue( "QgsVariableEditor/" + item->text( 0 ) + "/expanded", item->isExpanded() );
}
void QgsVariableEditorTree::editNext( const QModelIndex& index )
{
if ( !index.isValid() )
return;
if ( index.column() == 0 )
{
//switch to next column
QModelIndex nextIndex = index.sibling( index.row(), 1 );
if ( nextIndex.isValid() )
{
setCurrentIndex( nextIndex );
edit( nextIndex );
}
}
else
{
QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
if ( nextIndex.isValid() )
{
//start editing next row
setCurrentIndex( nextIndex );
edit( nextIndex );
}
else
{
edit( index );
}
}
}
void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
{
switch ( event->key() )
{
case Qt::Key_Return:
case Qt::Key_Enter:
case Qt::Key_Space:
{
QTreeWidgetItem *item = currentItem();
if ( item && !item->parent() )
{
event->accept();
toggleContextExpanded( item );
return;
}
else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
{
event->accept();
editNext( currentIndex() );
return;
}
break;
}
default:
break;
}
QTreeWidget::keyPressEvent( event );
}
void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
{
QTreeWidget::mousePressEvent( event );
QTreeWidgetItem* item = itemAt( event->pos() );
if ( !item )
return;
if ( item->parent() )
{
//not a top-level item
return;
}
if ( event->pos().x() + header()->offset() > 20 )
{
//not clicking on expand icon
return;
}
if ( event->modifiers() & Qt::ShiftModifier )
{
//shift modifier expands all
if ( !item->isExpanded() )
{
expandAll();
}
else
{
collapseAll();
}
}
else
{
toggleContextExpanded( item );
}
}
//
// VariableEditorDelegate
//
QWidget* VariableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem&,
const QModelIndex &index ) const
{
if ( !mParentTree )
return 0;
//no editing for top level items
if ( !index.parent().isValid() )
return 0;
QTreeWidgetItem *item = mParentTree->indexToItem( index );
QgsExpressionContextScope* scope = mParentTree->scopeFromItem( item );
if ( !item || !scope )
return 0;
QString variableName = mParentTree->variableNameFromIndex( index );
//no editing inherited or read-only variables
if ( scope != mParentTree->editableScope() || scope->isReadOnly( variableName ) )
return 0;
QLineEdit *lineEdit = new QLineEdit( parent );
lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
lineEdit->setAutoFillBackground( true );
return lineEdit;
}
void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex & ) const
{
editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
}
QSize VariableEditorDelegate::sizeHint( const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
}
void VariableEditorDelegate::setModelData( QWidget* widget, QAbstractItemModel *model,
const QModelIndex& index ) const
{
Q_UNUSED( model );
if ( !mParentTree )
return;
QTreeWidgetItem *item = mParentTree->indexToItem( index );
QgsExpressionContextScope *scope = mParentTree->scopeFromItem( item );
if ( !item || !scope )
return;
QLineEdit* lineEdit = qobject_cast< QLineEdit* >( widget );
if ( !lineEdit )
return;
QString variableName = mParentTree->variableNameFromIndex( index );
if ( index.column() == 0 )
{
//edited variable name
QString newName = lineEdit->text();
//test for validity
if ( newName == variableName )
{
return;
}
if ( scope->hasVariable( newName ) )
{
//existing name
QMessageBox::warning( mParentTree, tr( "Rename variable" ), tr( "A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
newName.append( "_1" );
}
QString value = scope->variable( variableName ).toString();
mParentTree->renameItem( item, newName );
scope->removeVariable( variableName );
scope->setVariable( newName, value );
mParentTree->emitChanged();
}
else if ( index.column() == 1 )
{
//edited variable value
QString value = lineEdit->text();
if ( scope->variable( variableName ).toString() == value )
{
return;
}
scope->setVariable( variableName, value );
mParentTree->emitChanged();
}
mParentTree->refreshTree();
}

View File

@ -0,0 +1,205 @@
/***************************************************************************
qgsvariableeditorwidget.h
-------------------------
Date : April 2015
Copyright : (C) 2015 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSVARIABLEEDITORWIDGET_H
#define QGSVARIABLEEDITORWIDGET_H
#include "qgis.h"
#include <QWidget>
#include <QTreeWidget>
#include <QItemDelegate>
class QTableWidget;
class QgsVariableEditorTree;
class VariableEditorDelegate;
class QgsExpressionContextScope;
class QPushButton;
class QgsExpressionContext;
/** \ingroup gui
* \class QgsVariableEditorWidget
* A tree based widget for editing expression context scope variables. The widget allows editing
* variables from a QgsExpressionContextScope, and can optionally also show inherited
* variables from a QgsExpressionContext.
* \note added in QGIS 2.12
*/
class GUI_EXPORT QgsVariableEditorWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY( QString settingGroup READ settingGroup WRITE setSettingGroup )
public:
/** Constructor for QgsVariableEditorWidget.
* @param parent parent widget
*/
QgsVariableEditorWidget( QWidget *parent = 0 );
~QgsVariableEditorWidget();
/** Overwrites the QgsExpressionContext for the widget. Setting a context
* allows the widget to show all inherited variables for the context,
* and highlight any overriden variables within scopes.
* @param context expression context
* @see context()
*/
void setContext( QgsExpressionContext* context );
/** Returns the current expression context for the widget. QgsVariableEditorWidget widgets
* are created with an empty context by default.
* @see setContext()
*/
QgsExpressionContext* context() const { return mContext.data(); }
/** Reloads all scopes from the editor's current context. This method should be called
* after adding or removing scopes from the attached context.
* @see context()
*/
void reloadContext();
/** Sets the editable scope for the widget. Only variables from the editable scope can
* be modified by users.
* @param scopeIndex index of current editable scope. Set to -1 to disable
* editing and make the widget read-only.
* @see editableScope()
*/
void setEditableScopeIndex( int scopeIndex );
/** Returns the current editable scope for the widget.
* @returns editable scope, or 0 if no editable scope is set
* @see setEditableScopeIndex()
*/
QgsExpressionContextScope* editableScope() const;
/** Sets the setting group for the widget. QgsVariableEditorWidget widgets with
* the same setting group will synchronise their settings, eg the size
* of columns in the tree widget.
* @param group setting group
* @see settingGroup()
*/
void setSettingGroup( const QString &group ) { mSettingGroup = group; }
/** Returns the setting group for the widget. QgsVariableEditorWidget widgets with
* the same setting group will synchronise their settings, eg the size
* of columns in the tree widget.
* @returns setting group name
* @see setSettingGroup()
*/
QString settingGroup() const { return mSettingGroup; }
/** Returns a map variables set within the editable scope. Read only variables are not
* returned. This method can be used to retrieve the variables edited an added by
* users via the widget.
*/
QgsStringMap variablesInActiveScope() const;
signals:
/** Emitted when the user has modified a scope using the widget.
*/
void scopeChanged();
protected:
void showEvent( QShowEvent *event ) override;
private:
QScopedPointer<QgsExpressionContext> mContext;
int mEditableScopeIndex;
QgsVariableEditorTree* mTreeWidget;
QPushButton* mAddButton;
QPushButton* mRemoveButton;
QString mSettingGroup;
bool mShown;
QString saveKey() const;
private slots:
void on_mAddButton_clicked();
void on_mRemoveButton_clicked();
void selectionChanged();
};
/// @cond
/* QgsVariableEditorTree is NOT part of the public QGIS api. It's only
* public here as Qt meta objects can't be nested classes
*/
class QgsVariableEditorTree : public QTreeWidget
{
Q_OBJECT
public:
enum VaribleRoles
{
ContextIndex = Qt::UserRole,
RowBaseColor
};
QgsVariableEditorTree( QWidget *parent = 0 );
QTreeWidgetItem *indexToItem( const QModelIndex &index ) const { return itemFromIndex( index ); }
QModelIndex itemToIndex( QTreeWidgetItem* item ) const { return indexFromItem( item ); }
QString variableNameFromItem( QTreeWidgetItem* item ) const { return item ? item->text( 0 ) : QString(); }
QString variableNameFromIndex( const QModelIndex& index ) const { return variableNameFromItem( itemFromIndex( index ) ); }
QgsExpressionContextScope* scopeFromItem( QTreeWidgetItem* item ) const;
QTreeWidgetItem* itemFromVariable( QgsExpressionContextScope* scope, const QString& name ) const;
void setEditableScopeIndex( int scopeIndex ) { mEditableScopeIndex = scopeIndex; }
QgsExpressionContextScope* editableScope();
void setContext( QgsExpressionContext* context ) { mContext = context; }
void refreshTree();
void removeItem( QTreeWidgetItem* item );
void renameItem( QTreeWidgetItem *item, const QString &name );
void resetTree();
void emitChanged();
signals:
void scopeChanged();
protected:
void keyPressEvent( QKeyEvent *event ) override;
void mousePressEvent( QMouseEvent *event ) override;
void drawRow( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
QColor rowColor( int index ) const;
void toggleContextExpanded( QTreeWidgetItem *item );
void editNext( const QModelIndex &index );
static QIcon mExpandIcon;
private:
VariableEditorDelegate* mEditorDelegate;
int mEditableScopeIndex;
QgsExpressionContext* mContext;
QMap< QPair<int, QString>, QTreeWidgetItem* > mVariableToItem;
QMap< int, QTreeWidgetItem* > mScopeToItem;
void refreshScopeItems( QgsExpressionContextScope* scope, int scopeIndex );
void refreshScopeVariables( QgsExpressionContextScope* scope, int scopeIndex );
};
/// @endcond
#endif //QGSVARIABLEEDITORWIDGET_H

View File

@ -248,6 +248,15 @@
<normaloff>:/images/themes/default/propertyicons/network_and_proxy.png</normaloff>:/images/themes/default/propertyicons/network_and_proxy.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Variables</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconExpression.svg</normaloff>:/images/themes/default/mIconExpression.svg</iconset>
</property>
</item>
</widget>
</item>
</layout>
@ -4826,6 +4835,26 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_40">
<item>
<widget class="QGroupBox" name="groupBox_25">
<property name="title">
<string>Variables</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_41">
<item>
<widget class="QgsVariableEditorWidget" name="mVariableEditor">
<property name="settingGroup" stdset="0">
<string notr="true">globalOptions</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -4911,6 +4940,12 @@
<header location="global">qgsprojectionselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsVariableEditorWidget</class>
<extends>QWidget</extends>
<header location="global">qgsvariableeditorwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mOptionsListWidget</tabstop>

View File

@ -42,7 +42,16 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -161,6 +170,15 @@
<normaloff>:/images/themes/default/relation.svg</normaloff>:/images/themes/default/relation.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Variables</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconExpression.svg</normaloff>:/images/themes/default/mIconExpression.svg</iconset>
</property>
</item>
</widget>
</item>
</layout>
@ -179,7 +197,16 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -191,11 +218,20 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>4</number>
<number>7</number>
</property>
<widget class="QWidget" name="mProjOpts_01">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -211,8 +247,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>780</height>
<width>656</width>
<height>657</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
@ -227,7 +263,7 @@
<property name="title">
<string>General settings</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_26">
@ -427,7 +463,7 @@
<property name="title">
<string>Measure tool</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayoutMeasureTool">
@ -479,7 +515,7 @@
<property name="title">
<string>Canvas units</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projgeneral</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
@ -596,7 +632,7 @@
<property name="title">
<string>Precision</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_28">
@ -609,7 +645,16 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -690,7 +735,7 @@
<property name="checked">
<bool>false</bool>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
@ -782,7 +827,16 @@
</widget>
<widget class="QWidget" name="mProjOpts_02">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -798,8 +852,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>780</height>
<width>326</width>
<height>46</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
@ -832,7 +886,16 @@
</widget>
<widget class="QWidget" name="mProjOpts_03">
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -848,8 +911,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>780</height>
<width>133</width>
<height>100</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
@ -904,7 +967,16 @@
</widget>
<widget class="QWidget" name="mProjOpts_04">
<layout class="QVBoxLayout" name="verticalLayout_11">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -920,8 +992,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>780</height>
<width>379</width>
<height>582</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
@ -930,7 +1002,7 @@
<property name="title">
<string>Default symbols</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projstyles</string>
</property>
<layout class="QGridLayout" name="gridLayout_11">
@ -1145,7 +1217,7 @@
<property name="title">
<string>Options</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projstyles</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_18">
@ -1293,7 +1365,7 @@
</widget>
</item>
<item row="0" column="0" rowspan="8">
<widget class="QgsColorSchemeList" name="mTreeProjectColors"/>
<widget class="QgsColorSchemeList" name="mTreeProjectColors" native="true"/>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="mButtonImportColors">
@ -1320,7 +1392,16 @@
</widget>
<widget class="QWidget" name="mProjOpts_05">
<layout class="QVBoxLayout" name="verticalLayout_14">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -1336,8 +1417,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>667</width>
<height>1562</height>
<width>542</width>
<height>1633</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
@ -1358,13 +1439,13 @@
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed">
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<property name="saveCollapsedState">
<property name="saveCollapsedState" stdset="0">
<bool>true</bool>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
@ -1491,7 +1572,7 @@
<property name="title">
<string>WMS capabilities</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_13">
@ -1506,10 +1587,10 @@
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed">
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<property name="saveCollapsedState">
<property name="saveCollapsedState" stdset="0">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_10">
@ -1565,10 +1646,10 @@
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed">
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<property name="saveCollapsedState">
<property name="saveCollapsedState" stdset="0">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
@ -1624,10 +1705,10 @@
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed">
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<property name="saveCollapsedState">
<property name="saveCollapsedState" stdset="0">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
@ -1733,10 +1814,10 @@
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed">
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<property name="saveCollapsedState">
<property name="saveCollapsedState" stdset="0">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
@ -1909,7 +1990,7 @@
<property name="title">
<string>WFS capabilities (also influences DXF export)</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
@ -1995,7 +2076,7 @@
<property name="title">
<string>WCS capabilities</string>
</property>
<property name="syncGroup">
<property name="syncGroup" stdset="0">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_9">
@ -2071,7 +2152,16 @@
</widget>
<widget class="QWidget" name="mProjOpts_06">
<layout class="QVBoxLayout" name="verticalLayout_15">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -2087,8 +2177,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>780</height>
<width>168</width>
<height>46</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
@ -2124,9 +2214,38 @@
</widget>
<widget class="QWidget" name="mTabRelations">
<layout class="QGridLayout" name="gridLayout_16">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_23">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Variables</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QgsVariableEditorWidget" name="mVariableEditor" native="true">
<property name="settingGroup" stdset="0">
<string notr="true">projectProperties</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
@ -2150,7 +2269,16 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -2179,6 +2307,19 @@
<class>QgsColorButtonV2</class>
<extends>QToolButton</extends>
<header>qgscolorbuttonv2.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsColorSchemeList</class>
<extends>QWidget</extends>
<header location="global">qgscolorschemelist.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsVariableEditorWidget</class>
<extends>QWidget</extends>
<header location="global">qgsvariableeditorwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsProjectionSelector</class>
@ -2192,11 +2333,6 @@
<header>qgscodeeditorpython.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsColorSchemeList</class>
<extends>QTreeView</extends>
<header>qgscolorschemelist.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>scrollArea_2</tabstop>