QGIS API Documentation  2.14.11-Essen
qgsvaluerelationwidgetwrapper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvaluerelationwidgetwrapper.cpp
3  --------------------------------------
4  Date : 5.1.2014
5  Copyright : (C) 2014 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgis.h"
19 #include "qgsfield.h"
20 #include "qgsmaplayerregistry.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsfilterlineedit.h"
24 
25 #include <QStringListModel>
26 #include <QCompleter>
27 
30 {
31  return qgsVariantLessThan( p1.first, p2.first );
32 }
33 
36 {
37  return qgsVariantLessThan( p1.second, p2.second );
38 }
39 
41  : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
42  , mComboBox( nullptr )
43  , mListWidget( nullptr )
44  , mLineEdit( nullptr )
45  , mLayer( nullptr )
46 {
47 }
48 
49 
51 {
52  QVariant v;
53 
54  if ( mComboBox )
55  {
56  int cbxIdx = mComboBox->currentIndex();
57  if ( cbxIdx > -1 )
58  {
59  v = mComboBox->itemData( mComboBox->currentIndex() );
60  }
61  }
62 
63  if ( mListWidget )
64  {
65  QStringList selection;
66  for ( int i = 0; i < mListWidget->count(); ++i )
67  {
68  QListWidgetItem* item = mListWidget->item( i );
69  if ( item->checkState() == Qt::Checked )
70  selection << item->data( Qt::UserRole ).toString();
71  }
72 
73  v = selection.join( "," ).prepend( '{' ).append( '}' );
74  }
75 
76  if ( mLineEdit )
77  {
78  Q_FOREACH ( const ValueRelationItem& i , mCache )
79  {
80  if ( i.second == mLineEdit->text() )
81  {
82  v = i.first;
83  break;
84  }
85  }
86  }
87 
88  return v;
89 }
90 
92 {
93  if ( config( "AllowMulti" ).toBool() )
94  {
95  return new QListWidget( parent );
96  }
97  else if ( config( "UseCompleter" ).toBool() )
98  {
99  return new QgsFilterLineEdit( parent );
100  }
101  {
102  return new QComboBox( parent );
103  }
104 }
105 
107 {
108  mCache = createCache( config() );
109 
110  mComboBox = qobject_cast<QComboBox*>( editor );
111  mListWidget = qobject_cast<QListWidget*>( editor );
112  mLineEdit = qobject_cast<QLineEdit*>( editor );
113 
114  if ( mComboBox )
115  {
116  if ( config( "AllowNull" ).toBool() )
117  {
118  mComboBox->addItem( tr( "(no selection)" ), QVariant( field().type() ) );
119  }
120 
121  Q_FOREACH ( const ValueRelationItem& element, mCache )
122  {
123  mComboBox->addItem( element.second, element.first );
124  }
125 
126  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( valueChanged() ) );
127  }
128  else if ( mListWidget )
129  {
130  Q_FOREACH ( const ValueRelationItem& element, mCache )
131  {
132  QListWidgetItem *item;
133  item = new QListWidgetItem( element.second );
134  item->setData( Qt::UserRole, element.first );
135 
136  mListWidget->addItem( item );
137  }
138  connect( mListWidget, SIGNAL( itemChanged( QListWidgetItem* ) ), this, SLOT( valueChanged() ) );
139  }
140  else if ( mLineEdit )
141  {
142  QStringList values;
143  Q_FOREACH ( const ValueRelationItem& i, mCache )
144  {
145  values << i.second;
146  }
147 
148  QStringListModel* m = new QStringListModel( values, mLineEdit );
149  QCompleter* completer = new QCompleter( m, mLineEdit );
150  completer->setCaseSensitivity( Qt::CaseInsensitive );
151  mLineEdit->setCompleter( completer );
152  }
153 }
154 
156 {
157  return mListWidget || mLineEdit || mComboBox;
158 }
159 
161 {
162  if ( mListWidget )
163  {
164  QStringList checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( ',' );
165 
166  for ( int i = 0; i < mListWidget->count(); ++i )
167  {
168  QListWidgetItem* item = mListWidget->item( i );
169  if ( config( "OrderByValue" ).toBool() )
170  {
171  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
172  }
173  else
174  {
175  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
176  }
177  }
178  }
179  else if ( mComboBox )
180  {
181  mComboBox->setCurrentIndex( mComboBox->findData( value ) );
182  }
183  else if ( mLineEdit )
184  {
185  Q_FOREACH ( ValueRelationItem i, mCache )
186  {
187  if ( i.first == value )
188  {
189  mLineEdit->setText( i.second );
190  break;
191  }
192  }
193  }
194 }
195 
196 
198 {
199  ValueRelationCache cache;
200 
201  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( config.value( "Layer" ).toString() ) );
202 
203  if ( layer )
204  {
205  int ki = layer->fieldNameIndex( config.value( "Key" ).toString() );
206  int vi = layer->fieldNameIndex( config.value( "Value" ).toString() );
207 
212 
213  QgsExpression *e = nullptr;
214  if ( !config.value( "FilterExpression" ).toString().isEmpty() )
215  {
216  e = new QgsExpression( config.value( "FilterExpression" ).toString() );
217  if ( e->hasParserError() || !e->prepare( &context ) )
218  ki = -1;
219  }
220 
221  if ( ki >= 0 && vi >= 0 )
222  {
223  QSet<int> attributes;
224  attributes << ki << vi;
225 
226  QgsFeatureRequest::Flags flags = QgsFeatureRequest::NoGeometry;
227 
228  bool requiresAllAttributes = false;
229  if ( e )
230  {
231  if ( e->needsGeometry() )
233 
234  Q_FOREACH ( const QString& field, e->referencedColumns() )
235  {
236  if ( field == QgsFeatureRequest::AllAttributes )
237  {
238  requiresAllAttributes = true;
239  break;
240  }
241  int idx = layer->fieldNameIndex( field );
242  if ( idx < 0 )
243  continue;
244  attributes << idx;
245  }
246  }
247 
249  if ( !requiresAllAttributes )
250  {
251  fr.setSubsetOfAttributes( attributes.toList() );
252  }
253 
254  QgsFeatureIterator fit = layer->getFeatures( fr );
255 
256  QgsFeature f;
257  while ( fit.nextFeature( f ) )
258  {
259  context.setFeature( f );
260  if ( e && !e->evaluate( &context ).toBool() )
261  continue;
262 
263  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
264  }
265  }
266  delete e;
267  }
268 
269  if ( config.value( "OrderByValue" ).toBool() )
270  {
271  qSort( cache.begin(), cache.end(), orderByValueLessThan );
272  }
273  else
274  {
275  qSort( cache.begin(), cache.end(), orderByKeyLessThan );
276  }
277 
278  return cache;
279 }
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Wrapper for iterator of features from vector data provider or vector layer.
void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
QString & append(QChar ch)
Qt::CheckState checkState() const
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
void valueChanged()
Will call the value() method to determine the emitted value.
QgsField field() const
Access the field.
void append(const T &value)
iterator begin()
Q_DECL_DEPRECATED bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Manages an editor widget Widget and wrapper share the same parent.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QStringList referencedColumns() const
Get list of columns referenced by the expression.
QString & prepend(QChar ch)
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
void setValue(const QVariant &value) override
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QString join(const QString &separator) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
QString & remove(int position, int n)
bool valid() const override
Return true if the widget has been properly initialized.
QString tr(const char *sourceText, const char *disambiguation, int n)
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:267
QgsMapLayer * mapLayer(const QString &theLayerId)
Retrieve a pointer to a loaded layer by id.
void addItem(const QString &text, const QVariant &userData)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QPair< QVariant, QString > ValueRelationItem
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
virtual QVariant data(int role) const
Lineedit with builtin clear button.
static const QString AllAttributes
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFlags(const QgsFeatureRequest::Flags &flags)
Set flags that affect how features will be fetched.
void setCheckState(Qt::CheckState state)
QListWidgetItem * item(int row) const
QVariant itemData(int index, int role) const
const QgsAttributeEditorContext & context() const
Returns information about the context in which this widget is shown.
virtual void setData(int role, const QVariant &value)
static ValueRelationCache createCache(const QgsEditorWidgetConfig &config)
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
int findData(const QVariant &data, int role, QFlags< Qt::MatchFlag > flags) const
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
bool toBool() const
static bool orderByValueLessThan(const QgsValueRelationWidgetWrapper::ValueRelationItem &p1, const QgsValueRelationWidgetWrapper::ValueRelationItem &p2)
QList< T > toList() const
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
QgsEditorWidgetConfig config() const
Returns the whole config.
int fieldIdx() const
Access the field index.
bool nextFeature(QgsFeature &f)
QgsVectorLayer * layer() const
Access the QgsVectorLayer, you are working on.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
static bool orderByKeyLessThan(const QgsValueRelationWidgetWrapper::ValueRelationItem &p1, const QgsValueRelationWidgetWrapper::ValueRelationItem &p2)
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
QString toString() const
iterator end()
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QVariant value() const override
Will be used to access the widget&#39;s value.
QgsValueRelationWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=nullptr, QWidget *parent=nullptr)
const T value(const Key &key) const