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