31 #include <QHeaderView>
34 #include <QTableWidget>
35 #include <QStringListModel>
39 #include <nlohmann/json.hpp>
40 using namespace nlohmann;
55 int cbxIdx = mComboBox->currentIndex();
58 v = mComboBox->currentData();
62 const int nofColumns = columnCount();
66 QStringList selection;
67 for (
int j = 0; j < mTableWidget->rowCount(); j++ )
69 for (
int i = 0; i < nofColumns; ++i )
71 QTableWidgetItem *item = mTableWidget->item( j, i );
74 if ( item->checkState() == Qt::Checked )
75 selection << item->data( Qt::UserRole ).toString();
81 if ( selection.isEmpty() && !
config( QStringLiteral(
"AllowNull" ) ).toBool( ) )
83 return QVariant( QVariant::Type::List );
88 for (
const QString &s : std::as_const( selection ) )
91 const QVariant::Type type { fkType() };
94 case QVariant::Type::Int:
95 vl.push_back( s.toInt() );
97 case QVariant::Type::LongLong:
98 vl.push_back( s.toLongLong() );
106 if (
layer()->fields().at(
fieldIdx() ).type() == QVariant::Map ||
107 layer()->fields().at(
fieldIdx() ).type() == QVariant::List )
122 if ( item.value == mLineEdit->text() )
139 mExpression =
config().value( QStringLiteral(
"FilterExpression" ) ).toString();
141 if (
config( QStringLiteral(
"AllowMulti" ) ).toBool() )
143 return new QTableWidget( parent );
145 else if (
config( QStringLiteral(
"UseCompleter" ) ).toBool() )
151 return new QComboBox( parent );
158 mComboBox = qobject_cast<QComboBox *>( editor );
159 mTableWidget = qobject_cast<QTableWidget *>( editor );
160 mLineEdit = qobject_cast<QLineEdit *>( editor );
167 connect( mComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
170 else if ( mTableWidget )
172 mTableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
173 mTableWidget->horizontalHeader()->setVisible(
false );
174 mTableWidget->verticalHeader()->setSectionResizeMode( QHeaderView::Stretch );
175 mTableWidget->verticalHeader()->setVisible(
false );
176 mTableWidget->setShowGrid(
false );
177 mTableWidget->setEditTriggers( QAbstractItemView::NoEditTriggers );
178 mTableWidget->setSelectionMode( QAbstractItemView::NoSelection );
181 else if ( mLineEdit )
183 connect( mLineEdit, &QLineEdit::textChanged,
this, &QgsValueRelationWidgetWrapper::emitValueChangedInternal, Qt::UniqueConnection );
189 return mTableWidget || mLineEdit || mComboBox;
192 void QgsValueRelationWidgetWrapper::updateValues(
const QVariant &value,
const QVariantList & )
196 QStringList checkList;
198 if (
layer()->fields().at(
fieldIdx() ).type() == QVariant::Map ||
199 layer()->fields().at(
fieldIdx() ).type() == QVariant::List )
201 checkList =
value.toStringList();
208 QTableWidgetItem *lastChangedItem =
nullptr;
210 const int nofColumns = columnCount();
214 for (
int j = 0; j < mTableWidget->rowCount(); j++ )
216 auto signalBlockedTableWidget =
whileBlocking( mTableWidget );
217 Q_UNUSED( signalBlockedTableWidget )
219 for (
int i = 0; i < nofColumns; ++i )
221 QTableWidgetItem *item = mTableWidget->item( j, i );
224 item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
226 item->setFlags( mEnabled ? item->flags() | Qt::ItemIsEnabled : item->flags() & ~Qt::ItemIsEnabled );
227 lastChangedItem = item;
232 if ( lastChangedItem )
233 lastChangedItem->setCheckState( checkList.contains( lastChangedItem->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
236 else if ( mComboBox )
241 for (
int i = 0; i < mComboBox->count(); i++ )
243 QVariant v( mComboBox->itemData( i ) );
250 mComboBox->setCurrentIndex( idx );
252 else if ( mLineEdit )
255 bool wasFound {
false };
258 if ( i.key ==
value )
260 mLineEdit->setText( i.value );
268 mLineEdit->setText( tr(
"(no selection)" ) );
277 if ( attributeChanged )
279 QVariant oldValue(
value( ) );
287 updateValues(
value( ) );
302 QString attributeName( formFields.
names().at(
fieldIdx() ) );
324 if (
context().attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode
326 && ! mCache.isEmpty()
327 && !
config( QStringLiteral(
"AllowNull" ) ).toBool( ) )
331 QTimer::singleShot( 0,
this, [
this ]
333 if ( ! mCache.isEmpty() )
335 updateValues( formFeature().attribute( fieldIdx() ).isValid() ? formFeature().attribute( fieldIdx() ) : mCache.at( 0 ).key );
341 int QgsValueRelationWidgetWrapper::columnCount()
const
343 return std::max( 1,
config( QStringLiteral(
"NofColumns" ) ).toInt() );
347 QVariant::Type QgsValueRelationWidgetWrapper::fkType()
const
356 return fields.
at( idx ).
type();
359 return QVariant::Type::Invalid;
362 void QgsValueRelationWidgetWrapper::populate( )
368 if (
context().parentFormFeature().isValid() )
377 else if ( mCache.empty() )
385 if (
config( QStringLiteral(
"AllowNull" ) ).toBool( ) )
387 whileBlocking( mComboBox )->addItem( tr(
"(no selection)" ), QVariant(
field().type( ) ) );
392 whileBlocking( mComboBox )->addItem( element.value, element.key );
393 if ( !element.description.isEmpty() )
394 mComboBox->setItemData( mComboBox->count() - 1, element.description, Qt::ToolTipRole );
398 else if ( mTableWidget )
400 const int nofColumns = columnCount();
402 if ( ! mCache.empty() )
404 mTableWidget->setRowCount( ( mCache.size() + nofColumns - 1 ) / nofColumns );
407 mTableWidget->setRowCount( 1 );
408 mTableWidget->setColumnCount( nofColumns );
415 if ( column == nofColumns )
420 QTableWidgetItem *item =
nullptr;
421 item =
new QTableWidgetItem( element.value );
422 item->setData( Qt::UserRole, element.key );
428 else if ( mLineEdit )
431 values.reserve( mCache.size() );
436 QStringListModel *m =
new QStringListModel( values, mLineEdit );
437 QCompleter *completer =
new QCompleter( m, mLineEdit );
438 completer->setCaseSensitivity( Qt::CaseInsensitive );
439 mLineEdit->setCompleter( completer );
445 const int nofColumns = columnCount();
449 for (
int j = 0; j < mTableWidget->rowCount(); j++ )
451 for (
int i = 0; i < nofColumns; ++i )
453 whileBlocking( mTableWidget )->item( j, i )->setCheckState( Qt::PartiallyChecked );
457 else if ( mComboBox )
461 else if ( mLineEdit )
469 if ( mEnabled == enabled )
476 auto signalBlockedTableWidget =
whileBlocking( mTableWidget );
477 Q_UNUSED( signalBlockedTableWidget )
479 for (
int j = 0; j < mTableWidget->rowCount(); j++ )
481 for (
int i = 0; i < mTableWidget->columnCount(); ++i )
483 QTableWidgetItem *item = mTableWidget->item( j, i );
486 item->setFlags( enabled ? item->flags() | Qt::ItemIsEnabled : item->flags() & ~Qt::ItemIsEnabled );
502 ctx.setParentFormFeature( feature );
507 && (
config( QStringLiteral(
"Value" ) ).toString() == attribute ||
508 config( QStringLiteral(
"Key" ) ).toString() == attribute ||
517 void QgsValueRelationWidgetWrapper::emitValueChangedInternal(
const QString &value )
This class contains context information for attribute editor widgets.
QgsFeature parentFormFeature() const
Returns the feature of the currently edited parent form in its actual state.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Container of fields for a vector layer.
int count() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QStringList names() const
Returns a list with field names.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
static QString buildArray(const QVariantList &list)
Build a postgres array like formatted list in a string from a QVariantList.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, two NULL values are always treated a...
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.