QGIS API Documentation 4.3.0-Master (91d4a333d5d)
Loading...
Searching...
No Matches
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
21#include "qgsgui.h"
22#include "qgsmapcanvas.h"
23#include "qgsproject.h"
24#include "qgsproviderregistry.h"
25#include "qgsvectorlayer.h"
27
28#include <QItemSelectionModel>
29#include <QMenu>
30#include <QPushButton>
31#include <QString>
32
33#include "moc_qgsdbimportvectorlayerdialog.cpp"
34
35using namespace Qt::StringLiterals;
36
38 : QDialog( parent )
39 , mConnection( connection )
40{
41 setupUi( this );
42 setObjectName( "QgsDbImportVectorLayerDialog" );
44
45 mSourceLayerComboBox->setFilters( Qgis::LayerFilter::VectorLayer );
46 connect( mSourceLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsDbImportVectorLayerDialog::sourceLayerComboChanged );
47 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, [this]( const QgsCoordinateReferenceSystem &crs ) { mExtentGroupBox->setOutputCrs( crs ); } );
48
49 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
50 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsDbImportVectorLayerDialog::doImport );
51
52 Q_ASSERT( connection );
53
54 mFieldsView->setDestinationEditable( true );
55 try
56 {
57 mFieldsView->setNativeTypes( connection->nativeTypes() );
58 }
60 {
61 QgsDebugError( u"Could not retrieve connection native types: %1"_s.arg( e.what() ) );
62 }
63 connect( mResetButton, &QPushButton::clicked, this, &QgsDbImportVectorLayerDialog::loadFieldsFromLayer );
64 connect( mAddButton, &QPushButton::clicked, this, &QgsDbImportVectorLayerDialog::addField );
65 connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::removeSelectedFields );
66 connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsUp );
67 connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsDown );
68
69 mEditButton->setPopupMode( QToolButton::InstantPopup ); // Show menu on button click
70
71 // Create a menu for the dropdown
72 QMenu *menu = new QMenu( mEditButton );
73
74 // Add menu items
75 menu->addAction( tr( "Convert All Field Names to Lowercase" ), this, [this]() {
76 QgsFieldMappingModel *model = mFieldsView->model();
77 for ( int i = 0; i < model->rowCount(); i++ )
78 {
79 const QModelIndex index = model->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationName ) );
80 const QString name = model->data( index, Qt::EditRole ).toString();
81 model->setData( index, name.toLower(), Qt::EditRole );
82 }
83 } );
84
85 menu->addAction( tr( "Convert All Field Names to Uppercase" ), this, [this]() {
86 QgsFieldMappingModel *model = mFieldsView->model();
87 for ( int i = 0; i < model->rowCount(); i++ )
88 {
89 const QModelIndex index = model->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationName ) );
90 const QString name = model->data( index, Qt::EditRole ).toString();
91 model->setData( index, name.toUpper(), Qt::EditRole );
92 }
93 } );
94
95 mEditButton->setMenu( menu );
96
97 const bool supportsSchemas = mConnection->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Schemas );
98 if ( supportsSchemas )
99 {
100 std::unique_ptr<QgsAbstractDatabaseProviderConnection> schemeComboConn;
101 QgsProviderMetadata *md = QgsProviderRegistry::instance()->providerMetadata( mConnection->providerKey() );
102 mSchemaCombo = new QgsDatabaseSchemaComboBox( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( mConnection->uri(), QVariantMap() ) ) );
103 mLayoutSchemeCombo->addWidget( mSchemaCombo );
104 }
105 else
106 {
107 delete mLabelSchemas;
108 mLabelSchemas = nullptr;
109 delete mLayoutSchemeCombo;
110 mLayoutSchemeCombo = nullptr;
111 }
112
113 const bool supportsPrimaryKeyName = mConnection->tableImportCapabilities().testFlag( Qgis::DatabaseProviderTableImportCapability::SetPrimaryKeyName );
114 if ( !supportsPrimaryKeyName )
115 {
116 delete mLabelPrimaryKey;
117 mLabelPrimaryKey = nullptr;
118 delete mEditPrimaryKey;
119 mEditPrimaryKey = nullptr;
120 }
121
122 const bool supportsGeomColumnName = mConnection->tableImportCapabilities().testFlag( Qgis::DatabaseProviderTableImportCapability::SetGeometryColumnName );
123 if ( !supportsGeomColumnName )
124 {
125 delete mLabelGeometryColumn;
126 mLabelGeometryColumn = nullptr;
127 delete mEditGeometryColumnName;
128 mEditGeometryColumnName = nullptr;
129 }
130
131 const bool supportsTableComments = mConnection->capabilities2().testFlag( Qgis::DatabaseProviderConnectionCapability2::SetTableComment );
132 if ( !supportsTableComments )
133 {
134 delete mLabelComment;
135 mLabelComment = nullptr;
136 delete mEditComment;
137 mEditComment = nullptr;
138 }
139
140 const bool supportsStyles = mConnection->capabilities2().testFlag( Qgis::DatabaseProviderConnectionCapability2::StyleStorage );
141 if ( !supportsStyles )
142 {
143 delete mChkSaveStyleToDatabase;
144 mChkSaveStyleToDatabase = nullptr;
145 }
146
147 mExtentGroupBox->setTitleBase( tr( "Filter by Extent" ) );
148 mExtentGroupBox->setCheckable( true );
149 mExtentGroupBox->setChecked( false );
150 mExtentGroupBox->setCollapsed( true );
151
152 mFilterExpressionWidget->registerExpressionContextGenerator( this );
153
154 // populate initial layer
155 sourceLayerComboChanged();
156}
157
159{
160 // these widgets all potentially access mOwnedSource, so we need to force
161 // them to be deleted BEFORE the layer
162 delete mSourceLayerComboBox;
163 mSourceLayerComboBox = nullptr;
164 delete mFilterExpressionWidget;
165 mFilterExpressionWidget = nullptr;
166 delete mFieldsView;
167 mFieldsView = nullptr;
168}
169
171{
172 if ( mSchemaCombo )
173 mSchemaCombo->setSchema( schema );
174}
175
177{
178 mOwnedSource.reset();
179 mSourceLayer = nullptr;
180
181 bool owner = false;
182 QString error;
183 QgsVectorLayer *vl = uri.vectorLayer( owner, error, QgsProject::instance() );
184 if ( owner )
185 {
186 mOwnedSource.reset( vl );
187 mBlockSourceLayerChanges++;
188 mSourceLayerComboBox->setAdditionalLayers( { vl } );
189 mSourceLayerComboBox->setLayer( vl );
190 mBlockSourceLayerChanges--;
191 setSourceLayer( mOwnedSource.get() );
192 }
193 else if ( vl )
194 {
195 mBlockSourceLayerChanges++;
196 mSourceLayerComboBox->setLayer( vl );
197 mBlockSourceLayerChanges--;
198 setSourceLayer( vl );
199 }
200}
201
202void QgsDbImportVectorLayerDialog::setSourceLayer( QgsVectorLayer *layer )
203{
204 mSourceLayer = layer;
205 if ( !mSourceLayer || !mSourceLayer->dataProvider() )
206 return;
207
208 mEditTable->setText( layer->name() );
209
210 const bool isSpatial = mSourceLayer->isSpatial();
211 if ( mEditGeometryColumnName )
212 mEditGeometryColumnName->setEnabled( isSpatial );
213 if ( mCrsSelector )
214 mCrsSelector->setEnabled( isSpatial );
215
216 mExtentGroupBox->setEnabled( isSpatial );
217 if ( !isSpatial )
218 mExtentGroupBox->setChecked( false );
219
220 const bool extentFilterEnabled = mExtentGroupBox->isChecked();
221 mExtentGroupBox->setOriginalExtent( mSourceLayer->extent(), mSourceLayer->crs() );
222 mExtentGroupBox->setOutputExtentFromOriginal();
223 mExtentGroupBox->setChecked( extentFilterEnabled );
224 mExtentGroupBox->setCollapsed( !extentFilterEnabled );
225
226 mFilterExpressionWidget->setLayer( mSourceLayer );
227
228 if ( mEditPrimaryKey )
229 {
230 // set initial geometry column name. We use the source layer's primary key column name if available,
231 // else fallback to a default value given by the connection
232 const QgsAttributeList pkAttributes = mSourceLayer->dataProvider()->pkAttributeIndexes();
233 QString primaryKey = !pkAttributes.isEmpty() ? mSourceLayer->dataProvider()->fields().at( pkAttributes.at( 0 ) ).name() : QString();
234 if ( primaryKey.isEmpty() )
235 {
236 QgsDataSourceUri dsUri( mSourceLayer->source() );
237 primaryKey = dsUri.keyColumn();
238 }
239 if ( primaryKey.isEmpty() )
240 {
241 primaryKey = mConnection->defaultPrimaryKeyColumnName();
242 }
243
244 mEditPrimaryKey->setText( primaryKey );
245 }
246
247 if ( mEditGeometryColumnName )
248 {
249 // set initial geometry column name. We use the source layer's geometry name if available,
250 // else fallback to a default value given by the connection
251 QString geomColumn = mSourceLayer->dataProvider()->geometryColumnName();
252 if ( geomColumn.isEmpty() )
253 {
254 QgsDataSourceUri dsUri( mSourceLayer->source() );
255 geomColumn = dsUri.geometryColumn();
256 }
257 if ( geomColumn.isEmpty() )
258 {
259 geomColumn = mConnection->defaultGeometryColumnName();
260 }
261
262 mEditGeometryColumnName->setText( geomColumn );
263 }
264
265 if ( mCrsSelector )
266 {
267 mCrsSelector->setCrs( mSourceLayer->crs() );
268 }
269
270 if ( mEditComment )
271 {
272 mEditComment->setPlainText( mSourceLayer->metadata().abstract() );
273 }
274
275 mFieldsView->setSourceLayer( mSourceLayer );
276 mFieldsView->setSourceFields( mSourceLayer->fields() );
277 mFieldsView->setDestinationFields( mSourceLayer->fields() );
278
279 const bool selectedFeatures = mSourceLayer->selectedFeatureCount() > 0;
280 mSourceLayerOnlySelected->setEnabled( selectedFeatures );
281}
282
283void QgsDbImportVectorLayerDialog::loadFieldsFromLayer()
284{
285 if ( mSourceLayer )
286 {
287 mFieldsView->setSourceFields( mSourceLayer->fields() );
288 mFieldsView->setDestinationFields( mSourceLayer->fields() );
289 }
290}
291
292void QgsDbImportVectorLayerDialog::addField()
293{
294 const int rowCount = mFieldsView->model()->rowCount();
295 mFieldsView->appendField( QgsField( u"new_field"_s ), u"NULL"_s );
296 const QModelIndex index = mFieldsView->model()->index( rowCount, 0 );
297 mFieldsView->selectionModel()->select( index, QItemSelectionModel::SelectionFlags( QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows ) );
298 mFieldsView->scrollTo( index );
299}
300
302{
303 return mSchemaCombo ? mSchemaCombo->currentSchema() : QString();
304}
305
307{
308 return mEditTable->text();
309}
310
312{
313 return mEditComment ? mEditComment->toPlainText() : QString();
314}
315
317{
318 return mChkSaveStyleToDatabase && mChkSaveStyleToDatabase->isChecked();
319}
320
322{
323 return mSourceLayer;
324}
325
327{
328 return mEditGeometryColumnName ? mEditGeometryColumnName->text() : QString();
329}
330
332{
333 if ( canvas )
334 {
335 mExtentGroupBox->setCurrentExtent( canvas->mapSettings().visibleExtent(), canvas->mapSettings().destinationCrs() );
336 mExtentGroupBox->setMapCanvas( canvas, false );
337 }
338}
339
340void QgsDbImportVectorLayerDialog::doImport()
341{
342 // TODO -- validate
343
344 accept();
345}
346
347std::unique_ptr<QgsVectorLayerExporterTask> QgsDbImportVectorLayerDialog::createExporterTask( const QVariantMap &extraProviderOptions )
348{
349 if ( !mSourceLayer || !mSourceLayer->dataProvider() )
350 return nullptr;
351
352 QString destinationUri;
353 QVariantMap providerOptions;
354
356 exporterOptions.layerName = mEditTable->text();
357 if ( mSchemaCombo )
358 exporterOptions.schema = mSchemaCombo->currentSchema();
359 exporterOptions.wkbType = mSourceLayer->wkbType();
360 if ( mEditPrimaryKey && !mEditPrimaryKey->text().trimmed().isEmpty() )
361 exporterOptions.primaryKeyColumns << mEditPrimaryKey->text();
362 if ( mEditGeometryColumnName )
363 exporterOptions.geometryColumn = mEditGeometryColumnName->text();
364
365 try
366 {
367 destinationUri = mConnection->createVectorLayerExporterDestinationUri( exporterOptions, providerOptions );
368 }
370 {
371 return nullptr;
372 }
373
374 // options given to us by createVectorLayerExporterDestinationUri above should overwrite generic ones passed to this method
375 QVariantMap allProviderOptions = extraProviderOptions;
376 for ( auto it = providerOptions.constBegin(); it != providerOptions.constEnd(); ++it )
377 {
378 allProviderOptions.insert( it.key(), it.value() );
379 }
380
381 // overwrite?
382 if ( mChkDropTable->isChecked() )
383 {
384 allProviderOptions.insert( u"overwrite"_s, true );
385 }
386
387 // This flag tells to the provider that field types do not need conversion -- we have already
388 // explicitly set all fields to provider-specific field types and we do not need to treat
389 // them as generic/different provider fields
390 allProviderOptions.insert( u"skipConvertFields"_s, true );
391
393 if ( mCrsSelector )
394 {
395 exportOptions.setDestinationCrs( mCrsSelector->crs() );
396 }
397 exportOptions.setTransformContext( mSourceLayer->transformContext() );
398 if ( !mFilterExpressionWidget->expression().isEmpty() )
399 {
400 exportOptions.setFilterExpression( mFilterExpressionWidget->expression() );
402 }
403
404 if ( mExtentGroupBox->isEnabled() && mExtentGroupBox->isChecked() )
405 {
406 exportOptions.setExtent( QgsReferencedRectangle( mExtentGroupBox->outputExtent(), mExtentGroupBox->outputCrs() ) );
407 }
408
409 if ( mSourceLayerOnlySelected->isEnabled() && mSourceLayerOnlySelected->isChecked() )
410 {
411 exportOptions.setSelectedOnly( true );
412 }
413
414 const QList<QgsFieldMappingModel::Field> fieldMapping = mFieldsView->mapping();
415 QList<QgsVectorLayerExporter::OutputField> outputFields;
416 outputFields.reserve( fieldMapping.size() );
417 for ( const QgsFieldMappingModel::Field &field : fieldMapping )
418 {
419 outputFields.append( QgsVectorLayerExporter::OutputField( field.field, field.expression ) );
420 }
421 exportOptions.setOutputFields( outputFields );
422
423 return std::make_unique<QgsVectorLayerExporterTask>( mSourceLayer->clone(), destinationUri, mConnection->providerKey(), exportOptions, allProviderOptions, true );
424}
425
432
433void QgsDbImportVectorLayerDialog::sourceLayerComboChanged()
434{
435 if ( mBlockSourceLayerChanges )
436 return;
437
438 if ( mSourceLayerComboBox->currentLayer() == mSourceLayer )
439 return;
440
441 setSourceLayer( qobject_cast< QgsVectorLayer * >( mSourceLayerComboBox->currentLayer() ) );
442}
@ SetPrimaryKeyName
Can set the name of the primary key column.
Definition qgis.h:6242
@ SetGeometryColumnName
Can set the name of the geometry column.
Definition qgis.h:6241
@ SetTableComment
Can set comments for tables via setTableComment().
Definition qgis.h:6225
@ StyleStorage
Can save/load styles to/from the database.
Definition qgis.h:6228
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 setDestinationSchema(const QString &schema)
Sets the destination schema for the new table.
QgsVectorLayer * sourceLayer() const
Returns the source layer, or nullptr if no source layer is set.
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.
bool saveStyleToDatabase() const
Returns true if the style should be saved to the database after import.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the dialog.
QString geometryColumn() const
Returns the geometry column name for the destination table.
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 std::vector< std::unique_ptr< 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.
Holds mapping information for mapping from one set of QgsFields to another.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
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.
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:225
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:88
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.
static QgsProject * instance()
Returns the QgsProject singleton instance.
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 setSelectedOnly(bool selected)
Sets whether the export should only include selected features.
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:30
#define QgsDebugError(str)
Definition qgslogger.h:71
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.
Q_DECL_DEPRECATED 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.