[FEATHRE] attribute action improvements

- add python bindings for attribute actions
- support actions as context menu in feature form (ui)
- add action types:
  * generic: commands that should work on all platforms
  * windows, mac, unix: commands that should work and are only shown on one platform respectively
    (eg. vim on unix, notepad on windows and textmate on Mac)
  * python: python strings to be executed instead of a command
    (eg. qgis.utils.plugins['apluginname'].amethod('[%someattribute%]'))

[BUGFIXES]
- ogr support for relative filenames fixed
- relative filename support also for feature form uis


git-svn-id: http://svn.osgeo.org/qgis/trunk@12113 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
jef 2009-11-15 00:15:55 +00:00
parent 095ff726cb
commit 03224425e5
15 changed files with 409 additions and 256 deletions

View File

@ -68,6 +68,7 @@
%Include qgssymbologyutils.sip
%Include qgstolerance.sip
%Include qgsuniquevaluerenderer.sip
%Include qgsattributeaction.sip
%Include qgsvectordataprovider.sip
%Include qgsvectorfilewriter.sip
%Include qgsvectorlayer.sip

View File

@ -0,0 +1,77 @@
class QgsAction
{
%TypeHeaderCode
#include "qgsattributeaction.h"
%End
public:
enum ActionType
{
Generic,
GenericPython,
Mac,
Windows,
Unix,
};
QgsAction( ActionType type, QString name, QString action, bool capture );
//! The name of the action
QString name() const;
//! The action
QString action() const;
//! The action type
ActionType type() const;
//! Whether to capture output for display when this action is run
bool capture() const;
bool runable() const;
};
class QgsAttributeAction
{
%TypeHeaderCode
#include "qgsattributeaction.h"
%End
public:
QgsAttributeAction();
//! Destructor
virtual ~QgsAttributeAction();
//! Add an action with the given name and action details.
// Will happily have duplicate names and actions. If
// capture is true, when running the action using doAction(),
// any stdout from the process will be captured and displayed in a
// dialog box.
void addAction( QgsAction::ActionType type, QString name, QString action, bool capture = false );
/*
//! Does the action using the given values. defaultValueIndex is an
// index into values which indicates which value in the values vector
// is to be used if the action has a default placeholder.
void doAction( int index, const QList< QPair<QString, QString> > &values,
int defaultValueIndex = 0, void *executePython = 0 );
*/
//! Removes all actions
void clearActions();
//! Expands the given action, replacing all %'s with the value as
// given.
static QString expandAction( QString action, const QList< QPair<QString, QString> > &values,
uint defaultValueIndex );
//! Writes the actions out in XML format
bool writeXML( QDomNode& layer_node, QDomDocument& doc ) const;
//! Reads the actions in in XML format
bool readXML( const QDomNode& layer_node );
//! interface to inherited methods from QList<QgsAction>
const QgsAction &at( int idx );
const int size();
};

View File

@ -63,7 +63,7 @@ public:
QgsLabel *label();
// TODO: wrap QgsAttributeAction* actions();
QgsAttributeAction *actions();
/** The number of features that are selected in this layer */
int selectedFeatureCount();

View File

@ -6397,3 +6397,11 @@ void QgisApp::updateUndoActions()
mActionUndo->setEnabled( canUndo );
mActionRedo->setEnabled( canRedo );
}
void QgisApp::runPythonString( const QString &expr )
{
if ( mPythonUtils )
{
mPythonUtils->runStringUnsafe( expr );
}
}

View File

@ -340,6 +340,9 @@ class QgisApp : public QMainWindow
QToolBar *pluginToolBar() { return mPluginToolBar; }
QToolBar *helpToolBar() { return mHelpToolBar; }
//! run python
void runPythonString( const QString &expr );
public slots:
//! Zoom to full extent
void zoomFull();

View File

@ -68,24 +68,26 @@ void QgsAttributeActionDialog::init()
attributeActionTable->setRowCount( 0 );
// Populate with our actions.
QgsAttributeAction::AttributeActions::const_iterator
iter = mActions->begin();
int i = 0;
for ( ; iter != mActions->end(); ++iter, ++i )
for ( int i = 0; i < mActions->size(); i++ )
{
insertRow( i, iter->name(), iter->action(), iter->capture() );
const QgsAction action = ( *mActions )[i];
insertRow( i, action.type(), action.name(), action.action(), action.capture() );
}
}
void QgsAttributeActionDialog::insertRow( int row, const QString &name, const QString &action, bool capture )
void QgsAttributeActionDialog::insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &action, bool capture )
{
QTableWidgetItem* item;
attributeActionTable->insertRow( row );
attributeActionTable->setItem( row, 0, new QTableWidgetItem( name ) );
attributeActionTable->setItem( row, 1, new QTableWidgetItem( action ) );
QTableWidgetItem* item = new QTableWidgetItem();
item = new QTableWidgetItem( actionType->itemText( type ) );
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
attributeActionTable->setItem( row, 0, item );
attributeActionTable->setItem( row, 1, new QTableWidgetItem( name ) );
attributeActionTable->setItem( row, 2, new QTableWidgetItem( action ) );
item = new QTableWidgetItem();
item->setFlags( item->flags() & ~( Qt::ItemIsEditable | Qt::ItemIsUserCheckable ) );
item->setCheckState( capture ? Qt::Checked : Qt::Unchecked );
attributeActionTable->setItem( row, 2, item );
attributeActionTable->setItem( row, 3, item );
}
void QgsAttributeActionDialog::moveUp()
@ -208,15 +210,15 @@ void QgsAttributeActionDialog::insert( int pos )
if ( pos >= numRows )
{
// Expand the table to have a row with index pos
insertRow( pos, name, actionAction->text(), captureCB->isChecked() );
insertRow( pos, ( QgsAction::ActionType ) actionType->currentIndex(), name, actionAction->text(), captureCB->isChecked() );
}
else
{
// Update existing row
attributeActionTable->item( pos, 0 )->setText( name );
attributeActionTable->item( pos, 1 )->setText( actionAction->text() );
attributeActionTable->item( pos, 2 )->setCheckState(
captureCB->isChecked() ? Qt::Checked : Qt::Unchecked );
attributeActionTable->item( pos, 0 )->setText( actionType->currentText() );
attributeActionTable->item( pos, 1 )->setText( name );
attributeActionTable->item( pos, 2 )->setText( actionAction->text() );
attributeActionTable->item( pos, 3 )->setCheckState( captureCB->isChecked() ? Qt::Checked : Qt::Unchecked );
}
}
}
@ -253,12 +255,13 @@ void QgsAttributeActionDialog::apply()
mActions->clearActions();
for ( int i = 0; i < attributeActionTable->rowCount(); ++i )
{
const QString &name = attributeActionTable->item( i, 0 )->text();
const QString &action = attributeActionTable->item( i, 1 )->text();
const QgsAction::ActionType type = ( QgsAction::ActionType ) actionType->findText( attributeActionTable->item( i, 0 )->text() );
const QString &name = attributeActionTable->item( i, 1 )->text();
const QString &action = attributeActionTable->item( i, 2 )->text();
if ( !name.isEmpty() && !action.isEmpty() )
{
QTableWidgetItem *item = attributeActionTable->item( i, 2 );
mActions->addAction( name, action, item->checkState() == Qt::Checked );
QTableWidgetItem *item = attributeActionTable->item( i, 3 );
mActions->addAction( type, name, action, item->checkState() == Qt::Checked );
}
}
}
@ -292,8 +295,9 @@ void QgsAttributeActionDialog::rowSelected( int row )
if ( item )
{
// Only if a populated row was selected
actionName->setText( attributeActionTable->item( row, 0 )->text() );
actionAction->setText( attributeActionTable->item( row, 1 )->text() );
actionType->setCurrentIndex( actionType->findText( attributeActionTable->item( row, 0 )->text() ) );
actionName->setText( attributeActionTable->item( row, 1 )->text() );
actionAction->setText( attributeActionTable->item( row, 2 )->text() );
captureCB->setChecked( item->checkState() == Qt::Checked );
}
}

View File

@ -25,6 +25,7 @@ back to QgsVectorLayer.
#define QGSATTRIBUTEACTIONDIALOG_H
#include "ui_qgsattributeactiondialogbase.h"
#include "qgsattributeaction.h"
#include "qgsfield.h"
#include <QMap>
@ -56,7 +57,7 @@ class QgsAttributeActionDialog: public QWidget, private Ui::QgsAttributeActionDi
private:
void insertRow( int row, const QString &name, const QString &action, bool capture );
void insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &action, bool capture );
void swapRows( int row1, int row2 );
void insert( int pos );
@ -66,7 +67,7 @@ class QgsAttributeActionDialog: public QWidget, private Ui::QgsAttributeActionDi
QString uniqueName( QString name );
// Pointer to the QgsAttributeAction in the class that created us.
QgsAttributeAction* mActions;
QgsAttributeAction *mActions;
};
#endif

View File

@ -45,6 +45,8 @@ class QgsAttributeDialog : public QObject
*/
void restoreGeometry();
QDialog *dialog() { return mDialog; }
public slots:
/** Overloaded accept method which will write the feature field
* values, then delegate to QDialog::accept()

View File

@ -38,9 +38,22 @@
#include <QMenu>
#include <QClipboard>
#include <QDockWidget>
#include <QMenuBar>
#include "qgslogger.h"
static void _runPythonString( const QString &expr )
{
QgisApp::instance()->runPythonString( expr );
}
void QgsFeatureAction::execute()
{
QList< QPair<QString, QString> > attributes;
mResults->retrieveAttributes( mFeatItem, attributes );
mLayer->actions()->doAction( mAction, attributes, 0, _runPythonString );
}
class QgsIdentifyResultsDock : public QDockWidget
{
public:
@ -96,7 +109,7 @@ QgsIdentifyResults::QgsIdentifyResults( QgsMapCanvas *canvas, QWidget *parent, Q
setColumnText( 0, tr( "Feature" ) );
setColumnText( 1, tr( "Value" ) );
connect( buttonBox,SIGNAL( helpRequested() ), this, SLOT( helpClicked() ) );
connect( buttonBox, SIGNAL( helpRequested() ), this, SLOT( helpClicked() ) );
connect( buttonBox, SIGNAL( clicked() ), this, SLOT( close() ) );
@ -182,16 +195,19 @@ void QgsIdentifyResults::addFeature( QgsMapLayer *layer, int fid,
actionItem->setData( 0, Qt::UserRole, "actions" );
featItem->addChild( actionItem );
QTreeWidgetItem *editItem = new QTreeWidgetItem( QStringList() << "" << (vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) ) );
QTreeWidgetItem *editItem = new QTreeWidgetItem( QStringList() << "" << ( vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) ) );
editItem->setIcon( 0, QgisApp::getThemeIcon( vlayer->isEditable() ? "/mIconEditable.png" : "/mIconEditable.png" ) );
editItem->setData( 0, Qt::UserRole, "edit" );
actionItem->addChild( editItem );
for ( int i = 0; i < vlayer->actions()->size(); i++ )
{
QgsAttributeAction::aIter iter = vlayer->actions()->retrieveAction( i );
const QgsAction &action = vlayer->actions()->at( i );
QTreeWidgetItem *twi = new QTreeWidgetItem( QStringList() << "" << iter->name() );
if ( !action.runable() )
continue;
QTreeWidgetItem *twi = new QTreeWidgetItem( QStringList() << "" << action.name() );
twi->setIcon( 0, QgisApp::getThemeIcon( "/mAction.png" ) );
twi->setData( 0, Qt::UserRole, "action" );
twi->setData( 0, Qt::UserRole + 1, QVariant::fromValue( i ) );
@ -206,31 +222,31 @@ void QgsIdentifyResults::editingToggled()
{
QTreeWidgetItem *layItem = layerItem( sender() );
QgsVectorLayer *vlayer = vectorLayer( layItem );
if( !layItem || !vlayer )
if ( !layItem || !vlayer )
return;
// iterate features
int i;
for( i=0; i<layItem->childCount(); i++ )
for ( i = 0; i < layItem->childCount(); i++ )
{
QTreeWidgetItem *featItem = layItem->child(i);
QTreeWidgetItem *featItem = layItem->child( i );
int j;
for( j=0; j<featItem->childCount() && featItem->child(j)->data( 0, Qt::UserRole ).toString() != "actions"; j++ )
QgsDebugMsg( QString("%1: skipped %2").arg( featItem->child(j)->data( 0, Qt::UserRole ).toString() ) );
for ( j = 0; j < featItem->childCount() && featItem->child( j )->data( 0, Qt::UserRole ).toString() != "actions"; j++ )
QgsDebugMsg( QString( "%1: skipped %2" ).arg( featItem->child( j )->data( 0, Qt::UserRole ).toString() ) );
if( j==featItem->childCount() || featItem->child(j)->childCount()<1 )
if ( j == featItem->childCount() || featItem->child( j )->childCount() < 1 )
continue;
QTreeWidgetItem *actions = featItem->child(j);
QTreeWidgetItem *actions = featItem->child( j );
for( j=0; i<actions->childCount() && actions->child(j)->data( 0, Qt::UserRole ).toString() != "edit"; j++ )
for ( j = 0; i < actions->childCount() && actions->child( j )->data( 0, Qt::UserRole ).toString() != "edit"; j++ )
;
if( j==actions->childCount() )
if ( j == actions->childCount() )
continue;
QTreeWidgetItem *editItem = actions->child(j);
QTreeWidgetItem *editItem = actions->child( j );
editItem->setIcon( 0, QgisApp::getThemeIcon( vlayer->isEditable() ? "/mIconEditable.png" : "/mIconEditable.png" ) );
editItem->setText( 1, vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) );
}
@ -257,7 +273,8 @@ void QgsIdentifyResults::show()
// if this is the only feature and it's on a vector layer
// don't show the form dialog instead of the results window
featureForm( featItem );
lstResults->setCurrentItem( featItem );
featureForm();
return;
}
}
@ -291,7 +308,8 @@ void QgsIdentifyResults::itemClicked( QTreeWidgetItem *item, int column )
{
if ( item->data( 0, Qt::UserRole ).toString() == "edit" )
{
featureForm( item );
lstResults->setCurrentItem( item );
featureForm();
}
else if ( item->data( 0, Qt::UserRole ).toString() == "action" )
{
@ -319,36 +337,15 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event )
mActionPopup = new QMenu();
QAction *a;
a = mActionPopup->addAction( vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) );
a->setEnabled( true );
a->setData( QVariant::fromValue( -6 ) );
a = mActionPopup->addAction( tr( "Zoom to feature" ) );
a->setEnabled( true );
a->setData( QVariant::fromValue( -5 ) );
a = mActionPopup->addAction( tr( "Copy attribute value" ) );
a->setEnabled( true );
a->setData( QVariant::fromValue( -4 ) );
a = mActionPopup->addAction( tr( "Copy feature attributes" ) );
a->setEnabled( true );
a->setData( QVariant::fromValue( -3 ) );
mActionPopup->addAction( vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ), this, SLOT( featureForm() ) );
mActionPopup->addAction( tr( "Zoom to feature" ), this, SLOT( zoomToFeature() ) );
mActionPopup->addAction( tr( "Copy attribute value" ), this, SLOT( copyAttributeValue() ) );
mActionPopup->addAction( tr( "Copy feature attributes" ), this, SLOT( copyFeatureAttributes() ) );
mActionPopup->addSeparator();
mActionPopup->addAction( tr( "Expand all" ), this, SLOT( expandAll() ) );
mActionPopup->addAction( tr( "Collapse all" ), this, SLOT( collapseAll() ) );
a = mActionPopup->addAction( tr( "Expand all" ) );
a->setEnabled( true );
a->setData( QVariant::fromValue( -2 ) );
a = mActionPopup->addAction( tr( "Collapse all" ) );
a->setEnabled( true );
a->setData( QVariant::fromValue( -1 ) );
QgsAttributeAction *actions = vlayer->actions();
if ( actions && actions->size() > 0 )
if ( vlayer->actions()->size() > 0 )
{
mActionPopup->addSeparator();
@ -356,22 +353,21 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event )
// created for each new Identify Results dialog box, and that the
// contents of the popup menu doesn't change during the time that
// such a dialog box is around.
a = mActionPopup->addAction( tr( "Run action" ) );
QAction *a = mActionPopup->addAction( tr( "Run action" ) );
a->setEnabled( false );
QgsAttributeAction::aIter iter = actions->begin();
for ( int i = 0; iter != actions->end(); ++iter, ++i )
for ( int i = 0; i < vlayer->actions()->size(); i++ )
{
QAction* a = mActionPopup->addAction( iter->name() );
a->setEnabled( true );
// The menu action stores an integer that is used later on to
// associate an menu action with an actual qgis action.
a->setData( QVariant::fromValue( i ) );
const QgsAction &action = vlayer->actions()->at( i );
if ( !action.runable() )
continue;
QgsFeatureAction *a = new QgsFeatureAction( action.name(), this, vlayer, i, featureItem( item ) );
mActionPopup->addAction( action.name(), a, SLOT( execute() ) );
}
}
connect( mActionPopup, SIGNAL( triggered( QAction* ) ), this, SLOT( popupItemSelected( QAction* ) ) );
mActionPopup->popup( event->globalPos() );
}
@ -396,69 +392,6 @@ void QgsIdentifyResults::setColumnText( int column, const QString & label )
header->setText( column, label );
}
// Run the action that was selected in the popup menu
void QgsIdentifyResults::popupItemSelected( QAction* menuAction )
{
QTreeWidgetItem *item = lstResults->currentItem();
if ( item == 0 )
return;
int action = menuAction->data().toInt();
if ( action < 0 )
{
switch ( action )
{
case -6:
featureForm( item );
break;
case -5:
zoomToFeature( item );
break;
case -4:
case -3:
{
QClipboard *clipboard = QApplication::clipboard();
QString text;
if ( action == -4 )
{
text = item->data( 1, Qt::DisplayRole ).toString();
}
else
{
std::vector< std::pair<QString, QString> > attributes;
retrieveAttributes( item, attributes );
for ( std::vector< std::pair<QString, QString> >::iterator it = attributes.begin(); it != attributes.end(); it++ )
{
text += QString( "%1: %2\n" ).arg( it->first ).arg( it->second );
}
}
QgsDebugMsg( QString( "set clipboard: %1" ).arg( text ) );
clipboard->setText( text );
}
break;
case -2:
lstResults->expandAll();
break;
case -1:
lstResults->collapseAll();
break;
}
}
else
{
doAction( item, action );
}
}
void QgsIdentifyResults::expandColumnsToFit()
{
lstResults->resizeColumnToContents( 0 );
@ -511,7 +444,7 @@ void QgsIdentifyResults::clearRubberBand()
void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action )
{
std::vector< std::pair<QString, QString> > attributes;
QList< QPair<QString, QString> > attributes;
QTreeWidgetItem *featItem = retrieveAttributes( item, attributes );
if ( !featItem )
return;
@ -585,7 +518,7 @@ QgsVectorLayer *QgsIdentifyResults::vectorLayer( QTreeWidgetItem *item )
}
QTreeWidgetItem *QgsIdentifyResults::retrieveAttributes( QTreeWidgetItem *item, std::vector< std::pair<QString, QString> > &attributes )
QTreeWidgetItem *QgsIdentifyResults::retrieveAttributes( QTreeWidgetItem *item, QList< QPair<QString, QString> > &attributes )
{
QTreeWidgetItem *featItem = featureItem( item );
@ -595,7 +528,7 @@ QTreeWidgetItem *QgsIdentifyResults::retrieveAttributes( QTreeWidgetItem *item,
QTreeWidgetItem *item = featItem->child( i );
if ( item->childCount() > 0 )
continue;
attributes.push_back( std::make_pair( item->data( 0, Qt::DisplayRole ).toString(), item->data( 1, Qt::DisplayRole ).toString() ) );
attributes << QPair<QString, QString>( item->data( 0, Qt::DisplayRole ).toString(), item->data( 1, Qt::DisplayRole ).toString() );
}
return featItem;
@ -729,8 +662,10 @@ void QgsIdentifyResults::highlightFeature( QTreeWidgetItem *item )
}
}
void QgsIdentifyResults::zoomToFeature( QTreeWidgetItem *item )
void QgsIdentifyResults::zoomToFeature()
{
QTreeWidgetItem *item = lstResults->currentItem();
QgsVectorLayer *layer = vectorLayer( item );
if ( !layer )
return;
@ -765,11 +700,12 @@ void QgsIdentifyResults::zoomToFeature( QTreeWidgetItem *item )
mCanvas->refresh();
}
void QgsIdentifyResults::featureForm( QTreeWidgetItem *item )
void QgsIdentifyResults::featureForm()
{
QgsVectorLayer *layer = vectorLayer( item );
if ( !layer )
QTreeWidgetItem *item = lstResults->currentItem();
QgsVectorLayer *vlayer = vectorLayer( item );
if ( !vlayer )
return;
QTreeWidgetItem *featItem = featureItem( item );
@ -779,13 +715,35 @@ void QgsIdentifyResults::featureForm( QTreeWidgetItem *item )
int fid = featItem->data( 0, Qt::UserRole ).toInt();
QgsFeature f;
if ( ! layer->featureAtId( fid, f ) )
if ( !vlayer->featureAtId( fid, f ) )
return;
QgsAttributeMap src = f.attributeMap();
layer->beginEditCommand( tr( "Attribute changed" ) );
QgsAttributeDialog *ad = new QgsAttributeDialog( layer, &f );
vlayer->beginEditCommand( tr( "Attribute changed" ) );
QgsAttributeDialog *ad = new QgsAttributeDialog( vlayer, &f );
if ( !vlayer->isEditable() && vlayer->actions()->size() > 0 )
{
ad->dialog()->setContextMenuPolicy( Qt::ActionsContextMenu );
QAction *a = new QAction( tr( "Run actions" ), ad->dialog() );
a->setEnabled( false );
ad->dialog()->addAction( a );
for ( int i = 0; i < vlayer->actions()->size(); i++ )
{
const QgsAction &action = vlayer->actions()->at( i );
if ( !action.runable() )
continue;
QgsFeatureAction *a = new QgsFeatureAction( action.name(), this, vlayer, i, featItem );
ad->dialog()->addAction( a );
connect( a, SIGNAL( triggered() ), a, SLOT( execute() ) );
}
}
if ( ad->exec() )
{
const QgsAttributeMap &dst = f.attributeMap();
@ -793,16 +751,51 @@ void QgsIdentifyResults::featureForm( QTreeWidgetItem *item )
{
if ( !src.contains( it.key() ) || it.value() != src[it.key()] )
{
layer->changeAttributeValue( f.id(), it.key(), it.value() );
vlayer->changeAttributeValue( f.id(), it.key(), it.value() );
}
}
layer->endEditCommand();
vlayer->endEditCommand();
}
else
{
layer->destroyEditCommand();
vlayer->destroyEditCommand();
}
delete ad;
mCanvas->refresh();
}
void QgsIdentifyResults::expandAll()
{
lstResults->expandAll();
}
void QgsIdentifyResults::collapseAll()
{
lstResults->collapseAll();
}
void QgsIdentifyResults::copyAttributeValue()
{
QClipboard *clipboard = QApplication::clipboard();
QString text = lstResults->currentItem()->data( 1, Qt::DisplayRole ).toString();
QgsDebugMsg( QString( "set clipboard: %1" ).arg( text ) );
clipboard->setText( text );
}
void QgsIdentifyResults::copyFeatureAttributes()
{
QClipboard *clipboard = QApplication::clipboard();
QString text;
QList< QPair<QString, QString> > attributes;
retrieveAttributes( lstResults->currentItem(), attributes );
for ( QList< QPair<QString, QString> >::iterator it = attributes.begin(); it != attributes.end(); it++ )
{
text += QString( "%1: %2\n" ).arg( it->first ).arg( it->second );
}
QgsDebugMsg( QString( "set clipboard: %1" ).arg( text ) );
clipboard->setText( text );
}

View File

@ -21,9 +21,9 @@
#include "ui_qgsidentifyresultsbase.h"
#include "qgsattributeaction.h"
#include <QWidget>
#include <vector>
#include <map>
#include <QList>
class QCloseEvent;
class QTreeWidgetItem;
@ -77,12 +77,18 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase
void close();
void contextMenuEvent( QContextMenuEvent* );
void popupItemSelected( QAction* menuAction );
void layerDestroyed();
void editingToggled();
void featureDeleted( int fid );
void featureForm();
void zoomToFeature();
void copyAttributeValue();
void copyFeatureAttributes();
void expandAll();
void collapseAll();
//! Context help
void helpClicked();
@ -96,6 +102,8 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase
/* Item in tree was clicked */
void itemClicked( QTreeWidgetItem *lvi, int column );
QTreeWidgetItem *retrieveAttributes( QTreeWidgetItem *item, QList< QPair<QString, QString> > &attributes );
private:
QMenu *mActionPopup;
QgsVectorLayer *mRubberBandLayer;
@ -108,7 +116,7 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase
QgsVectorLayer *vectorLayer( QTreeWidgetItem *item );
QTreeWidgetItem *featureItem( QTreeWidgetItem *item );
QTreeWidgetItem *layerItem( QObject *layer );
QTreeWidgetItem *retrieveAttributes( QTreeWidgetItem *item, std::vector< std::pair<QString, QString> > &attributes );
void clearRubberBand();
void disconnectLayer( QObject *object );
@ -118,12 +126,29 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase
void restorePosition();
void highlightFeature( QTreeWidgetItem *item );
void zoomToFeature( QTreeWidgetItem *item );
void featureForm( QTreeWidgetItem *item );
void doAction( QTreeWidgetItem *item, int action );
QDockWidget *mDock;
};
class QgsFeatureAction : public QAction
{
Q_OBJECT
public:
QgsFeatureAction( const QString &name, QgsIdentifyResults *results, QgsVectorLayer *vl, int action, QTreeWidgetItem *featItem ) :
QAction( name, results ), mResults( results ), mLayer( vl ), mAction( action ), mFeatItem( featItem )
{}
public slots:
void execute();
private:
QgsIdentifyResults *mResults;
QgsVectorLayer *mLayer;
int mAction;
QTreeWidgetItem *mFeatItem;
};
#endif

View File

@ -23,7 +23,7 @@
***************************************************************************/
/* $Id$ */
#include <vector>
#include <QList>
#include <QStringList>
#include <QDomElement>
@ -33,16 +33,21 @@
static const char * const ident_ = "$Id$";
void QgsAttributeAction::addAction( QString name, QString action,
bool capture )
void QgsAttributeAction::addAction( QgsAction::ActionType type, QString name, QString action, bool capture )
{
mActions.push_back( QgsAction( name, action, capture ) );
*this << QgsAction( type, name, action, capture );
}
void QgsAttributeAction::doAction( unsigned int index, const std::vector< std::pair<QString, QString> > &values,
uint defaultValueIndex )
void QgsAttributeAction::doAction( int index, const QList< QPair<QString, QString> > &values,
int defaultValueIndex, void ( *executePython )( const QString & ) )
{
aIter action = retrieveAction( index );
if ( index < 0 || index >= size() )
return;
const QgsAction &action = at( index );
if ( !action.runable() )
return;
// A couple of extra options for running the action may be
// useful. For example,
@ -54,31 +59,23 @@ void QgsAttributeAction::doAction( unsigned int index, const std::vector< std::p
// the UI and the code in this function to select on the
// action.capture() return value.
if ( action != end() )
// The QgsRunProcess instance created by this static function
// deletes itself when no longer needed.
QString expandedAction = expandAction( action.action(), values, defaultValueIndex );
if ( action.type() == QgsAction::GenericPython )
{
// The QgsRunProcess instance created by this static function
// deletes itself when no longer needed.
QString expandedAction = expandAction( action->action(), values, defaultValueIndex );
QgsRunProcess::create( expandedAction, action->capture() );
if ( executePython )
{
executePython( expandedAction );
}
}
else
{
QgsRunProcess::create( expandedAction, action.capture() );
}
}
QgsAttributeAction::aIter QgsAttributeAction::retrieveAction( unsigned int index ) const
{
// This function returns an iterator so that it's easy to deal with
// an invalid index being given.
aIter a_iter = end();
if ( index >= 0 && index < mActions.size() )
{
a_iter = mActions.begin();
for ( unsigned int i = 0; i < index; ++i, ++a_iter )
{}
}
return a_iter;
}
QString QgsAttributeAction::expandAction( QString action, const std::vector< std::pair<QString, QString> > &values,
QString QgsAttributeAction::expandAction( QString action, const QList< QPair<QString, QString> > &values,
uint clickedOnValue )
{
// This function currently replaces all %% characters in the action
@ -102,7 +99,7 @@ QString QgsAttributeAction::expandAction( QString action, const std::vector< std
else
expanded_action = action;
for ( unsigned int i = 0; i < values.size(); ++i )
for ( int i = 0; i < values.size(); ++i )
{
// Check for a replace a quoted version and a non-quoted version.
QString to_replace_1 = "[%" + values[i].first + "]";
@ -119,14 +116,13 @@ bool QgsAttributeAction::writeXML( QDomNode& layer_node, QDomDocument& doc ) con
{
QDomElement aActions = doc.createElement( "attributeactions" );
aIter a_iter = begin();
for ( ; a_iter != end(); ++a_iter )
for ( int i = 0; i < size(); i++ )
{
QDomElement actionSetting = doc.createElement( "actionsetting" );
actionSetting.setAttribute( "name", a_iter->name() );
actionSetting.setAttribute( "action", a_iter->action() );
actionSetting.setAttribute( "capture", a_iter->capture() );
actionSetting.setAttribute( "type", at( i ).type() );
actionSetting.setAttribute( "name", at( i ).name() );
actionSetting.setAttribute( "action", at( i ).action() );
actionSetting.setAttribute( "capture", at( i ).capture() );
aActions.appendChild( actionSetting );
}
layer_node.appendChild( aActions );
@ -136,7 +132,7 @@ bool QgsAttributeAction::writeXML( QDomNode& layer_node, QDomDocument& doc ) con
bool QgsAttributeAction::readXML( const QDomNode& layer_node )
{
mActions.clear();
clear();
QDomNode aaNode = layer_node.namedItem( "attributeactions" );
@ -146,10 +142,10 @@ bool QgsAttributeAction::readXML( const QDomNode& layer_node )
for ( unsigned int i = 0; i < actionsettings.length(); ++i )
{
QDomElement setting = actionsettings.item( i ).toElement();
int capture = setting.attributeNode( "capture" ).value().toInt();
addAction( setting.attributeNode( "name" ).value(),
setting.attributeNode( "action" ).value(),
capture == 0 ? false : true );
addAction(( QgsAction::ActionType ) setting.attributeNode( "type" ).value().toInt(),
setting.attributeNode( "name" ).value(),
setting.attributeNode( "action" ).value(),
setting.attributeNode( "capture" ).value().toInt() != 0 );
}
}
return true;

View File

@ -27,23 +27,29 @@
#include <QString>
#include <QObject>
#include <list>
#include <vector>
#include <utility>
#include <QList>
#include <QPair>
class QDomNode;
class QDomDocument;
/** \ingroup core
* Utility class that encapsulates an action based on vector attributes.
*/
class CORE_EXPORT QgsAction
{
public:
QgsAction( QString name, QString action, bool capture ) :
mName( name ), mAction( action ), mCaptureOutput( capture ) {}
enum ActionType
{
Generic,
GenericPython,
Mac,
Windows,
Unix,
};
QgsAction( ActionType type, QString name, QString action, bool capture ) :
mType( type ), mName( name ), mAction( action ), mCaptureOutput( capture ) {}
//! The name of the action
QString name() const { return mName; }
@ -51,10 +57,29 @@ class CORE_EXPORT QgsAction
//! The action
QString action() const { return mAction; }
//! The action type
ActionType type() const { return mType; }
//! Whether to capture output for display when this action is run
bool capture() const { return mCaptureOutput; }
//!
bool runable() const
{
return mType == Generic ||
mType == GenericPython ||
#if defined(Q_OS_WIN)
mType == Windows
#elif defined(Q_OS_MAC)
mType == Mac
#else
mType == Unix
#endif
;
}
private:
ActionType mType;
QString mName;
QString mAction;
bool mCaptureOutput;
@ -65,13 +90,9 @@ class CORE_EXPORT QgsAction
* attributes.
*/
class CORE_EXPORT QgsAttributeAction
class CORE_EXPORT QgsAttributeAction : public QList<QgsAction>
{
public:
typedef std::list<QgsAction> AttributeActions;
typedef AttributeActions::const_iterator aIter;
//! Constructor
QgsAttributeAction() {};
@ -83,38 +104,20 @@ class CORE_EXPORT QgsAttributeAction
// capture is true, when running the action using doAction(),
// any stdout from the process will be captured and displayed in a
// dialog box.
void addAction( QString name, QString action, bool capture = false );
void addAction( QgsAction::ActionType type, QString name, QString action, bool capture = false );
//! Does the action using the given values. defaultValueIndex is an
// index into values which indicates which value in the values vector
// is to be used if the action has a default placeholder.
void doAction( unsigned int index, const std::vector< std::pair<QString, QString> > &values,
uint defaultValueIndex = 0 );
//! Returns a const_iterator that points to the QgsAction at the
// given position in the data collection. The insertion order is
// preserved. The index starts at 0 and goes to one less than the
// number of actions. An index outside that range will return an
// a const_iterator equal to that returned by end().
aIter retrieveAction( unsigned int index ) const;
void doAction( int index, const QList< QPair<QString, QString> > &values,
int defaultValueIndex = 0, void ( *executePython )( const QString & ) = 0 );
//! Removes all actions
void clearActions() { mActions.clear(); }
//! A const iterator to the start of the action pairs
const AttributeActions::const_iterator begin() const
{ return mActions.begin(); }
//! A const iterator to the one past the end of the action pairs
const AttributeActions::const_iterator end() const
{ return mActions.end(); }
//! Returns the number of stored actions
int size() const { return mActions.size(); }
void clearActions() { clear(); }
//! Expands the given action, replacing all %'s with the value as
// given.
static QString expandAction( QString action, const std::vector< std::pair<QString, QString> > &values,
static QString expandAction( QString action, const QList< QPair<QString, QString> > &values,
uint defaultValueIndex );
//! Writes the actions out in XML format
@ -122,11 +125,6 @@ class CORE_EXPORT QgsAttributeAction
//! Reads the actions in in XML format
bool readXML( const QDomNode& layer_node );
private:
// Stores the name/action pairs.
AttributeActions mActions;
};
#endif

View File

@ -292,7 +292,7 @@ bool QgsMapLayer::writeXML( QDomNode & layer_node, QDomDocument & document )
else if ( vlayer && vlayer->providerType() == "ogr" )
{
QStringList theURIParts = src.split( "|" );
theURIParts[0] = QgsProject::instance()->readPath( theURIParts[0] );
theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0] );
src = theURIParts.join( "|" );
}
else

View File

@ -72,6 +72,7 @@
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
#include "qgsclipper.h"
#include "qgsproject.h"
#ifdef TESTPROVIDERLIB
#include <dlfcn.h>
@ -2357,7 +2358,7 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
mRanges[ name ] = RangeData( min, max, step );
}
else if( editType == CheckBox )
else if ( editType == CheckBox )
{
mCheckedStates[ name ] = QPair<QString, QString>( editTypeElement.attribute( "checked" ), editTypeElement.attribute( "unchecked" ) );
}
@ -2368,7 +2369,7 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
if ( !editFormNode.isNull() )
{
QDomElement e = editFormNode.toElement();
mEditForm = e.text();
mEditForm = QgsProject::instance()->readPath( e.text() );
}
mAttributeAliasMap.clear();
@ -2531,7 +2532,7 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
}
QDomElement efField = doc.createElement( "editform" );
QDomText efText = doc.createTextNode( mEditForm );
QDomText efText = doc.createTextNode( QgsProject::instance()->writePath( mEditForm ) );
efField.appendChild( efText );
node.appendChild( efField );
@ -4130,14 +4131,14 @@ void QgsVectorLayer::setCheckedState( int idx, QString checked, QString unchecke
{
const QgsFieldMap &fields = pendingFields();
if ( fields.contains( idx ) )
mCheckedStates[ fields[idx].name() ] = QPair<QString, QString>( checked, unchecked );
mCheckedStates[ fields[idx].name()] = QPair<QString, QString>( checked, unchecked );
}
QPair<QString, QString> QgsVectorLayer::checkedState( int idx )
{
const QgsFieldMap &fields = pendingFields();
if ( fields.contains( idx ) && mCheckedStates.contains( fields[idx].name() ) )
return mCheckedStates[ fields[idx].name() ];
return mCheckedStates[ fields[idx].name()];
else
return QPair<QString,QString>( "1", "0" );
return QPair<QString, QString>( "1", "0" );
}

View File

@ -47,8 +47,13 @@
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>3</number>
<number>4</number>
</property>
<column>
<property name="text">
<string>New Column</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
@ -56,7 +61,7 @@
</column>
<column>
<property name="text">
<string>Action</string>
<string>Type</string>
</property>
</column>
<column>
@ -125,7 +130,7 @@
<string>Action properties</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="textLabel1">
<property name="whatsThis">
<string>Enter the name of an action here. The name should be unique (qgis will make it unique if necessary).</string>
@ -138,7 +143,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QLineEdit" name="actionName">
<property name="toolTip">
<string>Enter the action name here</string>
@ -148,7 +153,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="textLabel2">
<property name="whatsThis">
<string>Enter the action here. This can be any program, script or command that is available on your system. When the action is invoked any set of characters that start with a % and then have the name of a field will be replaced by the value of that field. The special characters %% will be replaced by the value of the field that was selected. Double quote marks group text into single arguments to the program, script or command. Double quotes will be ignored if prefixed with a backslash</string>
@ -161,7 +166,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="actionAction">
@ -191,7 +196,7 @@
</item>
</layout>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QCheckBox" name="captureCB">
<property name="toolTip">
<string>Captures any output from the action</string>
@ -204,7 +209,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="fieldComboBox">
@ -225,6 +230,45 @@
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="actionType">
<item>
<property name="text">
<string>Generic</string>
</property>
</item>
<item>
<property name="text">
<string>Python</string>
</property>
</item>
<item>
<property name="text">
<string>Windows</string>
</property>
</item>
<item>
<property name="text">
<string>Mac</string>
</property>
</item>
<item>
<property name="text">
<string>Unix</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type</string>
</property>
<property name="buddy">
<cstring>actionType</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>