QGIS API Documentation  2.14.11-Essen
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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").
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
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
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.
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
QString & prepend(QChar ch)
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QgsField field() const
Access the field.
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
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)
const QgsAttributeEditorContext & context() const
Returns information about the context in which this widget is shown.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QPair< QVariant, QString > ValueRelationItem
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
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
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.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
int findData(const QVariant &data, int role, QFlags< Qt::MatchFlag > flags) const
QgsVectorLayer * layer() const
Access the QgsVectorLayer, you are working on.
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
QgsEditorWidgetConfig config() const
Returns the whole config.
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.
bool nextFeature(QgsFeature &f)
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)
static bool orderByKeyLessThan(const QgsValueRelationWidgetWrapper::ValueRelationItem &p1, const QgsValueRelationWidgetWrapper::ValueRelationItem &p2)
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QString toString() const
iterator end()
QVariant value() const override
Will be used to access the widget's value.
QgsValueRelationWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=nullptr, QWidget *parent=nullptr)
const T value(const Key &key) const