QGIS API Documentation 3.99.0-Master (1d785854362)
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
92{
93 mComboSplitPolicy->setEnabled( editable );
94 mComboMergePolicy->setEnabled( editable );
95}
96
97QgsFieldDomain *QgsRangeDomainWidget::createFieldDomain( const QString &name, const QString &description, QMetaType::Type fieldType ) const
98{
99 return new QgsRangeFieldDomain( name, description, fieldType, mMinSpinBox->value(), mMinInclusiveCheckBox->isChecked(), mMaxSpinBox->value(), mMaxInclusiveCheckBox->isChecked() );
100}
101
103{
104 return mMinSpinBox->value() <= mMaxSpinBox->value();
105}
106
107//
108// QgsGlobDomainWidget
109//
110
113{
114 setupUi( this );
115
116 connect( mEditGlob, &QLineEdit::textChanged, this, &QgsAbstractFieldDomainWidget::changed );
117}
118
120{
121 const QgsGlobFieldDomain *globDomain = dynamic_cast<const QgsGlobFieldDomain *>( domain );
122 if ( !globDomain )
123 return;
124
125 mEditGlob->setText( globDomain->glob() );
126}
127
129{
130 mWidget->setNameEditable( editable );
131}
132
134{
135 mWidget->setPoliciesEditable( editable );
136}
137
138QgsFieldDomain *QgsGlobDomainWidget::createFieldDomain( const QString &name, const QString &description, QMetaType::Type fieldType ) const
139{
140 return new QgsGlobFieldDomain( name, description, fieldType, mEditGlob->text() );
141}
142
144{
145 return !mEditGlob->text().trimmed().isEmpty();
146}
147
148//
149// QgsCodedFieldDomainWidget
150//
151
154{
155 setupUi( this );
156
157 mModel = new QgsCodedValueTableModel( this );
158 mValuesTable->setModel( mModel );
159
160 connect( mButtonAddRow, &QToolButton::clicked, this, [this] {
161 mModel->insertRow( mModel->rowCount() );
162 } );
163 connect( mButtonRemoveRow, &QToolButton::clicked, this, [this] {
164 QItemSelectionModel *selectionModel = mValuesTable->selectionModel();
165 const QModelIndexList selectedRows = selectionModel->selectedIndexes();
166 if ( !selectedRows.empty() )
167 {
168 mModel->removeRow( selectedRows.first().row() );
169 }
170 } );
171
172 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsAbstractFieldDomainWidget::changed );
173 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsAbstractFieldDomainWidget::changed );
174 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsAbstractFieldDomainWidget::changed );
175}
176
178{
179 const QgsCodedFieldDomain *codedDomain = dynamic_cast<const QgsCodedFieldDomain *>( domain );
180 if ( !codedDomain )
181 return;
182
183 mModel->setValues( codedDomain->values() );
184}
185
186QgsFieldDomain *QgsCodedFieldDomainWidget::createFieldDomain( const QString &name, const QString &description, QMetaType::Type fieldType ) const
187{
188 return new QgsCodedFieldDomain( name, description, fieldType, mModel->values() );
189}
190
192{
193 return true;
194}
195
196
197//
198// QgsCodedValueTableModel
199//
200
202 : QAbstractTableModel( parent )
203{
204}
205
206int QgsCodedValueTableModel::rowCount( const QModelIndex & ) const
207{
208 return mValues.count();
209}
210
211int QgsCodedValueTableModel::columnCount( const QModelIndex & ) const
212{
213 return 2;
214}
215
216QVariant QgsCodedValueTableModel::data( const QModelIndex &index, int role ) const
217{
218 if ( index.row() < 0 || index.row() >= mValues.count()
219 || index.column() < 0 || index.column() >= 2 )
220 return QVariant();
221
222 const QgsCodedValue &value = mValues[index.row()];
223 switch ( role )
224 {
225 case Qt::DisplayRole:
226 case Qt::EditRole:
227 case Qt::ToolTipRole:
228 {
229 switch ( index.column() )
230 {
231 case 0:
232 return value.code();
233 case 1:
234 return value.value();
235 }
236 break;
237 }
238
239 default:
240 break;
241 }
242 return QVariant();
243}
244
245bool QgsCodedValueTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
246{
247 if ( index.row() < 0 || index.row() >= mValues.count()
248 || index.column() < 0 || index.column() >= 2 )
249 return false;
250
251 const QgsCodedValue codedValue = mValues.at( index.row() );
252 switch ( role )
253 {
254 case Qt::EditRole:
255
256 switch ( index.column() )
257 {
258 case 0:
259 {
260 const QgsCodedValue newValue( value.toString(), codedValue.value() );
261 mValues.replace( index.row(), newValue );
262 emit dataChanged( index, index );
263 return true;
264 }
265
266 case 1:
267 {
268 const QgsCodedValue newValue( codedValue.code(), value.toString() );
269 mValues.replace( index.row(), newValue );
270 emit dataChanged( index, index );
271 return true;
272 }
273
274 default:
275 break;
276 }
277
278 break;
279 }
280 return false;
281}
282
283Qt::ItemFlags QgsCodedValueTableModel::flags( const QModelIndex &index ) const
284{
285 if ( index.row() < 0
286 || index.row() >= mValues.size()
287 || index.column() < 0
288 || index.column() >= columnCount() )
289 return QAbstractTableModel::flags( index );
290
291 return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable;
292}
293
294QVariant QgsCodedValueTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
295{
296 switch ( orientation )
297 {
298 case Qt::Horizontal:
299 switch ( role )
300 {
301 case Qt::DisplayRole:
302 case Qt::ToolTipRole:
303 {
304 switch ( section )
305 {
306 case 0:
307 return tr( "Code" );
308 case 1:
309 return tr( "Value" );
310 default:
311 break;
312 }
313 }
314 break;
315 default:
316 break;
317 }
318
319 break;
320 case Qt::Vertical:
321 break;
322 }
323 return QVariant();
324}
325
326bool QgsCodedValueTableModel::insertRows( int row, int count, const QModelIndex &parent )
327{
328 if ( parent.isValid() )
329 return false;
330
331 beginInsertRows( QModelIndex(), row, row + count - 1 );
332 for ( int i = row; i < row + count; ++i )
333 {
334 mValues.insert( i, QgsCodedValue( QString(), QString() ) );
335 }
336 endInsertRows();
337 return true;
338}
339
340bool QgsCodedValueTableModel::removeRows( int row, int count, const QModelIndex &parent )
341{
342 if ( row < 0 || row >= mValues.count() )
343 return false;
344
345 if ( parent.isValid() )
346 return false;
347
348 for ( int i = row + count - 1; i >= row; --i )
349 {
350 beginRemoveRows( parent, i, i );
351 mValues.removeAt( i );
352 endRemoveRows();
353 }
354 return true;
355}
356
357void QgsCodedValueTableModel::setValues( const QList<QgsCodedValue> &values )
358{
359 beginResetModel();
360 mValues = values;
361 endResetModel();
362}
363
364//
365// QgsFieldDomainWidget
366//
367
369 : QWidget( parent )
370{
371 setupUi( this );
372
373 mComboSplitPolicy->addItem( tr( "Default Value" ), static_cast<int>( Qgis::FieldDomainSplitPolicy::DefaultValue ) );
374 mComboSplitPolicy->addItem( tr( "Duplicate" ), static_cast<int>( Qgis::FieldDomainSplitPolicy::Duplicate ) );
375 mComboSplitPolicy->addItem( tr( "Ratio of Geometries" ), static_cast<int>( Qgis::FieldDomainSplitPolicy::GeometryRatio ) );
376
377 mComboMergePolicy->addItem( tr( "Default Value" ), static_cast<int>( Qgis::FieldDomainMergePolicy::DefaultValue ) );
378 mComboMergePolicy->addItem( tr( "Sum" ), static_cast<int>( Qgis::FieldDomainMergePolicy::Sum ) );
379 mComboMergePolicy->addItem( tr( "Geometry Weighted Average" ), static_cast<int>( Qgis::FieldDomainMergePolicy::GeometryWeighted ) );
380
381 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), static_cast<int>( QMetaType::Type::Bool ) );
382 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), static_cast<int>( QMetaType::Type::QString ) );
383 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), static_cast<int>( QMetaType::Type::Int ) );
384 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::LongLong ), static_cast<int>( QMetaType::Type::LongLong ) );
385 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), static_cast<int>( QMetaType::Type::Double ) );
386#if 0 // not supported by any formats yet...
387 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Date ), static_cast< int >( QVariant::Date ) );
388 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Time ), static_cast< int >( QVariant::Time ) );
389 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::DateTime ), static_cast< int >( QVariant::DateTime ) );
390#endif
391
392 switch ( type )
393 {
395 mDomainWidget = new QgsCodedFieldDomainWidget();
396 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( QMetaType::Type::QString ) ) );
397 break;
398
400 mDomainWidget = new QgsRangeDomainWidget();
401 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( QMetaType::Type::Double ) ) );
402 break;
403
405 mDomainWidget = new QgsGlobDomainWidget();
406 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( QMetaType::Type::QString ) ) );
407 break;
408 }
409
410 mStackedWidget->addWidget( mDomainWidget );
411 mStackedWidget->setCurrentWidget( mDomainWidget );
412
413 connect( mNameEdit, &QLineEdit::textChanged, this, [this] {
414 emit validityChanged( isValid() );
415 } );
416
417 connect( mDomainWidget, &QgsAbstractFieldDomainWidget::changed, this, [this] {
418 emit validityChanged( isValid() );
419 } );
420}
421
423{
424 if ( !domain )
425 return;
426
427 mNameEdit->setText( domain->name() );
428 mDescriptionEdit->setText( domain->description() );
429 mComboMergePolicy->setCurrentIndex( mComboMergePolicy->findData( static_cast<int>( domain->mergePolicy() ) ) );
430 mComboSplitPolicy->setCurrentIndex( mComboSplitPolicy->findData( static_cast<int>( domain->splitPolicy() ) ) );
431 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( domain->fieldType() ) ) );
432
433 if ( mDomainWidget )
434 mDomainWidget->setFieldDomain( domain );
435}
436
438{
439 if ( !mDomainWidget )
440 return nullptr;
441
442 std::unique_ptr<QgsFieldDomain> res( mDomainWidget->createFieldDomain( mNameEdit->text(), mDescriptionEdit->text(), static_cast<QMetaType::Type>( mFieldTypeCombo->currentData().toInt() ) ) );
443
444 res->setMergePolicy( static_cast<Qgis::FieldDomainMergePolicy>( mComboMergePolicy->currentData().toInt() ) );
445 res->setSplitPolicy( static_cast<Qgis::FieldDomainSplitPolicy>( mComboSplitPolicy->currentData().toInt() ) );
446 return res.release();
447}
448
450{
451 if ( mNameEdit->text().trimmed().isEmpty() )
452 return false;
453
454 return mDomainWidget && mDomainWidget->isValid();
455}
456
457//
458// QgsFieldDomainDialog
459//
460
461QgsFieldDomainDialog::QgsFieldDomainDialog( Qgis::FieldDomainType type, QWidget *parent, Qt::WindowFlags flags )
462 : QDialog( parent, flags )
463{
464 setObjectName( u"QgsFieldDomainDialog"_s );
465
466 QVBoxLayout *vLayout = new QVBoxLayout();
467 mWidget = new QgsFieldDomainWidget( type );
468 vLayout->addWidget( mWidget, 1 );
469
470 mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
471 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
472 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
473 vLayout->addWidget( mButtonBox );
474
475 setLayout( vLayout );
476 connect( mWidget, &QgsFieldDomainWidget::validityChanged, this, &QgsFieldDomainDialog::validityChanged );
477 validityChanged( mWidget->isValid() );
478
480}
481
483{
484 mWidget->setFieldDomain( domain );
485}
486
488{
489 return mWidget->createFieldDomain();
490}
491
493{
494 if ( !mWidget->isValid() )
495 return;
496
497 QDialog::accept();
498}
499
500void QgsFieldDomainDialog::validityChanged( bool isValid )
501{
502 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( isValid );
503}
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 ...
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
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.