28#include "moc_qgsnewvectortabledialog.cpp"
30using namespace Qt::StringLiterals;
41 mFieldModel =
new QgsNewVectorTableFieldModel( mConnection->nativeTypes(),
this );
45 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"
48 QTimer::singleShot( 0,
this, [
this] { reject(); } );
52 Q_ASSERT( !mFieldModel->nativeTypes().isEmpty() );
55 setWindowTitle( tr(
"New Table" ) );
57 auto updateTableNames = [
this, conn](
const QString &schema = QString() ) {
61 const auto constTables { conn->
tables( schema ) };
62 for (
const auto &tp : constTables )
64 mTableNames.push_back( tp.tableName() );
71 QgsDebugError( u
"Error retrieving tables from connection: %1"_s.arg( ex.
what() ) );
76 connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset,
this, [
this]() {
80 mTableName->setText( u
"new_table_name"_s );
81 mFieldsTableView->setModel( mFieldModel );
82 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->nativeTypes(),
this ) };
83 mFieldsTableView->setItemDelegate( delegate );
84 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
85 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
86 mFieldsTableView->setVerticalHeader(
nullptr );
89 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
90 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
95 mSchemaCbo->addItems( mConnection->schemas() );
96 connect( mSchemaCbo, &QComboBox::currentTextChanged,
this, [updateTableNames](
const QString &schema ) {
97 updateTableNames( schema );
103 mSchemaLabel->hide();
108 mSpatialIndexChk->setChecked(
false );
109 mSpatialIndexChk->hide();
110 mSpatialIndexLabel->hide();
113 mIllegalFieldNames = mConnection->illegalFieldNames();
116 updateTableNames( mSchemaCbo->currentText() );
119 connect( mTableName, &QLineEdit::textChanged,
this, [
this](
const QString & ) {
123 connect( mGeomColumn, &QLineEdit::textChanged,
this, [
this](
const QString & ) {
128 connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [
this](
int index ) {
129 const bool hasGeom { index != 0 };
130 mGeomColumn->setEnabled( hasGeom );
131 mGeomColumnLabel->setEnabled( hasGeom );
132 mSpatialIndexChk->setEnabled( hasGeom );
133 mSpatialIndexLabel->setEnabled( hasGeom );
134 mCrs->setEnabled( hasGeom );
135 mCrsLabel->setEnabled( hasGeom );
136 mDimensionsLabel->setEnabled( hasGeom );
137 mHasMChk->setEnabled( hasGeom );
138 mHasZChk->setEnabled( hasGeom );
142 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 && !hasZ )
195 mHasZChk->setVisible(
false );
196 mHasMChk->setVisible(
false );
197 mDimensionsLabel->setVisible(
false );
200 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [
this](
const QItemSelection &selected,
const QItemSelection & ) {
201 if ( !selected.isEmpty() )
203 mCurrentRow = selected.indexes().first().row();
209 const QMetaType::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
210 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
213 connect( mAddFieldBtn, &QPushButton::clicked,
this, [
this, defaultFieldType, defaultFieldTypeName] {
215 QgsField newField { u
"new_field_name"_s, defaultFieldType, defaultFieldTypeName };
216 fieldList.
append( newField );
217 setFields( fieldList );
218 selectRow( fieldList.
count() - 1 );
221 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [
this] {
223 if ( fieldList.
exists( mCurrentRow ) )
225 fieldList.
remove( mCurrentRow );
226 setFields( fieldList );
231 connect( mFieldUpBtn, &QPushButton::clicked,
this, [
this] {
232 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
235 for (
int i = 0; i < fields().count(); ++i )
237 if ( i == mCurrentRow - 1 )
239 fieldList.
append( fields().at( mCurrentRow ) );
240 fieldList.
append( fields().at( mCurrentRow - 1 ) );
242 else if ( i != mCurrentRow )
244 fieldList.
append( fields().at( i ) );
247 setFields( fieldList );
248 selectRow( mCurrentRow - 1 );
252 connect( mFieldDownBtn, &QPushButton::clicked,
this, [
this] {
253 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
256 for (
int i = 0; i < fields().count(); ++i )
258 if ( i == mCurrentRow )
260 fieldList.
append( fields().at( mCurrentRow + 1 ) );
261 fieldList.
append( fields().at( mCurrentRow ) );
263 else if ( i != mCurrentRow + 1 )
265 fieldList.
append( fields().at( i ) );
268 setFields( fieldList );
269 selectRow( mCurrentRow + 1 );
279 mSchemaCbo->setCurrentText( name );
284 mTableName->setText( name );
289 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData(
static_cast<quint32
>( type ) ) );
304 return mTableName->text();
309 return mSchemaCbo->currentText();
314 return mGeomColumn->text();
319 return mFieldModel ? mFieldModel->fields() :
QgsFields();
325 if ( mHasMChk->isChecked() )
329 if ( mHasZChk->isChecked() )
341 mFieldModel->setFields(
fields );
347 return mSpatialIndexChk->isChecked();
352 return mValidationErrors;
355void QgsNewVectorTableDialog::updateButtons()
357 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
358 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
359 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
362void QgsNewVectorTableDialog::selectRow(
int row )
364 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
365 mFieldsTableView->setCurrentIndex( index );
366 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
367 mFieldsTableView->selectionModel()->select( index, flags );
368 mFieldsTableView->scrollTo( index );
371void QgsNewVectorTableDialog::validate()
373 mValidationErrors.clear();
375 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
376 if ( mTableName->text().trimmed().isEmpty() )
378 mValidationErrors.push_back( tr(
"Table name cannot be empty" ) );
380 else if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
382 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists" ).arg( mTableName->text() ) );
385 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
387 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name" ).arg( mGeomColumn->text() ) );
390 if ( !isSpatial &&
fields().count() == 0 )
392 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields" ) );
395 const QgsFields cFields {
fields() };
396 for (
const QgsField &f : cFields )
398 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
400 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length" ).arg( f.name() ) );
403 if ( f.name().trimmed().isEmpty() )
405 mValidationErrors.push_back( tr(
"Field name cannot be empty" ) );
409 for (
const QString &illegalName : std::as_const( mIllegalFieldNames ) )
411 if ( f.name().compare( illegalName, Qt::CaseInsensitive ) == 0 )
413 mValidationErrors.push_back( tr(
"<b>%1</b> is an illegal field name for this format and cannot be used" ).arg( f.name() ) );
419 const bool isValid { mValidationErrors.isEmpty() };
422 mValidationResults->setText( mValidationErrors.join(
"<br>"_L1 ) );
425 mValidationFrame->setVisible( !isValid );
426 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
431 QDialog::showEvent( event );
432 mTableName->setFocus();
433 mTableName->selectAll();
440QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
441 : QStyledItemDelegate( parent )
442 , mTypeList( typeList )
446QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
448 switch ( index.column() )
450 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
452 QComboBox *cbo =
new QComboBox { parent };
453 cbo->setEditable(
false );
454 cbo->setFrame(
false );
455 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
456 for (
const auto &f : std::as_const( mTypeList ) )
462 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
464 QSpinBox *sp {
new QSpinBox { parent } };
465 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
469 sp->setRange( nt.
mMinPrec, std::min<int>( nt.
mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
473 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
475 QSpinBox *sp {
new QSpinBox { parent } };
476 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
480 sp->setRange( std::max<int>( nt.
mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.
mMaxLen );
486 return QStyledItemDelegate::createEditor( parent, option, index );
491void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
493 const auto m { index.model() };
494 switch ( index.column() )
496 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
498 const QString txt = m->data( index, Qt::DisplayRole ).toString();
499 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
502 cbo->setCurrentIndex( cbo->findText( txt ) );
506 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
507 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
509 const int value = m->data( index, Qt::DisplayRole ).toInt();
510 QSpinBox *sp { qobject_cast<QSpinBox *>( editor ) };
513 sp->setValue( value );
519 QStyledItemDelegate::setEditorData( editor, index );
524void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
526 switch ( index.column() )
528 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
530 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
533 model->setData( index, cbo->currentData() );
539 QStyledItemDelegate::setModelData( editor, model, index );
544void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
547 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
550 emit commitData( cb );
554QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
556 , 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:
588 case ColumnHeaders::Comment:
592 case ColumnHeaders::Precision:
596 case ColumnHeaders::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() ) )
714 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
719 case ColumnHeaders::Precision:
721 if ( mFields.exists( index.row() ) )
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 )
756QMetaType::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 )
771 QgsDebugError( u
"Cannot get field native type for: %1"_s.arg( typeName ) );
772 return mNativeTypes.first();
777 if ( mFields.exists( row ) )
779 return nativeType( mFields.at( row ).typeName() );
782 QgsDebugError( u
"Cannot get field for row: %1"_s.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:
802 const auto tp { nativeType( value.toString() ) };
804 field.
setLength( std::max( std::min<int>( field.
length(), tp.mMaxLen ), tp.mMinLen ) );
808 case ColumnHeaders::Comment:
813 case ColumnHeaders::ProviderType:
818 case ColumnHeaders::Length:
823 case ColumnHeaders::Precision:
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.
@ 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)