QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 dot kuhn at gmx 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 
220  QgsExpression *e = 0;
221  if ( !config.value( "FilterExpression" ).toString().isEmpty() )
222  {
223  e = new QgsExpression( config.value( "FilterExpression" ).toString() );
224  if ( e->hasParserError() || !e->prepare( layer->pendingFields() ) )
225  ki = -1;
226  }
227 
228  if ( ki >= 0 && vi >= 0 )
229  {
230  QSet<int> attributes;
231  attributes << ki << vi;
232 
233  QgsFeatureRequest::Flags flags = QgsFeatureRequest::NoGeometry;
234 
235  bool requiresAllAttributes = false;
236  if ( e )
237  {
238  if ( e->needsGeometry() )
240 
241  Q_FOREACH ( const QString& field, e->referencedColumns() )
242  {
243  if ( field == QgsFeatureRequest::AllAttributes )
244  {
245  requiresAllAttributes = true;
246  break;
247  }
248  int idx = layer->fieldNameIndex( field );
249  if ( idx < 0 )
250  continue;
251  attributes << idx;
252  }
253  }
254 
256  if ( !requiresAllAttributes )
257  {
258  fr.setSubsetOfAttributes( attributes.toList() );
259  }
260 
261  QgsFeatureIterator fit = layer->getFeatures( fr );
262 
263  QgsFeature f;
264  while ( fit.nextFeature( f ) )
265  {
266  if ( e && !e->evaluate( &f ).toBool() )
267  continue;
268 
269  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
270  }
271  }
272  delete e;
273  }
274 
275  if ( config.value( "OrderByValue" ).toBool() )
276  {
277  qSort( cache.begin(), cache.end(), orderByValueLessThan );
278  }
279  else
280  {
281  qSort( cache.begin(), cache.end(), orderByKeyLessThan );
282  }
283 
284  return cache;
285 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
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.
Definition: qgsexpression.h:93
QStringList referencedColumns() const
Get list of columns referenced by the expression.
void valueChanged()
Will call the value() method to determine the emitted value.
void append(const T &value)
iterator begin()
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
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:162
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.
void addItem(const QString &text, const QVariant &userData)
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.
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
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:236
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
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
const QgsFields & pendingFields() const
Returns field list in the to-be-committed state.
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.
QgsFeatureRequest & setFlags(Flags flags)
Set flags that affect how features will be fetched.
QString toString() const
iterator end()
bool valid() override
Return true if the widget has been properly initialized.
const T value(const Key &key) const