mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
382 lines
16 KiB
C++
382 lines
16 KiB
C++
/***************************************************************************
|
|
qgsattributeactiondialog.cpp - attribute action dialog
|
|
-------------------
|
|
|
|
This class creates and manages the Action tab of the Vector Layer
|
|
Properties dialog box. Changes made in the dialog box are propagated
|
|
back to QgsVectorLayer.
|
|
|
|
begin : October 2004
|
|
copyright : (C) 2004 by Gavin Macaulay
|
|
email : gavin at macaulay dot co dot nz
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "qgsattributeactiondialog.h"
|
|
#include "qgsactionmanager.h"
|
|
#include "qgsexpressionbuilderdialog.h"
|
|
#include "qgsattributeactionpropertiesdialog.h"
|
|
#include "qgisapp.h"
|
|
#include "qgsproject.h"
|
|
#include "qgsmapcanvas.h"
|
|
#include "qgsvectorlayer.h"
|
|
|
|
#include <QFileDialog>
|
|
#include <QHeaderView>
|
|
#include <QMessageBox>
|
|
#include <QSettings>
|
|
#include <QImageWriter>
|
|
#include <QTableWidget>
|
|
|
|
QgsAttributeActionDialog::QgsAttributeActionDialog( const QgsActionManager& actions, QWidget* parent )
|
|
: QWidget( parent )
|
|
, mLayer( actions.layer() )
|
|
{
|
|
setupUi( this );
|
|
QHeaderView* header = mAttributeActionTable->horizontalHeader();
|
|
header->setHighlightSections( false );
|
|
header->setStretchLastSection( true );
|
|
mAttributeActionTable->setColumnWidth( 0, 100 );
|
|
mAttributeActionTable->setColumnWidth( 1, 230 );
|
|
mAttributeActionTable->setCornerButtonEnabled( false );
|
|
mAttributeActionTable->setEditTriggers( QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked );
|
|
|
|
connect( mAttributeActionTable, SIGNAL( itemDoubleClicked( QTableWidgetItem* ) ), this, SLOT( itemDoubleClicked( QTableWidgetItem* ) ) );
|
|
connect( mAttributeActionTable, SIGNAL( itemSelectionChanged() ), this, SLOT( updateButtons() ) );
|
|
connect( mMoveUpButton, SIGNAL( clicked() ), this, SLOT( moveUp() ) );
|
|
connect( mMoveDownButton, SIGNAL( clicked() ), this, SLOT( moveDown() ) );
|
|
connect( mRemoveButton, SIGNAL( clicked() ), this, SLOT( remove() ) );
|
|
connect( mAddButton, SIGNAL( clicked( bool ) ), this, SLOT( insert() ) );
|
|
connect( mAddDefaultActionsButton, SIGNAL( clicked() ), this, SLOT( addDefaultActions() ) );
|
|
|
|
init( actions, mLayer->attributeTableConfig() );
|
|
}
|
|
|
|
void QgsAttributeActionDialog::init( const QgsActionManager& actions, const QgsAttributeTableConfig& attributeTableConfig )
|
|
{
|
|
// Start from a fresh slate.
|
|
mAttributeActionTable->setRowCount( 0 );
|
|
|
|
int i = 0;
|
|
// Populate with our actions.
|
|
Q_FOREACH ( const QgsAction& action, actions.actions() )
|
|
{
|
|
insertRow( i++, action );
|
|
}
|
|
|
|
updateButtons();
|
|
|
|
QgsAttributeTableConfig::ColumnConfig visibleActionWidgetConfig = QgsAttributeTableConfig::ColumnConfig();
|
|
visibleActionWidgetConfig.type = QgsAttributeTableConfig::Action;
|
|
visibleActionWidgetConfig.hidden = false;
|
|
|
|
mShowInAttributeTable->setChecked( attributeTableConfig.actionWidgetVisible() );
|
|
mAttributeTableWidgetType->setCurrentIndex( attributeTableConfig.actionWidgetStyle() );
|
|
}
|
|
|
|
QList<QgsAction> QgsAttributeActionDialog::actions() const
|
|
{
|
|
QList<QgsAction> actions;
|
|
|
|
for ( int i = 0; i < mAttributeActionTable->rowCount(); ++i )
|
|
{
|
|
actions.append( rowToAction( i ) );
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
bool QgsAttributeActionDialog::showWidgetInAttributeTable() const
|
|
{
|
|
return mShowInAttributeTable->isChecked();
|
|
}
|
|
|
|
QgsAttributeTableConfig::ActionWidgetStyle QgsAttributeActionDialog::attributeTableWidgetStyle() const
|
|
{
|
|
return static_cast<QgsAttributeTableConfig::ActionWidgetStyle>( mAttributeTableWidgetType->currentIndex() );
|
|
}
|
|
|
|
void QgsAttributeActionDialog::insertRow( int row, const QgsAction& action )
|
|
{
|
|
QTableWidgetItem* item;
|
|
mAttributeActionTable->insertRow( row );
|
|
|
|
// Type
|
|
item = new QTableWidgetItem( textForType( action.type() ) );
|
|
item->setData( Qt::UserRole, action.type() );
|
|
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
|
|
mAttributeActionTable->setItem( row, Type, item );
|
|
|
|
// Description
|
|
mAttributeActionTable->setItem( row, Description, new QTableWidgetItem( action.name() ) );
|
|
|
|
// Short Title
|
|
mAttributeActionTable->setItem( row, ShortTitle, new QTableWidgetItem( action.shortTitle() ) );
|
|
|
|
// Action text
|
|
mAttributeActionTable->setItem( row, ActionText, new QTableWidgetItem( action.command() ) );
|
|
|
|
// Capture output
|
|
item = new QTableWidgetItem();
|
|
item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
|
|
item->setCheckState( action.capture() ? Qt::Checked : Qt::Unchecked );
|
|
mAttributeActionTable->setItem( row, Capture, item );
|
|
|
|
// Scopes
|
|
item = new QTableWidgetItem();
|
|
item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
|
|
QStringList actionScopes = action.actionScopes().toList();
|
|
std::sort( actionScopes.begin(), actionScopes.end() );
|
|
item->setText( actionScopes.join( QStringLiteral( ", " ) ) );
|
|
item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( action.actionScopes() ) );
|
|
mAttributeActionTable->setItem( row, ActionScopes, item );
|
|
|
|
// Icon
|
|
QIcon icon = action.icon();
|
|
QTableWidgetItem* headerItem = new QTableWidgetItem( icon, QLatin1String( "" ) );
|
|
headerItem->setData( Qt::UserRole, action.iconPath() );
|
|
mAttributeActionTable->setVerticalHeaderItem( row, headerItem );
|
|
|
|
updateButtons();
|
|
}
|
|
|
|
void QgsAttributeActionDialog::insertRow( int row, QgsAction::ActionType type, const QString& name, const QString& actionText, const QString& iconPath, bool capture, const QString& shortTitle, const QSet<QString>& actionScopes )
|
|
{
|
|
if ( uniqueName( name ) == name )
|
|
insertRow( row, QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes ) );
|
|
}
|
|
|
|
void QgsAttributeActionDialog::moveUp()
|
|
{
|
|
// Swap the selected row with the one above
|
|
|
|
int row1 = -1, row2 = -1;
|
|
QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
|
|
if ( !selection.isEmpty() )
|
|
{
|
|
row1 = selection.first()->row();
|
|
}
|
|
|
|
if ( row1 > 0 )
|
|
row2 = row1 - 1;
|
|
|
|
if ( row1 != -1 && row2 != -1 )
|
|
{
|
|
swapRows( row1, row2 );
|
|
// Move the selection to follow
|
|
mAttributeActionTable->selectRow( row2 );
|
|
}
|
|
}
|
|
|
|
void QgsAttributeActionDialog::moveDown()
|
|
{
|
|
// Swap the selected row with the one below
|
|
int row1 = -1, row2 = -1;
|
|
QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
|
|
if ( !selection.isEmpty() )
|
|
{
|
|
row1 = selection.first()->row();
|
|
}
|
|
|
|
if ( row1 < mAttributeActionTable->rowCount() - 1 )
|
|
row2 = row1 + 1;
|
|
|
|
if ( row1 != -1 && row2 != -1 )
|
|
{
|
|
swapRows( row1, row2 );
|
|
// Move the selection to follow
|
|
mAttributeActionTable->selectRow( row2 );
|
|
}
|
|
}
|
|
|
|
void QgsAttributeActionDialog::swapRows( int row1, int row2 )
|
|
{
|
|
int colCount = mAttributeActionTable->columnCount();
|
|
for ( int col = 0; col < colCount; col++ )
|
|
{
|
|
QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
|
|
mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
|
|
mAttributeActionTable->setItem( row2, col, item );
|
|
}
|
|
QTableWidgetItem* header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
|
|
mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
|
|
mAttributeActionTable->setVerticalHeaderItem( row2, header );
|
|
}
|
|
|
|
QgsAction QgsAttributeActionDialog::rowToAction( int row ) const
|
|
{
|
|
QgsAction action( static_cast<QgsAction::ActionType>( mAttributeActionTable->item( row, Type )->data( Qt::UserRole ).toInt() ),
|
|
mAttributeActionTable->item( row, Description )->text(),
|
|
mAttributeActionTable->item( row, ActionText )->text(),
|
|
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>>() );
|
|
return action;
|
|
}
|
|
|
|
QString QgsAttributeActionDialog::textForType( QgsAction::ActionType type )
|
|
{
|
|
switch ( type )
|
|
{
|
|
case QgsAction::Generic:
|
|
return tr( "Generic" );
|
|
case QgsAction::GenericPython:
|
|
return tr( "Python" );
|
|
case QgsAction::Mac:
|
|
return tr( "Mac" );
|
|
case QgsAction::Windows:
|
|
return tr( "Windows" );
|
|
case QgsAction::Unix:
|
|
return tr( "Unix" );
|
|
case QgsAction::OpenUrl:
|
|
return tr( "Open URL" );
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void QgsAttributeActionDialog::remove()
|
|
{
|
|
QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
|
|
if ( !selection.isEmpty() )
|
|
{
|
|
// Remove the selected row.
|
|
int row = selection.first()->row();
|
|
mAttributeActionTable->removeRow( row );
|
|
|
|
// And select the row below the one that was selected or the last one.
|
|
if ( row >= mAttributeActionTable->rowCount() )
|
|
row = mAttributeActionTable->rowCount() - 1;
|
|
mAttributeActionTable->selectRow( row );
|
|
|
|
updateButtons();
|
|
}
|
|
}
|
|
|
|
void QgsAttributeActionDialog::insert()
|
|
{
|
|
// Add the action details as a new row in the table.
|
|
int pos = mAttributeActionTable->rowCount();
|
|
|
|
QgsAttributeActionPropertiesDialog dlg( mLayer, this );
|
|
dlg.setWindowTitle( tr( "Add new action" ) );
|
|
|
|
if ( dlg.exec() )
|
|
{
|
|
QString name = uniqueName( dlg.description() );
|
|
|
|
insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes() );
|
|
}
|
|
}
|
|
|
|
void QgsAttributeActionDialog::updateButtons()
|
|
{
|
|
QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
|
|
bool hasSelection = !selection.isEmpty();
|
|
|
|
if ( hasSelection )
|
|
{
|
|
int row = selection.first()->row();
|
|
mMoveUpButton->setEnabled( row >= 1 );
|
|
mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
|
|
}
|
|
else
|
|
{
|
|
mMoveUpButton->setEnabled( false );
|
|
mMoveDownButton->setEnabled( false );
|
|
}
|
|
|
|
mRemoveButton->setEnabled( hasSelection );
|
|
}
|
|
|
|
void QgsAttributeActionDialog::addDefaultActions()
|
|
{
|
|
int pos = 0;
|
|
insertRow( pos++, QgsAction::Generic, tr( "Echo attribute's value" ), QStringLiteral( "echo \"[% \"MY_FIELD\" %]\"" ), QLatin1String( "" ), true, tr( "Attribute Value" ), QSet<QString>() << QStringLiteral( "Field" ) );
|
|
insertRow( pos++, QgsAction::Generic, tr( "Run an application" ), QStringLiteral( "ogr2ogr -f \"ESRI Shapefile\" \"[% \"OUTPUT_PATH\" %]\" \"[% \"INPUT_FILE\" %]\"" ), QLatin1String( "" ), true, tr( "Run application" ), QSet<QString>() << QStringLiteral( "Feature" ) << QStringLiteral( "Canvas" ) );
|
|
insertRow( pos++, QgsAction::GenericPython, tr( "Get feature id" ), QStringLiteral( "from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Feature id\", \"feature id is [% $id %]\")" ), QLatin1String( "" ), false, tr( "Feature ID" ), QSet<QString>() << QStringLiteral( "Feature" ) << QStringLiteral( "Canvas" ) );
|
|
insertRow( pos++, QgsAction::GenericPython, tr( "Selected field's value (Identify features tool)" ), QStringLiteral( "from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Current field's value\", \"[% @current_field %]\")" ), QLatin1String( "" ), false, tr( "Field Value" ), QSet<QString>() << QStringLiteral( "Field" ) );
|
|
insertRow( pos++, QgsAction::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 %])\")" ), QLatin1String( "" ), false, tr( "Clicked Coordinate" ), QSet<QString>() << QStringLiteral( "Canvas" ) );
|
|
insertRow( pos++, QgsAction::OpenUrl, tr( "Open file" ), QStringLiteral( "[% \"PATH\" %]" ), QLatin1String( "" ), false, tr( "Open file" ), QSet<QString>() << QStringLiteral( "Feature" ) << QStringLiteral( "Canvas" ) );
|
|
insertRow( pos++, QgsAction::OpenUrl, tr( "Search on web based on attribute's value" ), QStringLiteral( "http://www.google.com/search?q=[% \"ATTRIBUTE\" %]" ), QLatin1String( "" ), false, tr( "Search Web" ), QSet<QString>() << QStringLiteral( "Field" ) );
|
|
insertRow( pos++, QgsAction::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]))" ), QLatin1String( "" ), false, tr( "List feature ids" ), QSet<QString>() << QStringLiteral( "Layer" ) );
|
|
}
|
|
|
|
void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem* item )
|
|
{
|
|
int row = item->row();
|
|
|
|
QgsAttributeActionPropertiesDialog actionProperties(
|
|
static_cast<QgsAction::ActionType>( mAttributeActionTable->item( row, Type )->data( Qt::UserRole ).toInt() ),
|
|
mAttributeActionTable->item( row, Description )->text(),
|
|
mAttributeActionTable->item( row, ShortTitle )->text(),
|
|
mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
|
|
mAttributeActionTable->item( row, ActionText )->text(),
|
|
mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
|
|
mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
|
|
mLayer
|
|
);
|
|
|
|
actionProperties.setWindowTitle( tr( "Edit action" ) );
|
|
|
|
if ( actionProperties.exec() )
|
|
{
|
|
mAttributeActionTable->item( row, Type )->setData( Qt::UserRole, actionProperties.type() );
|
|
mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
|
|
mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
|
|
mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
|
|
mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText() );
|
|
mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
|
|
|
|
QTableWidgetItem* item = mAttributeActionTable->item( row, ActionScopes );
|
|
QStringList actionScopes = actionProperties.actionScopes().toList();
|
|
std::sort( actionScopes.begin(), actionScopes.end() );
|
|
item->setText( actionScopes.join( QStringLiteral( ", " ) ) );
|
|
item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
|
|
|
|
mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
|
|
mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
|
|
}
|
|
}
|
|
|
|
QString QgsAttributeActionDialog::uniqueName( QString name )
|
|
{
|
|
// Make sure that the given name is unique, adding a numerical
|
|
// suffix if necessary.
|
|
|
|
int pos = mAttributeActionTable->rowCount();
|
|
bool unique = true;
|
|
|
|
for ( int i = 0; i < pos; ++i )
|
|
{
|
|
if ( mAttributeActionTable->item( i, Description )->text() == name )
|
|
unique = false;
|
|
}
|
|
|
|
if ( !unique )
|
|
{
|
|
int suffix_num = 1;
|
|
QString new_name;
|
|
while ( !unique )
|
|
{
|
|
QString suffix = QString::number( suffix_num );
|
|
new_name = name + '_' + suffix;
|
|
unique = true;
|
|
for ( int i = 0; i < pos; ++i )
|
|
if ( mAttributeActionTable->item( i, 0 )->text() == new_name )
|
|
unique = false;
|
|
++suffix_num;
|
|
}
|
|
name = new_name;
|
|
}
|
|
return name;
|
|
}
|