35 mFieldModel =
new QgsNewVectorTableFieldModel( mConnection->
nativeTypes(),
this );
39 QMessageBox::critical(
nullptr, tr(
"Cannot Create New Tables" ), tr(
"Error retrieving native types from the data provider: creation of new tables is not possible.\n"
40 "Error message: %1" ).arg( ex.
what() ) );
41 QTimer::singleShot( 0,
this, [ = ] { reject(); } );
45 Q_ASSERT( ! mFieldModel->nativeTypes().isEmpty() );
48 setWindowTitle( tr(
"New Table" ) );
50 auto updateTableNames = [ = ](
const QString &schema = QString( ) )
55 const auto constTables { conn->
tables( schema ) };
56 for (
const auto &tp : constTables )
58 mTableNames.push_back( tp.tableName() );
65 QgsDebugError( QStringLiteral(
"Error retrieving tables from connection: %1" ).arg( ex.
what() ) );
70 connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset,
this, [ = ]()
75 mTableName->setText( QStringLiteral(
"new_table_name" ) );
76 mFieldsTableView->setModel( mFieldModel );
77 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->
nativeTypes(),
this )};
78 mFieldsTableView->setItemDelegate( delegate );
79 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
80 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
81 mFieldsTableView->setVerticalHeader(
nullptr );
84 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
85 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
90 mSchemaCbo->addItems( mConnection->
schemas() );
91 connect( mSchemaCbo, &QComboBox::currentTextChanged,
this, [ = ](
const QString & schema )
93 updateTableNames( schema );
104 mSpatialIndexChk->setChecked(
false );
105 mSpatialIndexChk->hide();
106 mSpatialIndexLabel->hide();
112 updateTableNames( mSchemaCbo->currentText() );
115 connect( mTableName, &QLineEdit::textChanged,
this, [ = ](
const QString & )
120 connect( mGeomColumn, &QLineEdit::textChanged,
this, [ = ](
const QString & )
126 connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [ = ](
int index )
128 const bool hasGeom { index != 0 };
129 mGeomColumn->setEnabled( hasGeom );
130 mGeomColumnLabel->setEnabled( hasGeom );
131 mSpatialIndexChk->setEnabled( hasGeom );
132 mSpatialIndexLabel->setEnabled( hasGeom );
133 mCrs->setEnabled( hasGeom );
134 mCrsLabel->setEnabled( hasGeom );
135 mDimensionsLabel->setEnabled( hasGeom );
136 mHasMChk->setEnabled( hasGeom );
137 mHasZChk->setEnabled( hasGeom );
141 mCrs->setShowAccuracyWarnings(
true );
172 mGeomTypeCbo->setCurrentIndex( 0 );
178 mHasMChk->setEnabled(
false );
179 mHasMChk->setChecked(
false );
183 mHasZChk->setEnabled(
false );
184 mHasZChk->setChecked(
false );
186 if ( ! hasM && ! hasM )
188 mHasZChk->setVisible(
false );
189 mHasMChk->setVisible(
false );
190 mDimensionsLabel->setVisible(
false );
193 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [ = ](
const QItemSelection & selected,
const QItemSelection & )
195 if ( ! selected.isEmpty() )
197 mCurrentRow = selected.indexes().first().row();
203 const QVariant::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
204 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
207 connect( mAddFieldBtn, &QPushButton::clicked,
this, [ = ]
210 QgsField newField { QStringLiteral(
"new_field_name" ), defaultFieldType, defaultFieldTypeName };
211 fieldList.append( newField );
212 setFields( fieldList );
213 selectRow( fieldList.count() - 1 );
216 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [ = ]
219 if ( fieldList.exists( mCurrentRow ) )
221 fieldList.
remove( mCurrentRow );
222 setFields( fieldList );
227 connect( mFieldUpBtn, &QPushButton::clicked,
this, [ = ]
229 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
232 for (
int i = 0; i < fields().count(); ++i )
234 if ( i == mCurrentRow - 1 )
236 fieldList.
append( fields().at( mCurrentRow ) );
237 fieldList.
append( fields().at( mCurrentRow - 1 ) );
239 else if ( i != mCurrentRow )
241 fieldList.
append( fields().at( i ) );
244 setFields( fieldList );
245 selectRow( mCurrentRow - 1 );
249 connect( mFieldDownBtn, &QPushButton::clicked,
this, [ = ]
251 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
254 for (
int i = 0; i < fields().count(); ++i )
256 if ( i == mCurrentRow )
258 fieldList.
append( fields().at( mCurrentRow + 1 ) );
259 fieldList.
append( fields().at( mCurrentRow ) );
261 else if ( i != mCurrentRow + 1 )
263 fieldList.
append( fields().at( i ) );
266 setFields( fieldList );
267 selectRow( mCurrentRow + 1 );
277 mSchemaCbo->setCurrentText( name );
282 mTableName->setText( name );
287 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData(
static_cast< quint32
>( type ) ) );
302 return mTableName->text();
307 return mSchemaCbo->currentText();
312 return mGeomColumn->text();
317 return mFieldModel ? mFieldModel->fields() :
QgsFields();
323 if ( mHasMChk->isChecked() )
327 if ( mHasZChk->isChecked() )
339 mFieldModel->setFields(
fields );
345 return mSpatialIndexChk->isChecked();
350 return mValidationErrors;
353void QgsNewVectorTableDialog::updateButtons()
355 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
356 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
357 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
360void QgsNewVectorTableDialog::selectRow(
int row )
362 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
363 mFieldsTableView->setCurrentIndex( index );
364 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
365 mFieldsTableView->selectionModel()->select( index, flags );
366 mFieldsTableView->scrollTo( index );
369void QgsNewVectorTableDialog::validate()
371 mValidationErrors.clear();
373 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
374 if ( mTableName->text().trimmed().isEmpty() )
376 mValidationErrors.push_back( tr(
"Table name cannot be empty" ) );
378 else if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
380 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists" ).arg( mTableName->text() ) );
383 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
385 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name" ).arg( mGeomColumn->text() ) );
388 if ( ! isSpatial &&
fields().count() == 0 )
390 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields" ) );
396 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
398 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length" ).arg( f.name() ) );
401 if ( f.name().trimmed().isEmpty() )
403 mValidationErrors.push_back( tr(
"Field name cannot be empty" ) );
407 for (
const QString &illegalName : std::as_const( mIllegalFieldNames ) )
409 if ( f.name().compare( illegalName, Qt::CaseInsensitive ) == 0 )
411 mValidationErrors.push_back( tr(
"<b>%1</b> is an illegal field name for this format and cannot be used" ).arg( f.name() ) );
417 const bool isValid { mValidationErrors.isEmpty() };
420 mValidationResults->setText( mValidationErrors.join( QLatin1String(
"<br>" ) ) );
423 mValidationFrame->setVisible( ! isValid );
424 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
429 QDialog::showEvent( event );
430 mTableName->setFocus();
431 mTableName->selectAll();
438QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
439 : QStyledItemDelegate( parent )
440 , mTypeList( typeList )
445QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
447 switch ( index.column() )
449 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
451 QComboBox *cbo =
new QComboBox { parent };
452 cbo->setEditable(
false );
453 cbo->setFrame(
false );
454 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
455 for (
const auto &f : std::as_const( mTypeList ) )
461 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
463 QSpinBox *sp {
new QSpinBox { parent } };
464 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
468 sp->setRange( nt.mMinPrec, std::min<int>( nt.mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
472 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
474 QSpinBox *sp {
new QSpinBox { parent } };
475 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
479 sp->setRange( std::max<int>( nt.mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.mMaxLen );
485 return QStyledItemDelegate::createEditor( parent, option, index );
490void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
492 const auto m { index.model() };
493 switch ( index.column() )
495 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
497 const QString txt = m->data( index, Qt::DisplayRole ).toString();
498 QComboBox *cbo{ qobject_cast<QComboBox *>( editor ) };
501 cbo->setCurrentIndex( cbo->findText( txt ) );
505 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
506 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
508 const int value = m->data( index, Qt::DisplayRole ).toInt();
509 QSpinBox *sp{ qobject_cast<QSpinBox *>( editor ) };
512 sp->setValue( value );
518 QStyledItemDelegate::setEditorData( editor, index );
523void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
525 switch ( index.column() )
527 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
529 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
532 model->setData( index, cbo->currentData() );
538 QStyledItemDelegate::setModelData( editor, model, index );
543void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
546 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
549 emit commitData( cb );
553QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
555 , mNativeTypes( typeList )
560int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
565QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
567 if ( mFields.exists( index.row() ) )
569 const QgsField field { mFields.at( index.row() ) };
572 case Qt::ItemDataRole::DisplayRole:
574 switch (
static_cast<ColumnHeaders
>( index.column() ) )
576 case ColumnHeaders::Name:
580 case ColumnHeaders::Type:
582 return typeDesc( field.typeName() );
584 case ColumnHeaders::ProviderType:
586 return field.typeName();
588 case ColumnHeaders::Comment:
590 return field.comment();
592 case ColumnHeaders::Precision:
594 return field.precision();
596 case ColumnHeaders::Length:
598 return field.length();
605 case Qt::ItemDataRole::TextAlignmentRole:
607 switch (
static_cast<ColumnHeaders
>( index.column() ) )
609 case ColumnHeaders::Precision:
610 case ColumnHeaders::Length:
612 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
621 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
631QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
633 if ( orientation == Qt::Orientation::Horizontal )
637 case Qt::ItemDataRole::DisplayRole:
639 switch (
static_cast<ColumnHeaders
>( section ) )
641 case ColumnHeaders::Name:
645 case ColumnHeaders::Type:
649 case ColumnHeaders::Comment:
651 return tr(
"Comment" );
653 case ColumnHeaders::ProviderType:
655 return tr(
"Provider type" );
657 case ColumnHeaders::Length:
659 return tr(
"Length" );
661 case ColumnHeaders::Precision:
663 return tr(
"Precision" );
670 case Qt::ItemDataRole::TextAlignmentRole:
672 switch (
static_cast<ColumnHeaders
>( section ) )
674 case ColumnHeaders::Name:
675 case ColumnHeaders::Comment:
676 case ColumnHeaders::Type:
677 case ColumnHeaders::ProviderType:
679 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
683 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
690 QgsFieldModel::headerData( section, orientation, role );
697Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
699 switch (
static_cast<ColumnHeaders
>( index.column() ) )
701 case ColumnHeaders::Name:
702 case ColumnHeaders::Comment:
703 case ColumnHeaders::Type:
705 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
707 case ColumnHeaders::Length:
709 if ( mFields.exists( index.row( ) ) )
712 if ( nt.mMinLen < nt.mMaxLen )
714 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
719 case ColumnHeaders::Precision:
721 if ( mFields.exists( index.row( ) ) )
724 if ( nt.mMinPrec < nt.mMaxPrec )
726 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
731 case ColumnHeaders::ProviderType:
733 return QgsFieldModel::flags( index );
736 return QgsFieldModel::flags( index );
739QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
744QString QgsNewVectorTableFieldModel::typeDesc(
const QString &
typeName )
const
746 for (
const auto &t : std::as_const( mNativeTypes ) )
748 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
756QVariant::Type QgsNewVectorTableFieldModel::type(
const QString &
typeName )
const
758 return nativeType(
typeName ).mType;
763 for (
const auto &t : std::as_const( mNativeTypes ) )
765 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
772 return mNativeTypes.first();
777 if ( mFields.exists( row ) )
779 return nativeType( mFields.at( row ).typeName() );
782 QgsDebugError( QStringLiteral(
"Cannot get field for row: %1" ).arg( row ) );
783 return mNativeTypes.first();
786bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
788 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
790 const int fieldIdx { index.row() };
791 QgsField field {mFields.at( fieldIdx )};
792 switch (
static_cast<ColumnHeaders
>( index.column() ) )
794 case ColumnHeaders::Name:
796 field.
setName( value.toString() );
799 case ColumnHeaders::Type:
801 field.setTypeName( value.toString() );
802 const auto tp { nativeType( value.toString() ) };
803 field.setType( tp.mType );
804 field.setLength( std::max( std::min<int>( field.length(), tp.mMaxLen ), tp.mMinLen ) );
805 field.setPrecision( std::max( std::min<int>( field.precision(), tp.mMaxPrec ), tp.mMinPrec ) );
808 case ColumnHeaders::Comment:
810 field.setComment( value.toString() );
813 case ColumnHeaders::ProviderType:
815 field.setTypeName( value.toString() );
818 case ColumnHeaders::Length:
820 field.setLength( value.toInt() );
823 case ColumnHeaders::Precision:
825 field.setPrecision( value.toInt() );
831 for (
int i = 0; i < mFields.count(); ++i )
839 fields.
append( mFields.at( i ) );
844 return QgsFieldModel::setData( index, value, role );
WkbType
The WKB type describes the number of dimensions a geometry has.
@ CompoundCurve
CompoundCurve.
@ MultiPolygon
MultiPolygon.
@ MultiLineString
MultiLineString.
@ CurvePolygon
CurvePolygon.
@ MultiSurface
MultiSurface.
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
@ SinglePart
Multi and single part types are distinct types. Deprecated since QGIS 3.28 – use the granular SingleP...
@ SinglePolygon
Supports single polygon types (as distinct from multi polygon types) (since QGIS 3....
@ SinglePoint
Supports single point types (as distinct from multi point types) (since QGIS 3.28)
@ SingleLineString
Supports single linestring types (as distinct from multi line types) (since QGIS 3....
virtual QList< QgsAbstractDatabaseProviderConnection::TableProperty > tables(const QString &schema=QString(), const QgsAbstractDatabaseProviderConnection::TableFlags &flags=QgsAbstractDatabaseProviderConnection::TableFlags(), QgsFeedback *feedback=nullptr) const
Returns information on the tables in the given schema.
@ CreateSpatialIndex
The connection can create spatial indices.
@ Schemas
Can list schemas (if not set, the connection does not support schemas)
virtual GeometryColumnCapabilities geometryColumnCapabilities()
Returns connection geometry column capabilities (Z, M, SinglePart, Curves).
virtual QSet< QString > illegalFieldNames() const
Returns a list of field names which are considered illegal by the connection and should not be used w...
virtual QStringList schemas() const
Returns information about the existing schemas.
virtual QList< QgsVectorDataProvider::NativeType > nativeTypes() const =0
Returns a list of native types supported by the connection.
Capabilities capabilities() const
Returns connection capabilities.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class represents a coordinate reference system (CRS).
The QgsFieldModel class is a model to display the list of fields in widgets (optionally associated wi...
QVariant data(const QModelIndex &index, int role) const override
Encapsulate a field in an attribute table or data source.
void setName(const QString &name)
Set the field name.
Container of fields for a vector layer.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
void remove(int fieldIdx)
Removes the field with the given index.
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid, const QString &typeString=QString())
Returns an icon corresponding to a field type.
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...
static QIcon iconForWkbType(Qgis::WkbType type)
Returns the icon for a vector layer whose geometry type is provided.
QString schemaName() const
Returns the schema name.
bool createSpatialIndex()
Returns true if spatialindex checkbox is cheched.
void setGeometryType(Qgis::WkbType type)
Sets the geometry type.
QgsFields fields() const
Returns the fields.
QStringList validationErrors() const
Returns the validation errors or an empty list if the dialog is valid.
void setFields(const QgsFields &fields)
Sets the fields to fields.
QgsCoordinateReferenceSystem crs() const
Returns the CRS.
QString tableName() const
Returns the table name.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS to crs.
QString geometryColumnName() const
Returns the geometry column name.
void setSchemaName(const QString &name)
Sets the schema name.
Qgis::WkbType geometryType() const
Returns the geometry type.
QgsNewVectorTableDialog(QgsAbstractDatabaseProviderConnection *conn, QWidget *parent=nullptr)
QgsNewVectorTableDialog constructor.
void setTableName(const QString &name)
Sets the table name.
void showEvent(QShowEvent *event) override
Custom exception class for provider connection related exceptions.
static QString translatedDisplayString(Qgis::WkbType type)
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
#define QgsDebugError(str)
const QgsCoordinateReferenceSystem & crs