32#include <QImageWriter>
35#include <QTableWidget>
37#include "moc_qgsattributeactiondialog.cpp"
44 QHeaderView *header = mAttributeActionTable->horizontalHeader();
45 header->setHighlightSections(
false );
46 header->setStretchLastSection(
true );
47 mAttributeActionTable->setColumnWidth( 0, 100 );
48 mAttributeActionTable->setColumnWidth( 1, 230 );
49 mAttributeActionTable->setCornerButtonEnabled(
false );
50 mAttributeActionTable->setEditTriggers( QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked );
52 connect( mAttributeActionTable, &QTableWidget::itemDoubleClicked,
this, &QgsAttributeActionDialog::itemDoubleClicked );
53 connect( mAttributeActionTable, &QTableWidget::itemSelectionChanged,
this, &QgsAttributeActionDialog::updateButtons );
54 connect( mMoveUpButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::moveUp );
55 connect( mMoveDownButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::moveDown );
56 connect( mRemoveButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::remove );
57 connect( mAddButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::insert );
58 connect( mDuplicateButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::duplicate );
59 connect( mAddDefaultActionsButton, &QAbstractButton::clicked,
this, &QgsAttributeActionDialog::addDefaultActions );
67 mAttributeActionTable->setRowCount( 0 );
71 const auto constActions =
actions.actions();
72 for (
const QgsAction &action : constActions )
74 insertRow( i++, action );
81 visibleActionWidgetConfig.
hidden =
false;
84 mAttributeTableWidgetType->setCurrentIndex( attributeTableConfig.
actionWidgetStyle() );
91 for (
int i = 0; i < mAttributeActionTable->rowCount(); ++i )
93 actions.append( rowToAction( i ) );
101 return mShowInAttributeTable->isChecked();
109void QgsAttributeActionDialog::insertRow(
int row,
const QgsAction &action )
111 QTableWidgetItem *item =
nullptr;
112 mAttributeActionTable->insertRow( row );
115 item =
new QTableWidgetItem( textForType( action.
type() ) );
116 item->setData( Role::ActionType,
static_cast<int>( action.
type() ) );
117 item->setData( Role::ActionId, action.
id() );
118 item->setFlags( item->flags() & ~Qt::ItemIsEditable );
119 mAttributeActionTable->setItem( row, Type, item );
122 mAttributeActionTable->setItem( row, Description,
new QTableWidgetItem( action.
name() ) );
125 mAttributeActionTable->setItem( row, ShortTitle,
new QTableWidgetItem( action.
shortTitle() ) );
128 item =
new QTableWidgetItem( action.
command().length() > 30 ? action.
command().left( 27 ) +
"…" : action.
command() );
129 item->setData( Qt::UserRole, action.
command() );
130 mAttributeActionTable->setItem( row, ActionText, item );
133 item =
new QTableWidgetItem();
134 item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
135 item->setCheckState( action.
capture() ? Qt::Checked : Qt::Unchecked );
136 mAttributeActionTable->setItem( row, Capture, item );
139 item =
new QTableWidgetItem();
140 item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
141 QStringList actionScopes = qgis::setToList( action.
actionScopes() );
142 std::sort( actionScopes.begin(), actionScopes.end() );
143 item->setText( actionScopes.join( QLatin1String(
", " ) ) );
144 item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( action.
actionScopes() ) );
145 mAttributeActionTable->setItem( row, ActionScopes, item );
148 const QIcon icon = action.
icon();
149 QTableWidgetItem *headerItem =
new QTableWidgetItem( icon, QString() );
150 headerItem->setData( Qt::UserRole, action.
iconPath() );
151 mAttributeActionTable->setVerticalHeaderItem( row, headerItem );
154 mAttributeActionTable->setItem( row, NotificationMessage,
new QTableWidgetItem( action.
notificationMessage() ) );
157 item =
new QTableWidgetItem();
158 item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
160 mAttributeActionTable->setItem( row, EnabledOnlyWhenEditable, item );
165void 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 )
167 if ( uniqueName( name ) == name )
168 insertRow( row, QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
171void QgsAttributeActionDialog::moveUp()
175 int row1 = -1, row2 = -1;
176 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
177 if ( !selection.isEmpty() )
179 row1 = selection.first()->row();
185 if ( row1 != -1 && row2 != -1 )
187 swapRows( row1, row2 );
189 mAttributeActionTable->selectRow( row2 );
193void QgsAttributeActionDialog::moveDown()
196 int row1 = -1, row2 = -1;
197 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
198 if ( !selection.isEmpty() )
200 row1 = selection.first()->row();
203 if ( row1 < mAttributeActionTable->rowCount() - 1 )
206 if ( row1 != -1 && row2 != -1 )
208 swapRows( row1, row2 );
210 mAttributeActionTable->selectRow( row2 );
214void QgsAttributeActionDialog::swapRows(
int row1,
int row2 )
216 const int colCount = mAttributeActionTable->columnCount();
217 for (
int col = 0; col < colCount; col++ )
219 QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
220 mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
221 mAttributeActionTable->setItem( row2, col, item );
223 QTableWidgetItem *header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
224 mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
225 mAttributeActionTable->setVerticalHeaderItem( row2, header );
228QgsAction QgsAttributeActionDialog::rowToAction(
int row )
const
230 const QUuid
id { mAttributeActionTable->item( row, Type )->data( Role::ActionId ).toUuid() };
231 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 );
240 return tr(
"Generic" );
242 return tr(
"Python" );
244 return tr(
"macOS" );
246 return tr(
"Windows" );
250 return tr(
"Open URL" );
252 return tr(
"Submit URL (urlencoded or JSON)" );
254 return tr(
"Submit URL (multipart)" );
259void QgsAttributeActionDialog::remove()
261 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
262 if ( !selection.isEmpty() )
265 int row = selection.first()->row();
266 mAttributeActionTable->removeRow( row );
269 if ( row >= mAttributeActionTable->rowCount() )
270 row = mAttributeActionTable->rowCount() - 1;
271 mAttributeActionTable->selectRow( row );
277void QgsAttributeActionDialog::insert()
280 const int pos = mAttributeActionTable->rowCount();
282 QgsAttributeActionPropertiesDialog dlg( mLayer,
this );
283 dlg.setWindowTitle( tr(
"Add New Action" ) );
287 const QString name = uniqueName( dlg.description() );
289 insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
293void QgsAttributeActionDialog::duplicate()
296 const int pos = mAttributeActionTable->rowCount();
297 const int row = mAttributeActionTable->currentRow();
299 QgsAttributeActionPropertiesDialog dlg(
301 mAttributeActionTable->item( row, Description )->text(),
302 mAttributeActionTable->item( row, ShortTitle )->text(),
303 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
304 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
305 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
306 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
307 mAttributeActionTable->item( row, NotificationMessage )->text(),
308 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
312 dlg.setWindowTitle( tr(
"Duplicate Action" ) );
316 const QString name = uniqueName( dlg.description() );
318 insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
322void QgsAttributeActionDialog::updateButtons()
324 QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
325 const bool hasSelection = !selection.isEmpty();
329 const int row = selection.first()->row();
330 mMoveUpButton->setEnabled( row >= 1 );
331 mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
335 mMoveUpButton->setEnabled(
false );
336 mMoveDownButton->setEnabled(
false );
339 mRemoveButton->setEnabled( hasSelection );
340 mDuplicateButton->setEnabled( hasSelection );
343void QgsAttributeActionDialog::addDefaultActions()
346 insertRow( pos++,
Qgis::AttributeActionType::Generic, tr(
"Echo attribute's value" ), QStringLiteral(
"echo \"[% @field_value %]\"" ), QString(),
true, tr(
"Attribute Value" ), QSet<QString>() << QStringLiteral(
"Field" ), QString() );
347 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() );
348 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() );
349 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() );
350 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() );
351 insertRow( pos++,
Qgis::AttributeActionType::OpenUrl, tr(
"Open file" ), QStringLiteral(
"[% \"PATH\" %]" ), QString(),
false, tr(
"Open file" ), QSet<QString>() << QStringLiteral(
"Feature" ) << QStringLiteral(
"Canvas" ), QString() );
352 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() );
353 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() );
354 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 );
357void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
359 const int row = item->row();
361 QgsAttributeActionPropertiesDialog actionProperties(
363 mAttributeActionTable->item( row, Description )->text(),
364 mAttributeActionTable->item( row, ShortTitle )->text(),
365 mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
366 mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
367 mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
368 mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
369 mAttributeActionTable->item( row, NotificationMessage )->text(),
370 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
374 actionProperties.setWindowTitle( tr(
"Edit Action" ) );
376 if ( actionProperties.exec() )
378 mAttributeActionTable->item( row, Type )->setData( Role::ActionType,
static_cast<int>( actionProperties.type() ) );
379 mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
380 mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
381 mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
382 mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText().length() > 30 ? actionProperties.actionText().left( 27 ) +
"…" : actionProperties.actionText() );
383 mAttributeActionTable->item( row, ActionText )->setData( Qt::UserRole, actionProperties.actionText() );
384 mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
385 mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
386 mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
388 QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
389 QStringList actionScopes = qgis::setToList( actionProperties.actionScopes() );
390 std::sort( actionScopes.begin(), actionScopes.end() );
391 item->setText( actionScopes.join( QLatin1String(
", " ) ) );
392 item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
394 mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
395 mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
399QString QgsAttributeActionDialog::uniqueName( QString name )
404 const int pos = mAttributeActionTable->rowCount();
407 for (
int i = 0; i < pos; ++i )
409 if ( mAttributeActionTable->item( i, Description )->text() == name )
419 const QString suffix = QString::number( suffix_num );
420 new_name = name +
'_' + suffix;
422 for (
int i = 0; i < pos; ++i )
423 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.