QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
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] { mModel->insertRow( mModel->rowCount() ); } );
161 connect( mButtonRemoveRow, &QToolButton::clicked, this, [this] {
162 QItemSelectionModel *selectionModel = mValuesTable->selectionModel();
163 const QModelIndexList selectedRows = selectionModel->selectedIndexes();
164 if ( !selectedRows.empty() )
165 {
166 mModel->removeRow( selectedRows.first().row() );
167 }
168 } );
169
170 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsAbstractFieldDomainWidget::changed );
171 connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsAbstractFieldDomainWidget::changed );
172 connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsAbstractFieldDomainWidget::changed );
173}
174
176{
177 const QgsCodedFieldDomain *codedDomain = dynamic_cast<const QgsCodedFieldDomain *>( domain );
178 if ( !codedDomain )
179 return;
180
181 mModel->setValues( codedDomain->values() );
182}
183
184QgsFieldDomain *QgsCodedFieldDomainWidget::createFieldDomain( const QString &name, const QString &description, QMetaType::Type fieldType ) const
185{
186 return new QgsCodedFieldDomain( name, description, fieldType, mModel->values() );
187}
188
190{
191 return true;
192}
193
194
195//
196// QgsCodedValueTableModel
197//
198
200 : QAbstractTableModel( parent )
201{}
202
203int QgsCodedValueTableModel::rowCount( const QModelIndex & ) const
204{
205 return mValues.count();
206}
207
208int QgsCodedValueTableModel::columnCount( const QModelIndex & ) const
209{
210 return 2;
211}
212
213QVariant QgsCodedValueTableModel::data( const QModelIndex &index, int role ) const
214{
215 if ( index.row() < 0 || index.row() >= mValues.count() || index.column() < 0 || index.column() >= 2 )
216 return QVariant();
217
218 const QgsCodedValue &value = mValues[index.row()];
219 switch ( role )
220 {
221 case Qt::DisplayRole:
222 case Qt::EditRole:
223 case Qt::ToolTipRole:
224 {
225 switch ( index.column() )
226 {
227 case 0:
228 return value.code();
229 case 1:
230 return value.value();
231 }
232 break;
233 }
234
235 default:
236 break;
237 }
238 return QVariant();
239}
240
241bool QgsCodedValueTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
242{
243 if ( index.row() < 0 || index.row() >= mValues.count() || index.column() < 0 || index.column() >= 2 )
244 return false;
245
246 const QgsCodedValue codedValue = mValues.at( index.row() );
247 switch ( role )
248 {
249 case Qt::EditRole:
250
251 switch ( index.column() )
252 {
253 case 0:
254 {
255 const QgsCodedValue newValue( value.toString(), codedValue.value() );
256 mValues.replace( index.row(), newValue );
257 emit dataChanged( index, index );
258 return true;
259 }
260
261 case 1:
262 {
263 const QgsCodedValue newValue( codedValue.code(), value.toString() );
264 mValues.replace( index.row(), newValue );
265 emit dataChanged( index, index );
266 return true;
267 }
268
269 default:
270 break;
271 }
272
273 break;
274 }
275 return false;
276}
277
278Qt::ItemFlags QgsCodedValueTableModel::flags( const QModelIndex &index ) const
279{
280 if ( index.row() < 0 || index.row() >= mValues.size() || index.column() < 0 || index.column() >= columnCount() )
281 return QAbstractTableModel::flags( index );
282
283 return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable;
284}
285
286QVariant QgsCodedValueTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
287{
288 switch ( orientation )
289 {
290 case Qt::Horizontal:
291 switch ( role )
292 {
293 case Qt::DisplayRole:
294 case Qt::ToolTipRole:
295 {
296 switch ( section )
297 {
298 case 0:
299 return tr( "Code" );
300 case 1:
301 return tr( "Value" );
302 default:
303 break;
304 }
305 }
306 break;
307 default:
308 break;
309 }
310
311 break;
312 case Qt::Vertical:
313 break;
314 }
315 return QVariant();
316}
317
318bool QgsCodedValueTableModel::insertRows( int row, int count, const QModelIndex &parent )
319{
320 if ( parent.isValid() )
321 return false;
322
323 beginInsertRows( QModelIndex(), row, row + count - 1 );
324 for ( int i = row; i < row + count; ++i )
325 {
326 mValues.insert( i, QgsCodedValue( QString(), QString() ) );
327 }
328 endInsertRows();
329 return true;
330}
331
332bool QgsCodedValueTableModel::removeRows( int row, int count, const QModelIndex &parent )
333{
334 if ( row < 0 || row >= mValues.count() )
335 return false;
336
337 if ( parent.isValid() )
338 return false;
339
340 for ( int i = row + count - 1; i >= row; --i )
341 {
342 beginRemoveRows( parent, i, i );
343 mValues.removeAt( i );
344 endRemoveRows();
345 }
346 return true;
347}
348
349void QgsCodedValueTableModel::setValues( const QList<QgsCodedValue> &values )
350{
351 beginResetModel();
352 mValues = values;
353 endResetModel();
354}
355
356//
357// QgsFieldDomainWidget
358//
359
361 : QWidget( parent )
362{
363 setupUi( this );
364
365 mComboSplitPolicy->addItem( tr( "Default Value" ), static_cast<int>( Qgis::FieldDomainSplitPolicy::DefaultValue ) );
366 mComboSplitPolicy->addItem( tr( "Duplicate" ), static_cast<int>( Qgis::FieldDomainSplitPolicy::Duplicate ) );
367 mComboSplitPolicy->addItem( tr( "Ratio of Geometries" ), static_cast<int>( Qgis::FieldDomainSplitPolicy::GeometryRatio ) );
368
369 mComboMergePolicy->addItem( tr( "Default Value" ), static_cast<int>( Qgis::FieldDomainMergePolicy::DefaultValue ) );
370 mComboMergePolicy->addItem( tr( "Sum" ), static_cast<int>( Qgis::FieldDomainMergePolicy::Sum ) );
371 mComboMergePolicy->addItem( tr( "Geometry Weighted Average" ), static_cast<int>( Qgis::FieldDomainMergePolicy::GeometryWeighted ) );
372
373 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), static_cast<int>( QMetaType::Type::Bool ) );
374 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), static_cast<int>( QMetaType::Type::QString ) );
375 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), static_cast<int>( QMetaType::Type::Int ) );
376 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::LongLong ), static_cast<int>( QMetaType::Type::LongLong ) );
377 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), static_cast<int>( QMetaType::Type::Double ) );
378#if 0 // not supported by any formats yet...
379 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Date ), static_cast< int >( QVariant::Date ) );
380 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::Time ), static_cast< int >( QVariant::Time ) );
381 mFieldTypeCombo->addItem( QgsVariantUtils::typeToDisplayString( QVariant::DateTime ), static_cast< int >( QVariant::DateTime ) );
382#endif
383
384 switch ( type )
385 {
387 mDomainWidget = new QgsCodedFieldDomainWidget();
388 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( QMetaType::Type::QString ) ) );
389 break;
390
392 mDomainWidget = new QgsRangeDomainWidget();
393 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( QMetaType::Type::Double ) ) );
394 break;
395
397 mDomainWidget = new QgsGlobDomainWidget();
398 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( QMetaType::Type::QString ) ) );
399 break;
400 }
401
402 mStackedWidget->addWidget( mDomainWidget );
403 mStackedWidget->setCurrentWidget( mDomainWidget );
404
405 connect( mNameEdit, &QLineEdit::textChanged, this, [this] { emit validityChanged( isValid() ); } );
406
407 connect( mDomainWidget, &QgsAbstractFieldDomainWidget::changed, this, [this] { emit validityChanged( isValid() ); } );
408}
409
411{
412 if ( !domain )
413 return;
414
415 mNameEdit->setText( domain->name() );
416 mDescriptionEdit->setText( domain->description() );
417 mComboMergePolicy->setCurrentIndex( mComboMergePolicy->findData( static_cast<int>( domain->mergePolicy() ) ) );
418 mComboSplitPolicy->setCurrentIndex( mComboSplitPolicy->findData( static_cast<int>( domain->splitPolicy() ) ) );
419 mFieldTypeCombo->setCurrentIndex( mFieldTypeCombo->findData( static_cast<int>( domain->fieldType() ) ) );
420
421 if ( mDomainWidget )
422 mDomainWidget->setFieldDomain( domain );
423}
424
426{
427 if ( !mDomainWidget )
428 return nullptr;
429
430 std::unique_ptr<QgsFieldDomain> res( mDomainWidget->createFieldDomain( mNameEdit->text(), mDescriptionEdit->text(), static_cast<QMetaType::Type>( mFieldTypeCombo->currentData().toInt() ) ) );
431
432 res->setMergePolicy( static_cast<Qgis::FieldDomainMergePolicy>( mComboMergePolicy->currentData().toInt() ) );
433 res->setSplitPolicy( static_cast<Qgis::FieldDomainSplitPolicy>( mComboSplitPolicy->currentData().toInt() ) );
434 return res.release();
435}
436
438{
439 if ( mNameEdit->text().trimmed().isEmpty() )
440 return false;
441
442 return mDomainWidget && mDomainWidget->isValid();
443}
444
445//
446// QgsFieldDomainDialog
447//
448
449QgsFieldDomainDialog::QgsFieldDomainDialog( Qgis::FieldDomainType type, QWidget *parent, Qt::WindowFlags flags )
450 : QDialog( parent, flags )
451{
452 setObjectName( u"QgsFieldDomainDialog"_s );
453
454 QVBoxLayout *vLayout = new QVBoxLayout();
455 mWidget = new QgsFieldDomainWidget( type );
456 vLayout->addWidget( mWidget, 1 );
457
458 mButtonBox = new QDialogButtonBox( QDialogButtonBox::StandardButton::Cancel | QDialogButtonBox::StandardButton::Help | QDialogButtonBox::StandardButton::Ok );
459 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
460 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
461 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, [] { QgsHelp::openHelp( u"managing_data_source/supported_data.rst#field-domain"_s ); } );
462 vLayout->addWidget( mButtonBox );
463
464 setLayout( vLayout );
465 connect( mWidget, &QgsFieldDomainWidget::validityChanged, this, &QgsFieldDomainDialog::validityChanged );
466 validityChanged( mWidget->isValid() );
467
469}
470
472{
473 mWidget->setFieldDomain( domain );
474}
475
477{
478 return mWidget->createFieldDomain();
479}
480
482{
483 if ( !mWidget->isValid() )
484 return;
485
486 QDialog::accept();
487}
488
489void QgsFieldDomainDialog::validityChanged( bool isValid )
490{
491 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( isValid );
492}
FieldDomainMergePolicy
Merge policy for field domains.
Definition qgis.h:4040
@ GeometryWeighted
New values are computed as the weighted average of the source values.
Definition qgis.h:4043
@ DefaultValue
Use default field value.
Definition qgis.h:4041
@ Sum
Sum of values.
Definition qgis.h:4042
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:4023
@ GeometryRatio
New values are computed by the ratio of their area/length compared to the area/length of the original...
Definition qgis.h:4026
@ DefaultValue
Use default field value.
Definition qgis.h:4024
@ Duplicate
Duplicate original value.
Definition qgis.h:4025
FieldDomainType
Types of field domain.
Definition qgis.h:4073
@ Coded
Coded field domain.
Definition qgis.h:4074
@ Range
Numeric range field domain (min/max).
Definition qgis.h:4075
@ Glob
Glob string pattern field domain.
Definition qgis.h:4076
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.