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