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 );
176 mGeomTypeCbo->setCurrentIndex( 0 );
182 mHasMChk->setEnabled(
false );
183 mHasMChk->setChecked(
false );
187 mHasZChk->setEnabled(
false );
188 mHasZChk->setChecked(
false );
190 if ( !hasM && !hasZ )
192 mHasZChk->setVisible(
false );
193 mHasMChk->setVisible(
false );
194 mDimensionsLabel->setVisible(
false );
197 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [
this](
const QItemSelection &selected,
const QItemSelection & ) {
198 if ( !selected.isEmpty() )
200 mCurrentRow = selected.indexes().first().row();
206 const QMetaType::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
207 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
210 connect( mAddFieldBtn, &QPushButton::clicked,
this, [
this, defaultFieldType, defaultFieldTypeName] {
212 QgsField newField { u
"new_field_name"_s, defaultFieldType, defaultFieldTypeName };
213 fieldList.
append( newField );
214 setFields( fieldList );
215 selectRow( fieldList.
count() - 1 );
218 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [
this] {
220 if ( fieldList.
exists( mCurrentRow ) )
222 fieldList.
remove( mCurrentRow );
223 setFields( fieldList );
228 connect( mFieldUpBtn, &QPushButton::clicked,
this, [
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, [
this] {
250 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
253 for (
int i = 0; i < fields().count(); ++i )
255 if ( i == mCurrentRow )
257 fieldList.
append( fields().at( mCurrentRow + 1 ) );
258 fieldList.
append( fields().at( mCurrentRow ) );
260 else if ( i != mCurrentRow + 1 )
262 fieldList.
append( fields().at( i ) );
265 setFields( fieldList );
266 selectRow( mCurrentRow + 1 );
276 mSchemaCbo->setCurrentText( name );
281 mTableName->setText( name );
286 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData(
static_cast<quint32
>( type ) ) );
301 return mTableName->text();
306 return mSchemaCbo->currentText();
311 return mGeomColumn->text();
316 return mFieldModel ? mFieldModel->fields() :
QgsFields();
322 if ( mHasMChk->isChecked() )
326 if ( mHasZChk->isChecked() )
338 mFieldModel->setFields(
fields );
344 return mSpatialIndexChk->isChecked();
349 return mValidationErrors;
352void QgsNewVectorTableDialog::updateButtons()
354 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
355 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
356 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
359void QgsNewVectorTableDialog::selectRow(
int row )
361 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
362 mFieldsTableView->setCurrentIndex( index );
363 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
364 mFieldsTableView->selectionModel()->select( index, flags );
365 mFieldsTableView->scrollTo( index );
368void QgsNewVectorTableDialog::validate()
370 mValidationErrors.clear();
372 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
373 if ( mTableName->text().trimmed().isEmpty() )
375 mValidationErrors.push_back( tr(
"Table name cannot be empty" ) );
377 else if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
379 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists" ).arg( mTableName->text() ) );
382 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
384 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name" ).arg( mGeomColumn->text() ) );
387 if ( !isSpatial &&
fields().count() == 0 )
389 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields" ) );
392 const QgsFields cFields {
fields() };
393 for (
const QgsField &f : cFields )
395 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
397 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length" ).arg( f.name() ) );
400 if ( f.name().trimmed().isEmpty() )
402 mValidationErrors.push_back( tr(
"Field name cannot be empty" ) );
406 for (
const QString &illegalName : std::as_const( mIllegalFieldNames ) )
408 if ( f.name().compare( illegalName, Qt::CaseInsensitive ) == 0 )
410 mValidationErrors.push_back( tr(
"<b>%1</b> is an illegal field name for this format and cannot be used" ).arg( f.name() ) );
416 const bool isValid { mValidationErrors.isEmpty() };
419 mValidationResults->setText( mValidationErrors.join(
"<br>"_L1 ) );
422 mValidationFrame->setVisible( !isValid );
423 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
428 QDialog::showEvent( event );
429 mTableName->setFocus();
430 mTableName->selectAll();
437QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
438 : QStyledItemDelegate( parent )
439 , mTypeList( typeList )
442QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
444 switch ( index.column() )
446 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
448 QComboBox *cbo =
new QComboBox { parent };
449 cbo->setEditable(
false );
450 cbo->setFrame(
false );
451 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
452 for (
const auto &f : std::as_const( mTypeList ) )
458 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
460 QSpinBox *sp {
new QSpinBox { parent } };
461 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
465 sp->setRange( nt.
mMinPrec, std::min<int>( nt.
mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
469 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
471 QSpinBox *sp {
new QSpinBox { parent } };
472 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
476 sp->setRange( std::max<int>( nt.
mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.
mMaxLen );
482 return QStyledItemDelegate::createEditor( parent, option, index );
487void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
489 const auto m { index.model() };
490 switch ( index.column() )
492 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
494 const QString txt = m->data( index, Qt::DisplayRole ).toString();
495 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
498 cbo->setCurrentIndex( cbo->findText( txt ) );
502 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
503 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
505 const int value = m->data( index, Qt::DisplayRole ).toInt();
506 QSpinBox *sp { qobject_cast<QSpinBox *>( editor ) };
509 sp->setValue( value );
515 QStyledItemDelegate::setEditorData( editor, index );
520void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
522 switch ( index.column() )
524 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
526 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
529 model->setData( index, cbo->currentData() );
535 QStyledItemDelegate::setModelData( editor, model, index );
540void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
543 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
546 emit commitData( cb );
550QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
552 , mNativeTypes( typeList )
555int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
560QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
562 if ( mFields.exists( index.row() ) )
564 const QgsField field { mFields.at( index.row() ) };
567 case Qt::ItemDataRole::DisplayRole:
569 switch (
static_cast<ColumnHeaders
>( index.column() ) )
571 case ColumnHeaders::Name:
575 case ColumnHeaders::Type:
577 return typeDesc( field.
typeName() );
579 case ColumnHeaders::ProviderType:
583 case ColumnHeaders::Comment:
587 case ColumnHeaders::Precision:
591 case ColumnHeaders::Length:
600 case Qt::ItemDataRole::TextAlignmentRole:
602 switch (
static_cast<ColumnHeaders
>( index.column() ) )
604 case ColumnHeaders::Precision:
605 case ColumnHeaders::Length:
607 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
616 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
626QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
628 if ( orientation == Qt::Orientation::Horizontal )
632 case Qt::ItemDataRole::DisplayRole:
634 switch (
static_cast<ColumnHeaders
>( section ) )
636 case ColumnHeaders::Name:
640 case ColumnHeaders::Type:
644 case ColumnHeaders::Comment:
646 return tr(
"Comment" );
648 case ColumnHeaders::ProviderType:
650 return tr(
"Provider type" );
652 case ColumnHeaders::Length:
654 return tr(
"Length" );
656 case ColumnHeaders::Precision:
658 return tr(
"Precision" );
665 case Qt::ItemDataRole::TextAlignmentRole:
667 switch (
static_cast<ColumnHeaders
>( section ) )
669 case ColumnHeaders::Name:
670 case ColumnHeaders::Comment:
671 case ColumnHeaders::Type:
672 case ColumnHeaders::ProviderType:
674 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
678 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
685 QgsFieldModel::headerData( section, orientation, role );
692Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
694 switch (
static_cast<ColumnHeaders
>( index.column() ) )
696 case ColumnHeaders::Name:
697 case ColumnHeaders::Comment:
698 case ColumnHeaders::Type:
700 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
702 case ColumnHeaders::Length:
704 if ( mFields.exists( index.row() ) )
709 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
714 case ColumnHeaders::Precision:
716 if ( mFields.exists( index.row() ) )
721 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
726 case ColumnHeaders::ProviderType:
728 return QgsFieldModel::flags( index );
731 return QgsFieldModel::flags( index );
734QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
739QString QgsNewVectorTableFieldModel::typeDesc(
const QString &typeName )
const
741 for (
const auto &t : std::as_const( mNativeTypes ) )
743 if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
751QMetaType::Type QgsNewVectorTableFieldModel::type(
const QString &typeName )
const
753 return nativeType( typeName ).mType;
758 for (
const auto &t : std::as_const( mNativeTypes ) )
760 if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
766 QgsDebugError( u
"Cannot get field native type for: %1"_s.arg( typeName ) );
767 return mNativeTypes.first();
772 if ( mFields.exists( row ) )
774 return nativeType( mFields.at( row ).typeName() );
777 QgsDebugError( u
"Cannot get field for row: %1"_s.arg( row ) );
778 return mNativeTypes.first();
781bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
783 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
785 const int fieldIdx { index.row() };
786 QgsField field { mFields.at( fieldIdx ) };
787 switch (
static_cast<ColumnHeaders
>( index.column() ) )
789 case ColumnHeaders::Name:
791 field.
setName( value.toString() );
794 case ColumnHeaders::Type:
797 const auto tp { nativeType( value.toString() ) };
799 field.
setLength( std::max( std::min<int>( field.
length(), tp.mMaxLen ), tp.mMinLen ) );
803 case ColumnHeaders::Comment:
808 case ColumnHeaders::ProviderType:
813 case ColumnHeaders::Length:
818 case ColumnHeaders::Precision:
826 for (
int i = 0; i < mFields.count(); ++i )
834 fields.
append( mFields.at( i ) );
839 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)