Skip to content

Commit 3da2826

Browse files
committed
Add unique values cpp model
1 parent ea0867d commit 3da2826

4 files changed

Lines changed: 205 additions & 1 deletion

File tree

app/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ set(MM_SRCS
5656
featurelayerpair.cpp
5757
featuresmodel.cpp
5858
fieldsmodel.cpp
59+
filter/uniquevaluesfiltermodel.cpp
5960
filtercontroller.cpp
6061
guidelinecontroller.cpp
6162
hapticsmodel.cpp
@@ -148,6 +149,7 @@ set(MM_HDRS
148149
featurelayerpair.h
149150
featuresmodel.h
150151
fieldsmodel.h
152+
filter/uniquevaluesfiltermodel.h
151153
filtercontroller.h
152154
guidelinecontroller.h
153155
hapticsmodel.h
@@ -320,6 +322,7 @@ target_include_directories(
320322
MerginMaps
321323
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/
322324
${CMAKE_CURRENT_SOURCE_DIR}/attributes
325+
${CMAKE_CURRENT_SOURCE_DIR}/filter
323326
${CMAKE_CURRENT_SOURCE_DIR}/map
324327
${CMAKE_CURRENT_SOURCE_DIR}/layer
325328
${CMAKE_CURRENT_SOURCE_DIR}/maptools

app/attributes/attributeformmodel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ bool AttributeFormModel::setData( const QModelIndex &index, const QVariant &valu
178178
case AttributeValue:
179179
{
180180
const FormItem *item = mController->formItem( uuid );
181-
//if ( mController->formValue( item->fieldIndex() ) == value )
181+
182182
if ( item->rawValue() == value )
183183
{
184184
return false;
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/***************************************************************************
2+
* *
3+
* This program is free software; you can redistribute it and/or modify *
4+
* it under the terms of the GNU General Public License as published by *
5+
* the Free Software Foundation; either version 2 of the License, or *
6+
* (at your option) any later version. *
7+
* *
8+
***************************************************************************/
9+
10+
#include "uniquevaluesfiltermodel.h"
11+
#include "coreutils.h"
12+
13+
#include <qgsproject.h>
14+
#include <qgsvectorlayer.h>
15+
#include <QtConcurrentRun>
16+
17+
18+
UniqueValuesFilterModel::UniqueValuesFilterModel( QObject *parent )
19+
: QAbstractListModel( parent )
20+
{
21+
// <TODO> remove me once this class is used! :)
22+
const QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
23+
for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
24+
{
25+
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( it.value() );
26+
if ( vectorLayer )
27+
{
28+
setLayer( vectorLayer );
29+
setFieldName( "fid" );
30+
break;
31+
}
32+
}
33+
// </TODO>
34+
35+
connect( &mResultWatcher, &QFutureWatcher<QVariantList>::finished, this, &UniqueValuesFilterModel::onLoadingFinished );
36+
}
37+
38+
int UniqueValuesFilterModel::rowCount( const QModelIndex &parent ) const
39+
{
40+
Q_UNUSED( parent )
41+
return mItems.size();
42+
}
43+
44+
QVariant UniqueValuesFilterModel::data( const QModelIndex &index, int role ) const
45+
{
46+
if ( !index.isValid() || index.row() >= mItems.size() )
47+
return {};
48+
49+
switch ( role )
50+
{
51+
case Qt::DisplayRole:
52+
return mItems.at( index.row() );
53+
default:
54+
return {};
55+
}
56+
}
57+
58+
QgsVectorLayer *UniqueValuesFilterModel::layer() const
59+
{
60+
return mLayer;
61+
}
62+
63+
void UniqueValuesFilterModel::setLayer( QgsVectorLayer *layer )
64+
{
65+
if ( mLayer == layer )
66+
return;
67+
68+
mLayer = layer;
69+
emit layerChanged();
70+
}
71+
72+
QString UniqueValuesFilterModel::fieldName() const
73+
{
74+
return mFieldName;
75+
}
76+
77+
void UniqueValuesFilterModel::setFieldName( const QString &fieldName )
78+
{
79+
if ( mFieldName == fieldName )
80+
return;
81+
82+
mFieldName = fieldName;
83+
emit fieldNameChanged();
84+
}
85+
86+
void UniqueValuesFilterModel::populate()
87+
{
88+
if ( !mLayer || mFieldName.isEmpty() )
89+
return;
90+
91+
int fieldIndex = mLayer->fields().lookupField( mFieldName );
92+
if ( fieldIndex < 0 )
93+
{
94+
CoreUtils::log( QStringLiteral( "Filtering" ), QStringLiteral( "Error, field %1 could not be found, dropdown filter won't work." ).arg( mFieldName ) );
95+
return;
96+
}
97+
98+
if ( mItems.size() > 0 ) return;
99+
100+
if ( mResultWatcher.isRunning() ) return;
101+
102+
QgsVectorLayer *layerClone = mLayer->clone();
103+
104+
mResultWatcher.setFuture( QtConcurrent::run( &UniqueValuesFilterModel::loadUniqueValues, this, layerClone, fieldIndex ) );
105+
}
106+
107+
QVariantList UniqueValuesFilterModel::loadUniqueValues( QgsVectorLayer *layer, int fieldIndex )
108+
{
109+
std::unique_ptr<QgsVectorLayer> l( layer );
110+
111+
const QSet<QVariant> uniqueValues = l->uniqueValues( fieldIndex, 1000000 );
112+
113+
QVariantList results;
114+
115+
results.reserve( uniqueValues.size() );
116+
117+
for ( const QVariant &v : uniqueValues )
118+
{
119+
results.append( v );
120+
}
121+
122+
std::sort( results.begin(), results.end(), []( const QVariant & a, const QVariant & b )
123+
{
124+
return a.toString() < b.toString();
125+
} );
126+
127+
return results;
128+
}
129+
130+
void UniqueValuesFilterModel::onLoadingFinished()
131+
{
132+
beginResetModel();
133+
134+
mItems.clear();
135+
mItems = mResultWatcher.result();
136+
137+
// TODO: do we need boolean to indicate if the model is loading?
138+
// TODO: measure how long it takes to move results from future result to mItems ~ there might be a way to avoid the copy
139+
140+
endResetModel();
141+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/***************************************************************************
2+
* *
3+
* This program is free software; you can redistribute it and/or modify *
4+
* it under the terms of the GNU General Public License as published by *
5+
* the Free Software Foundation; either version 2 of the License, or *
6+
* (at your option) any later version. *
7+
* *
8+
***************************************************************************/
9+
10+
#ifndef UNIQUEVALUESFILTERMODEL_H
11+
#define UNIQUEVALUESFILTERMODEL_H
12+
13+
#include <QAbstractListModel>
14+
#include <QFutureWatcher>
15+
#include <QtQml/qqmlregistration.h>
16+
17+
class QgsVectorLayer;
18+
19+
// This model loads unique values from the selected layer+field and exposes them via Qt::DisplayRole
20+
class UniqueValuesFilterModel : public QAbstractListModel
21+
{
22+
Q_OBJECT
23+
QML_ELEMENT
24+
25+
Q_PROPERTY( QgsVectorLayer *layer READ layer WRITE setLayer NOTIFY layerChanged )
26+
Q_PROPERTY( QString fieldName READ fieldName WRITE setFieldName NOTIFY fieldNameChanged )
27+
28+
public:
29+
explicit UniqueValuesFilterModel( QObject *parent = nullptr );
30+
~UniqueValuesFilterModel() override = default;
31+
32+
int rowCount( const QModelIndex &parent = QModelIndex() ) const override;
33+
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
34+
35+
QgsVectorLayer *layer() const;
36+
void setLayer( QgsVectorLayer *layer );
37+
38+
QString fieldName() const;
39+
void setFieldName( const QString &fieldName );
40+
41+
Q_INVOKABLE void populate();
42+
43+
signals:
44+
void layerChanged();
45+
void fieldNameChanged();
46+
47+
public slots:
48+
void onLoadingFinished();
49+
50+
private:
51+
QVariantList loadUniqueValues( QgsVectorLayer *layer, int fieldIndex );
52+
53+
QgsVectorLayer *mLayer = nullptr;
54+
QString mFieldName;
55+
56+
QVariantList mItems;
57+
QFutureWatcher<QVariantList> mResultWatcher;
58+
};
59+
60+
#endif // UNIQUEVALUESFILTERMODEL_H

0 commit comments

Comments
 (0)