32#include <QImageWriter>
36#include <QTableWidget>
38#include "moc_qgsattributeactiondialog.cpp"
40using namespace Qt::StringLiterals;
47 QHeaderView *header = mAttributeActionTable->horizontalHeader();
48 header->setHighlightSections(
false );
49 header->setStretchLastSection(
true );
50 mAttributeActionTable->setColumnWidth( 0, 100 );
51 mAttributeActionTable->setColumnWidth( 1, 230 );
52 mAttributeActionTable->setCornerButtonEnabled(
false );
53 mAttributeActionTable->setEditTriggers( QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked );
55 connect( mAttributeActionTable, &QTableWidget::itemDoubleClicked,
this, &QgsAttributeActionDialog::itemDoubleClicked );
56 connect( mAttributeActionTable, &QTableWidget::itemSelectionChanged,
this, &QgsAttributeActionDialog::updateButtons );
57 connect( mMoveUpButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::moveUp );
58 connect( mMoveDownButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::moveDown );
59 connect( mRemoveButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::remove );
60 connect( mAddButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::insert );
61 connect( mDuplicateButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::duplicate );
62 connect( mAddDefaultActionsButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::addDefaultActions );
70 mAttributeActionTable->setRowCount( 0 );
74 const auto constActions =
actions.actions();
75 for (
const QgsAction &action : constActions )
77 insertRow( i++, action );
84 visibleActionWidgetConfig.
hidden =
false;
87 mAttributeTableWidgetType->setCurrentIndex( attributeTableConfig.
actionWidgetStyle() );
94 for (
int i = 0; i < mAttributeActionTable->rowCount(); ++i )
96 actions.append( rowToAction( i ) );
104 return mShowInAttributeTable->isChecked();
112void QgsAttributeActionDialog::insertRow(
int row,
const QgsAction &action )
114 QTableWidgetItem *item =
nullptr;
115 mAttributeActionTable->insertRow( row );
118 item =
new QTableWidgetItem( textForType( action.
type() ) );
119 item->setData( Role::ActionType,
static_cast<int>( action.
type() ) );
120 item->setData( Role::ActionId, action.
id() );
121 item->setFlags( item->flags() & ~Qt::ItemIsEditable );
122 mAttributeActionTable->setItem( row, Type, item );
125 mAttributeActionTable->setItem( row, Description,
new QTableWidgetItem( action.
name() ) );
128 mAttributeActionTable->setItem( row, ShortTitle,
new QTableWidgetItem( action.
shortTitle() ) );
131 item =
new QTableWidgetItem( action.
command().length() > 30 ? action.
command().left( 27 ) +
"…" : action.
command() );
132 item->setData( Qt::UserRole, action.
command() );
133 mAttributeActionTable->setItem( row, ActionText, item );
136 item =
new QTableWidgetItem();
137 item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
138 item->setCheckState( action.
capture() ? Qt::Checked : Qt::Unchecked );
139 mAttributeActionTable->setItem( row, Capture, item );
142 item =
new QTableWidgetItem();
143 item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
144 QStringList actionScopes = qgis::setToList( action.
actionScopes() );
145 std::sort( actionScopes.begin(), actionScopes.end() );
146 item->setText( actionScopes.join(
", "_L1 ) );
147 item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( action.
actionScopes() ) );
148 mAttributeActionTable->setItem( row, ActionScopes, item );
151 const QIcon icon = action.
icon();
152 QTableWidgetItem *headerItem =
new QTableWidgetItem( icon, QString() );
153 headerItem->setData( Qt::UserRole, action.
iconPath() );
154 mAttributeActionTable->setVerticalHeaderItem( row, headerItem );
157 mAttributeActionTable->setItem( row, NotificationMessage,
new QTableWidgetItem( action.
notificationMessage() ) );
160 item =
new QTableWidgetItem();
161 item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
163 mAttributeActionTable->setItem( row, EnabledOnlyWhenEditable, item );
168void 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 )
170 if ( uniqueName( name ) == name )
171 insertRow( row, QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
174void QgsAttributeActionDialog::moveUp()
178 int row1 = -1, row2 = -1;
179 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
180 if ( !selection.isEmpty() )
182 row1 = selection.first()->row();
188 if ( row1 != -1 && row2 != -1 )
190 swapRows( row1, row2 );
192 mAttributeActionTable->selectRow( row2 );
196void QgsAttributeActionDialog::moveDown()
199 int row1 = -1, row2 = -1;
200 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
201 if ( !selection.isEmpty() )
203 row1 = selection.first()->row();
206 if ( row1 < mAttributeActionTable->rowCount() - 1 )
209 if ( row1 != -1 && row2 != -1 )
211 swapRows( row1, row2 );
213 mAttributeActionTable->selectRow( row2 );
217void QgsAttributeActionDialog::swapRows(
int row1,
int row2 )
219 const int colCount = mAttributeActionTable->columnCount();
220 for (
int col = 0; col < colCount; col++ )
222 QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
223 mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
224 mAttributeActionTable->setItem( row2, col, item );
226 QTableWidgetItem *header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
227 mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
228 mAttributeActionTable->setVerticalHeaderItem( row2, header );
231QgsAction QgsAttributeActionDialog::rowToAction(
int row )
const
233 const QUuid
id { mAttributeActionTable->item( row, Type )->data( Role::ActionId ).toUuid() };
234 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 );
243 return tr(
"Generic" );
245 return tr(
"Python" );
247 return tr(
"macOS" );
249 return tr(
"Windows" );
253 return tr(
"Open URL" );
255 return tr(
"Submit URL (urlencoded or JSON)" );
257 return tr(
"Submit URL (multipart)" );
262void QgsAttributeActionDialog::remove()
264 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
265 if ( !selection.isEmpty() )
268 int row = selection.first()->row();
269 mAttributeActionTable->removeRow( row );
272 if ( row >= mAttributeActionTable->rowCount() )
273 row = mAttributeActionTable->rowCount() - 1;
274 mAttributeActionTable->selectRow( row );
280void QgsAttributeActionDialog::insert()
283 const int pos = mAttributeActionTable->rowCount();
285 QgsAttributeActionPropertiesDialog dlg( mLayer,
this );
286 dlg.setWindowTitle( tr(
"Add New Action" ) );
290 const QString name = uniqueName( dlg.description() );
292 insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
296void QgsAttributeActionDialog::duplicate()
299 const int pos = mAttributeActionTable->rowCount();
300 const int row = mAttributeActionTable->currentRow();
302 QgsAttributeActionPropertiesDialog dlg(
304 mAttributeActionTable->item( row, Description )->text(),
305 mAttributeActionTable->item( row, ShortTitle )->text(),
306 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
307 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
308 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
309 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
310 mAttributeActionTable->item( row, NotificationMessage )->text(),
311 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
315 dlg.setWindowTitle( tr(
"Duplicate Action" ) );
319 const QString name = uniqueName( dlg.description() );
321 insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
325void QgsAttributeActionDialog::updateButtons()
327 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
328 const bool hasSelection = !selection.isEmpty();
332 const int row = selection.first()->row();
333 mMoveUpButton->setEnabled( row >= 1 );
334 mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
338 mMoveUpButton->setEnabled(
false );
339 mMoveDownButton->setEnabled(
false );
342 mRemoveButton->setEnabled( hasSelection );
343 mDuplicateButton->setEnabled( hasSelection );
346void QgsAttributeActionDialog::addDefaultActions()
349 insertRow( pos++,
Qgis::AttributeActionType::Generic, tr(
"Echo attribute's value" ), u
"echo \"[% @field_value %]\""_s, QString(),
true, tr(
"Attribute Value" ), QSet<QString>() << u
"Field"_s, QString() );
350 insertRow( pos++,
Qgis::AttributeActionType::Generic, tr(
"Run an application" ), u
"ogr2ogr -f \"GPKG\" \"[% \"OUTPUT_PATH\" %]\" \"[% \"INPUT_FILE\" %]\""_s, QString(),
true, tr(
"Run application" ), QSet<QString>() << u
"Feature"_s << u
"Canvas"_s, QString() );
351 insertRow( pos++,
Qgis::AttributeActionType::GenericPython, tr(
"Display the feature id in the message bar" ), u
"from qgis.utils import iface\n\niface.messageBar().pushInfo(\"Feature id\", \"The feature id is [% $id %]\")"_s, QString(),
false, tr(
"Feature ID" ), QSet<QString>() << u
"Feature"_s << u
"Canvas"_s, QString() );
352 insertRow( pos++,
Qgis::AttributeActionType::GenericPython, tr(
"Selected field's value (Identify features tool)" ), u
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Current field's value\", \"[% @field_name %] = [% @field_value %]\")"_s, QString(),
false, tr(
"Field Value" ), QSet<QString>() << u
"Field"_s, QString() );
353 insertRow( pos++,
Qgis::AttributeActionType::GenericPython, tr(
"Clicked coordinates (Run feature actions tool)" ), u
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Clicked coords\", \"layer: [% @layer_id %]\\ncoords: ([% @click_x %],[% @click_y %])\")"_s, QString(),
false, tr(
"Clicked Coordinate" ), QSet<QString>() << u
"Canvas"_s, QString() );
354 insertRow( pos++,
Qgis::AttributeActionType::OpenUrl, tr(
"Open file" ), u
"[% \"PATH\" %]"_s, QString(),
false, tr(
"Open file" ), QSet<QString>() << u
"Feature"_s << u
"Canvas"_s, QString() );
355 insertRow( pos++,
Qgis::AttributeActionType::OpenUrl, tr(
"Search on web based on attribute's value" ), u
"https://www.google.com/search?q=[% @field_value %]"_s, QString(),
false, tr(
"Search Web" ), QSet<QString>() << u
"Field"_s, QString() );
356 insertRow( pos++,
Qgis::AttributeActionType::GenericPython, tr(
"List feature ids" ), u
"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]))"_s, QString(),
false, tr(
"List feature ids" ), QSet<QString>() << u
"Layer"_s, QString() );
357 insertRow( pos++,
Qgis::AttributeActionType::GenericPython, tr(
"Duplicate selected features" ), u
"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 ) )"_s, QString(),
false, tr(
"Duplicate selected" ), QSet<QString>() << u
"Layer"_s, QString(),
true );
360void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
362 const int row = item->row();
364 QgsAttributeActionPropertiesDialog actionProperties(
366 mAttributeActionTable->item( row, Description )->text(),
367 mAttributeActionTable->item( row, ShortTitle )->text(),
368 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
369 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
370 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
371 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
372 mAttributeActionTable->item( row, NotificationMessage )->text(),
373 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
377 actionProperties.setWindowTitle( tr(
"Edit Action" ) );
379 if ( actionProperties.exec() )
381 mAttributeActionTable->item( row, Type )->setData( Role::ActionType,
static_cast<int>( actionProperties.type() ) );
382 mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
383 mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
384 mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
385 mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText().length() > 30 ? actionProperties.actionText().left( 27 ) +
"…" : actionProperties.actionText() );
386 mAttributeActionTable->item( row, ActionText )->setData( Qt::UserRole, actionProperties.actionText() );
387 mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
388 mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
389 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
391 QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
392 QStringList actionScopes = qgis::setToList( actionProperties.actionScopes() );
393 std::sort( actionScopes.begin(), actionScopes.end() );
394 item->setText( actionScopes.join(
", "_L1 ) );
395 item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
397 mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
398 mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
402QString QgsAttributeActionDialog::uniqueName( QString name )
407 const int pos = mAttributeActionTable->rowCount();
410 for (
int i = 0; i < pos; ++i )
412 if ( mAttributeActionTable->item( i, Description )->text() == name )
422 const QString suffix = QString::number( suffix_num );
423 new_name = name +
'_' + suffix;
425 for (
int i = 0; i < pos; ++i )
426 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 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.
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.