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