Skip to content

Commit

Permalink
Merge pull request #6447 from nyalldawson/backport_action
Browse files Browse the repository at this point in the history
Backport action API tweaks to 3.0
  • Loading branch information
nyalldawson committed Feb 25, 2018
2 parents 001c80b + b768e6d commit 5d23bc0
Show file tree
Hide file tree
Showing 31 changed files with 504 additions and 102 deletions.
9 changes: 9 additions & 0 deletions python/core/qgsaction.sip.in
Expand Up @@ -42,6 +42,7 @@ Create a new QgsAction
:param description: A human readable description string
:param command: The action text. Its interpretation depends on the type
:param capture: If this is set to true, the output will be captured when an action is run
:param enabledOnlyWhenEditable: if true then action is only enable in editmode
%End

QgsAction( ActionType type, const QString &description, const QString &action, const QString &icon, bool capture, const QString &shortTitle = QString(), const QSet<QString> &actionScopes = QSet<QString>(), const QString &notificationMessage = QString() );
Expand All @@ -56,6 +57,7 @@ Create a new QgsAction
:param shortTitle: A short string used to label user interface elements like buttons
:param actionScopes: A set of scopes in which this action will be available
:param notificationMessage: A particular message which reception will trigger the action
:param enabledOnlyWhenEditable: if true then action is only enable in editmode
%End

QString name() const;
Expand Down Expand Up @@ -118,6 +120,13 @@ The action type
Whether to capture output for display when this action is run
%End


bool isEnabledOnlyWhenEditable() const;
%Docstring
Return whether only enabled in editable mode
%End


bool runable() const;
%Docstring
Checks if the action is runable on the current platform
Expand Down
7 changes: 7 additions & 0 deletions python/gui/qgsactionmenu.sip.in
Expand Up @@ -71,6 +71,13 @@ Change the feature on which actions are performed

:param feature: A feature. Will not take ownership. It's the callers responsibility to keep the feature
as long as the menu is displayed and the action is running.
%End

void setMode( const QgsAttributeForm::Mode mode );
%Docstring
Change the mode of the actions

:param mode: The mode of the attribute form
%End

void setExpressionContextScope( const QgsExpressionContextScope &scope );
Expand Down
1 change: 1 addition & 0 deletions python/gui/qgsattributeform.sip.in
Expand Up @@ -26,6 +26,7 @@ class QgsAttributeForm : QWidget
MultiEditMode,
SearchMode,
AggregateSearchMode,
IdentifyMode
};

enum FilterType
Expand Down
31 changes: 28 additions & 3 deletions python/gui/qgsmaplayeractionregistry.sip.in
Expand Up @@ -30,7 +30,15 @@ An action which can run on map layers
typedef QFlags<QgsMapLayerAction::Target> Targets;


QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, Targets targets = AllActions, const QIcon &icon = QIcon() );
enum Flag
{
EnabledOnlyWhenEditable,
};

typedef QFlags<QgsMapLayerAction::Flag> Flags;


QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
%Docstring
Creates a map layer action which can run on any layer

Expand All @@ -39,18 +47,25 @@ Creates a map layer action which can run on any layer
using AllActions as a target probably does not make a lot of sense. This default action was settled for API compatibility reasons.
%End

QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon() );
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer *layer, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
%Docstring
Creates a map layer action which can run only on a specific layer
%End

QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon() );
QgsMapLayerAction( const QString &name, QObject *parent /TransferThis/, QgsMapLayer::LayerType layerType, Targets targets = AllActions, const QIcon &icon = QIcon(), QgsMapLayerAction::Flags flags = 0 );
%Docstring
Creates a map layer action which can run on a specific type of layer
%End

~QgsMapLayerAction();

QgsMapLayerAction::Flags flags() const;
%Docstring
Layer behavior flags.

.. versionadded:: 3.0
%End

bool canRunUsingLayer( QgsMapLayer *layer ) const;
%Docstring
True if action can run using the specified layer
Expand Down Expand Up @@ -78,6 +93,13 @@ Define the targets of the action
const Targets &targets() const;
%Docstring
Return availibity of action
%End

bool isEnabledOnlyWhenEditable() const;
%Docstring
Returns true if the action is only enabled for layers in editable mode.

.. versionadded:: 3.0
%End

signals:
Expand Down Expand Up @@ -158,6 +180,9 @@ Triggered when an action is added or removed from the registry

};

QFlags<QgsMapLayerAction::Flag> operator|(QgsMapLayerAction::Flag f1, QFlags<QgsMapLayerAction::Flag> f2);


/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
4 changes: 0 additions & 4 deletions resources/qgis_global_settings.ini
Expand Up @@ -34,7 +34,3 @@ connections-xyz\OpenStreetMap\zmin=0
# for now this is online version of the User Guide for latest (LTR) release
helpSearchPath=https://docs.qgis.org/$qgis_short_version/$qgis_locale/docs/user_manual/

[app]

# If true, the experimental "duplicate feature" actions will be shown in the QGIS UI
tools\showDuplicateFeatureActions=false
57 changes: 34 additions & 23 deletions src/app/qgisapp.cpp
Expand Up @@ -6464,6 +6464,9 @@ void QgisApp::refreshFeatureActions()
QList<QgsAction> actions = vlayer->actions()->actions( QStringLiteral( "Canvas" ) );
Q_FOREACH ( const QgsAction &action, actions )
{
if ( !vlayer->isEditable() && action.isEnabledOnlyWhenEditable() )
continue;

QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name() : QStringLiteral( "" );
QAction *qAction = new QAction( action.icon(), actionTitle, mFeatureActionMenu );
qAction->setData( QVariant::fromValue<QgsAction>( action ) );
Expand Down Expand Up @@ -7492,31 +7495,27 @@ void QgisApp::setupLayoutManagerConnections()

void QgisApp::setupDuplicateFeaturesAction()
{
QgsSettings settings;
if ( settings.value( QStringLiteral( "tools/showDuplicateFeatureActions" ), false, QgsSettings::App ).toBool() )
{
mDuplicateFeatureAction.reset( new QgsMapLayerAction( tr( "Duplicate feature" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) ) );
mDuplicateFeatureAction.reset( new QgsMapLayerAction( tr( "Duplicate feature" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ), QgsMapLayerAction::EnabledOnlyWhenEditable ) );

QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureAction.get() );
connect( mDuplicateFeatureAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatures( layer, feat );
}
);
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureAction.get() );
connect( mDuplicateFeatureAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatures( layer, feat );
}
);

mDuplicateFeatureDigitizeAction.reset( new QgsMapLayerAction( tr( "Duplicate feature and digitize" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeatureDigitized.svg" ) ) ) );
mDuplicateFeatureDigitizeAction.reset( new QgsMapLayerAction( tr( "Duplicate feature and digitize" ),
nullptr, QgsMapLayerAction::SingleFeature,
QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeatureDigitized.svg" ) ), QgsMapLayerAction::EnabledOnlyWhenEditable ) );

QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureDigitizeAction.get() );
connect( mDuplicateFeatureDigitizeAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatureDigitized( layer, feat );
}
);
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mDuplicateFeatureDigitizeAction.get() );
connect( mDuplicateFeatureDigitizeAction.get(), &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
{
duplicateFeatureDigitized( layer, feat );
}
);
}

void QgisApp::setupAtlasMapLayerAction( QgsPrintLayout *layout, bool enableAction )
Expand Down Expand Up @@ -13204,7 +13203,13 @@ QgsFeature QgisApp::duplicateFeatures( QgsMapLayer *mlayer, const QgsFeature &fe

QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mlayer );

layer->startEditing();
if ( !layer->isEditable() )
{
//should never happen because the action should be disabled
QString msg = tr( "Cannot duplicate feature in not editable mode on layer %1" ).arg( layer->name() );
messageBar()->pushMessage( msg, Qgis::Warning, 3 );
return QgsFeature();
}

QgsFeatureList featureList;

Expand Down Expand Up @@ -13252,7 +13257,13 @@ QgsFeature QgisApp::duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFea

QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mlayer );

layer->startEditing();
if ( !layer->isEditable() )
{
//should never happen because the action should be disabled
QString msg = tr( "Cannot duplicate feature in not editable mode on layer %1" ).arg( layer->name() );
messageBar()->pushMessage( msg, Qgis::Warning, 3 );
return QgsFeature();
}

QgsMapToolDigitizeFeature *digitizeFeature = new QgsMapToolDigitizeFeature( mMapCanvas, mlayer, QgsMapToolCapture::CaptureNone );

Expand Down
19 changes: 14 additions & 5 deletions src/app/qgsattributeactiondialog.cpp
Expand Up @@ -148,13 +148,19 @@ void QgsAttributeActionDialog::insertRow( int row, const QgsAction &action )
// Notification message
mAttributeActionTable->setItem( row, NotificationMessage, new QTableWidgetItem( action.notificationMessage() ) );

// EnabledOnlyWhenEditable
item = new QTableWidgetItem();
item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
item->setCheckState( action.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
mAttributeActionTable->setItem( row, EnabledOnlyWhenEditable, item );

updateButtons();
}

void QgsAttributeActionDialog::insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &actionText, const QString &iconPath, bool capture, const QString &shortTitle, const QSet<QString> &actionScopes, const QString &notificationMessage )
void QgsAttributeActionDialog::insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &actionText, const QString &iconPath, bool capture, const QString &shortTitle, const QSet<QString> &actionScopes, const QString &notificationMessage, bool isEnabledOnlyWhenEditable )
{
if ( uniqueName( name ) == name )
insertRow( row, QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage ) );
insertRow( row, QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
}

void QgsAttributeActionDialog::moveUp()
Expand Down Expand Up @@ -223,7 +229,8 @@ QgsAction QgsAttributeActionDialog::rowToAction( int row ) const
mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
mAttributeActionTable->item( row, ShortTitle )->text(),
mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
mAttributeActionTable->item( row, NotificationMessage )->text()
mAttributeActionTable->item( row, NotificationMessage )->text(),
mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked
);
return action;
}
Expand Down Expand Up @@ -278,7 +285,7 @@ void QgsAttributeActionDialog::insert()
{
QString name = uniqueName( dlg.description() );

insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage() );
insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
}
}

Expand Down Expand Up @@ -313,7 +320,7 @@ void QgsAttributeActionDialog::addDefaultActions()
insertRow( pos++, QgsAction::OpenUrl, tr( "Open file" ), QStringLiteral( "[% \"PATH\" %]" ), QLatin1String( "" ), false, tr( "Open file" ), QSet<QString>() << QStringLiteral( "Feature" ) << QStringLiteral( "Canvas" ), QString() );
insertRow( pos++, QgsAction::OpenUrl, tr( "Search on web based on attribute's value" ), QStringLiteral( "http://www.google.com/search?q=[% \"ATTRIBUTE\" %]" ), QLatin1String( "" ), false, tr( "Search Web" ), QSet<QString>() << QStringLiteral( "Field" ), QString() );
insertRow( pos++, QgsAction::GenericPython, tr( "List feature ids" ), QStringLiteral( "from qgis.PyQt import QtWidgets\n\nlayer = QgsProject.instance().mapLayer('[% @layer_id %]')\nif layer.selectedFeatureCount():\n ids = layer.selectedFeatureIds()\nelse:\n ids = [f.id() for f in layer.getFeatures()]\n\nQtWidgets.QMessageBox.information(None, \"Feature ids\", ', '.join([str(id) for id in ids]))" ), QLatin1String( "" ), false, tr( "List feature ids" ), QSet<QString>() << QStringLiteral( "Layer" ), QString() );
insertRow( pos++, QgsAction::GenericPython, tr( "Duplicate selected features" ), QStringLiteral( "project = QgsProject.instance()\nlayer = QgsProject.instance().mapLayer('[% @layer_id %]')\nlayer.startEditing()\nfeatures=[]\nif len('[% $id %]')>0:\n features.append( layer.getFeature( [% $id %] ) )\nelse:\n for x in layer.selectedFeatures():\n features.append( x )\nfeature_count=0\nchildren_info=''\nfeatureids=[]\nfor f in features:\n result=QgsVectorLayerUtils.duplicateFeature(layer, f, project, 0 )\n featureids.append( result[0].id() )\n feature_count+=1\n for ch_layer in result[1].layers():\n children_info+='{number_of_children} children on layer {children_layer}\\n'.format( number_of_children=str( len( result[1].duplicatedFeatures(ch_layer) ) ), children_layer=ch_layer.name() )\n ch_layer.selectByIds( result[1].duplicatedFeatures(ch_layer) )\nlayer.selectByIds( featureids )\nqgis.utils.iface.messageBar().pushMessage( '{number_of_features} features on layer {layer} duplicated with\\n{children_info}'.format( number_of_features=str( feature_count ), layer=layer.name(), children_info=children_info ) )" ), QLatin1String( "" ), false, tr( "Duplicate selected" ), QSet<QString>() << QStringLiteral( "Layer" ), QString() );
insertRow( pos++, QgsAction::GenericPython, tr( "Duplicate selected features" ), QStringLiteral( "project = QgsProject.instance()\nlayer = QgsProject.instance().mapLayer('[% @layer_id %]')\nif not layer.isEditable():\n qgis.utils.iface.messageBar().pushMessage( 'Cannot duplicate feature in not editable mode on layer {layer}'.format( layer=layer.name() ) )\nelse:\n features=[]\n if len('[% $id %]')>0:\n features.append( layer.getFeature( [% $id %] ) )\n else:\n for x in layer.selectedFeatures():\n features.append( x )\n feature_count=0\n children_info=''\n featureids=[]\n for f in features:\n result=QgsVectorLayerUtils.duplicateFeature(layer, f, project, 0 )\n featureids.append( result[0].id() )\n feature_count+=1\n for ch_layer in result[1].layers():\n children_info+='{number_of_children} children on layer {children_layer}\\n'.format( number_of_children=str( len( result[1].duplicatedFeatures(ch_layer) ) ), children_layer=ch_layer.name() )\n ch_layer.selectByIds( result[1].duplicatedFeatures(ch_layer) )\n layer.selectByIds( featureids )\n qgis.utils.iface.messageBar().pushMessage( '{number_of_features} features on layer {layer} duplicated with\\n{children_info}'.format( number_of_features=str( feature_count ), layer=layer.name(), children_info=children_info ) )" ), QLatin1String( "" ), false, tr( "Duplicate selected" ), QSet<QString>() << QStringLiteral( "Layer" ), QString(), true );

}

Expand All @@ -330,6 +337,7 @@ void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
mAttributeActionTable->item( row, NotificationMessage )->text(),
mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
mLayer
);

Expand All @@ -344,6 +352,7 @@ void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText() );
mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );

QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
QStringList actionScopes = actionProperties.actionScopes().toList();
Expand Down
5 changes: 3 additions & 2 deletions src/app/qgsattributeactiondialog.h
Expand Up @@ -44,7 +44,8 @@ class APP_EXPORT QgsAttributeActionDialog: public QWidget, private Ui::QgsAttrib
ActionText,
Capture,
ActionScopes,
NotificationMessage
NotificationMessage,
EnabledOnlyWhenEditable
};

public:
Expand All @@ -70,7 +71,7 @@ class APP_EXPORT QgsAttributeActionDialog: public QWidget, private Ui::QgsAttrib

private:
void insertRow( int row, const QgsAction &action );
void insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &actionText, const QString &iconPath, bool capture, const QString &shortTitle, const QSet<QString> &actionScopes, const QString &notificationMessage );
void insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &actionText, const QString &iconPath, bool capture, const QString &shortTitle, const QSet<QString> &actionScopes, const QString &notificationMessage, const bool isEnabledOnlyWhenEditable = false );
void swapRows( int row1, int row2 );
QgsAction rowToAction( int row ) const;

Expand Down
7 changes: 6 additions & 1 deletion src/app/qgsattributeactionpropertiesdialog.cpp
Expand Up @@ -31,7 +31,7 @@
#include <QFileDialog>
#include <QImageWriter>

QgsAttributeActionPropertiesDialog::QgsAttributeActionPropertiesDialog( QgsAction::ActionType type, const QString &description, const QString &shortTitle, const QString &iconPath, const QString &actionText, bool capture, const QSet<QString> &actionScopes, const QString &notificationMessage, QgsVectorLayer *layer, QWidget *parent )
QgsAttributeActionPropertiesDialog::QgsAttributeActionPropertiesDialog( QgsAction::ActionType type, const QString &description, const QString &shortTitle, const QString &iconPath, const QString &actionText, bool capture, const QSet<QString> &actionScopes, const QString &notificationMessage, bool isEnabledOnlyWhenEditable, QgsVectorLayer *layer, QWidget *parent )
: QDialog( parent )
, mLayer( layer )
{
Expand All @@ -45,6 +45,7 @@ QgsAttributeActionPropertiesDialog::QgsAttributeActionPropertiesDialog( QgsActio
mActionText->setText( actionText );
mCaptureOutput->setChecked( capture );
mNotificationMessage->setText( notificationMessage );
mIsEnabledOnlyWhenEditable->setChecked( isEnabledOnlyWhenEditable );

init( actionScopes );
}
Expand Down Expand Up @@ -107,6 +108,10 @@ QString QgsAttributeActionPropertiesDialog::notificationMessage() const
return mNotificationMessage->text();
}

bool QgsAttributeActionPropertiesDialog::isEnabledOnlyWhenEditable() const
{
return mIsEnabledOnlyWhenEditable->isChecked();
}

bool QgsAttributeActionPropertiesDialog::capture() const
{
Expand Down

0 comments on commit 5d23bc0

Please sign in to comment.