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