QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsfielddomainwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfielddomainwidget.cpp
3 ------------------
4 Date : February 2022
5 Copyright : (C) 2022 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
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#include "qgsfielddomain.h"
18#include "qgsvariantutils.h"
19#include "qgsgui.h"
20#include <QDialogButtonBox>
21#include <QPushButton>
22
23//
24// QgsAbstractFieldDomainWidget
25//
26
28 : QWidget( parent )
29{
30
31}
32
34
35
36//
37// QgsRangeDomainWidget
38//
39
42{
43 setupUi( this );
44
45
46 mMinSpinBox->setMinimum( std::numeric_limits<double>::lowest() );
47 mMinSpinBox->setMaximum( std::numeric_limits<double>::max() );
48 mMinSpinBox->setValue( 0 );
49 mMinSpinBox->setDecimals( 6 );
50
51 mMaxSpinBox->setMinimum( std::numeric_limits<double>::lowest() );
52 mMaxSpinBox->setMaximum( std::numeric_limits<double>::max() );
53 // any value we pick here is wrong!
54 mMaxSpinBox->setValue( 100 );
55 mMaxSpinBox->setDecimals( 6 );
56
57 mMinInclusiveCheckBox->setChecked( true );
58 mMaxInclusiveCheckBox->setChecked( true );
59
60 connect( mMinSpinBox, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, &QgsAbstractFieldDomainWidget::changed );
61 connect( mMaxSpinBox, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, &QgsAbstractFieldDomainWidget::changed );
62 connect( mMinInclusiveCheckBox, &QCheckBox::toggled, this, &QgsAbstractFieldDomainWidget::changed );
63 connect( mMaxInclusiveCheckBox, &QCheckBox::toggled, this, &QgsAbstractFieldDomainWidget::changed );
64}
65
67{
68 const QgsRangeFieldDomain *rangeDomain = dynamic_cast< const QgsRangeFieldDomain *>( domain );
69 if ( !rangeDomain )
70 return;
71
72 // currently only supported data type is double, but in future we *may* need to handle dates/etc here
73 mMinSpinBox->setValue( rangeDomain->minimum().toDouble() );
74 mMaxSpinBox->setValue( rangeDomain->maximum().toDouble() );
75
76 mMinInclusiveCheckBox->setChecked( rangeDomain->minimumIsInclusive() );
77 mMaxInclusiveCheckBox->setChecked( rangeDomain->maximumIsInclusive() );
78}
79
80QgsFieldDomain *QgsRangeDomainWidget::createFieldDomain( const QString &name, const QString &description, QVariant::Type fieldType ) const
81{
82 return new QgsRangeFieldDomain( name, description, fieldType,
83 mMinSpinBox->value(), mMinInclusiveCheckBox->isChecked(),
84 mMaxSpinBox->value(), mMaxInclusiveCheckBox->isChecked() );
85}
86
88{
89 return mMinSpinBox->value() <= mMaxSpinBox->value();
90}
91
92//
93// QgsGlobDomainWidget
94//
95
98{
99 setupUi( this );
100
101 connect( mEditGlob, &QLineEdit::textChanged, this, &QgsAbstractFieldDomainWidget::changed );
102}
103
105{
106 const QgsGlobFieldDomain *globDomain = dynamic_cast< const QgsGlobFieldDomain *>( domain );
107 if ( !globDomain )
108 return;
109
110 mEditGlob->setText( globDomain->glob() );
111}
112
113QgsFieldDomain *QgsGlobDomainWidget::createFieldDomain( const QString &name, const QString &description, QVariant::Type fieldType ) const
114{
115 return new QgsGlobFieldDomain( name, description, fieldType, mEditGlob->text() );
116}
117
119{
120 return !mEditGlob->text().trimmed().isEmpty();
121}
122
123//
124// QgsCodedFieldDomainWidget
125//
126
129{
130 setupUi( this );
131
132 mModel = new QgsCodedValueTableModel( this );
133 mValuesTable->setModel( mModel );
134
135 connect( mButtonAddRow, &QToolButton::clicked, this, [ = ]
136 {
137 mModel->insertRow( mModel->rowCount() );
138 } );
139 connect( mButtonRemoveRow, &QToolButton::clicked, this, [ = ]
140 {
141 QItemSelectionModel *selectionModel = mValuesTable->selectionModel();
142 const QModelIndexList selectedRows = selectionModel->selectedIndexes();
143 if ( !selectedRows.empty() )
144 {
145 mModel->removeRow( selectedRows.first().row() );
146 }
147 } );
148
149 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsAbstractFieldDomainWidget::changed );
150 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsAbstractFieldDomainWidget::changed );
151 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsAbstractFieldDomainWidget::changed );
152}
153
155{
156 const QgsCodedFieldDomain *codedDomain = dynamic_cast< const QgsCodedFieldDomain *>( domain );
157 if ( !codedDomain )
158 return;
159
160 mModel->setValues( codedDomain->values() );
161}
162
163QgsFieldDomain *QgsCodedFieldDomainWidget::createFieldDomain( const QString &name, const QString &description, QVariant::Type fieldType ) const
164{
165 return new QgsCodedFieldDomain( name, description, fieldType, mModel->values() );
166}
167
169{
170 return true;
171}
172
173
174//
175// QgsCodedValueTableModel
176//
177
179 : QAbstractTableModel( parent )
180{
181
182}
183
184int QgsCodedValueTableModel::rowCount( const QModelIndex & ) const
185{
186 return mValues.count();
187}
188
189int QgsCodedValueTableModel::columnCount( const QModelIndex & ) const
190{
191 return 2;
192}
193
194QVariant QgsCodedValueTableModel::data( const QModelIndex &index, int role ) const
195{
196 if ( index.row() < 0 || index.row() >= mValues.count()
197 || index.column() < 0 || index.column() >= 2 )
198 return QVariant();
199
200 const QgsCodedValue &value = mValues[ index.row() ];
201 switch ( role )
202 {
203 case Qt::DisplayRole:
204 case Qt::EditRole:
205 case Qt::ToolTipRole:
206 {
207 switch ( index.column() )
208 {
209 case 0:
210 return value.code();
211 case 1:
212 return value.value();
213 }
214 break;
215 }
216
217 default:
218 break;
219 }
220 return QVariant();
221}
222
223bool QgsCodedValueTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
224{
225 if ( index.row() < 0 || index.row() >= mValues.count()
226 || index.column() < 0 || index.column() >= 2 )
227 return false;
228
229 const QgsCodedValue codedValue = mValues.at( index.row() );
230 switch ( role )
231 {
232 case Qt::EditRole:
233
234 switch ( index.column() )
235 {
236 case 0:
237 {
238 const QgsCodedValue newValue( value.toString(), codedValue.value() );
239 mValues.replace( index.row(), newValue );
240 emit dataChanged( index, index );
241 return true;
242 }
243
244 case 1:
245 {
246 const QgsCodedValue newValue( codedValue.code(), value.toString() );
247 mValues.replace( index.row(), newValue );
248 emit dataChanged( index, index );
249 return true;
250 }
251
252 default:
253 break;
254 }
255
256 break;
257 }
258 return false;
259}
260
261Qt::ItemFlags QgsCodedValueTableModel::flags( const QModelIndex &index ) const
262{
263 if ( index.row() < 0
264 || index.row() >= mValues.size()
265 || index.column() < 0
266 || index.column() >= columnCount() )
267 return QAbstractTableModel::flags( index );
268
269 return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable;
270}
271
272QVariant QgsCodedValueTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
273{
274 switch ( orientation )
275 {
276 case Qt::Horizontal:
277 switch ( role )
278 {
279 case Qt::DisplayRole:
280 case Qt::ToolTipRole:
281 {
282 switch ( section )
283 {
284 case 0:
285 return tr( "Code" );
286 case 1:
287 return tr( "Value" );
288 default:
289 break;
290 }
291 }
292 break;
293 default:
294 break;
295 }
296
297 break;
298 case Qt::Vertical:
299 break;
300 }
301 return QVariant();
302}
303
304bool QgsCodedValueTableModel::insertRows( int row, int count, const QModelIndex &parent )
305{
306 if ( parent.isValid() )
307 return false;
308
309 beginInsertRows( QModelIndex(), row, row + count - 1 );
310 for ( int i = row; i < row + count; ++i )
311 {
312 mValues.insert( i, QgsCodedValue( QString(), QString() ) );
313 }
314 endInsertRows();
315 return true;
316}
317
318bool QgsCodedValueTableModel::removeRows( int row, int count, const QModelIndex &parent )
319{
320 if ( row < 0 || row >= mValues.count() )
321 return false;
322
323 if ( parent.isValid() )
324 return false;
325
326 for ( int i = row + count - 1; i >= row; --i )
327 {
328 beginRemoveRows( parent, i, i );
329 mValues.removeAt( i );
330 endRemoveRows();
331 }
332 return true;
333}
334
335void QgsCodedValueTableModel::setValues( const QList<QgsCodedValue> &values )
336{
337 beginResetModel();
338 mValues = values;
339 endResetModel();
340}
341
342//
343// QgsFieldDomainWidget
344//
345
347 : QWidget( parent )
348{
349 setupUi( this );
350
351 mComboSplitPolicy->addItem( tr( "Default Value" ), static_cast< int >( Qgis::FieldDomainSplitPolicy::DefaultValue ) );
352 mComboSplitPolicy->addItem( tr( "Duplicate" ), static_cast< int >( Qgis::FieldDomainSplitPolicy::Duplicate ) );
353 mComboSplitPolicy->addItem( tr( "Ratio of Geometries" ), static_cast< int >( Qgis::FieldDomainSplitPolicy::GeometryRatio ) );
354
355 mComboMergePolicy->addItem( tr( "Default Value" ), static_cast< int >( Qgis::FieldDomainMergePolicy::DefaultValue ) );
356 mComboMergePolicy->addItem( tr( "Sum" ), static_cast< int >( Qgis::FieldDomainMergePolicy::Sum ) );
357 mComboMergePolicy->addItem( tr( "Geometry Weighted Average" ), static_cast< int >( Qgis::FieldDomainMergePolicy::GeometryWeighted ) );
358
359 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Bool ), static_cast< int >( QVariant::Bool ) );
360 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::String ), static_cast< int >( QVariant::String ) );
361 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Int ), static_cast< int >( QVariant::Int ) );
362 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::LongLong ), static_cast< int >( QVariant::LongLong ) );
363 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Double ), static_cast< int >( QVariant::Double ) );
364#if 0 // not supported by any formats yet...
365 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Date ), static_cast< int >( QVariant::Date ) );
366 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Time ), static_cast< int >( QVariant::Time ) );
367 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::DateTime ), static_cast< int >( QVariant::DateTime ) );
368#endif
369
370 switch ( type )
371 {
373 mDomainWidget = new QgsCodedFieldDomainWidget();
374 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast< int >( QVariant::String ) ) );
375 break;
376
378 mDomainWidget = new QgsRangeDomainWidget();
379 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast< int >( QVariant::Double ) ) );
380 break;
381
383 mDomainWidget = new QgsGlobDomainWidget();
384 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast< int >( QVariant::String ) ) );
385 break;
386 }
387
388 mStackedWidget->addWidget( mDomainWidget );
389 mStackedWidget->setCurrentWidget( mDomainWidget );
390
391 connect( mNameEdit, &QLineEdit::textChanged, this, [ = ]
392 {
393 emit validityChanged( isValid() );
394 } );
395
396 connect( mDomainWidget, &QgsAbstractFieldDomainWidget::changed, this, [ = ]
397 {
398 emit validityChanged( isValid() );
399 } );
400}
401
403{
404 if ( !domain )
405 return;
406
407 mNameEdit->setText( domain->name() );
408 mDescriptionEdit->setText( domain->description() );
409 mComboMergePolicy->setCurrentIndex( mComboMergePolicy->findData( static_cast< int >( domain->mergePolicy() ) ) );
410 mComboSplitPolicy->setCurrentIndex( mComboSplitPolicy->findData( static_cast< int >( domain->splitPolicy() ) ) );
411 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast< int >( domain->fieldType() ) ) );
412
413 if ( mDomainWidget )
414 mDomainWidget->setFieldDomain( domain );
415}
416
418{
419 if ( !mDomainWidget )
420 return nullptr;
421
422 std::unique_ptr< QgsFieldDomain > res( mDomainWidget->createFieldDomain( mNameEdit->text(),
423 mDescriptionEdit->text(),
424 static_cast< QVariant::Type >( mFieldTypeCombo->currentData().toInt() ) ) );
425
426 res->setMergePolicy( static_cast< Qgis::FieldDomainMergePolicy >( mComboMergePolicy->currentData().toInt() ) );
427 res->setSplitPolicy( static_cast< Qgis::FieldDomainSplitPolicy >( mComboSplitPolicy->currentData().toInt() ) );
428 return res.release();
429}
430
432{
433 if ( mNameEdit->text().trimmed().isEmpty() )
434 return false;
435
436 return mDomainWidget && mDomainWidget->isValid();
437}
438
439//
440// QgsFieldDomainDialog
441//
442
443QgsFieldDomainDialog::QgsFieldDomainDialog( Qgis::FieldDomainType type, QWidget *parent, Qt::WindowFlags flags )
444 : QDialog( parent, flags )
445{
446 setObjectName( QStringLiteral( "QgsFieldDomainDialog" ) );
447
448 QVBoxLayout *vLayout = new QVBoxLayout();
449 mWidget = new QgsFieldDomainWidget( type );
450 vLayout->addWidget( mWidget, 1 );
451
452 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
453 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
454 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
455 vLayout->addWidget( mButtonBox );
456
457 setLayout( vLayout );
458 connect( mWidget, &QgsFieldDomainWidget::validityChanged, this, &QgsFieldDomainDialog::validityChanged );
459 validityChanged( mWidget->isValid() );
460
462}
463
465{
466 mWidget->setFieldDomain( domain );
467}
468
470{
471 return mWidget->createFieldDomain();
472}
473
475{
476 if ( !mWidget->isValid() )
477 return;
478
479 QDialog::accept();
480}
481
482void QgsFieldDomainDialog::validityChanged( bool isValid )
483{
484 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( isValid );
485}
FieldDomainMergePolicy
Merge policy for field domains.
Definition: qgis.h:3199
@ GeometryWeighted
New values are computed as the weighted average of the source values.
@ DefaultValue
Use default field value.
FieldDomainSplitPolicy
Split policy for field domains.
Definition: qgis.h:3182
@ GeometryRatio
New values are computed by the ratio of their area/length compared to the area/length of the original...
@ DefaultValue
Use default field value.
@ Duplicate
Duplicate original value.
FieldDomainType
Types of field domain.
Definition: qgis.h:3212
@ Coded
Coded field domain.
@ Range
Numeric range field domain (min/max)
@ Glob
Glob string pattern field domain.
Abstract base class for widgets which configure the extended properties of a QgsFieldDomain subclass.
virtual QgsFieldDomain * createFieldDomain(const QString &name, const QString &description, QVariant::Type fieldType) const =0
Creates a new field domain using the properties from the widget.
void changed()
Emitted whenever the field domain configuration in the widget changes.
virtual bool isValid() const =0
Returns true if the widget currently represents a valid field domain configuration.
QgsAbstractFieldDomainWidget(QWidget *parent=nullptr)
Constructor for QgsAbstractFieldDomainWidget, with the specified parent widget.
virtual void setFieldDomain(const QgsFieldDomain *domain)=0
Sets the current field domain to show properties for in the widget.
A widget for configuration of the extended properties of a QgsCodedFieldDomain.
void setFieldDomain(const QgsFieldDomain *domain) override
Sets the current field domain to show properties for in the widget.
QgsCodedFieldDomainWidget(QWidget *parent=nullptr)
Constructor for QgsCodedFieldDomainWidget, with the specified parent widget.
bool isValid() const override
Returns true if the widget currently represents a valid field domain configuration.
QgsFieldDomain * createFieldDomain(const QString &name, const QString &description, QVariant::Type fieldType) const override
Creates a new field domain using the properties from the widget.
Definition of a coded / enumerated field domain.
QList< QgsCodedValue > values() const
Returns the enumeration as QgsCodedValue values.
A table model for representing the values in a QgsCodedValue list.
bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QgsCodedValueTableModel(QObject *parent)
Constructor for QgsCodedValueTableModel, with the specified parent object.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Qt::ItemFlags flags(const QModelIndex &index) const override
void setValues(const QList< QgsCodedValue > &values)
Sets the values to show in the model.
QList< QgsCodedValue > values() const
Returns the values from the model.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Associates a code and a value.
QVariant code() const
Returns the associated code, which is the underlying value stored in fields.
QString value() const
Returns the associated value, which is the user-friendly string representation.
void setFieldDomain(const QgsFieldDomain *domain)
Sets the current field domain to show properties for in the dialog.
QgsFieldDomain * createFieldDomain() const
Creates a new field domain using the properties from the dialog.
QgsFieldDomainDialog(Qgis::FieldDomainType type, QWidget *parent=nullptr, Qt::WindowFlags flags=Qt::WindowFlags())
Constructor for QgsFieldDomainDialog for the given domain type, with the specified parent widget and ...
A widget for configuration of the properties of a QgsFieldDomain.
QgsFieldDomainWidget(Qgis::FieldDomainType type, QWidget *parent=nullptr)
Constructor for QgsFieldDomainWidget for the given domain type, with the specified parent widget.
QgsFieldDomain * createFieldDomain() const
Creates a new field domain using the properties from the widget.
void setFieldDomain(const QgsFieldDomain *domain)
Sets the current field domain to show properties for in the widget.
bool isValid() const
Returns true if the widget currently represents a valid field domain configuration.
void validityChanged(bool isValid)
Emitted whenever the validity of the field domain configuration in the widget changes.
Base class for field domains.
Qgis::FieldDomainMergePolicy mergePolicy() const
Returns the merge policy.
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the split policy.
QString name() const
Returns the name of the field domain.
QVariant::Type fieldType() const
Returns the associated field type.
QString description() const
Returns the description of the field domain.
A widget for configuration of the extended properties of a QgsGlobFieldDomain.
QgsGlobDomainWidget(QWidget *parent=nullptr)
Constructor for QgsGlobDomainWidget, with the specified parent widget.
void setFieldDomain(const QgsFieldDomain *domain) override
Sets the current field domain to show properties for in the widget.
QgsFieldDomain * createFieldDomain(const QString &name, const QString &description, QVariant::Type fieldType) const override
Creates a new field domain using the properties from the widget.
bool isValid() const override
Returns true if the widget currently represents a valid field domain configuration.
Definition of a field domain for field content validated by a glob.
QString glob() const
Returns the glob expression.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:194
A widget for configuration of the extended properties of a QgsRangeFieldDomain.
QgsFieldDomain * createFieldDomain(const QString &name, const QString &description, QVariant::Type fieldType) const override
Creates a new field domain using the properties from the widget.
void setFieldDomain(const QgsFieldDomain *domain) override
Sets the current field domain to show properties for in the widget.
bool isValid() const override
Returns true if the widget currently represents a valid field domain configuration.
QgsRangeDomainWidget(QWidget *parent=nullptr)
Constructor for QgsRangeDomainWidget, with the specified parent widget.
Definition of a numeric field domain with a range of validity for values.
QVariant minimum() const
Returns the minimum value.
bool maximumIsInclusive() const
Returns true if the maximum value is inclusive.
bool minimumIsInclusive() const
Returns true if the minimum value is inclusive.
QVariant maximum() const
Returns the maximum value.
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.