Skip to content

Commit

Permalink
[FEATURE] allow filter expressions in value relations (and fix displa…
Browse files Browse the repository at this point in the history
…y of related values and value maps in attribute table)
  • Loading branch information
jef-n committed Feb 10, 2013
1 parent d70c0b2 commit ba2082e
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 103 deletions.
6 changes: 2 additions & 4 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -149,14 +149,12 @@ class QgsVectorLayer : QgsMapLayer
ValueRelationData();
ValueRelationData( QString layer, QString key, QString value, bool allowNull, bool orderByValue,
bool allowMulti = false,
QString filterAttributeColumn = QString::null,
QString filterAttributeValue = QString::null );
QString filterExpression = QString::null );

QString mLayer;
QString mKey;
QString mValue;
QString mFilterAttributeColumn;
QString mFilterAttributeValue;
QString mFilterExpression;
bool mAllowNull;
bool mOrderByValue;
bool mAllowMulti; /* allow selection of multiple keys @added in 1.9 */
Expand Down
80 changes: 30 additions & 50 deletions src/app/qgsattributetypedialog.cpp
Expand Up @@ -19,7 +19,10 @@
#include "qgsattributetypeloaddialog.h"
#include "qgsvectordataprovider.h"
#include "qgsmaplayerregistry.h"

#include "qgsmapcanvas.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgisapp.h"
#include "qgsproject.h"
#include "qgslogger.h"

#include <QTableWidgetItem>
Expand All @@ -42,6 +45,7 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl )
connect( loadFromLayerButton, SIGNAL( clicked() ), this, SLOT( loadFromLayerButtonPushed() ) );
connect( loadFromCSVButton, SIGNAL( clicked() ), this, SLOT( loadFromCSVButtonPushed() ) );
connect( tableWidget, SIGNAL( cellChanged( int, int ) ), this, SLOT( vCellChanged( int, int ) ) );
connect( valueRelationEditExpression, SIGNAL( clicked() ), this, SLOT( editValueRelationExpression() ) );

valueRelationLayer->clear();
foreach ( QgsMapLayer *l, QgsMapLayerRegistry::instance()->mapLayers() )
Expand All @@ -52,7 +56,6 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl )
}

connect( valueRelationLayer, SIGNAL( currentIndexChanged( int ) ), this, SLOT( updateLayerColumns( int ) ) );
connect( valueRelationFilterColumn, SIGNAL( currentIndexChanged( int ) ), this, SLOT( updateFilterColumn( int ) ) );
valueRelationLayer->setCurrentIndex( -1 );
}

Expand Down Expand Up @@ -125,6 +128,29 @@ void QgsAttributeTypeDialog::removeSelectedButtonPushed()
}
}

void QgsAttributeTypeDialog::editValueRelationExpression()
{
QString id = valueRelationLayer->itemData( valueRelationLayer->currentIndex() ).toString();

QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( id ) );
if ( !vl )
return;

QgsExpressionBuilderDialog dlg( vl, valueRelationFilterExpression->toPlainText(), this );
dlg.setWindowTitle( tr( "Edit filter expression" ) );

QgsDistanceArea myDa;
myDa.setSourceCrs( vl->crs().srsid() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
dlg.setGeomCalculator( myDa );

if ( dlg.exec() == QDialog::Accepted )
{
valueRelationFilterExpression->setText( dlg.expressionBuilder()->expressionText() );
}
}

void QgsAttributeTypeDialog::loadFromLayerButtonPushed()
{
QgsAttributeTypeLoadDialog layerDialog( mLayer );
Expand Down Expand Up @@ -436,6 +462,7 @@ void QgsAttributeTypeDialog::setIndex( int index, QgsVectorLayer::EditType editT
valueRelationAllowNull->setChecked( mValueRelationData.mAllowNull );
valueRelationOrderByValue->setChecked( mValueRelationData.mOrderByValue );
valueRelationAllowMulti->setChecked( mValueRelationData.mAllowMulti );
valueRelationFilterExpression->setText( mValueRelationData.mFilterExpression );
break;

case QgsVectorLayer::LineEdit:
Expand Down Expand Up @@ -609,16 +636,7 @@ void QgsAttributeTypeDialog::accept()
mValueRelationData.mAllowNull = valueRelationAllowNull->isChecked();
mValueRelationData.mOrderByValue = valueRelationOrderByValue->isChecked();
mValueRelationData.mAllowMulti = valueRelationAllowMulti->isChecked();
if ( valueRelationFilterColumn->currentIndex() == 0 )
{
mValueRelationData.mFilterAttributeColumn = QString::null;
mValueRelationData.mFilterAttributeValue = QString::null;
}
else
{
mValueRelationData.mFilterAttributeColumn = valueRelationFilterColumn->currentText();
mValueRelationData.mFilterAttributeValue = valueRelationFilterValue->currentText();
}
mValueRelationData.mFilterExpression = valueRelationFilterExpression->toPlainText();
break;
case 13:
mEditType = QgsVectorLayer::UuidGenerator;
Expand All @@ -644,52 +662,14 @@ void QgsAttributeTypeDialog::updateLayerColumns( int idx )
if ( !vl )
return;

valueRelationFilterColumn->addItem( tr( "No filter" ), -1 );

const QgsFields &fields = vl->pendingFields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
QString fieldName = fields[idx].name();
valueRelationKeyColumn->addItem( fieldName );
valueRelationValueColumn->addItem( fieldName );
valueRelationFilterColumn->addItem( fieldName, idx );
}

valueRelationKeyColumn->setCurrentIndex( valueRelationKeyColumn->findText( mValueRelationData.mKey ) );
valueRelationValueColumn->setCurrentIndex( valueRelationValueColumn->findText( mValueRelationData.mValue ) );

if ( mValueRelationData.mFilterAttributeColumn.isNull() )
{
valueRelationFilterColumn->setCurrentIndex( 0 );
}
else
{
valueRelationFilterColumn->setCurrentIndex( valueRelationFilterColumn->findText( mValueRelationData.mFilterAttributeColumn ) );
}
}

void QgsAttributeTypeDialog::updateFilterColumn( int idx )
{
valueRelationFilterValue->clear();
valueRelationFilterValue->setEnabled( idx > 0 );
if ( idx == 0 )
return;

QString id = valueRelationLayer->itemData( valueRelationLayer->currentIndex() ).toString();

QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( id ) );
if ( !vl )
return;

int fidx = valueRelationFilterColumn->itemData( idx ).toInt();

QList<QVariant> uniqueValues;
vl->uniqueValues( fidx, uniqueValues );

foreach ( const QVariant &v, uniqueValues )
{
valueRelationFilterValue->addItem( v.toString(), v );
}

valueRelationFilterValue->setCurrentIndex( valueRelationFilterValue->findText( mValueRelationData.mFilterAttributeValue ) );
}
4 changes: 2 additions & 2 deletions src/app/qgsattributetypedialog.h
Expand Up @@ -140,9 +140,9 @@ class QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttributeTypeDialog
void updateLayerColumns( int idx );

/**
* update filter value list
* edit the filter expression
*/
void updateFilterColumn( int idx );
void editValueRelationExpression();

private:

Expand Down
24 changes: 17 additions & 7 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -2656,9 +2656,21 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
bool allowNull = editTypeElement.attribute( "allowNull" ) == "true";
bool orderByValue = editTypeElement.attribute( "orderByValue" ) == "true";
bool allowMulti = editTypeElement.attribute( "allowMulti", "false" ) == "true";
QString filterAttributeColumn = editTypeElement.attribute( "filterAttributeColumn", QString::null );
QString filterAttributeValue = editTypeElement.attribute( "filterAttributeValue", QString::null );
mValueRelations[ name ] = ValueRelationData( id, key, value, allowNull, orderByValue, allowMulti, filterAttributeColumn, filterAttributeValue );

QString filterExpression;
if ( editTypeElement.hasAttribute( "filterAttributeColumn" ) &&
editTypeElement.hasAttribute( "filterAttributeValue" ) )
{
filterExpression = QString( "\"%1\"='%2'" )
.arg( editTypeElement.attribute( "filterAttributeColumn" ) )
.arg( editTypeElement.attribute( "filterAttributeValue" ) );
}
else
{
filterExpression = editTypeElement.attribute( "filterExpression", QString::null );
}

mValueRelations[ name ] = ValueRelationData( id, key, value, allowNull, orderByValue, allowMulti, filterExpression );
}
break;

Expand Down Expand Up @@ -2966,10 +2978,8 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
editTypeElement.setAttribute( "allowNull", data.mAllowNull ? "true" : "false" );
editTypeElement.setAttribute( "orderByValue", data.mOrderByValue ? "true" : "false" );
editTypeElement.setAttribute( "allowMulti", data.mAllowMulti ? "true" : "false" );
if ( !data.mFilterAttributeColumn.isNull() )
editTypeElement.setAttribute( "filterAttributeColumn", data.mFilterAttributeColumn );
if ( !data.mFilterAttributeValue.isNull() )
editTypeElement.setAttribute( "filterAttributeValue", data.mFilterAttributeValue );
if ( !data.mFilterExpression.isNull() )
editTypeElement.setAttribute( "filterExpression", data.mFilterExpression );
}
break;

Expand Down
9 changes: 3 additions & 6 deletions src/core/qgsvectorlayer.h
Expand Up @@ -194,13 +194,11 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
ValueRelationData() {}
ValueRelationData( QString layer, QString key, QString value, bool allowNull, bool orderByValue,
bool allowMulti = false,
QString filterAttributeColumn = QString::null,
QString filterAttributeValue = QString::null )
QString filterExpression = QString::null )
: mLayer( layer )
, mKey( key )
, mValue( value )
, mFilterAttributeColumn( filterAttributeColumn )
, mFilterAttributeValue( filterAttributeValue )
, mFilterExpression( filterExpression )
, mAllowNull( allowNull )
, mOrderByValue( orderByValue )
, mAllowMulti( allowMulti )
Expand All @@ -209,8 +207,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
QString mLayer;
QString mKey;
QString mValue;
QString mFilterAttributeColumn;
QString mFilterAttributeValue;
QString mFilterExpression;
bool mAllowNull;
bool mOrderByValue;
bool mAllowMulti; /* allow selection of multiple keys @added in 1.9 */
Expand Down
72 changes: 71 additions & 1 deletion src/gui/attributetable/qgsattributetablemodel.cpp
Expand Up @@ -22,6 +22,8 @@
#include "qgsattributeaction.h"
#include "qgsmapcanvas.h"
#include "qgsrendererv2.h"
#include "qgsmaplayerregistry.h"
#include "qgsexpression.h"

#include <QtGui>
#include <QVariant>
Expand Down Expand Up @@ -51,6 +53,18 @@ QgsAttributeTableModel::QgsAttributeTableModel( QgsMapCanvas *canvas, QgsVectorL
extentsChanged();
}

QgsAttributeTableModel::~QgsAttributeTableModel()
{
const QgsFields& fields = mLayer->pendingFields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
if ( mLayer->editType( idx ) != QgsVectorLayer::ValueRelation )
continue;

delete mValueMaps.take( idx );
}
}

bool QgsAttributeTableModel::featureAtId( QgsFeatureId fid ) const
{
QgsDebugMsgLevel( QString( "loading feature %1" ).arg( fid ), 3 );
Expand Down Expand Up @@ -215,6 +229,63 @@ void QgsAttributeTableModel::loadAttributes()
mValueMaps.insert( idx, &mLayer->valueMap( idx ) );
break;

case QgsVectorLayer::ValueRelation:
{
const QgsVectorLayer::ValueRelationData &data = mLayer->valueRelation( idx );

QgsVectorLayer *layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( data.mLayer ) );
if ( !layer )
continue;

int ki = layer->fieldNameIndex( data.mKey );
int vi = layer->fieldNameIndex( data.mValue );

QgsExpression *e = 0;
if ( !data.mFilterExpression.isEmpty() )
{
e = new QgsExpression( data.mFilterExpression );
if ( e->hasParserError() || !e->prepare( layer->pendingFields() ) )
continue;
}

if ( ki >= 0 && vi >= 0 )
{
QSet<int> attributes;
attributes << ki << vi;

QgsFeatureRequest::Flag flags = QgsFeatureRequest::NoGeometry;

if ( e )
{
if ( e->needsGeometry() )
flags = QgsFeatureRequest::NoFlags;

foreach ( const QString &field, e->referencedColumns() )
{
int idx = layer->fieldNameIndex( field );
if ( idx < 0 )
continue;
attributes << idx;
}
}

QMap< QString, QVariant > *map = new QMap< QString, QVariant >();

QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFlags( flags ).setSubsetOfAttributes( attributes.toList() ) );
QgsFeature f;
while ( fit.nextFeature( f ) )
{
if ( e && !e->evaluate( &f ).toBool() )
continue;

map->insert( f.attribute( vi ).toString(), f.attribute( ki ) );
}

mValueMaps.insert( idx, map );
}
}
break;

default:
break;
}
Expand All @@ -235,7 +306,6 @@ void QgsAttributeTableModel::loadAttributes()

mFieldCount = attributes.size();
mAttributes = attributes;
mValueMaps.clear();

if ( ins )
{
Expand Down
3 changes: 3 additions & 0 deletions src/gui/attributetable/qgsattributetablemodel.h
Expand Up @@ -41,6 +41,9 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
* @param parent parent pointer
*/
QgsAttributeTableModel( QgsMapCanvas *canvas, QgsVectorLayer *theLayer, QObject *parent = 0 );

~QgsAttributeTableModel();

/**
* Returns the number of rows
* @param parent parent index
Expand Down

0 comments on commit ba2082e

Please sign in to comment.