33#include <QImageWriter> 
   34#include <QTableWidget> 
   38  , mLayer( actions.layer() )
 
   41  QHeaderView *header = mAttributeActionTable->horizontalHeader();
 
   42  header->setHighlightSections( 
false );
 
   43  header->setStretchLastSection( 
true );
 
   44  mAttributeActionTable->setColumnWidth( 0, 100 );
 
   45  mAttributeActionTable->setColumnWidth( 1, 230 );
 
   46  mAttributeActionTable->setCornerButtonEnabled( 
false );
 
   47  mAttributeActionTable->setEditTriggers( QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked );
 
   49  connect( mAttributeActionTable, &QTableWidget::itemDoubleClicked, 
this, &QgsAttributeActionDialog::itemDoubleClicked );
 
   50  connect( mAttributeActionTable, &QTableWidget::itemSelectionChanged, 
this, &QgsAttributeActionDialog::updateButtons );
 
   51  connect( mMoveUpButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::moveUp );
 
   52  connect( mMoveDownButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::moveDown );
 
   53  connect( mRemoveButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::remove );
 
   54  connect( mAddButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::insert );
 
   55  connect( mAddDefaultActionsButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::addDefaultActions );
 
 
   63  mAttributeActionTable->setRowCount( 0 );
 
   67  const auto constActions = 
actions.actions();
 
   68  for ( 
const QgsAction &action : constActions )
 
   70    insertRow( i++, action );
 
   77  visibleActionWidgetConfig.
hidden = 
false;
 
   80  mAttributeTableWidgetType->setCurrentIndex( attributeTableConfig.
actionWidgetStyle() );
 
 
   87  for ( 
int i = 0; i < mAttributeActionTable->rowCount(); ++i )
 
   89    actions.append( rowToAction( i ) );
 
 
   97  return mShowInAttributeTable->isChecked();
 
 
  105void QgsAttributeActionDialog::insertRow( 
int row, 
const QgsAction &action )
 
  107  QTableWidgetItem *item = 
nullptr;
 
  108  mAttributeActionTable->insertRow( row );
 
  111  item = 
new QTableWidgetItem( textForType( action.
type() ) );
 
  112  item->setData( Role::ActionType, 
static_cast< int >( action.
type() ) );
 
  113  item->setData( Role::ActionId, action.
id() );
 
  114  item->setFlags( item->flags() & ~Qt::ItemIsEditable );
 
  115  mAttributeActionTable->setItem( row, Type, item );
 
  118  mAttributeActionTable->setItem( row, Description, 
new QTableWidgetItem( action.
name() ) );
 
  121  mAttributeActionTable->setItem( row, ShortTitle, 
new QTableWidgetItem( action.
shortTitle() ) );
 
  124  item = 
new QTableWidgetItem( action.
command().length() > 30 ? action.
command().left( 27 ) + 
"…" : action.command() );
 
  125  item->setData( Qt::UserRole, action.
command() );
 
  126  mAttributeActionTable->setItem( row, ActionText, item );
 
  129  item = 
new QTableWidgetItem();
 
  130  item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  131  item->setCheckState( action.
capture() ? Qt::Checked : Qt::Unchecked );
 
  132  mAttributeActionTable->setItem( row, Capture, item );
 
  135  item = 
new QTableWidgetItem();
 
  136  item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  137  QStringList actionScopes = qgis::setToList( action.
actionScopes() );
 
  138  std::sort( actionScopes.begin(), actionScopes.end() );
 
  139  item->setText( actionScopes.join( QLatin1String( 
", " ) ) );
 
  140  item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( action.
actionScopes() ) );
 
  141  mAttributeActionTable->setItem( row, ActionScopes, item );
 
  144  const QIcon icon = action.
icon();
 
  145  QTableWidgetItem *headerItem = 
new QTableWidgetItem( icon, QString() );
 
  146  headerItem->setData( Qt::UserRole, action.
iconPath() );
 
  147  mAttributeActionTable->setVerticalHeaderItem( row, headerItem );
 
  150  mAttributeActionTable->setItem( row, NotificationMessage, 
new QTableWidgetItem( action.
notificationMessage() ) );
 
  153  item = 
new QTableWidgetItem();
 
  154  item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  156  mAttributeActionTable->setItem( row, EnabledOnlyWhenEditable, item );
 
  161void 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 )
 
  163  if ( uniqueName( name ) == name )
 
  164    insertRow( row, 
QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
 
  167void QgsAttributeActionDialog::moveUp()
 
  171  int row1 = -1, row2 = -1;
 
  172  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  173  if ( !selection.isEmpty() )
 
  175    row1 = selection.first()->row();
 
  181  if ( row1 != -1 && row2 != -1 )
 
  183    swapRows( row1, row2 );
 
  185    mAttributeActionTable->selectRow( row2 );
 
  189void QgsAttributeActionDialog::moveDown()
 
  192  int row1 = -1, row2 = -1;
 
  193  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  194  if ( !selection.isEmpty() )
 
  196    row1 = selection.first()->row();
 
  199  if ( row1 < mAttributeActionTable->rowCount() - 1 )
 
  202  if ( row1 != -1 && row2 != -1 )
 
  204    swapRows( row1, row2 );
 
  206    mAttributeActionTable->selectRow( row2 );
 
  210void QgsAttributeActionDialog::swapRows( 
int row1, 
int row2 )
 
  212  const int colCount = mAttributeActionTable->columnCount();
 
  213  for ( 
int col = 0; col < colCount; col++ )
 
  215    QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
 
  216    mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
 
  217    mAttributeActionTable->setItem( row2, col, item );
 
  219  QTableWidgetItem *header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
 
  220  mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
 
  221  mAttributeActionTable->setVerticalHeaderItem( row2, header );
 
  224QgsAction QgsAttributeActionDialog::rowToAction( 
int row )
 const 
  226  const QUuid 
id { mAttributeActionTable->item( row, Type )->data( Role::ActionId ).toUuid() };
 
  229                    mAttributeActionTable->item( row, Description )->text(),
 
  230                    mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
 
  231                    mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
 
  232                    mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
 
  233                    mAttributeActionTable->item( row, ShortTitle )->text(),
 
  234                    mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
 
  235                    mAttributeActionTable->item( row, NotificationMessage )->text(),
 
  236                    mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked
 
  246      return tr( 
"Generic" );
 
  248      return tr( 
"Python" );
 
  250      return tr( 
"macOS" );
 
  252      return tr( 
"Windows" );
 
  256      return tr( 
"Open URL" );
 
  258      return tr( 
"Submit URL (urlencoded or JSON)" );
 
  260      return tr( 
"Submit URL (multipart)" );
 
  265void QgsAttributeActionDialog::remove()
 
  267  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  268  if ( !selection.isEmpty() )
 
  271    int row = selection.first()->row();
 
  272    mAttributeActionTable->removeRow( row );
 
  275    if ( row >= mAttributeActionTable->rowCount() )
 
  276      row = mAttributeActionTable->rowCount() - 1;
 
  277    mAttributeActionTable->selectRow( row );
 
  283void QgsAttributeActionDialog::insert()
 
  286  const int pos = mAttributeActionTable->rowCount();
 
  289  dlg.setWindowTitle( tr( 
"Add New Action" ) );
 
  293    const QString name = uniqueName( dlg.description() );
 
  295    insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
 
  299void QgsAttributeActionDialog::updateButtons()
 
  301  QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  302  const bool hasSelection = !selection.isEmpty();
 
  306    const int row = selection.first()->row();
 
  307    mMoveUpButton->setEnabled( row >= 1 );
 
  308    mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
 
  312    mMoveUpButton->setEnabled( 
false );
 
  313    mMoveDownButton->setEnabled( 
false );
 
  316  mRemoveButton->setEnabled( hasSelection );
 
  319void QgsAttributeActionDialog::addDefaultActions()
 
  322  insertRow( pos++, 
Qgis::AttributeActionType::Generic, tr( 
"Echo attribute's value" ), QStringLiteral( 
"echo \"[% @field_value %]\"" ), QString(), 
true, tr( 
"Attribute Value" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  323  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() );
 
  324  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() );
 
  325  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() );
 
  326  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() );
 
  327  insertRow( pos++, 
Qgis::AttributeActionType::OpenUrl, tr( 
"Open file" ), QStringLiteral( 
"[% \"PATH\" %]" ), QString(), 
false, tr( 
"Open file" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  328  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() );
 
  329  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() );
 
  330  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 );
 
  334void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
 
  336  const int row = item->row();
 
  340    mAttributeActionTable->item( row, Description )->text(),
 
  341    mAttributeActionTable->item( row, ShortTitle )->text(),
 
  342    mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
 
  343    mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
 
  344    mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
 
  345    mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
 
  346    mAttributeActionTable->item( row, NotificationMessage )->text(),
 
  347    mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
 
  351  actionProperties.setWindowTitle( tr( 
"Edit Action" ) );
 
  353  if ( actionProperties.exec() )
 
  355    mAttributeActionTable->item( row, Type )->setData( Role::ActionType, 
static_cast< int >( actionProperties.type() ) );
 
  356    mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
 
  357    mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
 
  358    mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
 
  359    mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText().length() > 30 ? actionProperties.actionText().left( 27 ) + 
"…" : actionProperties.actionText() );
 
  360    mAttributeActionTable->item( row, ActionText )->setData( Qt::UserRole, actionProperties.actionText() );
 
  361    mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
 
  362    mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
 
  363    mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
 
  365    QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
 
  366    QStringList actionScopes = qgis::setToList( actionProperties.actionScopes() );
 
  367    std::sort( actionScopes.begin(), actionScopes.end() );
 
  368    item->setText( actionScopes.join( QLatin1String( 
", " ) ) );
 
  369    item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
 
  371    mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
 
  372    mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
 
  376QString QgsAttributeActionDialog::uniqueName( QString name )
 
  381  const int pos = mAttributeActionTable->rowCount();
 
  384  for ( 
int i = 0; i < pos; ++i )
 
  386    if ( mAttributeActionTable->item( i, Description )->text() == name )
 
  396      const QString suffix = QString::number( suffix_num );
 
  397      new_name = name + 
'_' + suffix;
 
  399      for ( 
int i = 0; i < pos; ++i )
 
  400        if ( mAttributeActionTable->item( i, 0 )->text() == new_name )
 
AttributeActionType
Map layer action flags.
 
@ 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
 
This is 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.