27#include "moc_qgsnewvectortabledialog.cpp"
38 mFieldModel =
new QgsNewVectorTableFieldModel( mConnection->nativeTypes(),
this );
42 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"
45 QTimer::singleShot( 0,
this, [
this] { reject(); } );
49 Q_ASSERT( !mFieldModel->nativeTypes().isEmpty() );
52 setWindowTitle( tr(
"New Table" ) );
54 auto updateTableNames = [
this, conn](
const QString &schema = QString() ) {
58 const auto constTables { conn->
tables( schema ) };
59 for (
const auto &tp : constTables )
61 mTableNames.push_back( tp.tableName() );
68 QgsDebugError( QStringLiteral(
"Error retrieving tables from connection: %1" ).arg( ex.
what() ) );
73 connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset,
this, [
this]() {
77 mTableName->setText( QStringLiteral(
"new_table_name" ) );
78 mFieldsTableView->setModel( mFieldModel );
79 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->nativeTypes(),
this ) };
80 mFieldsTableView->setItemDelegate( delegate );
81 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
82 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
83 mFieldsTableView->setVerticalHeader(
nullptr );
86 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
87 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
92 mSchemaCbo->addItems( mConnection->schemas() );
93 connect( mSchemaCbo, &QComboBox::currentTextChanged,
this, [updateTableNames](
const QString &schema ) {
94 updateTableNames( schema );
100 mSchemaLabel->hide();
105 mSpatialIndexChk->setChecked(
false );
106 mSpatialIndexChk->hide();
107 mSpatialIndexLabel->hide();
110 mIllegalFieldNames = mConnection->illegalFieldNames();
113 updateTableNames( mSchemaCbo->currentText() );
116 connect( mTableName, &QLineEdit::textChanged,
this, [
this](
const QString & ) {
120 connect( mGeomColumn, &QLineEdit::textChanged,
this, [
this](
const QString & ) {
125 connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [
this](
int index ) {
126 const bool hasGeom { index != 0 };
127 mGeomColumn->setEnabled( hasGeom );
128 mGeomColumnLabel->setEnabled( hasGeom );
129 mSpatialIndexChk->setEnabled( hasGeom );
130 mSpatialIndexLabel->setEnabled( hasGeom );
131 mCrs->setEnabled( hasGeom );
132 mCrsLabel->setEnabled( hasGeom );
133 mDimensionsLabel->setEnabled( hasGeom );
134 mHasMChk->setEnabled( hasGeom );
135 mHasZChk->setEnabled( hasGeom );
139 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 { QStringLiteral(
"new_field_name" ), 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( QLatin1String(
"<br>" ) ) );
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 )
443QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
445 switch ( index.column() )
447 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
449 QComboBox *cbo =
new QComboBox { parent };
450 cbo->setEditable(
false );
451 cbo->setFrame(
false );
452 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
453 for (
const auto &f : std::as_const( mTypeList ) )
459 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
461 QSpinBox *sp {
new QSpinBox { parent } };
462 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
466 sp->setRange( nt.
mMinPrec, std::min<int>( nt.
mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
470 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
472 QSpinBox *sp {
new QSpinBox { parent } };
473 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
477 sp->setRange( std::max<int>( nt.
mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.
mMaxLen );
483 return QStyledItemDelegate::createEditor( parent, option, index );
488void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
490 const auto m { index.model() };
491 switch ( index.column() )
493 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
495 const QString txt = m->data( index, Qt::DisplayRole ).toString();
496 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
499 cbo->setCurrentIndex( cbo->findText( txt ) );
503 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
504 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
506 const int value = m->data( index, Qt::DisplayRole ).toInt();
507 QSpinBox *sp { qobject_cast<QSpinBox *>( editor ) };
510 sp->setValue( value );
516 QStyledItemDelegate::setEditorData( editor, index );
521void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
523 switch ( index.column() )
525 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
527 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
530 model->setData( index, cbo->currentData() );
536 QStyledItemDelegate::setModelData( editor, model, index );
541void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
544 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
547 emit commitData( cb );
551QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
553 , mNativeTypes( typeList )
557int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
562QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
564 if ( mFields.exists( index.row() ) )
566 const QgsField field { mFields.at( index.row() ) };
569 case Qt::ItemDataRole::DisplayRole:
571 switch (
static_cast<ColumnHeaders
>( index.column() ) )
573 case ColumnHeaders::Name:
577 case ColumnHeaders::Type:
579 return typeDesc( field.
typeName() );
581 case ColumnHeaders::ProviderType:
585 case ColumnHeaders::Comment:
589 case ColumnHeaders::Precision:
593 case ColumnHeaders::Length:
602 case Qt::ItemDataRole::TextAlignmentRole:
604 switch (
static_cast<ColumnHeaders
>( index.column() ) )
606 case ColumnHeaders::Precision:
607 case ColumnHeaders::Length:
609 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
618 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
628QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
630 if ( orientation == Qt::Orientation::Horizontal )
634 case Qt::ItemDataRole::DisplayRole:
636 switch (
static_cast<ColumnHeaders
>( section ) )
638 case ColumnHeaders::Name:
642 case ColumnHeaders::Type:
646 case ColumnHeaders::Comment:
648 return tr(
"Comment" );
650 case ColumnHeaders::ProviderType:
652 return tr(
"Provider type" );
654 case ColumnHeaders::Length:
656 return tr(
"Length" );
658 case ColumnHeaders::Precision:
660 return tr(
"Precision" );
667 case Qt::ItemDataRole::TextAlignmentRole:
669 switch (
static_cast<ColumnHeaders
>( section ) )
671 case ColumnHeaders::Name:
672 case ColumnHeaders::Comment:
673 case ColumnHeaders::Type:
674 case ColumnHeaders::ProviderType:
676 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
680 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
687 QgsFieldModel::headerData( section, orientation, role );
694Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
696 switch (
static_cast<ColumnHeaders
>( index.column() ) )
698 case ColumnHeaders::Name:
699 case ColumnHeaders::Comment:
700 case ColumnHeaders::Type:
702 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
704 case ColumnHeaders::Length:
706 if ( mFields.exists( index.row() ) )
711 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
716 case ColumnHeaders::Precision:
718 if ( mFields.exists( index.row() ) )
723 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
728 case ColumnHeaders::ProviderType:
730 return QgsFieldModel::flags( index );
733 return QgsFieldModel::flags( index );
736QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
741QString QgsNewVectorTableFieldModel::typeDesc(
const QString &typeName )
const
743 for (
const auto &t : std::as_const( mNativeTypes ) )
745 if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
753QMetaType::Type QgsNewVectorTableFieldModel::type(
const QString &typeName )
const
755 return nativeType( typeName ).mType;
760 for (
const auto &t : std::as_const( mNativeTypes ) )
762 if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
768 QgsDebugError( QStringLiteral(
"Cannot get field native type for: %1" ).arg( typeName ) );
769 return mNativeTypes.first();
774 if ( mFields.exists( row ) )
776 return nativeType( mFields.at( row ).typeName() );
779 QgsDebugError( QStringLiteral(
"Cannot get field for row: %1" ).arg( row ) );
780 return mNativeTypes.first();
783bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
785 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
787 const int fieldIdx { index.row() };
788 QgsField field { mFields.at( fieldIdx ) };
789 switch (
static_cast<ColumnHeaders
>( index.column() ) )
791 case ColumnHeaders::Name:
793 field.
setName( value.toString() );
796 case ColumnHeaders::Type:
799 const auto tp { nativeType( value.toString() ) };
801 field.
setLength( std::max( std::min<int>( field.
length(), tp.mMaxLen ), tp.mMinLen ) );
805 case ColumnHeaders::Comment:
810 case ColumnHeaders::ProviderType:
815 case ColumnHeaders::Length:
820 case ColumnHeaders::Precision:
828 for (
int i = 0; i < mFields.count(); ++i )
836 fields.
append( mFields.at( i ) );
841 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)