QGIS API Documentation  2.14.0-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 "qgsfield.h"
19 #include "qgsmaplayerregistry.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsfilterlineedit.h"
23 
24 #include <QStringListModel>
25 #include <QCompleter>
26 
29 {
30  switch ( p1.first.type() )
31  {
32  case QVariant::String:
33  return p1.first.toString() < p2.first.toString();
34 
35  case QVariant::Double:
36  return p1.first.toDouble() < p2.first.toDouble();
37 
38  default:
39  return p1.first.toInt() < p2.first.toInt();
40  }
41 }
42 
45 {
46  return p1.second < p2.second;
47 }
48 
50  : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
51  , mComboBox( nullptr )
52  , mListWidget( nullptr )
53  , mLineEdit( nullptr )
54  , mLayer( nullptr )
55 {
56 }
57 
58 
60 {
61  QVariant v;
62 
63  if ( mComboBox )
64  {
65  int cbxIdx = mComboBox->currentIndex();
66  if ( cbxIdx > -1 )
67  {
68  v = mComboBox->itemData( mComboBox->currentIndex() );
69  }
70  }
71 
72  if ( mListWidget )
73  {
74  QStringList selection;
75  for ( int i = 0; i < mListWidget->count(); ++i )
76  {
77  QListWidgetItem* item = mListWidget->item( i );
78  if ( item->checkState() == Qt::Checked )
79  selection << item->data( Qt::UserRole ).toString();
80  }
81 
82  v = selection.join( "," ).prepend( '{' ).append( '}' );
83  }
84 
85  if ( mLineEdit )
86  {
87  Q_FOREACH ( const ValueRelationItem& i , mCache )
88  {
89  if ( i.second == mLineEdit->text() )
90  {
91  v = i.first;
92  break;
93  }
94  }
95  }
96 
97  return v;
98 }
99 
101 {
102  if ( config( "AllowMulti" ).toBool() )
103  {
104  return new QListWidget( parent );
105  }
106  else if ( config( "UseCompleter" ).toBool() )
107  {
108  return new QgsFilterLineEdit( parent );
109  }
110  {
111  return new QComboBox( parent );
112  }
113 }
114 
116 {
117  mCache = createCache( config() );
118 
119  mComboBox = qobject_cast<QComboBox*>( editor );
120  mListWidget = qobject_cast<QListWidget*>( editor );
121  mLineEdit = qobject_cast<QLineEdit*>( editor );
122 
123  if ( mComboBox )
124  {
125  if ( config( "AllowNull" ).toBool() )
126  {
127  mComboBox->addItem( tr( "(no selection)" ), QVariant( field().type() ) );
128  }
129 
130  Q_FOREACH ( const ValueRelationItem& element, mCache )
131  {
132  mComboBox->addItem( element.second, element.first );
133  }
134 
135  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( valueChanged() ) );
136  }
137  else if ( mListWidget )
138  {
139  Q_FOREACH ( const ValueRelationItem& element, mCache )
140  {
141  QListWidgetItem *item;
142  item = new QListWidgetItem( element.second );
143  item->setData( Qt::UserRole, element.first );
144 
145  mListWidget->addItem( item );
146  }
147  connect( mListWidget, SIGNAL( itemChanged( QListWidgetItem* ) ), this, SLOT( valueChanged() ) );
148  }
149  else if ( mLineEdit )
150  {
151  QStringList values;
152  Q_FOREACH ( const ValueRelationItem& i, mCache )
153  {
154  values << i.second;
155  }
156 
157  QStringListModel* m = new QStringListModel( values, mLineEdit );
158  QCompleter* completer = new QCompleter( m, mLineEdit );
159  completer->setCaseSensitivity( Qt::CaseInsensitive );
160  mLineEdit->setCompleter( completer );
161  }
162 }
163 
165 {
166  return mListWidget || mLineEdit || mComboBox;
167 }
168 
170 {
171  if ( mListWidget )
172  {
173  QStringList checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( ',' );
174 
175  for ( int i = 0; i < mListWidget->count(); ++i )
176  {
177  QListWidgetItem* item = mListWidget->item( i );
178  if ( config( "OrderByValue" ).toBool() )
179  {
180  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
181  }
182  else
183  {
184  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
185  }
186  }
187  }
188  else if ( mComboBox )
189  {
190  mComboBox->setCurrentIndex( mComboBox->findData( value ) );
191  }
192  else if ( mLineEdit )
193  {
194  Q_FOREACH ( ValueRelationItem i, mCache )
195  {
196  if ( i.first == value )
197  {
198  mLineEdit->setText( i.second );
199  break;
200  }
201  }
202  }
203 }
204 
205 
207 {
208  ValueRelationCache cache;
209 
210  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( config.value( "Layer" ).toString() ) );
211 
212  if ( layer )
213  {
214  int ki = layer->fieldNameIndex( config.value( "Key" ).toString() );
215  int vi = layer->fieldNameIndex( config.value( "Value" ).toString() );
216 
221 
222  QgsExpression *e = nullptr;
223  if ( !config.value( "FilterExpression" ).toString().isEmpty() )
224  {
225  e = new QgsExpression( config.value( "FilterExpression" ).toString() );
226  if ( e->hasParserError() || !e->prepare( &context ) )
227  ki = -1;
228  }
229 
230  if ( ki >= 0 && vi >= 0 )
231  {
232  QSet<int> attributes;
233  attributes << ki << vi;
234 
235  QgsFeatureRequest::Flags flags = QgsFeatureRequest::NoGeometry;
236 
237  bool requiresAllAttributes = false;
238  if ( e )
239  {
240  if ( e->needsGeometry() )
242 
243  Q_FOREACH ( const QString& field, e->referencedColumns() )
244  {
245  if ( field == QgsFeatureRequest::AllAttributes )
246  {
247  requiresAllAttributes = true;
248  break;
249  }
250  int idx = layer->fieldNameIndex( field );
251  if ( idx < 0 )
252  continue;
253  attributes << idx;
254  }
255  }
256 
258  if ( !requiresAllAttributes )
259  {
260  fr.setSubsetOfAttributes( attributes.toList() );
261  }
262 
263  QgsFeatureIterator fit = layer->getFeatures( fr );
264 
265  QgsFeature f;
266  while ( fit.nextFeature( f ) )
267  {
268  context.setFeature( f );
269  if ( e && !e->evaluate( &context ).toBool() )
270  continue;
271 
272  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
273  }
274  }
275  delete e;
276  }
277 
278  if ( config.value( "OrderByValue" ).toBool() )
279  {
280  qSort( cache.begin(), cache.end(), orderByValueLessThan );
281  }
282  else
283  {
284  qSort( cache.begin(), cache.end(), orderByKeyLessThan );
285  }
286 
287  return cache;
288 }
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)
int fieldIdx() const
Access the field index.
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)
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)
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.
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&#39;s value.
QgsValueRelationWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=nullptr, QWidget *parent=nullptr)
const T value(const Key &key) const