QGIS API Documentation  2.12.0-Lyon
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  break;
35 
36  case QVariant::Double:
37  return p1.first.toDouble() < p2.first.toDouble();
38  break;
39 
40  default:
41  return p1.first.toInt() < p2.first.toInt();
42  break;
43  }
44 }
45 
48 {
49  return p1.second < p2.second;
50 }
51 
53  : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
54  , mComboBox( 0 )
55  , mListWidget( 0 )
56  , mLineEdit( 0 )
57  , mLayer( 0 )
58 {
59 }
60 
61 
63 {
64  QVariant v;
65 
66  if ( mComboBox )
67  {
68  int cbxIdx = mComboBox->currentIndex();
69  if ( cbxIdx > -1 )
70  {
71  v = mComboBox->itemData( mComboBox->currentIndex() );
72  }
73  }
74 
75  if ( mListWidget )
76  {
77  QStringList selection;
78  for ( int i = 0; i < mListWidget->count(); ++i )
79  {
80  QListWidgetItem* item = mListWidget->item( i );
81  if ( item->checkState() == Qt::Checked )
82  selection << item->data( Qt::UserRole ).toString();
83  }
84 
85  v = selection.join( "," ).prepend( "{" ).append( "}" );
86  }
87 
88  if ( mLineEdit )
89  {
90  Q_FOREACH ( const ValueRelationItem& i , mCache )
91  {
92  if ( i.second == mLineEdit->text() )
93  {
94  v = i.first;
95  break;
96  }
97  }
98  }
99 
100  return v;
101 }
102 
104 {
105  if ( config( "AllowMulti" ).toBool() )
106  {
107  return new QListWidget( parent );
108  }
109  else if ( config( "UseCompleter" ).toBool() )
110  {
111  return new QgsFilterLineEdit( parent );
112  }
113  {
114  return new QComboBox( parent );
115  }
116 }
117 
119 {
120  mCache = createCache( config() );
121 
122  mComboBox = qobject_cast<QComboBox*>( editor );
123  mListWidget = qobject_cast<QListWidget*>( editor );
124  mLineEdit = qobject_cast<QLineEdit*>( editor );
125 
126  if ( mComboBox )
127  {
128  if ( config( "AllowNull" ).toBool() )
129  {
130  mComboBox->addItem( tr( "(no selection)" ), QVariant( field().type() ) );
131  }
132 
133  Q_FOREACH ( const ValueRelationItem& element, mCache )
134  {
135  mComboBox->addItem( element.second, element.first );
136  }
137 
138  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( valueChanged() ) );
139  }
140  else if ( mListWidget )
141  {
142  Q_FOREACH ( const ValueRelationItem& element, mCache )
143  {
144  QListWidgetItem *item;
145  item = new QListWidgetItem( element.second );
146  item->setData( Qt::UserRole, element.first );
147 
148  mListWidget->addItem( item );
149  }
150  connect( mListWidget, SIGNAL( itemChanged( QListWidgetItem* ) ), this, SLOT( valueChanged() ) );
151  }
152  else if ( mLineEdit )
153  {
154  QStringList values;
155  Q_FOREACH ( const ValueRelationItem& i, mCache )
156  {
157  values << i.second;
158  }
159 
160  QStringListModel* m = new QStringListModel( values, mLineEdit );
161  QCompleter* completer = new QCompleter( m, mLineEdit );
162  completer->setCaseSensitivity( Qt::CaseInsensitive );
163  mLineEdit->setCompleter( completer );
164  }
165 }
166 
168 {
169  return mListWidget || mLineEdit || mComboBox;
170 }
171 
173 {
174  if ( mListWidget )
175  {
176  QStringList checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( "," );
177 
178  for ( int i = 0; i < mListWidget->count(); ++i )
179  {
180  QListWidgetItem* item = mListWidget->item( i );
181  if ( config( "OrderByValue" ).toBool() )
182  {
183  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
184  }
185  else
186  {
187  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
188  }
189  }
190  }
191  else if ( mComboBox )
192  {
193  mComboBox->setCurrentIndex( mComboBox->findData( value ) );
194  }
195  else if ( mLineEdit )
196  {
197  Q_FOREACH ( ValueRelationItem i, mCache )
198  {
199  if ( i.first == value )
200  {
201  mLineEdit->setText( i.second );
202  break;
203  }
204  }
205  }
206 }
207 
208 
210 {
211  ValueRelationCache cache;
212 
213  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( config.value( "Layer" ).toString() ) );
214 
215  if ( layer )
216  {
217  int ki = layer->fieldNameIndex( config.value( "Key" ).toString() );
218  int vi = layer->fieldNameIndex( config.value( "Value" ).toString() );
219 
224 
225  QgsExpression *e = 0;
226  if ( !config.value( "FilterExpression" ).toString().isEmpty() )
227  {
228  e = new QgsExpression( config.value( "FilterExpression" ).toString() );
229  if ( e->hasParserError() || !e->prepare( &context ) )
230  ki = -1;
231  }
232 
233  if ( ki >= 0 && vi >= 0 )
234  {
235  QSet<int> attributes;
236  attributes << ki << vi;
237 
238  QgsFeatureRequest::Flags flags = QgsFeatureRequest::NoGeometry;
239 
240  bool requiresAllAttributes = false;
241  if ( e )
242  {
243  if ( e->needsGeometry() )
245 
246  Q_FOREACH ( const QString& field, e->referencedColumns() )
247  {
248  if ( field == QgsFeatureRequest::AllAttributes )
249  {
250  requiresAllAttributes = true;
251  break;
252  }
253  int idx = layer->fieldNameIndex( field );
254  if ( idx < 0 )
255  continue;
256  attributes << idx;
257  }
258  }
259 
261  if ( !requiresAllAttributes )
262  {
263  fr.setSubsetOfAttributes( attributes.toList() );
264  }
265 
266  QgsFeatureIterator fit = layer->getFeatures( fr );
267 
268  QgsFeature f;
269  while ( fit.nextFeature( f ) )
270  {
271  context.setFeature( f );
272  if ( e && !e->evaluate( &context ).toBool() )
273  continue;
274 
275  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
276  }
277  }
278  delete e;
279  }
280 
281  if ( config.value( "OrderByValue" ).toBool() )
282  {
283  qSort( cache.begin(), cache.end(), orderByValueLessThan );
284  }
285  else
286  {
287  qSort( cache.begin(), cache.end(), orderByKeyLessThan );
288  }
289 
290  return cache;
291 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
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.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsVectorLayer * layer()
Access the QgsVectorLayer, you are working on.
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:176
QgsValueRelationWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=0, QWidget *parent=0)
QString & remove(int position, int n)
QgsField field()
Access the field.
QString tr(const char *sourceText, const char *disambiguation, int n)
QVariant value() override
Will be used to access the widget's value.
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()
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
const QgsEditorWidgetConfig config()
Returns the whole config.
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:238
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.
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()
bool valid() override
Return true if the widget has been properly initialized.
const T value(const Key &key) const