24#include "moc_qgsattributeactiondialog.cpp" 
   34#include <QImageWriter> 
   35#include <QTableWidget> 
   39  , mLayer( actions.layer() )
 
   42  QHeaderView *header = mAttributeActionTable->horizontalHeader();
 
   43  header->setHighlightSections( 
false );
 
   44  header->setStretchLastSection( 
true );
 
   45  mAttributeActionTable->setColumnWidth( 0, 100 );
 
   46  mAttributeActionTable->setColumnWidth( 1, 230 );
 
   47  mAttributeActionTable->setCornerButtonEnabled( 
false );
 
   48  mAttributeActionTable->setEditTriggers( QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked );
 
   50  connect( mAttributeActionTable, &QTableWidget::itemDoubleClicked, 
this, &QgsAttributeActionDialog::itemDoubleClicked );
 
   51  connect( mAttributeActionTable, &QTableWidget::itemSelectionChanged, 
this, &QgsAttributeActionDialog::updateButtons );
 
   52  connect( mMoveUpButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::moveUp );
 
   53  connect( mMoveDownButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::moveDown );
 
   54  connect( mRemoveButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::remove );
 
   55  connect( mAddButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::insert );
 
   56  connect( mDuplicateButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::duplicate );
 
   57  connect( mAddDefaultActionsButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::addDefaultActions );
 
 
   65  mAttributeActionTable->setRowCount( 0 );
 
   69  const auto constActions = 
actions.actions();
 
   70  for ( 
const QgsAction &action : constActions )
 
   72    insertRow( i++, action );
 
   79  visibleActionWidgetConfig.
hidden = 
false;
 
   82  mAttributeTableWidgetType->setCurrentIndex( attributeTableConfig.
actionWidgetStyle() );
 
 
   89  for ( 
int i = 0; i < mAttributeActionTable->rowCount(); ++i )
 
   91    actions.append( rowToAction( i ) );
 
 
   99  return mShowInAttributeTable->isChecked();
 
 
  107void QgsAttributeActionDialog::insertRow( 
int row, 
const QgsAction &action )
 
  109  QTableWidgetItem *item = 
nullptr;
 
  110  mAttributeActionTable->insertRow( row );
 
  113  item = 
new QTableWidgetItem( textForType( action.
type() ) );
 
  114  item->setData( Role::ActionType, 
static_cast<int>( action.
type() ) );
 
  115  item->setData( Role::ActionId, action.
id() );
 
  116  item->setFlags( item->flags() & ~Qt::ItemIsEditable );
 
  117  mAttributeActionTable->setItem( row, Type, item );
 
  120  mAttributeActionTable->setItem( row, Description, 
new QTableWidgetItem( action.
name() ) );
 
  123  mAttributeActionTable->setItem( row, ShortTitle, 
new QTableWidgetItem( action.
shortTitle() ) );
 
  126  item = 
new QTableWidgetItem( action.
command().length() > 30 ? action.
command().left( 27 ) + 
"…" : action.command() );
 
  127  item->setData( Qt::UserRole, action.
command() );
 
  128  mAttributeActionTable->setItem( row, ActionText, item );
 
  131  item = 
new QTableWidgetItem();
 
  132  item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  133  item->setCheckState( action.
capture() ? Qt::Checked : Qt::Unchecked );
 
  134  mAttributeActionTable->setItem( row, Capture, item );
 
  137  item = 
new QTableWidgetItem();
 
  138  item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  139  QStringList actionScopes = qgis::setToList( action.
actionScopes() );
 
  140  std::sort( actionScopes.begin(), actionScopes.end() );
 
  141  item->setText( actionScopes.join( QLatin1String( 
", " ) ) );
 
  142  item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( action.
actionScopes() ) );
 
  143  mAttributeActionTable->setItem( row, ActionScopes, item );
 
  146  const QIcon icon = action.
icon();
 
  147  QTableWidgetItem *headerItem = 
new QTableWidgetItem( icon, QString() );
 
  148  headerItem->setData( Qt::UserRole, action.
iconPath() );
 
  149  mAttributeActionTable->setVerticalHeaderItem( row, headerItem );
 
  152  mAttributeActionTable->setItem( row, NotificationMessage, 
new QTableWidgetItem( action.
notificationMessage() ) );
 
  155  item = 
new QTableWidgetItem();
 
  156  item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  158  mAttributeActionTable->setItem( row, EnabledOnlyWhenEditable, item );
 
  163void QgsAttributeActionDialog::insertRow( 
int row, 
Qgis::AttributeActionType type, 
const QString &name, 
const QString &actionText, 
const QString &iconPath, 
bool capture, 
const QString &shortTitle, 
const QSet<QString> &actionScopes, 
const QString ¬ificationMessage, 
bool isEnabledOnlyWhenEditable )
 
  165  if ( uniqueName( name ) == name )
 
  166    insertRow( row, 
QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
 
  169void QgsAttributeActionDialog::moveUp()
 
  173  int row1 = -1, row2 = -1;
 
  174  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  175  if ( !selection.isEmpty() )
 
  177    row1 = selection.first()->row();
 
  183  if ( row1 != -1 && row2 != -1 )
 
  185    swapRows( row1, row2 );
 
  187    mAttributeActionTable->selectRow( row2 );
 
  191void QgsAttributeActionDialog::moveDown()
 
  194  int row1 = -1, row2 = -1;
 
  195  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  196  if ( !selection.isEmpty() )
 
  198    row1 = selection.first()->row();
 
  201  if ( row1 < mAttributeActionTable->rowCount() - 1 )
 
  204  if ( row1 != -1 && row2 != -1 )
 
  206    swapRows( row1, row2 );
 
  208    mAttributeActionTable->selectRow( row2 );
 
  212void QgsAttributeActionDialog::swapRows( 
int row1, 
int row2 )
 
  214  const int colCount = mAttributeActionTable->columnCount();
 
  215  for ( 
int col = 0; col < colCount; col++ )
 
  217    QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
 
  218    mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
 
  219    mAttributeActionTable->setItem( row2, col, item );
 
  221  QTableWidgetItem *header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
 
  222  mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
 
  223  mAttributeActionTable->setVerticalHeaderItem( row2, header );
 
  226QgsAction QgsAttributeActionDialog::rowToAction( 
int row )
 const 
  228  const QUuid 
id { mAttributeActionTable->item( row, Type )->data( Role::ActionId ).toUuid() };
 
  229  QgsAction action( 
id, 
static_cast<Qgis::AttributeActionType>( mAttributeActionTable->item( row, Type )->data( Role::ActionType ).toInt() ), mAttributeActionTable->item( row, Description )->text(), mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(), mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(), 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, EnabledOnlyWhenEditable )->checkState() == Qt::Checked );
 
  238      return tr( 
"Generic" );
 
  240      return tr( 
"Python" );
 
  242      return tr( 
"macOS" );
 
  244      return tr( 
"Windows" );
 
  248      return tr( 
"Open URL" );
 
  250      return tr( 
"Submit URL (urlencoded or JSON)" );
 
  252      return tr( 
"Submit URL (multipart)" );
 
  257void QgsAttributeActionDialog::remove()
 
  259  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  260  if ( !selection.isEmpty() )
 
  263    int row = selection.first()->row();
 
  264    mAttributeActionTable->removeRow( row );
 
  267    if ( row >= mAttributeActionTable->rowCount() )
 
  268      row = mAttributeActionTable->rowCount() - 1;
 
  269    mAttributeActionTable->selectRow( row );
 
  275void QgsAttributeActionDialog::insert()
 
  278  const int pos = mAttributeActionTable->rowCount();
 
  281  dlg.setWindowTitle( tr( 
"Add New Action" ) );
 
  285    const QString name = uniqueName( dlg.description() );
 
  287    insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
 
  291void QgsAttributeActionDialog::duplicate()
 
  294  const int pos = mAttributeActionTable->rowCount();
 
  295  const int row = mAttributeActionTable->currentRow();
 
  299    mAttributeActionTable->item( row, Description )->text(),
 
  300    mAttributeActionTable->item( row, ShortTitle )->text(),
 
  301    mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
 
  302    mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
 
  303    mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
 
  304    mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
 
  305    mAttributeActionTable->item( row, NotificationMessage )->text(),
 
  306    mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
 
  310  dlg.setWindowTitle( tr( 
"Duplicate Action" ) );
 
  314    const QString name = uniqueName( dlg.description() );
 
  316    insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
 
  320void QgsAttributeActionDialog::updateButtons()
 
  322  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  323  const bool hasSelection = !selection.isEmpty();
 
  327    const int row = selection.first()->row();
 
  328    mMoveUpButton->setEnabled( row >= 1 );
 
  329    mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
 
  333    mMoveUpButton->setEnabled( 
false );
 
  334    mMoveDownButton->setEnabled( 
false );
 
  337  mRemoveButton->setEnabled( hasSelection );
 
  338  mDuplicateButton->setEnabled( hasSelection );
 
  341void QgsAttributeActionDialog::addDefaultActions()
 
  344  insertRow( pos++, 
Qgis::AttributeActionType::Generic, tr( 
"Echo attribute's value" ), QStringLiteral( 
"echo \"[% @field_value %]\"" ), QString(), 
true, tr( 
"Attribute Value" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  345  insertRow( pos++, 
Qgis::AttributeActionType::Generic, tr( 
"Run an application" ), QStringLiteral( 
"ogr2ogr -f \"GPKG\" \"[% \"OUTPUT_PATH\" %]\" \"[% \"INPUT_FILE\" %]\"" ), QString(), 
true, tr( 
"Run application" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  346  insertRow( pos++, 
Qgis::AttributeActionType::GenericPython, tr( 
"Display the feature id in the message bar" ), QStringLiteral( 
"from qgis.utils import iface\n\niface.messageBar().pushInfo(\"Feature id\", \"The feature id is [% $id %]\")" ), QString(), 
false, tr( 
"Feature ID" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  347  insertRow( pos++, 
Qgis::AttributeActionType::GenericPython, tr( 
"Selected field's value (Identify features tool)" ), QStringLiteral( 
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Current field's value\", \"[% @field_name %] = [% @field_value %]\")" ), QString(), 
false, tr( 
"Field Value" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  348  insertRow( pos++, 
Qgis::AttributeActionType::GenericPython, tr( 
"Clicked coordinates (Run feature actions tool)" ), QStringLiteral( 
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Clicked coords\", \"layer: [% @layer_id %]\\ncoords: ([% @click_x %],[% @click_y %])\")" ), QString(), 
false, tr( 
"Clicked Coordinate" ), QSet<QString>() << QStringLiteral( 
"Canvas" ), QString() );
 
  349  insertRow( pos++, 
Qgis::AttributeActionType::OpenUrl, tr( 
"Open file" ), QStringLiteral( 
"[% \"PATH\" %]" ), QString(), 
false, tr( 
"Open file" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  350  insertRow( pos++, 
Qgis::AttributeActionType::OpenUrl, tr( 
"Search on web based on attribute's value" ), QStringLiteral( 
"https://www.google.com/search?q=[% @field_value %]" ), QString(), 
false, tr( 
"Search Web" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  351  insertRow( pos++, 
Qgis::AttributeActionType::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]))" ), QString(), 
false, tr( 
"List feature ids" ), QSet<QString>() << QStringLiteral( 
"Layer" ), QString() );
 
  352  insertRow( pos++, 
Qgis::AttributeActionType::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 ) )" ), QString(), 
false, tr( 
"Duplicate selected" ), QSet<QString>() << QStringLiteral( 
"Layer" ), QString(), 
true );
 
  355void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
 
  357  const int row = item->row();
 
  361    mAttributeActionTable->item( row, Description )->text(),
 
  362    mAttributeActionTable->item( row, ShortTitle )->text(),
 
  363    mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
 
  364    mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
 
  365    mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
 
  366    mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
 
  367    mAttributeActionTable->item( row, NotificationMessage )->text(),
 
  368    mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
 
  372  actionProperties.setWindowTitle( tr( 
"Edit Action" ) );
 
  374  if ( actionProperties.exec() )
 
  376    mAttributeActionTable->item( row, Type )->setData( Role::ActionType, 
static_cast<int>( actionProperties.type() ) );
 
  377    mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
 
  378    mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
 
  379    mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
 
  380    mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText().length() > 30 ? actionProperties.actionText().left( 27 ) + 
"…" : actionProperties.actionText() );
 
  381    mAttributeActionTable->item( row, ActionText )->setData( Qt::UserRole, actionProperties.actionText() );
 
  382    mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
 
  383    mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
 
  384    mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
 
  386    QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
 
  387    QStringList actionScopes = qgis::setToList( actionProperties.actionScopes() );
 
  388    std::sort( actionScopes.begin(), actionScopes.end() );
 
  389    item->setText( actionScopes.join( QLatin1String( 
", " ) ) );
 
  390    item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
 
  392    mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
 
  393    mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
 
  397QString QgsAttributeActionDialog::uniqueName( QString name )
 
  402  const int pos = mAttributeActionTable->rowCount();
 
  405  for ( 
int i = 0; i < pos; ++i )
 
  407    if ( mAttributeActionTable->item( i, Description )->text() == name )
 
  417      const QString suffix = QString::number( suffix_num );
 
  418      new_name = name + 
'_' + suffix;
 
  420      for ( 
int i = 0; i < pos; ++i )
 
  421        if ( mAttributeActionTable->item( i, 0 )->text() == new_name )
 
AttributeActionType
Attribute action types.
 
@ OpenUrl
Open URL action.
 
@ SubmitUrlMultipart
POST data to an URL using "multipart/form-data".
 
@ Windows
Windows specific.
 
@ SubmitUrlEncoded
POST data to an URL, using "application/x-www-form-urlencoded" or "application/json" if the body is v...
 
Storage and management of actions associated with a layer.
 
Utility class that encapsulates an action based on vector attributes.
 
QString notificationMessage() const
Returns the notification message that triggers the action.
 
QString name() const
The name of the action. This may be a longer description.
 
QSet< QString > actionScopes() const
The action scopes define where an action will be available.
 
Qgis::AttributeActionType type() const
The action type.
 
QIcon icon() const
The icon.
 
QString iconPath() const
The path to the icon.
 
QString command() const
Returns the command that is executed by this action.
 
QString shortTitle() const
The short title is used to label user interface elements like buttons.
 
bool isEnabledOnlyWhenEditable() const
Returns whether only enabled in editable mode.
 
bool capture() const
Whether to capture output for display when this action is run.
 
QUuid id() const
Returns a unique id for this action.
 
void init(const QgsActionManager &action, const QgsAttributeTableConfig &attributeTableConfig)
 
bool showWidgetInAttributeTable() const
 
QgsAttributeActionDialog(const QgsActionManager &actions, QWidget *parent=nullptr)
 
QList< QgsAction > actions() const
 
QgsAttributeTableConfig::ActionWidgetStyle attributeTableWidgetStyle() const
 
A dialog for configuring a map layer action.
 
A container for configuration of the attribute table.
 
@ Action
This column represents an action widget.
 
ActionWidgetStyle
The style of the action widget in the attribute table.
 
bool actionWidgetVisible() const
Returns true if the action widget is visible.
 
ActionWidgetStyle actionWidgetStyle() const
Gets the style of the action widget.
 
QgsAttributeTableConfig attributeTableConfig() const
Returns the attribute table configuration object.
 
Defines the configuration of a column in the attribute table.
 
QgsAttributeTableConfig::Type type
The type of this column.
 
bool hidden
Flag that controls if the column is hidden.