28#include "moc_qgsnewvectortabledialog.cpp"
30using namespace Qt::StringLiterals;
41 mFieldModel =
new QgsNewVectorTableFieldModel( mConnection->nativeTypes(),
this );
45 QMessageBox::critical(
47 tr(
"Cannot Create New Tables" ),
49 "Error retrieving native types from the data provider: creation of new tables is not possible.\n"
54 QTimer::singleShot( 0,
this, [
this] { reject(); } );
58 Q_ASSERT( !mFieldModel->nativeTypes().isEmpty() );
61 setWindowTitle( tr(
"New Table" ) );
63 auto updateTableNames = [
this, conn](
const QString &schema = QString() ) {
67 const auto constTables { conn->
tables( schema ) };
68 for (
const auto &tp : constTables )
70 mTableNames.push_back( tp.tableName() );
77 QgsDebugError( u
"Error retrieving tables from connection: %1"_s.arg( ex.
what() ) );
82 connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset,
this, [
this]() { validate(); } );
84 mTableName->setText( u
"new_table_name"_s );
85 mFieldsTableView->setModel( mFieldModel );
86 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->nativeTypes(),
this ) };
87 mFieldsTableView->setItemDelegate( delegate );
88 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
89 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
90 mFieldsTableView->setVerticalHeader(
nullptr );
93 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
94 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
99 mSchemaCbo->addItems( mConnection->schemas() );
100 connect( mSchemaCbo, &QComboBox::currentTextChanged,
this, [updateTableNames](
const QString &schema ) { updateTableNames( schema ); } );
105 mSchemaLabel->hide();
110 mSpatialIndexChk->setChecked(
false );
111 mSpatialIndexChk->hide();
112 mSpatialIndexLabel->hide();
115 mIllegalFieldNames = mConnection->illegalFieldNames();
118 updateTableNames( mSchemaCbo->currentText() );
121 connect( mTableName, &QLineEdit::textChanged,
this, [
this](
const QString & ) { validate(); } );
123 connect( mGeomColumn, &QLineEdit::textChanged,
this, [
this](
const QString & ) { validate(); } );
126 connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [
this](
int index ) {
127 const bool hasGeom { index != 0 };
128 mGeomColumn->setEnabled( hasGeom );
129 mGeomColumnLabel->setEnabled( hasGeom );
130 mSpatialIndexChk->setEnabled( hasGeom );
131 mSpatialIndexLabel->setEnabled( hasGeom );
132 mCrs->setEnabled( hasGeom );
133 mCrsLabel->setEnabled( hasGeom );
134 mDimensionsLabel->setEnabled( hasGeom );
135 mHasMChk->setEnabled( hasGeom );
136 mHasZChk->setEnabled( hasGeom );
140 mCrs->setShowAccuracyWarnings(
true );
175 mGeomTypeCbo->setCurrentIndex( 0 );
181 mHasMChk->setEnabled(
false );
182 mHasMChk->setChecked(
false );
186 mHasZChk->setEnabled(
false );
187 mHasZChk->setChecked(
false );
189 if ( !hasM && !hasZ )
191 mHasZChk->setVisible(
false );
192 mHasMChk->setVisible(
false );
193 mDimensionsLabel->setVisible(
false );
196 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [
this](
const QItemSelection &selected,
const QItemSelection & ) {
197 if ( !selected.isEmpty() )
199 mCurrentRow = selected.indexes().first().row();
205 const QMetaType::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
206 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
209 connect( mAddFieldBtn, &QPushButton::clicked,
this, [
this, defaultFieldType, defaultFieldTypeName] {
211 QgsField newField { u
"new_field_name"_s, defaultFieldType, defaultFieldTypeName };
212 fieldList.
append( newField );
213 setFields( fieldList );
214 selectRow( fieldList.
count() - 1 );
217 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [
this] {
219 if ( fieldList.
exists( mCurrentRow ) )
221 fieldList.
remove( mCurrentRow );
222 setFields( fieldList );
227 connect( mFieldUpBtn, &QPushButton::clicked,
this, [
this] {
228 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
231 for (
int i = 0; i < fields().count(); ++i )
233 if ( i == mCurrentRow - 1 )
235 fieldList.
append( fields().at( mCurrentRow ) );
236 fieldList.
append( fields().at( mCurrentRow - 1 ) );
238 else if ( i != mCurrentRow )
240 fieldList.
append( fields().at( i ) );
243 setFields( fieldList );
244 selectRow( mCurrentRow - 1 );
248 connect( mFieldDownBtn, &QPushButton::clicked,
this, [
this] {
249 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
252 for (
int i = 0; i < fields().count(); ++i )
254 if ( i == mCurrentRow )
256 fieldList.
append( fields().at( mCurrentRow + 1 ) );
257 fieldList.
append( fields().at( mCurrentRow ) );
259 else if ( i != mCurrentRow + 1 )
261 fieldList.
append( fields().at( i ) );
264 setFields( fieldList );
265 selectRow( mCurrentRow + 1 );
275 mSchemaCbo->setCurrentText( name );
280 mTableName->setText( name );
285 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData(
static_cast<quint32
>( type ) ) );
300 return mTableName->text();
305 return mSchemaCbo->currentText();
310 return mGeomColumn->text();
315 return mFieldModel ? mFieldModel->fields() :
QgsFields();
321 if ( mHasMChk->isChecked() )
325 if ( mHasZChk->isChecked() )
337 mFieldModel->setFields(
fields );
343 return mSpatialIndexChk->isChecked();
348 return mValidationErrors;
351void QgsNewVectorTableDialog::updateButtons()
353 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
354 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
355 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
358void QgsNewVectorTableDialog::selectRow(
int row )
360 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
361 mFieldsTableView->setCurrentIndex( index );
362 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
363 mFieldsTableView->selectionModel()->select( index, flags );
364 mFieldsTableView->scrollTo( index );
367void QgsNewVectorTableDialog::validate()
369 mValidationErrors.clear();
371 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
372 if ( mTableName->text().trimmed().isEmpty() )
374 mValidationErrors.push_back( tr(
"Table name cannot be empty" ) );
376 else if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
378 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists" ).arg( mTableName->text() ) );
381 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
383 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name" ).arg( mGeomColumn->text() ) );
386 if ( !isSpatial &&
fields().count() == 0 )
388 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields" ) );
391 const QgsFields cFields {
fields() };
392 for (
const QgsField &f : cFields )
394 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
396 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length" ).arg( f.name() ) );
399 if ( f.name().trimmed().isEmpty() )
401 mValidationErrors.push_back( tr(
"Field name cannot be empty" ) );
405 for (
const QString &illegalName : std::as_const( mIllegalFieldNames ) )
407 if ( f.name().compare( illegalName, Qt::CaseInsensitive ) == 0 )
409 mValidationErrors.push_back( tr(
"<b>%1</b> is an illegal field name for this format and cannot be used" ).arg( f.name() ) );
415 const bool isValid { mValidationErrors.isEmpty() };
418 mValidationResults->setText( mValidationErrors.join(
"<br>"_L1 ) );
421 mValidationFrame->setVisible( !isValid );
422 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
427 QDialog::showEvent( event );
428 mTableName->setFocus();
429 mTableName->selectAll();
436QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
437 : QStyledItemDelegate( parent )
438 , mTypeList( typeList )
441QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
443 switch ( index.column() )
445 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
447 QComboBox *cbo =
new QComboBox { parent };
448 cbo->setEditable(
false );
449 cbo->setFrame(
false );
450 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
451 for (
const auto &f : std::as_const( mTypeList ) )
457 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
459 QSpinBox *sp {
new QSpinBox { parent } };
460 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
464 sp->setRange( nt.
mMinPrec, std::min<int>( nt.
mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
468 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
470 QSpinBox *sp {
new QSpinBox { parent } };
471 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
475 sp->setRange( std::max<int>( nt.
mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.
mMaxLen );
481 return QStyledItemDelegate::createEditor( parent, option, index );
486void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
488 const auto m { index.model() };
489 switch ( index.column() )
491 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
493 const QString txt = m->data( index, Qt::DisplayRole ).toString();
494 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
497 cbo->setCurrentIndex( cbo->findText( txt ) );
501 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
502 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
504 const int value = m->data( index, Qt::DisplayRole ).toInt();
505 QSpinBox *sp { qobject_cast<QSpinBox *>( editor ) };
508 sp->setValue( value );
514 QStyledItemDelegate::setEditorData( editor, index );
519void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
521 switch ( index.column() )
523 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
525 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
528 model->setData( index, cbo->currentData() );
534 QStyledItemDelegate::setModelData( editor, model, index );
539void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
542 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
545 emit commitData( cb );
549QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
551 , mNativeTypes( typeList )
554int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
559QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
561 if ( mFields.exists( index.row() ) )
563 const QgsField field { mFields.at( index.row() ) };
566 case Qt::ItemDataRole::DisplayRole:
568 switch (
static_cast<ColumnHeaders
>( index.column() ) )
570 case ColumnHeaders::Name:
574 case ColumnHeaders::Type:
576 return typeDesc( field.
typeName() );
578 case ColumnHeaders::ProviderType:
582 case ColumnHeaders::Comment:
586 case ColumnHeaders::Precision:
590 case ColumnHeaders::Length:
599 case Qt::ItemDataRole::TextAlignmentRole:
601 switch (
static_cast<ColumnHeaders
>( index.column() ) )
603 case ColumnHeaders::Precision:
604 case ColumnHeaders::Length:
606 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
615 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
625QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
627 if ( orientation == Qt::Orientation::Horizontal )
631 case Qt::ItemDataRole::DisplayRole:
633 switch (
static_cast<ColumnHeaders
>( section ) )
635 case ColumnHeaders::Name:
639 case ColumnHeaders::Type:
643 case ColumnHeaders::Comment:
645 return tr(
"Comment" );
647 case ColumnHeaders::ProviderType:
649 return tr(
"Provider type" );
651 case ColumnHeaders::Length:
653 return tr(
"Length" );
655 case ColumnHeaders::Precision:
657 return tr(
"Precision" );
664 case Qt::ItemDataRole::TextAlignmentRole:
666 switch (
static_cast<ColumnHeaders
>( section ) )
668 case ColumnHeaders::Name:
669 case ColumnHeaders::Comment:
670 case ColumnHeaders::Type:
671 case ColumnHeaders::ProviderType:
673 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
677 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
684 QgsFieldModel::headerData( section, orientation, role );
691Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
693 switch (
static_cast<ColumnHeaders
>( index.column() ) )
695 case ColumnHeaders::Name:
696 case ColumnHeaders::Comment:
697 case ColumnHeaders::Type:
699 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
701 case ColumnHeaders::Length:
703 if ( mFields.exists( index.row() ) )
708 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
713 case ColumnHeaders::Precision:
715 if ( mFields.exists( index.row() ) )
720 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
725 case ColumnHeaders::ProviderType:
727 return QgsFieldModel::flags( index );
730 return QgsFieldModel::flags( index );
733QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
738QString QgsNewVectorTableFieldModel::typeDesc(
const QString &typeName )
const
740 for (
const auto &t : std::as_const( mNativeTypes ) )
742 if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
750QMetaType::Type QgsNewVectorTableFieldModel::type(
const QString &typeName )
const
752 return nativeType( typeName ).mType;
757 for (
const auto &t : std::as_const( mNativeTypes ) )
759 if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
765 QgsDebugError( u
"Cannot get field native type for: %1"_s.arg( typeName ) );
766 return mNativeTypes.first();
771 if ( mFields.exists( row ) )
773 return nativeType( mFields.at( row ).typeName() );
776 QgsDebugError( u
"Cannot get field for row: %1"_s.arg( row ) );
777 return mNativeTypes.first();
780bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
782 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
784 const int fieldIdx { index.row() };
785 QgsField field { mFields.at( fieldIdx ) };
786 switch (
static_cast<ColumnHeaders
>( index.column() ) )
788 case ColumnHeaders::Name:
790 field.
setName( value.toString() );
793 case ColumnHeaders::Type:
796 const auto tp { nativeType( value.toString() ) };
798 field.
setLength( std::max( std::min<int>( field.
length(), tp.mMaxLen ), tp.mMinLen ) );
802 case ColumnHeaders::Comment:
807 case ColumnHeaders::ProviderType:
812 case ColumnHeaders::Length:
817 case ColumnHeaders::Precision:
825 for (
int i = 0; i < mFields.count(); ++i )
833 fields.
append( mFields.at( i ) );
838 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.
Provides common functionality for database based connections.
@ 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).
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Represents a coordinate reference system (CRS).
A model which displays the list of fields in widgets (optionally associated with a vector layer).
QVariant data(const QModelIndex &index, int role) const override
Encapsulate a field in an attribute table or data source.
QString typeName() const
Gets the field type.
void setPrecision(int precision)
Set the field precision.
void setName(const QString &name)
Set the field name.
void setComment(const QString &comment)
Set the field comment.
void setType(QMetaType::Type type)
Set variant type.
void setLength(int len)
Set the field length.
void setTypeName(const QString &typeName)
Set the field type.
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.
Q_INVOKABLE bool exists(int i) const
Returns if a field index is valid.
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 Q_INVOKABLE 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)