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.