QGIS API Documentation 3.43.0-Master (9e873c7bc91)
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#include "moc_qgsdbimportvectorlayerdialog.cpp"
19#include "qgsgui.h"
20#include "qgsvectorlayer.h"
22#include "qgsmapcanvas.h"
24#include <QPushButton>
25#include <QItemSelectionModel>
26
28 : QDialog( parent )
29 , mConnection( connection )
30{
31 setupUi( this );
32 setObjectName( "QgsDbImportVectorLayerDialog" );
34
35 mSourceLayerComboBox->setFilters( Qgis::LayerFilter::VectorLayer );
36 connect( mSourceLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsDbImportVectorLayerDialog::sourceLayerComboChanged );
37 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, [this]( const QgsCoordinateReferenceSystem &crs ) {
38 mExtentGroupBox->setOutputCrs( crs );
39 } );
40
41 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
42 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsDbImportVectorLayerDialog::doImport );
43
44 Q_ASSERT( connection );
45
46 mEditSchema->setReadOnly( true );
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 delete mLabelSchemas;
67 mLabelSchemas = nullptr;
68 delete mEditSchema;
69 mEditSchema = nullptr;
70 }
71 const bool supportsPrimaryKeyName = mConnection->tableImportCapabilities().testFlag( Qgis::DatabaseProviderTableImportCapability::SetPrimaryKeyName );
72 if ( !supportsPrimaryKeyName )
73 {
74 delete mLabelPrimaryKey;
75 mLabelPrimaryKey = nullptr;
76 delete mEditPrimaryKey;
77 mEditPrimaryKey = nullptr;
78 }
79
80 const bool supportsGeomColumnName = mConnection->tableImportCapabilities().testFlag( Qgis::DatabaseProviderTableImportCapability::SetGeometryColumnName );
81 if ( !supportsGeomColumnName )
82 {
83 delete mLabelGeometryColumn;
84 mLabelGeometryColumn = nullptr;
85 delete mEditGeometryColumnName;
86 mEditGeometryColumnName = nullptr;
87 }
88
89 const bool supportsTableComments = mConnection->capabilities2().testFlag( Qgis::DatabaseProviderConnectionCapability2::SetTableComment );
90 if ( !supportsTableComments )
91 {
92 delete mLabelComment;
93 mLabelComment = nullptr;
94 delete mEditComment;
95 mEditComment = nullptr;
96 }
97
98 mExtentGroupBox->setTitleBase( tr( "Filter by Extent" ) );
99 mExtentGroupBox->setCheckable( true );
100 mExtentGroupBox->setChecked( false );
101 mExtentGroupBox->setCollapsed( true );
102
103 mFilterExpressionWidget->registerExpressionContextGenerator( this );
104}
105
107
109{
110 if ( mEditSchema )
111 mEditSchema->setText( schema );
112}
113
115{
116 mOwnedSource.reset();
117 mSourceLayer = nullptr;
118
119 bool owner = false;
120 QString error;
121 QgsVectorLayer *vl = uri.vectorLayer( owner, error );
122 if ( owner )
123 {
124 mOwnedSource.reset( vl );
125 mBlockSourceLayerChanges++;
126 mSourceLayerComboBox->setAdditionalLayers( { vl } );
127 mSourceLayerComboBox->setLayer( vl );
128 mBlockSourceLayerChanges--;
129 setSourceLayer( mOwnedSource.get() );
130 }
131 else if ( vl )
132 {
133 mBlockSourceLayerChanges++;
134 mSourceLayerComboBox->setLayer( vl );
135 mBlockSourceLayerChanges--;
136 setSourceLayer( vl );
137 }
138}
139
140void QgsDbImportVectorLayerDialog::setSourceLayer( QgsVectorLayer *layer )
141{
142 mSourceLayer = layer;
143 if ( !mSourceLayer || !mSourceLayer->dataProvider() )
144 return;
145
146 mEditTable->setText( layer->name() );
147
148 const bool isSpatial = mSourceLayer->isSpatial();
149 if ( mEditGeometryColumnName )
150 mEditGeometryColumnName->setEnabled( isSpatial );
151 if ( mCrsSelector )
152 mCrsSelector->setEnabled( isSpatial );
153
154 mExtentGroupBox->setEnabled( isSpatial );
155 if ( !isSpatial )
156 mExtentGroupBox->setChecked( false );
157
158 const bool extentFilterEnabled = mExtentGroupBox->isChecked();
159 mExtentGroupBox->setOriginalExtent( mSourceLayer->extent(), mSourceLayer->crs() );
160 mExtentGroupBox->setOutputExtentFromOriginal();
161 mExtentGroupBox->setChecked( extentFilterEnabled );
162 mExtentGroupBox->setCollapsed( !extentFilterEnabled );
163
164 mFilterExpressionWidget->setLayer( mSourceLayer );
165
166 if ( mEditPrimaryKey )
167 {
168 // set initial geometry column name. We use the source layer's primary key column name if available,
169 // else fallback to a default value given by the connection
170 const QgsAttributeList pkAttributes = mSourceLayer->dataProvider()->pkAttributeIndexes();
171 QString primaryKey = !pkAttributes.isEmpty() ? mSourceLayer->dataProvider()->fields().at( pkAttributes.at( 0 ) ).name() : QString();
172 if ( primaryKey.isEmpty() )
173 {
174 QgsDataSourceUri dsUri( mSourceLayer->source() );
175 primaryKey = dsUri.keyColumn();
176 }
177 if ( primaryKey.isEmpty() )
178 {
179 primaryKey = mConnection->defaultPrimaryKeyColumnName();
180 }
181
182 mEditPrimaryKey->setText( primaryKey );
183 }
184
185 if ( mEditGeometryColumnName )
186 {
187 // set initial geometry column name. We use the source layer's geometry name if available,
188 // else fallback to a default value given by the connection
189 QString geomColumn = mSourceLayer->dataProvider()->geometryColumnName();
190 if ( geomColumn.isEmpty() )
191 {
192 QgsDataSourceUri dsUri( mSourceLayer->source() );
193 geomColumn = dsUri.geometryColumn();
194 }
195 if ( geomColumn.isEmpty() )
196 {
197 geomColumn = mConnection->defaultGeometryColumnName();
198 }
199
200 mEditGeometryColumnName->setText( geomColumn );
201 }
202
203 if ( mCrsSelector )
204 {
205 mCrsSelector->setCrs( mSourceLayer->crs() );
206 }
207
208 if ( mEditComment )
209 {
210 mEditComment->setPlainText( mSourceLayer->metadata().abstract() );
211 }
212
213 mFieldsView->setSourceLayer( mSourceLayer );
214 mFieldsView->setSourceFields( mSourceLayer->fields() );
215 mFieldsView->setDestinationFields( mSourceLayer->fields() );
216}
217
218void QgsDbImportVectorLayerDialog::loadFieldsFromLayer()
219{
220 if ( mSourceLayer )
221 {
222 mFieldsView->setSourceFields( mSourceLayer->fields() );
223 mFieldsView->setDestinationFields( mSourceLayer->fields() );
224 }
225}
226
227void QgsDbImportVectorLayerDialog::addField()
228{
229 const int rowCount = mFieldsView->model()->rowCount();
230 mFieldsView->appendField( QgsField( QStringLiteral( "new_field" ) ) );
231 const QModelIndex index = mFieldsView->model()->index( rowCount, 0 );
232 mFieldsView->selectionModel()->select(
233 index,
234 QItemSelectionModel::SelectionFlags(
235 QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows
236 )
237 );
238 mFieldsView->scrollTo( index );
239}
240
242{
243 return mEditSchema ? mEditSchema->text() : QString();
244}
245
247{
248 return mEditTable->text();
249}
250
252{
253 return mEditComment ? mEditComment->toPlainText() : QString();
254}
255
257{
258 if ( canvas )
259 {
260 mExtentGroupBox->setCurrentExtent( canvas->mapSettings().visibleExtent(), canvas->mapSettings().destinationCrs() );
261 mExtentGroupBox->setMapCanvas( canvas, false );
262 }
263}
264
265void QgsDbImportVectorLayerDialog::doImport()
266{
267 // TODO -- validate
268
269 accept();
270}
271
272std::unique_ptr<QgsVectorLayerExporterTask> QgsDbImportVectorLayerDialog::createExporterTask( const QVariantMap &extraProviderOptions )
273{
274 if ( !mSourceLayer || !mSourceLayer->dataProvider() )
275 return nullptr;
276
277 QString destinationUri;
278 QVariantMap providerOptions;
279
281 exporterOptions.layerName = mEditTable->text();
282 if ( mEditSchema )
283 exporterOptions.schema = mEditSchema->text();
284 exporterOptions.wkbType = mSourceLayer->wkbType();
285 if ( mEditPrimaryKey && !mEditPrimaryKey->text().trimmed().isEmpty() )
286 exporterOptions.primaryKeyColumns << mEditPrimaryKey->text();
287 if ( mEditGeometryColumnName )
288 exporterOptions.geometryColumn = mEditGeometryColumnName->text();
289
290 try
291 {
292 destinationUri = mConnection->createVectorLayerExporterDestinationUri( exporterOptions, providerOptions );
293 }
295 {
296 return nullptr;
297 }
298
299 // options given to us by createVectorLayerExporterDestinationUri above should overwrite generic ones passed to this method
300 QVariantMap allProviderOptions = extraProviderOptions;
301 for ( auto it = providerOptions.constBegin(); it != providerOptions.constEnd(); ++it )
302 {
303 allProviderOptions.insert( it.key(), it.value() );
304 }
305
306 // overwrite?
307 if ( mChkDropTable->isChecked() )
308 {
309 allProviderOptions.insert( QStringLiteral( "overwrite" ), true );
310 }
311
313 if ( mCrsSelector )
314 {
315 exportOptions.setDestinationCrs( mCrsSelector->crs() );
316 }
317 exportOptions.setTransformContext( mSourceLayer->transformContext() );
318 if ( !mFilterExpressionWidget->expression().isEmpty() )
319 {
320 exportOptions.setFilterExpression( mFilterExpressionWidget->expression() );
322 }
323
324 if ( mExtentGroupBox->isEnabled() && mExtentGroupBox->isChecked() )
325 {
326 exportOptions.setExtent( QgsReferencedRectangle( mExtentGroupBox->outputExtent(), mExtentGroupBox->outputCrs() ) );
327 }
328
329 const QList<QgsFieldMappingModel::Field> fieldMapping = mFieldsView->mapping();
330 QList<QgsVectorLayerExporter::OutputField> outputFields;
331 outputFields.reserve( fieldMapping.size() );
332 for ( const QgsFieldMappingModel::Field &field : fieldMapping )
333 {
334 outputFields.append( QgsVectorLayerExporter::OutputField( field.field, field.expression ) );
335 }
336 exportOptions.setOutputFields( outputFields );
337
338 return std::make_unique<QgsVectorLayerExporterTask>( mSourceLayer->clone(), destinationUri, mConnection->providerKey(), exportOptions, allProviderOptions, true );
339}
340
347
348void QgsDbImportVectorLayerDialog::sourceLayerComboChanged()
349{
350 if ( mBlockSourceLayerChanges )
351 return;
352
353 if ( mSourceLayerComboBox->currentLayer() == mSourceLayer )
354 return;
355
356 setSourceLayer( qobject_cast< QgsVectorLayer * >( mSourceLayerComboBox->currentLayer() ) );
357}
@ 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()
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
@ 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.
This class represents a coordinate reference system (CRS).
Class for storing the component parts of a RDBMS data source URI (e.g.
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.
~QgsDbImportVectorLayerDialog() override
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:80
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.
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 data sets.
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.