Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add pre-filters to relation reference widget
  • Loading branch information
m-kuhn committed Mar 26, 2015
1 parent c5a4ea5 commit 868eeb2
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 24 deletions.
223 changes: 201 additions & 22 deletions src/gui/editorwidgets/qgsrelationreferencewidget.cpp
Expand Up @@ -32,6 +32,7 @@
#include "qgsmessagebar.h"
#include "qgsrelationreferenceconfigdlg.h"
#include "qgsvectorlayer.h"
#include "qgsattributetablemodel.h"

bool orderByLessThan( const QgsRelationReferenceWidget::ValueRelationItem& p1
, const QgsRelationReferenceWidget::ValueRelationItem& p2 )
Expand All @@ -58,7 +59,6 @@ QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
, mCanvas( NULL )
, mMessageBar( NULL )
, mForeignKey( QVariant() )
, mFeatureId( QgsFeatureId() )
, mFkeyFieldIdx( -1 )
, mAllowNull( true )
, mHighlight( NULL )
Expand All @@ -68,6 +68,9 @@ QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
, mReferencedAttributeForm( NULL )
, mReferencedLayer( NULL )
, mReferencingLayer( NULL )
, mMasterModel( 0 )
, mFilterModel( 0 )
, mFeatureListModel( 0 )
, mWindowWidget( NULL )
, mShown( false )
, mIsEditable( true )
Expand All @@ -86,9 +89,21 @@ QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
editLayout->setContentsMargins( 0, 0, 0, 0 );
editLayout->setSpacing( 2 );

// Prepare the container and layout for the filter comboboxes
mChooserGroupBox = new QGroupBox( this );
editLayout->addWidget( mChooserGroupBox );
QHBoxLayout* chooserLayout = new QHBoxLayout;
chooserLayout->setContentsMargins( 0, 0, 0, 0 );
mFilterLayout = new QHBoxLayout;
mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
mFilterContainer = new QWidget;
mFilterContainer->setLayout( mFilterLayout );
mChooserGroupBox->setLayout( chooserLayout );
chooserLayout->addWidget( mFilterContainer );

// combobox (for non-geometric relation)
mComboBox = new QComboBox( this );
editLayout->addWidget( mComboBox );
mChooserGroupBox->layout()->addWidget( mComboBox );

// read-only line edit
mLineEdit = new QLineEdit( this );
Expand Down Expand Up @@ -211,6 +226,7 @@ void QgsRelationReferenceWidget::setRelationEditable( bool editable )
if ( !editable )
unsetMapTool();

mFilterContainer->setEnabled( editable );
mComboBox->setEnabled( editable );
mMapIdentificationButton->setEnabled( editable );
mRemoveFKButton->setEnabled( editable );
Expand All @@ -225,38 +241,36 @@ void QgsRelationReferenceWidget::setForeignKey( const QVariant& value )
return;
}

QgsFeature f;
if ( !mReferencedLayer )
return;

// TODO: Rewrite using expression
QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest() );
while ( fit.nextFeature( f ) )
while ( fit.nextFeature( mFeature ) )
{
if ( f.attribute( mFkeyFieldIdx ) == value )
if ( mFeature.attribute( mFkeyFieldIdx ) == value )
{
break;
}
}

if ( !f.isValid() )
if ( !mFeature.isValid() )
{
deleteForeignKey();
return;
}

mForeignKey = f.attribute( mFkeyFieldIdx );
mForeignKey = mFeature.attribute( mFkeyFieldIdx );

if ( mReadOnlySelector )
{
QgsExpression expr( mReferencedLayer->displayExpression() );
QString title = expr.evaluate( &f ).toString();
QString title = expr.evaluate( &mFeature ).toString();
if ( expr.hasEvalError() )
{
title = f.attribute( mFkeyFieldIdx ).toString();
title = mFeature.attribute( mFkeyFieldIdx ).toString();
}
mLineEdit->setText( title );
mFeatureId = f.id();
}
else
{
Expand All @@ -272,8 +286,8 @@ void QgsRelationReferenceWidget::setForeignKey( const QVariant& value )
}

mRemoveFKButton->setEnabled( mIsEditable );
highlightFeature( f );
updateAttributeEditorFrame( f );
highlightFeature( mFeature );
updateAttributeEditorFrame( mFeature );
emit foreignKeyChanged( foreignKey() );
}

Expand All @@ -289,7 +303,7 @@ void QgsRelationReferenceWidget::deleteForeignKey()
}
mLineEdit->setText( nullText );
mForeignKey = QVariant();
mFeatureId = QgsFeatureId();
mFeature.setValid( false );
}
else
{
Expand All @@ -315,7 +329,7 @@ QgsFeature QgsRelationReferenceWidget::referencedFeature()
QgsFeatureId fid;
if ( mReadOnlySelector )
{
fid = mFeatureId;
fid = mFeature.id();
}
else
{
Expand Down Expand Up @@ -366,7 +380,7 @@ void QgsRelationReferenceWidget::setEmbedForm( bool display )

void QgsRelationReferenceWidget::setReadOnlySelector( bool readOnly )
{
mComboBox->setHidden( readOnly );
mChooserGroupBox->setHidden( readOnly );
mLineEdit->setVisible( readOnly );
mRemoveFKButton->setVisible( mAllowNull && readOnly );
mReadOnlySelector = readOnly;
Expand All @@ -384,6 +398,11 @@ void QgsRelationReferenceWidget::setOrderByValue( bool orderByValue )
mOrderByValue = orderByValue;
}

void QgsRelationReferenceWidget::setFilterFields( QStringList filterFields )
{
mFilterFields = filterFields;
}

void QgsRelationReferenceWidget::setOpenFormButtonVisible( bool openFormButtonVisible )
{
mOpenFormButton->setVisible( openFormButtonVisible );
Expand All @@ -404,6 +423,69 @@ void QgsRelationReferenceWidget::init()
if ( !mReadOnlySelector && mComboBox->count() == 0 && mReferencedLayer )
{
QApplication::setOverrideCursor( Qt::WaitCursor );

QSet<QString> requestedAttrs;

QgsVectorLayerCache* layerCache = new QgsVectorLayerCache( mReferencedLayer, 100000, this );

if ( mFilterFields.size() )
{
Q_FOREACH ( const QString& fieldName, mFilterFields )
{
QVariantList uniqueValues;
int idx = mReferencedLayer->fieldNameIndex( fieldName );
QComboBox* cb = new QComboBox();
cb->setProperty( "Field", fieldName );
mFilterComboBoxes << cb;
mReferencedLayer->uniqueValues( idx, uniqueValues );
cb->addItem( mReferencedLayer->attributeAlias( idx ).isEmpty() ? fieldName : mReferencedLayer->attributeAlias( idx ) );

Q_FOREACH ( QVariant v, uniqueValues )
{
cb->addItem( v.toString(), v );
}

connect( cb, SIGNAL( currentIndexChanged( int ) ), this, SLOT( filterChanged() ) );

// Request this attribute for caching
requestedAttrs << fieldName;

mFilterLayout->addWidget( cb );
}

if ( true )
{
QgsFeature ft;
QgsFeatureIterator fit = layerCache->getFeatures();
while ( fit.nextFeature( ft ) )
{
for ( int i = 0; i < mFilterComboBoxes.count() - 1; ++i )
{
mFilterCache[mFilterFields[i]][ft.attribute(mFilterFields[i]).toString()] << ft.attribute( mFilterFields[i + 1] ).toString();
}
}
}
}

QgsExpression exp( mReferencedLayer->displayExpression() );

requestedAttrs += exp.referencedColumns().toSet();
requestedAttrs << mRelation.fieldPairs().first().second;

QgsAttributeList attributes;
Q_FOREACH ( const QString& attr, requestedAttrs )
attributes << mReferencedLayer->fieldNameIndex( attr );

layerCache->setCacheSubsetOfAttributes( attributes );
mMasterModel = new QgsAttributeTableModel( layerCache );
mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->pendingFields() ) );
mFilterModel = new QgsAttributeTableFilterModel( mCanvas, mMasterModel, mMasterModel );
mFeatureListModel = new QgsFeatureListModel( mFilterModel, this );
mFeatureListModel->setDisplayExpression( mReferencedLayer->displayExpression() );

mMasterModel->loadLayer();

mComboBox->setModel( mFeatureListModel );
if ( mAllowNull )
{
const QString nullValue = QSettings().value( "qgis/nullValue", "NULL" ).toString();
Expand All @@ -412,12 +494,7 @@ void QgsRelationReferenceWidget::init()
mComboBox->setItemData( 0, QColor( Qt::gray ), Qt::ForegroundRole );
}

QgsExpression exp( mReferencedLayer->displayExpression() );

QStringList attrs = exp.referencedColumns();
attrs << mRelation.fieldPairs().first().second;

QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( attrs, mReferencedLayer->pendingFields() ) );
QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->pendingFields() ) );

exp.prepare( mReferencedLayer->pendingFields() );

Expand Down Expand Up @@ -461,6 +538,14 @@ void QgsRelationReferenceWidget::init()
}
}

if ( true && mFeature.isValid() )
{
for ( int i = 0; i < mFilterFields.size(); i++ )
{
mFilterComboBoxes[i]->setCurrentIndex( mFilterComboBoxes[i]->findText( mFeature.attribute( mFilterFields[i] ).toString() ) );
}
}

// Only connect after iterating, to have only one iterator on the referenced table at once
connect( mComboBox, SIGNAL( activated( int ) ), this, SLOT( comboReferenceChanged( int ) ) );
QApplication::restoreOverrideCursor();
Expand Down Expand Up @@ -634,7 +719,7 @@ void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
}
mLineEdit->setText( title );
mForeignKey = feature.attribute( mFkeyFieldIdx );
mFeatureId = feature.id();
mFeature = feature;
}
else
{
Expand Down Expand Up @@ -674,3 +759,97 @@ void QgsRelationReferenceWidget::mapToolDeactivated()
mMessageBarItem = NULL;
}

void QgsRelationReferenceWidget::filterChanged()
{
QStringList filters;
QgsAttributeList attrs;

QComboBox* scb = qobject_cast<QComboBox*>( sender() );

Q_ASSERT( scb );

if ( true )
{
QComboBox* ccb = 0;
Q_FOREACH( QComboBox* cb, mFilterComboBoxes )
{
if ( ccb == 0 )
{
if ( cb != scb )
continue;
else
{
ccb = cb;
continue;
}
}

if ( ccb->currentIndex() == 0 )
{
cb->setCurrentIndex( 0 );
cb->setEnabled( false );
}
else
{
cb->blockSignals( true );
cb->clear();
cb->addItem( cb->property( "Field" ).toString() );

// ccb = scb
// cb = scb + 1
Q_FOREACH( const QString& txt, mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] )
{
cb->addItem( txt );
}

cb->setEnabled( true );
cb->blockSignals( false );

ccb = cb;
}
}
}

Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
{
if ( cb->currentIndex() != 0 )
{
const QString fieldName = cb->property( "Field" ).toString();

cb->itemData( cb->currentIndex() );

if ( mReferencedLayer->pendingFields().field( fieldName ).type() == QVariant::String )
{
filters << QString( "\"%1\" = '%2'" ).arg( fieldName ).arg( cb->currentText() );
}
else
{
filters << QString( "\"%1\" = %2" ).arg( fieldName ).arg( cb->currentText() );
}

attrs << mReferencedLayer->fieldNameIndex( fieldName );
}
}

QString filterExpression = filters.join( " AND " );

QgsFeatureIterator it( mMasterModel->layerCache()->getFeatures( QgsFeatureRequest().setFilterExpression( filterExpression ).setSubsetOfAttributes( attrs ) ) );

QgsFeature f;
QgsFeatureIds featureIds;

while ( it.nextFeature( f ) )
{
featureIds << f.id();
}

mFilterModel->setFilteredFeatures( featureIds );

if ( mAllowNull )
{
const QString nullValue = QSettings().value( "qgis/nullValue", "NULL" ).toString();

mComboBox->addItem( tr( "%1 (no selection)" ).arg( nullValue ), QVariant( QVariant::Int ) );
mComboBox->setItemData( 0, QColor( Qt::gray ), Qt::ForegroundRole );
}
}

0 comments on commit 868eeb2

Please sign in to comment.