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