Skip to content

Commit 868eeb2

Browse files
committed
Add pre-filters to relation reference widget
1 parent c5a4ea5 commit 868eeb2

File tree

3 files changed

+223
-24
lines changed

3 files changed

+223
-24
lines changed

src/gui/editorwidgets/qgsrelationreferencewidget.cpp

+201-22
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "qgsmessagebar.h"
3333
#include "qgsrelationreferenceconfigdlg.h"
3434
#include "qgsvectorlayer.h"
35+
#include "qgsattributetablemodel.h"
3536

3637
bool orderByLessThan( const QgsRelationReferenceWidget::ValueRelationItem& p1
3738
, const QgsRelationReferenceWidget::ValueRelationItem& p2 )
@@ -58,7 +59,6 @@ QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
5859
, mCanvas( NULL )
5960
, mMessageBar( NULL )
6061
, mForeignKey( QVariant() )
61-
, mFeatureId( QgsFeatureId() )
6262
, mFkeyFieldIdx( -1 )
6363
, mAllowNull( true )
6464
, mHighlight( NULL )
@@ -68,6 +68,9 @@ QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
6868
, mReferencedAttributeForm( NULL )
6969
, mReferencedLayer( NULL )
7070
, mReferencingLayer( NULL )
71+
, mMasterModel( 0 )
72+
, mFilterModel( 0 )
73+
, mFeatureListModel( 0 )
7174
, mWindowWidget( NULL )
7275
, mShown( false )
7376
, mIsEditable( true )
@@ -86,9 +89,21 @@ QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
8689
editLayout->setContentsMargins( 0, 0, 0, 0 );
8790
editLayout->setSpacing( 2 );
8891

92+
// Prepare the container and layout for the filter comboboxes
93+
mChooserGroupBox = new QGroupBox( this );
94+
editLayout->addWidget( mChooserGroupBox );
95+
QHBoxLayout* chooserLayout = new QHBoxLayout;
96+
chooserLayout->setContentsMargins( 0, 0, 0, 0 );
97+
mFilterLayout = new QHBoxLayout;
98+
mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
99+
mFilterContainer = new QWidget;
100+
mFilterContainer->setLayout( mFilterLayout );
101+
mChooserGroupBox->setLayout( chooserLayout );
102+
chooserLayout->addWidget( mFilterContainer );
103+
89104
// combobox (for non-geometric relation)
90105
mComboBox = new QComboBox( this );
91-
editLayout->addWidget( mComboBox );
106+
mChooserGroupBox->layout()->addWidget( mComboBox );
92107

93108
// read-only line edit
94109
mLineEdit = new QLineEdit( this );
@@ -211,6 +226,7 @@ void QgsRelationReferenceWidget::setRelationEditable( bool editable )
211226
if ( !editable )
212227
unsetMapTool();
213228

229+
mFilterContainer->setEnabled( editable );
214230
mComboBox->setEnabled( editable );
215231
mMapIdentificationButton->setEnabled( editable );
216232
mRemoveFKButton->setEnabled( editable );
@@ -225,38 +241,36 @@ void QgsRelationReferenceWidget::setForeignKey( const QVariant& value )
225241
return;
226242
}
227243

228-
QgsFeature f;
229244
if ( !mReferencedLayer )
230245
return;
231246

232247
// TODO: Rewrite using expression
233248
QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest() );
234-
while ( fit.nextFeature( f ) )
249+
while ( fit.nextFeature( mFeature ) )
235250
{
236-
if ( f.attribute( mFkeyFieldIdx ) == value )
251+
if ( mFeature.attribute( mFkeyFieldIdx ) == value )
237252
{
238253
break;
239254
}
240255
}
241256

242-
if ( !f.isValid() )
257+
if ( !mFeature.isValid() )
243258
{
244259
deleteForeignKey();
245260
return;
246261
}
247262

248-
mForeignKey = f.attribute( mFkeyFieldIdx );
263+
mForeignKey = mFeature.attribute( mFkeyFieldIdx );
249264

250265
if ( mReadOnlySelector )
251266
{
252267
QgsExpression expr( mReferencedLayer->displayExpression() );
253-
QString title = expr.evaluate( &f ).toString();
268+
QString title = expr.evaluate( &mFeature ).toString();
254269
if ( expr.hasEvalError() )
255270
{
256-
title = f.attribute( mFkeyFieldIdx ).toString();
271+
title = mFeature.attribute( mFkeyFieldIdx ).toString();
257272
}
258273
mLineEdit->setText( title );
259-
mFeatureId = f.id();
260274
}
261275
else
262276
{
@@ -272,8 +286,8 @@ void QgsRelationReferenceWidget::setForeignKey( const QVariant& value )
272286
}
273287

274288
mRemoveFKButton->setEnabled( mIsEditable );
275-
highlightFeature( f );
276-
updateAttributeEditorFrame( f );
289+
highlightFeature( mFeature );
290+
updateAttributeEditorFrame( mFeature );
277291
emit foreignKeyChanged( foreignKey() );
278292
}
279293

@@ -289,7 +303,7 @@ void QgsRelationReferenceWidget::deleteForeignKey()
289303
}
290304
mLineEdit->setText( nullText );
291305
mForeignKey = QVariant();
292-
mFeatureId = QgsFeatureId();
306+
mFeature.setValid( false );
293307
}
294308
else
295309
{
@@ -315,7 +329,7 @@ QgsFeature QgsRelationReferenceWidget::referencedFeature()
315329
QgsFeatureId fid;
316330
if ( mReadOnlySelector )
317331
{
318-
fid = mFeatureId;
332+
fid = mFeature.id();
319333
}
320334
else
321335
{
@@ -366,7 +380,7 @@ void QgsRelationReferenceWidget::setEmbedForm( bool display )
366380

367381
void QgsRelationReferenceWidget::setReadOnlySelector( bool readOnly )
368382
{
369-
mComboBox->setHidden( readOnly );
383+
mChooserGroupBox->setHidden( readOnly );
370384
mLineEdit->setVisible( readOnly );
371385
mRemoveFKButton->setVisible( mAllowNull && readOnly );
372386
mReadOnlySelector = readOnly;
@@ -384,6 +398,11 @@ void QgsRelationReferenceWidget::setOrderByValue( bool orderByValue )
384398
mOrderByValue = orderByValue;
385399
}
386400

401+
void QgsRelationReferenceWidget::setFilterFields( QStringList filterFields )
402+
{
403+
mFilterFields = filterFields;
404+
}
405+
387406
void QgsRelationReferenceWidget::setOpenFormButtonVisible( bool openFormButtonVisible )
388407
{
389408
mOpenFormButton->setVisible( openFormButtonVisible );
@@ -404,6 +423,69 @@ void QgsRelationReferenceWidget::init()
404423
if ( !mReadOnlySelector && mComboBox->count() == 0 && mReferencedLayer )
405424
{
406425
QApplication::setOverrideCursor( Qt::WaitCursor );
426+
427+
QSet<QString> requestedAttrs;
428+
429+
QgsVectorLayerCache* layerCache = new QgsVectorLayerCache( mReferencedLayer, 100000, this );
430+
431+
if ( mFilterFields.size() )
432+
{
433+
Q_FOREACH ( const QString& fieldName, mFilterFields )
434+
{
435+
QVariantList uniqueValues;
436+
int idx = mReferencedLayer->fieldNameIndex( fieldName );
437+
QComboBox* cb = new QComboBox();
438+
cb->setProperty( "Field", fieldName );
439+
mFilterComboBoxes << cb;
440+
mReferencedLayer->uniqueValues( idx, uniqueValues );
441+
cb->addItem( mReferencedLayer->attributeAlias( idx ).isEmpty() ? fieldName : mReferencedLayer->attributeAlias( idx ) );
442+
443+
Q_FOREACH ( QVariant v, uniqueValues )
444+
{
445+
cb->addItem( v.toString(), v );
446+
}
447+
448+
connect( cb, SIGNAL( currentIndexChanged( int ) ), this, SLOT( filterChanged() ) );
449+
450+
// Request this attribute for caching
451+
requestedAttrs << fieldName;
452+
453+
mFilterLayout->addWidget( cb );
454+
}
455+
456+
if ( true )
457+
{
458+
QgsFeature ft;
459+
QgsFeatureIterator fit = layerCache->getFeatures();
460+
while ( fit.nextFeature( ft ) )
461+
{
462+
for ( int i = 0; i < mFilterComboBoxes.count() - 1; ++i )
463+
{
464+
mFilterCache[mFilterFields[i]][ft.attribute(mFilterFields[i]).toString()] << ft.attribute( mFilterFields[i + 1] ).toString();
465+
}
466+
}
467+
}
468+
}
469+
470+
QgsExpression exp( mReferencedLayer->displayExpression() );
471+
472+
requestedAttrs += exp.referencedColumns().toSet();
473+
requestedAttrs << mRelation.fieldPairs().first().second;
474+
475+
QgsAttributeList attributes;
476+
Q_FOREACH ( const QString& attr, requestedAttrs )
477+
attributes << mReferencedLayer->fieldNameIndex( attr );
478+
479+
layerCache->setCacheSubsetOfAttributes( attributes );
480+
mMasterModel = new QgsAttributeTableModel( layerCache );
481+
mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->pendingFields() ) );
482+
mFilterModel = new QgsAttributeTableFilterModel( mCanvas, mMasterModel, mMasterModel );
483+
mFeatureListModel = new QgsFeatureListModel( mFilterModel, this );
484+
mFeatureListModel->setDisplayExpression( mReferencedLayer->displayExpression() );
485+
486+
mMasterModel->loadLayer();
487+
488+
mComboBox->setModel( mFeatureListModel );
407489
if ( mAllowNull )
408490
{
409491
const QString nullValue = QSettings().value( "qgis/nullValue", "NULL" ).toString();
@@ -412,12 +494,7 @@ void QgsRelationReferenceWidget::init()
412494
mComboBox->setItemData( 0, QColor( Qt::gray ), Qt::ForegroundRole );
413495
}
414496

415-
QgsExpression exp( mReferencedLayer->displayExpression() );
416-
417-
QStringList attrs = exp.referencedColumns();
418-
attrs << mRelation.fieldPairs().first().second;
419-
420-
QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( attrs, mReferencedLayer->pendingFields() ) );
497+
QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->pendingFields() ) );
421498

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

@@ -461,6 +538,14 @@ void QgsRelationReferenceWidget::init()
461538
}
462539
}
463540

541+
if ( true && mFeature.isValid() )
542+
{
543+
for ( int i = 0; i < mFilterFields.size(); i++ )
544+
{
545+
mFilterComboBoxes[i]->setCurrentIndex( mFilterComboBoxes[i]->findText( mFeature.attribute( mFilterFields[i] ).toString() ) );
546+
}
547+
}
548+
464549
// Only connect after iterating, to have only one iterator on the referenced table at once
465550
connect( mComboBox, SIGNAL( activated( int ) ), this, SLOT( comboReferenceChanged( int ) ) );
466551
QApplication::restoreOverrideCursor();
@@ -634,7 +719,7 @@ void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
634719
}
635720
mLineEdit->setText( title );
636721
mForeignKey = feature.attribute( mFkeyFieldIdx );
637-
mFeatureId = feature.id();
722+
mFeature = feature;
638723
}
639724
else
640725
{
@@ -674,3 +759,97 @@ void QgsRelationReferenceWidget::mapToolDeactivated()
674759
mMessageBarItem = NULL;
675760
}
676761

762+
void QgsRelationReferenceWidget::filterChanged()
763+
{
764+
QStringList filters;
765+
QgsAttributeList attrs;
766+
767+
QComboBox* scb = qobject_cast<QComboBox*>( sender() );
768+
769+
Q_ASSERT( scb );
770+
771+
if ( true )
772+
{
773+
QComboBox* ccb = 0;
774+
Q_FOREACH( QComboBox* cb, mFilterComboBoxes )
775+
{
776+
if ( ccb == 0 )
777+
{
778+
if ( cb != scb )
779+
continue;
780+
else
781+
{
782+
ccb = cb;
783+
continue;
784+
}
785+
}
786+
787+
if ( ccb->currentIndex() == 0 )
788+
{
789+
cb->setCurrentIndex( 0 );
790+
cb->setEnabled( false );
791+
}
792+
else
793+
{
794+
cb->blockSignals( true );
795+
cb->clear();
796+
cb->addItem( cb->property( "Field" ).toString() );
797+
798+
// ccb = scb
799+
// cb = scb + 1
800+
Q_FOREACH( const QString& txt, mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] )
801+
{
802+
cb->addItem( txt );
803+
}
804+
805+
cb->setEnabled( true );
806+
cb->blockSignals( false );
807+
808+
ccb = cb;
809+
}
810+
}
811+
}
812+
813+
Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
814+
{
815+
if ( cb->currentIndex() != 0 )
816+
{
817+
const QString fieldName = cb->property( "Field" ).toString();
818+
819+
cb->itemData( cb->currentIndex() );
820+
821+
if ( mReferencedLayer->pendingFields().field( fieldName ).type() == QVariant::String )
822+
{
823+
filters << QString( "\"%1\" = '%2'" ).arg( fieldName ).arg( cb->currentText() );
824+
}
825+
else
826+
{
827+
filters << QString( "\"%1\" = %2" ).arg( fieldName ).arg( cb->currentText() );
828+
}
829+
830+
attrs << mReferencedLayer->fieldNameIndex( fieldName );
831+
}
832+
}
833+
834+
QString filterExpression = filters.join( " AND " );
835+
836+
QgsFeatureIterator it( mMasterModel->layerCache()->getFeatures( QgsFeatureRequest().setFilterExpression( filterExpression ).setSubsetOfAttributes( attrs ) ) );
837+
838+
QgsFeature f;
839+
QgsFeatureIds featureIds;
840+
841+
while ( it.nextFeature( f ) )
842+
{
843+
featureIds << f.id();
844+
}
845+
846+
mFilterModel->setFilteredFeatures( featureIds );
847+
848+
if ( mAllowNull )
849+
{
850+
const QString nullValue = QSettings().value( "qgis/nullValue", "NULL" ).toString();
851+
852+
mComboBox->addItem( tr( "%1 (no selection)" ).arg( nullValue ), QVariant( QVariant::Int ) );
853+
mComboBox->setItemData( 0, QColor( Qt::gray ), Qt::ForegroundRole );
854+
}
855+
}

0 commit comments

Comments
 (0)