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 );
179 mGeomTypeCbo->setCurrentIndex( 0 );
185 mHasMChk->setEnabled(
false );
186 mHasMChk->setChecked(
false );
190 mHasZChk->setEnabled(
false );
191 mHasZChk->setChecked(
false );
193 if ( ! hasM && ! hasM )
195 mHasZChk->setVisible(
false );
196 mHasMChk->setVisible(
false );
197 mDimensionsLabel->setVisible(
false );
200 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [ = ](
const QItemSelection & selected,
const QItemSelection & )
202 if ( ! selected.isEmpty() )
204 mCurrentRow = selected.indexes().first().row();
210 const QMetaType::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
211 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
214 connect( mAddFieldBtn, &QPushButton::clicked,
this, [ = ]
217 QgsField newField { QStringLiteral(
"new_field_name" ), defaultFieldType, defaultFieldTypeName };
218 fieldList.append( newField );
219 setFields( fieldList );
220 selectRow( fieldList.count() - 1 );
223 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [ = ]
226 if ( fieldList.exists( mCurrentRow ) )
228 fieldList.
remove( mCurrentRow );
229 setFields( fieldList );
234 connect( mFieldUpBtn, &QPushButton::clicked,
this, [ = ]
236 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
239 for (
int i = 0; i < fields().count(); ++i )
241 if ( i == mCurrentRow - 1 )
243 fieldList.
append( fields().at( mCurrentRow ) );
244 fieldList.
append( fields().at( mCurrentRow - 1 ) );
246 else if ( i != mCurrentRow )
248 fieldList.
append( fields().at( i ) );
251 setFields( fieldList );
252 selectRow( mCurrentRow - 1 );
256 connect( mFieldDownBtn, &QPushButton::clicked,
this, [ = ]
258 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
261 for (
int i = 0; i < fields().count(); ++i )
263 if ( i == mCurrentRow )
265 fieldList.
append( fields().at( mCurrentRow + 1 ) );
266 fieldList.
append( fields().at( mCurrentRow ) );
268 else if ( i != mCurrentRow + 1 )
270 fieldList.
append( fields().at( i ) );
273 setFields( fieldList );
274 selectRow( mCurrentRow + 1 );
284 mSchemaCbo->setCurrentText( name );
289 mTableName->setText( name );
294 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData(
static_cast< quint32
>( type ) ) );
309 return mTableName->text();
314 return mSchemaCbo->currentText();
319 return mGeomColumn->text();
324 return mFieldModel ? mFieldModel->fields() :
QgsFields();
330 if ( mHasMChk->isChecked() )
334 if ( mHasZChk->isChecked() )
346 mFieldModel->setFields(
fields );
352 return mSpatialIndexChk->isChecked();
357 return mValidationErrors;
360void QgsNewVectorTableDialog::updateButtons()
362 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
363 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
364 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
367void QgsNewVectorTableDialog::selectRow(
int row )
369 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
370 mFieldsTableView->setCurrentIndex( index );
371 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
372 mFieldsTableView->selectionModel()->select( index, flags );
373 mFieldsTableView->scrollTo( index );
376void QgsNewVectorTableDialog::validate()
378 mValidationErrors.clear();
380 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
381 if ( mTableName->text().trimmed().isEmpty() )
383 mValidationErrors.push_back( tr(
"Table name cannot be empty" ) );
385 else if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
387 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists" ).arg( mTableName->text() ) );
390 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
392 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name" ).arg( mGeomColumn->text() ) );
395 if ( ! isSpatial &&
fields().count() == 0 )
397 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields" ) );
403 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
405 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length" ).arg( f.name() ) );
408 if ( f.name().trimmed().isEmpty() )
410 mValidationErrors.push_back( tr(
"Field name cannot be empty" ) );
414 for (
const QString &illegalName : std::as_const( mIllegalFieldNames ) )
416 if ( f.name().compare( illegalName, Qt::CaseInsensitive ) == 0 )
418 mValidationErrors.push_back( tr(
"<b>%1</b> is an illegal field name for this format and cannot be used" ).arg( f.name() ) );
424 const bool isValid { mValidationErrors.isEmpty() };
427 mValidationResults->setText( mValidationErrors.join( QLatin1String(
"<br>" ) ) );
430 mValidationFrame->setVisible( ! isValid );
431 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
436 QDialog::showEvent( event );
437 mTableName->setFocus();
438 mTableName->selectAll();
445QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
446 : QStyledItemDelegate( parent )
447 , mTypeList( typeList )
452QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
454 switch ( index.column() )
456 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
458 QComboBox *cbo =
new QComboBox { parent };
459 cbo->setEditable(
false );
460 cbo->setFrame(
false );
461 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
462 for (
const auto &f : std::as_const( mTypeList ) )
468 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
470 QSpinBox *sp {
new QSpinBox { parent } };
471 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
475 sp->setRange( nt.mMinPrec, std::min<int>( nt.mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
479 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
481 QSpinBox *sp {
new QSpinBox { parent } };
482 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
486 sp->setRange( std::max<int>( nt.mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.mMaxLen );
492 return QStyledItemDelegate::createEditor( parent, option, index );
497void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
499 const auto m { index.model() };
500 switch ( index.column() )
502 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
504 const QString txt = m->data( index, Qt::DisplayRole ).toString();
505 QComboBox *cbo{ qobject_cast<QComboBox *>( editor ) };
508 cbo->setCurrentIndex( cbo->findText( txt ) );
512 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
513 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
515 const int value = m->data( index, Qt::DisplayRole ).toInt();
516 QSpinBox *sp{ qobject_cast<QSpinBox *>( editor ) };
519 sp->setValue( value );
525 QStyledItemDelegate::setEditorData( editor, index );
530void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
532 switch ( index.column() )
534 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
536 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
539 model->setData( index, cbo->currentData() );
545 QStyledItemDelegate::setModelData( editor, model, index );
550void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
553 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
556 emit commitData( cb );
560QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
562 , mNativeTypes( typeList )
567int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
572QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
574 if ( mFields.exists( index.row() ) )
576 const QgsField field { mFields.at( index.row() ) };
579 case Qt::ItemDataRole::DisplayRole:
581 switch (
static_cast<ColumnHeaders
>( index.column() ) )
583 case ColumnHeaders::Name:
587 case ColumnHeaders::Type:
589 return typeDesc( field.typeName() );
591 case ColumnHeaders::ProviderType:
593 return field.typeName();
595 case ColumnHeaders::Comment:
597 return field.comment();
599 case ColumnHeaders::Precision:
601 return field.precision();
603 case ColumnHeaders::Length:
605 return field.length();
612 case Qt::ItemDataRole::TextAlignmentRole:
614 switch (
static_cast<ColumnHeaders
>( index.column() ) )
616 case ColumnHeaders::Precision:
617 case ColumnHeaders::Length:
619 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
628 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
638QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
640 if ( orientation == Qt::Orientation::Horizontal )
644 case Qt::ItemDataRole::DisplayRole:
646 switch (
static_cast<ColumnHeaders
>( section ) )
648 case ColumnHeaders::Name:
652 case ColumnHeaders::Type:
656 case ColumnHeaders::Comment:
658 return tr(
"Comment" );
660 case ColumnHeaders::ProviderType:
662 return tr(
"Provider type" );
664 case ColumnHeaders::Length:
666 return tr(
"Length" );
668 case ColumnHeaders::Precision:
670 return tr(
"Precision" );
677 case Qt::ItemDataRole::TextAlignmentRole:
679 switch (
static_cast<ColumnHeaders
>( section ) )
681 case ColumnHeaders::Name:
682 case ColumnHeaders::Comment:
683 case ColumnHeaders::Type:
684 case ColumnHeaders::ProviderType:
686 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
690 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
697 QgsFieldModel::headerData( section, orientation, role );
704Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
706 switch (
static_cast<ColumnHeaders
>( index.column() ) )
708 case ColumnHeaders::Name:
709 case ColumnHeaders::Comment:
710 case ColumnHeaders::Type:
712 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
714 case ColumnHeaders::Length:
716 if ( mFields.exists( index.row( ) ) )
719 if ( nt.mMinLen < nt.mMaxLen )
721 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
726 case ColumnHeaders::Precision:
728 if ( mFields.exists( index.row( ) ) )
731 if ( nt.mMinPrec < nt.mMaxPrec )
733 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
738 case ColumnHeaders::ProviderType:
740 return QgsFieldModel::flags( index );
743 return QgsFieldModel::flags( index );
746QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
751QString QgsNewVectorTableFieldModel::typeDesc(
const QString &
typeName )
const
753 for (
const auto &t : std::as_const( mNativeTypes ) )
755 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
763QMetaType::Type QgsNewVectorTableFieldModel::type(
const QString &
typeName )
const
765 return nativeType(
typeName ).mType;
770 for (
const auto &t : std::as_const( mNativeTypes ) )
772 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
779 return mNativeTypes.first();
784 if ( mFields.exists( row ) )
786 return nativeType( mFields.at( row ).typeName() );
789 QgsDebugError( QStringLiteral(
"Cannot get field for row: %1" ).arg( row ) );
790 return mNativeTypes.first();
793bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
795 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
797 const int fieldIdx { index.row() };
798 QgsField field {mFields.at( fieldIdx )};
799 switch (
static_cast<ColumnHeaders
>( index.column() ) )
801 case ColumnHeaders::Name:
803 field.
setName( value.toString() );
806 case ColumnHeaders::Type:
808 field.setTypeName( value.toString() );
809 const auto tp { nativeType( value.toString() ) };
810 field.setType( tp.mType );
811 field.setLength( std::max( std::min<int>( field.length(), tp.mMaxLen ), tp.mMinLen ) );
812 field.setPrecision( std::max( std::min<int>( field.precision(), tp.mMaxPrec ), tp.mMinPrec ) );
815 case ColumnHeaders::Comment:
817 field.setComment( value.toString() );
820 case ColumnHeaders::ProviderType:
822 field.setTypeName( value.toString() );
825 case ColumnHeaders::Length:
827 field.setLength( value.toInt() );
830 case ColumnHeaders::Precision:
832 field.setPrecision( value.toInt() );
838 for (
int i = 0; i < mFields.count(); ++i )
846 fields.
append( mFields.at( i ) );
851 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.
@ PolyhedralSurface
PolyhedralSurface.
@ MultiSurface
MultiSurface.
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
@ PolyhedralSurfaces
Supports polyhedral surfaces (PolyhedralSurface, TIN) types (as distinct from multi polygon types)
@ 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)
@ SinglePoint
Supports single point types (as distinct from multi point types)
@ SingleLineString
Supports single linestring types (as distinct from multi line types)
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, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
void remove(int fieldIdx)
Removes the field with the given index.
static QIcon iconForFieldType(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType, 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 checked.
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