17#include "moc_qgsnewvectortabledialog.cpp"
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"
42 QTimer::singleShot( 0,
this, [=] { reject(); } );
46 Q_ASSERT( !mFieldModel->nativeTypes().isEmpty() );
49 setWindowTitle( tr(
"New Table" ) );
51 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, [=]() {
74 mTableName->setText( QStringLiteral(
"new_table_name" ) );
75 mFieldsTableView->setModel( mFieldModel );
76 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->
nativeTypes(),
this ) };
77 mFieldsTableView->setItemDelegate( delegate );
78 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
79 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
80 mFieldsTableView->setVerticalHeader(
nullptr );
83 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
84 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
89 mSchemaCbo->addItems( mConnection->
schemas() );
90 connect( mSchemaCbo, &QComboBox::currentTextChanged,
this, [=](
const QString &schema ) {
91 updateTableNames( schema );
102 mSpatialIndexChk->setChecked(
false );
103 mSpatialIndexChk->hide();
104 mSpatialIndexLabel->hide();
110 updateTableNames( mSchemaCbo->currentText() );
113 connect( mTableName, &QLineEdit::textChanged,
this, [=](
const QString & ) {
117 connect( mGeomColumn, &QLineEdit::textChanged,
this, [=](
const QString & ) {
122 connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [=](
int index ) {
123 const bool hasGeom { index != 0 };
124 mGeomColumn->setEnabled( hasGeom );
125 mGeomColumnLabel->setEnabled( hasGeom );
126 mSpatialIndexChk->setEnabled( hasGeom );
127 mSpatialIndexLabel->setEnabled( hasGeom );
128 mCrs->setEnabled( hasGeom );
129 mCrsLabel->setEnabled( hasGeom );
130 mDimensionsLabel->setEnabled( hasGeom );
131 mHasMChk->setEnabled( hasGeom );
132 mHasZChk->setEnabled( hasGeom );
136 mCrs->setShowAccuracyWarnings(
true );
173 mGeomTypeCbo->setCurrentIndex( 0 );
179 mHasMChk->setEnabled(
false );
180 mHasMChk->setChecked(
false );
184 mHasZChk->setEnabled(
false );
185 mHasZChk->setChecked(
false );
187 if ( !hasM && !hasZ )
189 mHasZChk->setVisible(
false );
190 mHasMChk->setVisible(
false );
191 mDimensionsLabel->setVisible(
false );
194 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [=](
const QItemSelection &selected,
const QItemSelection & ) {
195 if ( !selected.isEmpty() )
197 mCurrentRow = selected.indexes().first().row();
203 const QMetaType::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
204 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
207 connect( mAddFieldBtn, &QPushButton::clicked,
this, [=] {
209 QgsField newField { QStringLiteral(
"new_field_name" ), defaultFieldType, defaultFieldTypeName };
210 fieldList.append( newField );
211 setFields( fieldList );
212 selectRow( fieldList.count() - 1 );
215 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [=] {
217 if ( fieldList.exists( mCurrentRow ) )
219 fieldList.
remove( mCurrentRow );
220 setFields( fieldList );
225 connect( mFieldUpBtn, &QPushButton::clicked,
this, [=] {
226 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
229 for (
int i = 0; i < fields().count(); ++i )
231 if ( i == mCurrentRow - 1 )
233 fieldList.
append( fields().at( mCurrentRow ) );
234 fieldList.
append( fields().at( mCurrentRow - 1 ) );
236 else if ( i != mCurrentRow )
238 fieldList.
append( fields().at( i ) );
241 setFields( fieldList );
242 selectRow( mCurrentRow - 1 );
246 connect( mFieldDownBtn, &QPushButton::clicked,
this, [=] {
247 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
250 for (
int i = 0; i < fields().count(); ++i )
252 if ( i == mCurrentRow )
254 fieldList.
append( fields().at( mCurrentRow + 1 ) );
255 fieldList.
append( fields().at( mCurrentRow ) );
257 else if ( i != mCurrentRow + 1 )
259 fieldList.
append( fields().at( i ) );
262 setFields( fieldList );
263 selectRow( mCurrentRow + 1 );
273 mSchemaCbo->setCurrentText( name );
278 mTableName->setText( name );
283 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData(
static_cast<quint32
>( type ) ) );
298 return mTableName->text();
303 return mSchemaCbo->currentText();
308 return mGeomColumn->text();
313 return mFieldModel ? mFieldModel->fields() :
QgsFields();
319 if ( mHasMChk->isChecked() )
323 if ( mHasZChk->isChecked() )
335 mFieldModel->setFields(
fields );
341 return mSpatialIndexChk->isChecked();
346 return mValidationErrors;
349void QgsNewVectorTableDialog::updateButtons()
351 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
352 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
353 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
356void QgsNewVectorTableDialog::selectRow(
int row )
358 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
359 mFieldsTableView->setCurrentIndex( index );
360 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
361 mFieldsTableView->selectionModel()->select( index, flags );
362 mFieldsTableView->scrollTo( index );
365void QgsNewVectorTableDialog::validate()
367 mValidationErrors.clear();
369 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
370 if ( mTableName->text().trimmed().isEmpty() )
372 mValidationErrors.push_back( tr(
"Table name cannot be empty" ) );
374 else if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
376 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists" ).arg( mTableName->text() ) );
379 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
381 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name" ).arg( mGeomColumn->text() ) );
384 if ( !isSpatial &&
fields().count() == 0 )
386 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields" ) );
392 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
394 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length" ).arg( f.name() ) );
397 if ( f.name().trimmed().isEmpty() )
399 mValidationErrors.push_back( tr(
"Field name cannot be empty" ) );
403 for (
const QString &illegalName : std::as_const( mIllegalFieldNames ) )
405 if ( f.name().compare( illegalName, Qt::CaseInsensitive ) == 0 )
407 mValidationErrors.push_back( tr(
"<b>%1</b> is an illegal field name for this format and cannot be used" ).arg( f.name() ) );
413 const bool isValid { mValidationErrors.isEmpty() };
416 mValidationResults->setText( mValidationErrors.join( QLatin1String(
"<br>" ) ) );
419 mValidationFrame->setVisible( !isValid );
420 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
425 QDialog::showEvent( event );
426 mTableName->setFocus();
427 mTableName->selectAll();
434QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
435 : QStyledItemDelegate( parent )
436 , mTypeList( typeList )
440QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
442 switch ( index.column() )
444 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
446 QComboBox *cbo =
new QComboBox { parent };
447 cbo->setEditable(
false );
448 cbo->setFrame(
false );
449 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
450 for (
const auto &f : std::as_const( mTypeList ) )
456 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
458 QSpinBox *sp {
new QSpinBox { parent } };
459 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
463 sp->setRange( nt.mMinPrec, std::min<int>( nt.mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
467 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
469 QSpinBox *sp {
new QSpinBox { parent } };
470 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() ) };
474 sp->setRange( std::max<int>( nt.mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.mMaxLen );
480 return QStyledItemDelegate::createEditor( parent, option, index );
485void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
487 const auto m { index.model() };
488 switch ( index.column() )
490 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
492 const QString txt = m->data( index, Qt::DisplayRole ).toString();
493 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
496 cbo->setCurrentIndex( cbo->findText( txt ) );
500 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
501 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
503 const int value = m->data( index, Qt::DisplayRole ).toInt();
504 QSpinBox *sp { qobject_cast<QSpinBox *>( editor ) };
507 sp->setValue( value );
513 QStyledItemDelegate::setEditorData( editor, index );
518void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
520 switch ( index.column() )
522 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
524 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
527 model->setData( index, cbo->currentData() );
533 QStyledItemDelegate::setModelData( editor, model, index );
538void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
541 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
544 emit commitData( cb );
548QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
550 , 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:
580 return field.typeName();
582 case ColumnHeaders::Comment:
584 return field.comment();
586 case ColumnHeaders::Precision:
588 return field.precision();
590 case ColumnHeaders::Length:
592 return field.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() ) )
706 if ( nt.mMinLen < nt.mMaxLen )
708 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
713 case ColumnHeaders::Precision:
715 if ( mFields.exists( index.row() ) )
718 if ( nt.mMinPrec < nt.mMaxPrec )
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 )
766 return mNativeTypes.first();
771 if ( mFields.exists( row ) )
773 return nativeType( mFields.at( row ).typeName() );
776 QgsDebugError( QStringLiteral(
"Cannot get field for row: %1" ).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:
795 field.setTypeName( value.toString() );
796 const auto tp { nativeType( value.toString() ) };
797 field.setType( tp.mType );
798 field.setLength( std::max( std::min<int>( field.length(), tp.mMaxLen ), tp.mMinLen ) );
799 field.setPrecision( std::max( std::min<int>( field.precision(), tp.mMaxPrec ), tp.mMinPrec ) );
802 case ColumnHeaders::Comment:
804 field.setComment( value.toString() );
807 case ColumnHeaders::ProviderType:
809 field.setTypeName( value.toString() );
812 case ColumnHeaders::Length:
814 field.setLength( value.toInt() );
817 case ColumnHeaders::Precision:
819 field.setPrecision( value.toInt() );
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.
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