QGIS API Documentation 3.43.0-Master (b60ef06885e)
qgsdbimportvectorlayerdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdbimportvectorlayerdialog.cpp
3 --------------------------------------
4 Date : March 2025
5 Copyright : (C) 2025 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgsdbimportvectorlayerdialog.cpp"
19#include "qgsgui.h"
20#include "qgsvectorlayer.h"
22#include "qgsmapcanvas.h"
25#include "qgsproviderregistry.h"
26#include <QPushButton>
27#include <QItemSelectionModel>
28
30 : QDialog( parent )
31 , mConnection( connection )
32{
33 setupUi( this );
34 setObjectName( "QgsDbImportVectorLayerDialog" );
36
37 mSourceLayerComboBox->setFilters( Qgis::LayerFilter::VectorLayer );
38 connect( mSourceLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsDbImportVectorLayerDialog::sourceLayerComboChanged );
39 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, [this]( const QgsCoordinateReferenceSystem &crs ) {
40 mExtentGroupBox->setOutputCrs( crs );
41 } );
42
43 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
44 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsDbImportVectorLayerDialog::doImport );
45
46 Q_ASSERT( connection );
47
48 mFieldsView->setDestinationEditable( true );
49 try
50 {
51 mFieldsView->setNativeTypes( connection->nativeTypes() );
52 }
54 {
55 QgsDebugError( QStringLiteral( "Could not retrieve connection native types: %1" ).arg( e.what() ) );
56 }
57 connect( mResetButton, &QPushButton::clicked, this, &QgsDbImportVectorLayerDialog::loadFieldsFromLayer );
58 connect( mAddButton, &QPushButton::clicked, this, &QgsDbImportVectorLayerDialog::addField );
59 connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::removeSelectedFields );
60 connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsUp );
61 connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsDown );
62
63 const bool supportsSchemas = mConnection->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Schemas );
64 if ( supportsSchemas )
65 {
66 std::unique_ptr<QgsAbstractDatabaseProviderConnection> schemeComboConn;
67 QgsProviderMetadata *md = QgsProviderRegistry::instance()->providerMetadata( mConnection->providerKey() );
68 mSchemaCombo = new QgsDatabaseSchemaComboBox( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( mConnection->uri(), QVariantMap() ) ) );
69 mLayoutSchemeCombo->addWidget( mSchemaCombo );
70 }
71 else
72 {
73 delete mLabelSchemas;
74 mLabelSchemas = nullptr;
75 delete mLayoutSchemeCombo;
76 mLayoutSchemeCombo = nullptr;
77 }
78
79 const bool supportsPrimaryKeyName = mConnection->tableImportCapabilities().testFlag( Qgis::DatabaseProviderTableImportCapability::SetPrimaryKeyName );
80 if ( !supportsPrimaryKeyName )
81 {
82 delete mLabelPrimaryKey;
83 mLabelPrimaryKey = nullptr;
84 delete mEditPrimaryKey;
85 mEditPrimaryKey = nullptr;
86 }
87
88 const bool supportsGeomColumnName = mConnection->tableImportCapabilities().testFlag( Qgis::DatabaseProviderTableImportCapability::SetGeometryColumnName );
89 if ( !supportsGeomColumnName )
90 {
91 delete mLabelGeometryColumn;
92 mLabelGeometryColumn = nullptr;
93 delete mEditGeometryColumnName;
94 mEditGeometryColumnName = nullptr;
95 }
96
97 const bool supportsTableComments = mConnection->capabilities2().testFlag( Qgis::DatabaseProviderConnectionCapability2::SetTableComment );
98 if ( !supportsTableComments )
99 {
100 delete mLabelComment;
101 mLabelComment = nullptr;
102 delete mEditComment;
103 mEditComment = nullptr;
104 }
105
106 mExtentGroupBox->setTitleBase( tr( "Filter by Extent" ) );
107 mExtentGroupBox->setCheckable( true );
108 mExtentGroupBox->setChecked( false );
109 mExtentGroupBox->setCollapsed( true );
110
111 mFilterExpressionWidget->registerExpressionContextGenerator( this );
112
113 // populate initial layer
114 sourceLayerComboChanged();
115}
116
118{
119 // these widgets all potentially access mOwnedSource, so we need to force
120 // them to be deleted BEFORE the layer
121 delete mSourceLayerComboBox;
122 mSourceLayerComboBox = nullptr;
123 delete mFilterExpressionWidget;
124 mFilterExpressionWidget = nullptr;
125 delete mFieldsView;
126 mFieldsView = nullptr;
127}
128
130{
131 if ( mSchemaCombo )
132 mSchemaCombo->setSchema( schema );
133}
134
136{
137 mOwnedSource.reset();
138 mSourceLayer = nullptr;
139
140 bool owner = false;
141 QString error;
142 QgsVectorLayer *vl = uri.vectorLayer( owner, error );
143 if ( owner )
144 {
145 mOwnedSource.reset( vl );
146 mBlockSourceLayerChanges++;
147 mSourceLayerComboBox->setAdditionalLayers( { vl } );
148 mSourceLayerComboBox->setLayer( vl );
149 mBlockSourceLayerChanges--;
150 setSourceLayer( mOwnedSource.get() );
151 }
152 else if ( vl )
153 {
154 mBlockSourceLayerChanges++;
155 mSourceLayerComboBox->setLayer( vl );
156 mBlockSourceLayerChanges--;
157 setSourceLayer( vl );
158 }
159}
160
161void QgsDbImportVectorLayerDialog::setSourceLayer( QgsVectorLayer *layer )
162{
163 mSourceLayer = layer;
164 if ( !mSourceLayer || !mSourceLayer->dataProvider() )
165 return;
166
167 mEditTable->setText( layer->name() );
168
169 const bool isSpatial = mSourceLayer->isSpatial();
170 if ( mEditGeometryColumnName )
171 mEditGeometryColumnName->setEnabled( isSpatial );
172 if ( mCrsSelector )
173 mCrsSelector->setEnabled( isSpatial );
174
175 mExtentGroupBox->setEnabled( isSpatial );
176 if ( !isSpatial )
177 mExtentGroupBox->setChecked( false );
178
179 const bool extentFilterEnabled = mExtentGroupBox->isChecked();
180 mExtentGroupBox->setOriginalExtent( mSourceLayer->extent(), mSourceLayer->crs() );
181 mExtentGroupBox->setOutputExtentFromOriginal();
182 mExtentGroupBox->setChecked( extentFilterEnabled );
183 mExtentGroupBox->setCollapsed( !extentFilterEnabled );
184
185 mFilterExpressionWidget->setLayer( mSourceLayer );
186
187 if ( mEditPrimaryKey )
188 {
189 // set initial geometry column name. We use the source layer's primary key column name if available,
190 // else fallback to a default value given by the connection
191 const QgsAttributeList pkAttributes = mSourceLayer->dataProvider()->pkAttributeIndexes();
192 QString primaryKey = !pkAttributes.isEmpty() ? mSourceLayer->dataProvider()->fields().at( pkAttributes.at( 0 ) ).name() : QString();
193 if ( primaryKey.isEmpty() )
194 {
195 QgsDataSourceUri dsUri( mSourceLayer->source() );
196 primaryKey = dsUri.keyColumn();
197 }
198 if ( primaryKey.isEmpty() )
199 {
200 primaryKey = mConnection->defaultPrimaryKeyColumnName();
201 }
202
203 mEditPrimaryKey->setText( primaryKey );
204 }
205
206 if ( mEditGeometryColumnName )
207 {
208 // set initial geometry column name. We use the source layer's geometry name if available,
209 // else fallback to a default value given by the connection
210 QString geomColumn = mSourceLayer->dataProvider()->geometryColumnName();
211 if ( geomColumn.isEmpty() )
212 {
213 QgsDataSourceUri dsUri( mSourceLayer->source() );
214 geomColumn = dsUri.geometryColumn();
215 }
216 if ( geomColumn.isEmpty() )
217 {
218 geomColumn = mConnection->defaultGeometryColumnName();
219 }
220
221 mEditGeometryColumnName->setText( geomColumn );
222 }
223
224 if ( mCrsSelector )
225 {
226 mCrsSelector->setCrs( mSourceLayer->crs() );
227 }
228
229 if ( mEditComment )
230 {
231 mEditComment->setPlainText( mSourceLayer->metadata().abstract() );
232 }
233
234 mFieldsView->setSourceLayer( mSourceLayer );
235 mFieldsView->setSourceFields( mSourceLayer->fields() );
236 mFieldsView->setDestinationFields( mSourceLayer->fields() );
237}
238
239void QgsDbImportVectorLayerDialog::loadFieldsFromLayer()
240{
241 if ( mSourceLayer )
242 {
243 mFieldsView->setSourceFields( mSourceLayer->fields() );
244 mFieldsView->setDestinationFields( mSourceLayer->fields() );
245 }
246}
247
248void QgsDbImportVectorLayerDialog::addField()
249{
250 const int rowCount = mFieldsView->model()->rowCount();
251 mFieldsView->appendField( QgsField( QStringLiteral( "new_field" ) ), QStringLiteral( "NULL" ) );
252 const QModelIndex index = mFieldsView->model()->index( rowCount, 0 );
253 mFieldsView->selectionModel()->select(
254 index,
255 QItemSelectionModel::SelectionFlags(
256 QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows
257 )
258 );
259 mFieldsView->scrollTo( index );
260}
261
263{
264 return mSchemaCombo ? mSchemaCombo->currentSchema() : QString();
265}
266
268{
269 return mEditTable->text();
270}
271
273{
274 return mEditComment ? mEditComment->toPlainText() : QString();
275}
276
278{
279 if ( canvas )
280 {
281 mExtentGroupBox->setCurrentExtent( canvas->mapSettings().visibleExtent(), canvas->mapSettings().destinationCrs() );
282 mExtentGroupBox->setMapCanvas( canvas, false );
283 }
284}
285
286void QgsDbImportVectorLayerDialog::doImport()
287{
288 // TODO -- validate
289
290 accept();
291}
292
293std::unique_ptr<QgsVectorLayerExporterTask> QgsDbImportVectorLayerDialog::createExporterTask( const QVariantMap &extraProviderOptions )
294{
295 if ( !mSourceLayer || !mSourceLayer->dataProvider() )
296 return nullptr;
297
298 QString destinationUri;
299 QVariantMap providerOptions;
300
302 exporterOptions.layerName = mEditTable->text();
303 if ( mSchemaCombo )
304 exporterOptions.schema = mSchemaCombo->currentSchema();
305 exporterOptions.wkbType = mSourceLayer->wkbType();
306 if ( mEditPrimaryKey && !mEditPrimaryKey->text().trimmed().isEmpty() )
307 exporterOptions.primaryKeyColumns << mEditPrimaryKey->text();
308 if ( mEditGeometryColumnName )
309 exporterOptions.geometryColumn = mEditGeometryColumnName->text();
310
311 try
312 {
313 destinationUri = mConnection->createVectorLayerExporterDestinationUri( exporterOptions, providerOptions );
314 }
316 {
317 return nullptr;
318 }
319
320 // options given to us by createVectorLayerExporterDestinationUri above should overwrite generic ones passed to this method
321 QVariantMap allProviderOptions = extraProviderOptions;
322 for ( auto it = providerOptions.constBegin(); it != providerOptions.constEnd(); ++it )
323 {
324 allProviderOptions.insert( it.key(), it.value() );
325 }
326
327 // overwrite?
328 if ( mChkDropTable->isChecked() )
329 {
330 allProviderOptions.insert( QStringLiteral( "overwrite" ), true );
331 }
332
333 // This flag tells to the provider that field types do not need conversion -- we have already
334 // explicitly set all fields to provider-specific field types and we do not need to treat
335 // them as generic/different provider fields
336 allProviderOptions.insert( QStringLiteral( "skipConvertFields" ), true );
337
339 if ( mCrsSelector )
340 {
341 exportOptions.setDestinationCrs( mCrsSelector->crs() );
342 }
343 exportOptions.setTransformContext( mSourceLayer->transformContext() );
344 if ( !mFilterExpressionWidget->expression().isEmpty() )
345 {
346 exportOptions.setFilterExpression( mFilterExpressionWidget->expression() );
348 }
349
350 if ( mExtentGroupBox->isEnabled() && mExtentGroupBox->isChecked() )
351 {
352 exportOptions.setExtent( QgsReferencedRectangle( mExtentGroupBox->outputExtent(), mExtentGroupBox->outputCrs() ) );
353 }
354
355 const QList<QgsFieldMappingModel::Field> fieldMapping = mFieldsView->mapping();
356 QList<QgsVectorLayerExporter::OutputField> outputFields;
357 outputFields.reserve( fieldMapping.size() );
358 for ( const QgsFieldMappingModel::Field &field : fieldMapping )
359 {
360 outputFields.append( QgsVectorLayerExporter::OutputField( field.field, field.expression ) );
361 }
362 exportOptions.setOutputFields( outputFields );
363
364 return std::make_unique<QgsVectorLayerExporterTask>( mSourceLayer->clone(), destinationUri, mConnection->providerKey(), exportOptions, allProviderOptions, true );
365}
366
373
374void QgsDbImportVectorLayerDialog::sourceLayerComboChanged()
375{
376 if ( mBlockSourceLayerChanges )
377 return;
378
379 if ( mSourceLayerComboBox->currentLayer() == mSourceLayer )
380 return;
381
382 setSourceLayer( qobject_cast< QgsVectorLayer * >( mSourceLayerComboBox->currentLayer() ) );
383}
@ SetPrimaryKeyName
Can set the name of the primary key column.
@ SetGeometryColumnName
Can set the name of the geometry column.
@ SetTableComment
Can set comments for tables via setTableComment()
Provides common functionality for database based connections.
@ Schemas
Can list schemas (if not set, the connection does not support schemas)
virtual QList< QgsVectorDataProvider::NativeType > nativeTypes() const =0
Returns a list of native types supported by the connection.
Represents a coordinate reference system (CRS).
Stores the component parts of a data source URI (e.g.
A combo box which displays the list of schemas for a specific database connection.
void setSchema(const QString &schema)
Sets the current schema selected in the combo box.
QString currentSchema() const
Returns the name of the current schema selected in the combo box.
void setDestinationSchema(const QString &schema)
Sets the destination schema for the new table.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
std::unique_ptr< QgsVectorLayerExporterTask > createExporterTask(const QVariantMap &extraProviderOptions=QVariantMap())
Creates a new exporter task to match the settings defined in the dialog.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the dialog.
void setSourceUri(const QgsMimeDataUtils::Uri &uri)
Sets the source table uri.
QString schema() const
Returns the destination schema.
QString tableName() const
Returns the destination table name.
QString tableComment() const
Returns the optional comment to use for the new table.
QgsDbImportVectorLayerDialog(QgsAbstractDatabaseProviderConnection *connection, QWidget *parent=nullptr)
Constructor for QgsDbImportVectorLayerDialog.
QString what() const
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
bool removeSelectedFields()
Removes the currently selected field from the model.
bool moveSelectedFieldsDown()
Moves down the currently selected field.
bool moveSelectedFieldsUp()
Moves up currently selected field.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
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...
Definition qgsgui.cpp:210
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
QString name
Definition qgsmaplayer.h:81
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
Custom exception class for provider connection related exceptions.
Holds data provider key, description, and associated shared library file or function pointer informat...
virtual QgsAbstractProviderConnection * createConnection(const QString &uri, const QVariantMap &configuration)
Creates a new connection from uri and configuration, the newly created connection is not automaticall...
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
A QgsRectangle with associated coordinate reference system.
Encapsulates options for use with QgsVectorLayerExporter.
void setExtent(const QgsReferencedRectangle &extent)
Sets an extent filter for the features to export.
void setOutputFields(const QList< QgsVectorLayerExporter::OutputField > &fields)
Sets the output field definitions for the destination table.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system to use for exported features.
void setFilterExpression(const QString &expression)
Set the filter expression for the features to export.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context to use when a filterExpression() is set.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context to use when transforming exported features.
Represents a vector layer which manages a vector based dataset.
QList< int > QgsAttributeList
Definition qgsfield.h:27
#define QgsDebugError(str)
Definition qgslogger.h:40
const QgsCoordinateReferenceSystem & crs
Stores all information required to create a QgsVectorLayerExporter for the backend.
QStringList primaryKeyColumns
List of primary key column names. Note that some providers may ignore this if not supported.
QString schema
Optional schema for the new layer. May not be supported by all providers.
QString geometryColumn
Preferred name for the geometry column, if required. Note that some providers may ignore this if a sp...
The Field struct holds information about a mapped field.
QgsVectorLayer * vectorLayer(bool &owner, QString &error) const
Gets vector layer from uri if possible, otherwise returns nullptr and error is set.
Encapsulates output field definition.