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(
172 const QString &actionText,
173 const QString &iconPath,
175 const QString &shortTitle,
176 const QSet<QString> &actionScopes,
177 const QString ¬ificationMessage,
178 bool isEnabledOnlyWhenEditable
181 if ( uniqueName( name ) == name )
182 insertRow( row, QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
185void QgsAttributeActionDialog::moveUp()
189 int row1 = -1, row2 = -1;
190 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
191 if ( !selection.isEmpty() )
193 row1 = selection.first()->row();
199 if ( row1 != -1 && row2 != -1 )
201 swapRows( row1, row2 );
203 mAttributeActionTable->selectRow( row2 );
207void QgsAttributeActionDialog::moveDown()
210 int row1 = -1, row2 = -1;
211 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
212 if ( !selection.isEmpty() )
214 row1 = selection.first()->row();
217 if ( row1 < mAttributeActionTable->rowCount() - 1 )
220 if ( row1 != -1 && row2 != -1 )
222 swapRows( row1, row2 );
224 mAttributeActionTable->selectRow( row2 );
228void QgsAttributeActionDialog::swapRows(
int row1,
int row2 )
230 const int colCount = mAttributeActionTable->columnCount();
231 for (
int col = 0; col < colCount; col++ )
233 QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
234 mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
235 mAttributeActionTable->setItem( row2, col, item );
237 QTableWidgetItem *header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
238 mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
239 mAttributeActionTable->setVerticalHeaderItem( row2, header );
242QgsAction QgsAttributeActionDialog::rowToAction(
int row )
const
244 const QUuid
id { mAttributeActionTable->item( row, Type )->data( Role::ActionId ).toUuid() };
248 mAttributeActionTable->item( row, Description )->text(),
249 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
250 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
251 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
252 mAttributeActionTable->item( row, ShortTitle )->text(),
253 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
254 mAttributeActionTable->item( row, NotificationMessage )->text(),
255 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked
265 return tr(
"Generic" );
267 return tr(
"Python" );
269 return tr(
"macOS" );
271 return tr(
"Windows" );
275 return tr(
"Open URL" );
277 return tr(
"Submit URL (urlencoded or JSON)" );
279 return tr(
"Submit URL (multipart)" );
284void QgsAttributeActionDialog::remove()
286 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
287 if ( !selection.isEmpty() )
290 int row = selection.first()->row();
291 mAttributeActionTable->removeRow( row );
294 if ( row >= mAttributeActionTable->rowCount() )
295 row = mAttributeActionTable->rowCount() - 1;
296 mAttributeActionTable->selectRow( row );
302void QgsAttributeActionDialog::insert()
305 const int pos = mAttributeActionTable->rowCount();
307 QgsAttributeActionPropertiesDialog dlg( mLayer,
this );
308 dlg.setWindowTitle( tr(
"Add New Action" ) );
312 const QString name = uniqueName( dlg.description() );
314 insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
318void QgsAttributeActionDialog::duplicate()
321 const int pos = mAttributeActionTable->rowCount();
322 const int row = mAttributeActionTable->currentRow();
324 QgsAttributeActionPropertiesDialog dlg(
326 mAttributeActionTable->item( row, Description )->text(),
327 mAttributeActionTable->item( row, ShortTitle )->text(),
328 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
329 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
330 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
331 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
332 mAttributeActionTable->item( row, NotificationMessage )->text(),
333 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
337 dlg.setWindowTitle( tr(
"Duplicate Action" ) );
341 const QString name = uniqueName( dlg.description() );
343 insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
347void QgsAttributeActionDialog::updateButtons()
349 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
350 const bool hasSelection = !selection.isEmpty();
354 const int row = selection.first()->row();
355 mMoveUpButton->setEnabled( row >= 1 );
356 mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
360 mMoveUpButton->setEnabled(
false );
361 mMoveDownButton->setEnabled(
false );
364 mRemoveButton->setEnabled( hasSelection );
365 mDuplicateButton->setEnabled( hasSelection );
368void QgsAttributeActionDialog::addDefaultActions()
371 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() );
375 tr(
"Run an application" ),
376 u
"ogr2ogr -f \"GPKG\" \"[% \"OUTPUT_PATH\" %]\" \"[% \"INPUT_FILE\" %]\""_s,
379 tr(
"Run application" ),
380 QSet<QString>() << u
"Feature"_s << u
"Canvas"_s,
386 tr(
"Display the feature id in the message bar" ),
387 u
"from qgis.utils import iface\n\niface.messageBar().pushInfo(\"Feature id\", \"The feature id is [% $id %]\")"_s,
391 QSet<QString>() << u
"Feature"_s << u
"Canvas"_s,
397 tr(
"Selected field's value (Identify features tool)" ),
398 u
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Current field's value\", \"[% @field_name %] = [% @field_value %]\")"_s,
402 QSet<QString>() << u
"Field"_s,
408 tr(
"Clicked coordinates (Run feature actions tool)" ),
409 u
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Clicked coords\", \"layer: [% @layer_id %]\\ncoords: ([% @click_x %],[% @click_y %])\")"_s,
412 tr(
"Clicked Coordinate" ),
413 QSet<QString>() << u
"Canvas"_s,
416 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() );
417 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() );
421 tr(
"List feature ids" ),
422 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,
425 tr(
"List feature ids" ),
426 QSet<QString>() << u
"Layer"_s,
432 tr(
"Duplicate selected features" ),
433 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,
436 tr(
"Duplicate selected" ),
437 QSet<QString>() << u
"Layer"_s,
443void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
445 const int row = item->row();
447 QgsAttributeActionPropertiesDialog actionProperties(
449 mAttributeActionTable->item( row, Description )->text(),
450 mAttributeActionTable->item( row, ShortTitle )->text(),
451 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
452 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
453 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
454 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
455 mAttributeActionTable->item( row, NotificationMessage )->text(),
456 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
460 actionProperties.setWindowTitle( tr(
"Edit Action" ) );
462 if ( actionProperties.exec() )
464 mAttributeActionTable->item( row, Type )->setData( Role::ActionType,
static_cast<int>( actionProperties.type() ) );
465 mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
466 mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
467 mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
468 mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText().length() > 30 ? actionProperties.actionText().left( 27 ) +
"…" : actionProperties.actionText() );
469 mAttributeActionTable->item( row, ActionText )->setData( Qt::UserRole, actionProperties.actionText() );
470 mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
471 mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
472 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
474 QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
475 QStringList actionScopes = qgis::setToList( actionProperties.actionScopes() );
476 std::sort( actionScopes.begin(), actionScopes.end() );
477 item->setText( actionScopes.join(
", "_L1 ) );
478 item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
480 mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
481 mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
485QString QgsAttributeActionDialog::uniqueName( QString name )
490 const int pos = mAttributeActionTable->rowCount();
493 for (
int i = 0; i < pos; ++i )
495 if ( mAttributeActionTable->item( i, Description )->text() == name )
505 const QString suffix = QString::number( suffix_num );
506 new_name = name +
'_' + suffix;
508 for (
int i = 0; i < pos; ++i )
509 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.