Skip to content

Commit

Permalink
Merge pull request #40892 from 3nids/svg-dyn-gui
Browse files Browse the repository at this point in the history
GUI for dynamic SVGs
  • Loading branch information
3nids committed Jan 8, 2021
2 parents 70aea49 + c6d4e61 commit 0120e0f
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 28 deletions.
37 changes: 37 additions & 0 deletions python/gui/auto_generated/symbology/qgssvgselectorwidget.sip.in
Expand Up @@ -11,6 +11,8 @@





class QgsSvgSelectorListModel : QAbstractListModel
{
%Docstring
Expand Down Expand Up @@ -80,6 +82,13 @@ class QgsSvgSelectorWidget : QWidget
QgsSvgSelectorWidget( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsSvgSelectorWidget
%End

void initParametersModel( const QgsExpressionContextGenerator *generator, QgsVectorLayer *layer );
%Docstring
Initialize the parameters model so the context and the layer are referenced.

.. versionadded:: 3.18
%End

QString currentSvgPath() const;
Expand All @@ -89,17 +98,45 @@ Constructor for QgsSvgSelectorWidget
Returns the source line edit

.. versionadded:: 3.16
%End

void setAllowParameters( bool allow );
%Docstring
Defines if the group box to fill parameters is visible

.. versionadded:: 3.18
%End

bool allowParamerters() const;
%Docstring
Returns if the group box to fill parameters is visible

.. versionadded:: 3.18
%End

public slots:
void setSvgPath( const QString &svgPath );
%Docstring
Accepts absolute paths
%End

void setSvgParameters( const QMap<QString, QgsProperty> &parameters );
%Docstring
Sets the dynamic parameters

.. versionadded:: 3.18
%End

signals:
void svgSelected( const QString &path );

void svgParametersChanged( const QMap<QString, QgsProperty> &parameters );
%Docstring
Emitted when the parameters have changed

.. versionadded:: 3.18
%End

protected:
void populateList();

Expand Down
Expand Up @@ -453,6 +453,10 @@ Creates a new QgsSvgMarkerSymbolLayerWidget.
void setSvgPath( const QString &name );
%Docstring
Sets the SVG path
%End
void setSvgParameters( const QMap<QString, QgsProperty> &parameters );
%Docstring
Sets the dynamic SVG parameters
%End


Expand Down
241 changes: 237 additions & 4 deletions src/gui/symbology/qgssvgselectorwidget.cpp
Expand Up @@ -24,6 +24,9 @@
#include "qgssymbollayerutils.h"
#include "qgssettings.h"
#include "qgsgui.h"
#include "qgsfieldexpressionwidget.h"
#include "qgssymbollayerwidget.h"
#include "qgsvectorlayer.h"

#include <QAbstractListModel>
#include <QCheckBox>
Expand Down Expand Up @@ -396,10 +399,33 @@ QgsSvgSelectorWidget::QgsSvgSelectorWidget( QWidget *parent )
mGroupsTreeView->setHeaderHidden( true );
populateList();

connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &QgsSvgSelectorWidget::svgSelectionChanged );
connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &QgsSvgSelectorWidget::populateIcons );
mParametersModel = new QgsSvgParametersModel( this );
mParametersTreeView->setModel( mParametersModel );
mParametersGroupBox->setVisible( mAllowParameters );

mParametersTreeView->setItemDelegateForColumn( static_cast<int>( QgsSvgParametersModel::Column::ExpressionColumn ), new QgsSvgParameterValueDelegate( this ) );
mParametersTreeView->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
mParametersTreeView->header()->setStretchLastSection( true );
mParametersTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
mParametersTreeView->setSelectionMode( QAbstractItemView::MultiSelection );
mParametersTreeView->setEditTriggers( QAbstractItemView::DoubleClicked );

connect( mParametersModel, &QgsSvgParametersModel::parametersChanged, this, &QgsSvgSelectorWidget::svgParametersChanged );
connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::svgSelectionChanged );
connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::populateIcons );
connect( mAddParameterButton, &QToolButton::clicked, mParametersModel, &QgsSvgParametersModel::addParameter );
connect( mRemoveParameterButton, &QToolButton::clicked, this, [ = ]()
{
const QModelIndexList selectedRows = mParametersTreeView->selectionModel()->selectedRows();
if ( selectedRows.count() > 0 )
mParametersModel->removeParameters( selectedRows );
} );
}

void QgsSvgSelectorWidget::initParametersModel( const QgsExpressionContextGenerator *generator, QgsVectorLayer *layer )
{
mParametersModel->setExpressionContextGenerator( generator );
mParametersModel->setLayer( layer );
}

void QgsSvgSelectorWidget::setSvgPath( const QString &svgPath )
Expand All @@ -425,11 +451,25 @@ void QgsSvgSelectorWidget::setSvgPath( const QString &svgPath )
mImagesListView->selectionModel()->blockSignals( false );
}

void QgsSvgSelectorWidget::setSvgParameters( const QMap<QString, QgsProperty> &parameters )
{
mParametersModel->setParameters( parameters );
}

QString QgsSvgSelectorWidget::currentSvgPath() const
{
return mCurrentSvgPath;
}

void QgsSvgSelectorWidget::setAllowParameters( bool allow )
{
if ( mAllowParameters == allow )
return;

mAllowParameters = allow;
mParametersGroupBox->setVisible( allow );
}

void QgsSvgSelectorWidget::updateCurrentSvgPath( const QString &svgPath )
{
mCurrentSvgPath = svgPath;
Expand Down Expand Up @@ -508,3 +548,196 @@ QgsSvgSelectorDialog::QgsSvgSelectorDialog( QWidget *parent, Qt::WindowFlags fl,
setLayout( mLayout );
}


///@cond PRIVATE


QgsSvgParametersModel::QgsSvgParametersModel( QObject *parent )
: QAbstractTableModel( parent )
{
connect( this, &QAbstractTableModel::rowsInserted, this, [ = ]() {emit parametersChanged( parameters() );} );
connect( this, &QAbstractTableModel::rowsRemoved, this, [ = ]() {emit parametersChanged( parameters() );} );
connect( this, &QAbstractTableModel::dataChanged, this, [ = ]() {emit parametersChanged( parameters() );} );
}

void QgsSvgParametersModel::setParameters( const QMap<QString, QgsProperty> &parameters )
{
beginResetModel();
mParameters.clear();
QMap<QString, QgsProperty>::const_iterator paramIt = parameters.constBegin();
for ( ; paramIt != parameters.constEnd(); ++paramIt )
{
mParameters << Parameter( paramIt.key(), paramIt.value() );
}
endResetModel();
}

QMap<QString, QgsProperty> QgsSvgParametersModel::parameters() const
{
QMap<QString, QgsProperty> params;
for ( const Parameter &param : qgis::as_const( mParameters ) )
{
if ( !param.name.isEmpty() )
params.insert( param.name, param.property );
}
return params;
}

void QgsSvgParametersModel::removeParameters( const QModelIndexList &indexList )
{
if ( !indexList.count() )
return;

auto mm = std::minmax_element( indexList.constBegin(), indexList.constEnd(), []( const QModelIndex & i1, const QModelIndex & i2 ) {return i1.row() < i2.row();} );

beginRemoveRows( QModelIndex(), ( *mm.first ).row(), ( *mm.second ).row() );
for ( const QModelIndex &index : indexList )
mParameters.removeAt( index.row() );
endRemoveRows();
}

void QgsSvgParametersModel::setLayer( QgsVectorLayer *layer )
{
mLayer = layer;
}

void QgsSvgParametersModel::setExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
{
mExpressionContextGenerator = generator;
}

int QgsSvgParametersModel::rowCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent )
return mParameters.count();
}

int QgsSvgParametersModel::columnCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent )
return 2;
}

QVariant QgsSvgParametersModel::data( const QModelIndex &index, int role ) const
{
QgsSvgParametersModel::Column col = static_cast<QgsSvgParametersModel::Column>( index.column() );
if ( role == Qt::DisplayRole )
{
switch ( col )
{
case QgsSvgParametersModel::Column::NameColumn:
return mParameters.at( index.row() ).name;
case QgsSvgParametersModel::Column::ExpressionColumn:
return mParameters.at( index.row() ).property.expressionString();
}
}

return QVariant();
}

bool QgsSvgParametersModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( !index.isValid() || role != Qt::EditRole )
return false;

QgsSvgParametersModel::Column col = static_cast<QgsSvgParametersModel::Column>( index.column() );
switch ( col )
{
case QgsSvgParametersModel::Column::NameColumn:
{
QString oldName = mParameters.at( index.row() ).name;
QString newName = value.toString();
for ( const Parameter &param : qgis::as_const( mParameters ) )
{
if ( param.name == newName && param.name != oldName )
{
// names must be unique!
return false;
}
}
mParameters[index.row()].name = newName;
emit dataChanged( index, index );
return true;
}

case QgsSvgParametersModel::Column::ExpressionColumn:
mParameters[index.row()].property = QgsProperty::fromExpression( value.toString() );
emit dataChanged( index, index );
return true;
}

return false;
}

QVariant QgsSvgParametersModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( role == Qt::DisplayRole && orientation == Qt::Horizontal )
{
QgsSvgParametersModel::Column col = static_cast<QgsSvgParametersModel::Column>( section );
switch ( col )
{
case QgsSvgParametersModel::Column::NameColumn:
return tr( "Name" );
case QgsSvgParametersModel::Column::ExpressionColumn:
return tr( "Expression" );
}
}

return QVariant();
}

void QgsSvgParametersModel::addParameter()
{
int c = rowCount( QModelIndex() );
beginInsertRows( QModelIndex(), c, c );
int i = 1;
QStringList currentNames;
std::transform( mParameters.begin(), mParameters.end(), std::back_inserter( currentNames ), []( const Parameter & parameter ) {return parameter.name;} );
while ( currentNames.contains( QStringLiteral( "param%1" ).arg( i ) ) )
i++;
mParameters.append( Parameter( QStringLiteral( "param%1" ).arg( i ), QgsProperty() ) );
endResetModel();
}


Qt::ItemFlags QgsSvgParametersModel::flags( const QModelIndex &index ) const
{
Q_UNUSED( index )
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}


QWidget *QgsSvgParameterValueDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
Q_UNUSED( option )
QgsFieldExpressionWidget *w = new QgsFieldExpressionWidget( parent );
const QgsSvgParametersModel *model = qobject_cast<const QgsSvgParametersModel *>( index.model() );
w->registerExpressionContextGenerator( model->expressionContextGenerator() );
w->setLayer( model->layer() );
return w;
}

void QgsSvgParameterValueDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
{
QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( editor );
if ( !w )
return;

w->setExpression( index.model()->data( index ).toString() );
}

void QgsSvgParameterValueDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( editor );
if ( !w )
return;
model->setData( index, w->currentField() );
}

void QgsSvgParameterValueDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
Q_UNUSED( index )
editor->setGeometry( option.rect );
}

///@endcond

0 comments on commit 0120e0f

Please sign in to comment.