QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsprocessingoutputdestinationwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingmatrixparameterdialog.cpp
3 ------------------------------------
4 Date : February 2019
5 Copyright : (C) 2019 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_qgsprocessingoutputdestinationwidget.cpp"
20#include "qgssettings.h"
21#include "qgsfileutils.h"
22#include "qgsdatasourceuri.h"
28#include <QMenu>
29#include <QFileDialog>
30#include <QInputDialog>
31#include <QCheckBox>
32#include <QLocale>
33#include <QTextCodec>
34#include <QUrl>
35
37
38QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget( const QgsProcessingDestinationParameter *param, bool defaultSelection, QWidget *parent )
39 : QWidget( parent )
40 , mParameter( param )
41 , mDefaultSelection( defaultSelection )
42{
43 Q_ASSERT( mParameter );
44
45 setupUi( this );
46
47 leText->setClearButtonEnabled( false );
48
49 connect( leText, &QLineEdit::textEdited, this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
50
51 mMenu = new QMenu( this );
52 connect( mMenu, &QMenu::aboutToShow, this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
53 mSelectButton->setMenu( mMenu );
54 mSelectButton->setPopupMode( QToolButton::InstantPopup );
55
56 QgsSettings settings;
57 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( settings.value( QStringLiteral( "/Processing/encoding" ), QStringLiteral( "System" ) ).toString() );
58 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
59
60 if ( !mParameter->defaultValueForGui().isValid() )
61 {
62 // no default value -- we default to either skipping the output or a temporary output, depending on the createByDefault value
63 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional && !mParameter->createByDefault() )
64 setValue( QVariant() );
65 else
67 }
68 else
69 {
70 setValue( mParameter->defaultValueForGui() );
71 }
72
73 setToolTip( mParameter->toolTip() );
74
75 setAcceptDrops( true );
76 leText->setAcceptDrops( false );
77}
78
79bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped() const
80{
81 return leText->text().isEmpty() && !mUseTemporary;
82}
83
84void QgsProcessingLayerOutputDestinationWidget::setValue( const QVariant &value )
85{
86 const bool prevSkip = outputIsSkipped();
87 mUseRemapping = false;
88 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
89 {
90 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
91 skipOutput();
92 else
93 saveToTemporary();
94 }
95 else
96 {
97 if ( value.toString() == QLatin1String( "memory:" ) || value.toString() == QgsProcessing::TEMPORARY_OUTPUT )
98 {
99 saveToTemporary();
100 }
101 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
102 {
104 if ( def.sink.staticValue().toString() == QLatin1String( "memory:" ) || def.sink.staticValue().toString() == QgsProcessing::TEMPORARY_OUTPUT || def.sink.staticValue().toString().isEmpty() )
105 {
106 saveToTemporary();
107 }
108 else
109 {
110 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
111 leText->setText( def.sink.staticValue().toString() );
112 mUseTemporary = false;
113 if ( prevSkip )
114 emit skipOutputChanged( false );
115 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
116 emit destinationChanged();
117 }
118 mUseRemapping = def.useRemapping();
119 mRemapDefinition = def.remappingDefinition();
120 mEncoding = def.createOptions.value( QStringLiteral( "fileEncoding" ) ).toString();
121 }
122 else
123 {
124 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
125 leText->setText( value.toString() );
126 mUseTemporary = false;
127 if ( prevSkip )
128 emit skipOutputChanged( false );
129
130 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
131 {
132 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
133 emit destinationChanged();
134 }
135 else
136 {
137 if ( prev.userType() != qMetaTypeId<QgsProcessingOutputLayerDefinition>() || !( prev.value<QgsProcessingOutputLayerDefinition>() == QgsProcessingLayerOutputDestinationWidget::value().value<QgsProcessingOutputLayerDefinition>() ) )
138 emit destinationChanged();
139 }
140 }
141 }
142}
143
144QVariant QgsProcessingLayerOutputDestinationWidget::value() const
145{
146 QgsSettings settings;
147 QString key;
148 if ( mUseTemporary && mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
149 {
151 }
152 else if ( mUseTemporary && !mDefaultSelection )
153 {
155 }
156 else
157 {
158 key = leText->text();
159 }
160
161 if ( key.isEmpty() && mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
162 return QVariant();
163
164 QString provider;
165 QString uri;
166 if ( !key.isEmpty() && key != QgsProcessing::TEMPORARY_OUTPUT
167 && !key.startsWith( QLatin1String( "memory:" ) )
168 && !key.startsWith( QLatin1String( "ogr:" ) )
169 && !key.startsWith( QLatin1String( "postgres:" ) )
170 && !key.startsWith( QLatin1String( "postgis:" ) )
171 && !QgsProcessingUtils::decodeProviderKeyAndUri( key, provider, uri ) )
172 {
173 // output should be a file path
174 QString folder = QFileInfo( key ).path();
175 if ( folder == '.' )
176 {
177 // output name does not include a folder - use default
178 QString defaultFolder = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
179 key = QDir( defaultFolder ).filePath( key );
180 }
181 }
182
183 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
184 return key;
185 else if ( mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
186 return key;
187
189 value.createOptions.insert( QStringLiteral( "fileEncoding" ), mEncoding );
190 if ( mUseRemapping )
191 value.setRemappingDefinition( mRemapDefinition );
192 return value;
193}
194
195void QgsProcessingLayerOutputDestinationWidget::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
196{
197 mBrowserModel = context.browserModel();
198}
199
200void QgsProcessingLayerOutputDestinationWidget::setContext( QgsProcessingContext *context )
201{
202 mContext = context;
203}
204
205void QgsProcessingLayerOutputDestinationWidget::registerProcessingParametersGenerator( QgsProcessingParametersGenerator *generator )
206{
207 mParametersGenerator = generator;
208}
209
210void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
211{
212 Q_ASSERT( mOpenAfterRunningCheck == nullptr );
213 mOpenAfterRunningCheck = new QCheckBox( tr( "Open output file after running algorithm" ) );
214 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
215 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
216 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
217
218 connect( this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged, this, [=]( bool skipped ) {
219 bool enabled = !skipped;
220 mOpenAfterRunningCheck->setEnabled( enabled );
221 mOpenAfterRunningCheck->setChecked( enabled );
222 } );
223}
224
225bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning() const
226{
227 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
228}
229
230void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
231{
232 mMenu->clear();
233
234 if ( !mDefaultSelection )
235 {
236 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
237 {
238 QAction *actionSkipOutput = new QAction( tr( "Skip Output" ), this );
239 connect( actionSkipOutput, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
240 mMenu->addAction( actionSkipOutput );
241 }
242
243 QAction *actionSaveToTemp = nullptr;
244 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
245 {
246 // use memory layers for temporary layers if supported
247 actionSaveToTemp = new QAction( tr( "Create Temporary Layer" ), this );
248 }
249 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
250 {
251 actionSaveToTemp = new QAction( tr( "Save to a Temporary Directory" ), this );
252 }
253 else
254 {
255 actionSaveToTemp = new QAction( tr( "Save to a Temporary File" ), this );
256 }
257
258 connect( actionSaveToTemp, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
259 mMenu->addAction( actionSaveToTemp );
260 }
261
262 QAction *actionSaveToFile = nullptr;
263 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
264 {
265 actionSaveToFile = new QAction( tr( "Save to Directory…" ), this );
266 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
267 }
268 else
269 {
270 actionSaveToFile = new QAction( tr( "Save to File…" ), this );
271 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
272 }
273 mMenu->addAction( actionSaveToFile );
274
275 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
276 {
277 QAction *actionSaveToGpkg = new QAction( tr( "Save to GeoPackage…" ), this );
278 connect( actionSaveToGpkg, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
279 mMenu->addAction( actionSaveToGpkg );
280
281 QAction *actionSaveToDatabase = new QAction( tr( "Save to Database Table…" ), this );
282 connect( actionSaveToDatabase, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
283 mMenu->addAction( actionSaveToDatabase );
284
285 if ( mParameter->algorithm() && qgis::down_cast<const QgsProcessingParameterFeatureSink *>( mParameter )->supportsAppend() )
286 {
287 mMenu->addSeparator();
288 QAction *actionAppendToLayer = new QAction( tr( "Append to Layer…" ), this );
289 connect( actionAppendToLayer, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
290 mMenu->addAction( actionAppendToLayer );
291 if ( mUseRemapping )
292 {
293 QAction *editMappingAction = new QAction( tr( "Edit Field Mapping…" ), this );
294 connect( editMappingAction, &QAction::triggered, this, [=] {
295 setAppendDestination( value().value<QgsProcessingOutputLayerDefinition>().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
296 } );
297 mMenu->addAction( editMappingAction );
298 }
299 }
300 }
301
302 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
303 {
304 mMenu->addSeparator();
305 QAction *actionSetEncoding = new QAction( tr( "Change File Encoding (%1)…" ).arg( mEncoding ), this );
306 connect( actionSetEncoding, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
307 mMenu->addAction( actionSetEncoding );
308 }
309}
310
311void QgsProcessingLayerOutputDestinationWidget::skipOutput()
312{
313 leText->setPlaceholderText( tr( "[Skip output]" ) );
314 leText->clear();
315 mUseTemporary = false;
316 mUseRemapping = false;
317
318 emit skipOutputChanged( true );
319 emit destinationChanged();
320}
321
322void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
323{
324 const bool prevSkip = outputIsSkipped();
325
326 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
327 {
328 leText->setPlaceholderText( tr( "[Create temporary layer]" ) );
329 }
330 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
331 {
332 leText->setPlaceholderText( tr( "[Save to temporary folder]" ) );
333 }
334 else
335 {
336 leText->setPlaceholderText( tr( "[Save to temporary file]" ) );
337 }
338 leText->clear();
339
340 if ( mUseTemporary )
341 return;
342
343 mUseTemporary = true;
344 mUseRemapping = false;
345 if ( prevSkip )
346 emit skipOutputChanged( false );
347 emit destinationChanged();
348}
349
350void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
351{
352 QString lastDir = leText->text();
353 QgsSettings settings;
354 if ( lastDir.isEmpty() )
355 lastDir = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QDir::homePath() ).toString();
356
357 const QString dirName = QFileDialog::getExistingDirectory( this, tr( "Select Directory" ), lastDir, QFileDialog::Options() );
358 if ( !dirName.isEmpty() )
359 {
360 leText->setText( QDir::toNativeSeparators( dirName ) );
361 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), dirName );
362 mUseTemporary = false;
363 mUseRemapping = false;
364 emit skipOutputChanged( false );
365 emit destinationChanged();
366 }
367}
368
369void QgsProcessingLayerOutputDestinationWidget::selectFile()
370{
371 const QString fileFilter = mParameter->createFileFilter();
372
373 QgsSettings settings;
374
375 QString lastExtPath;
376 QString lastExt;
377 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() )
378 {
379 lastExtPath = QStringLiteral( "/Processing/LastVectorOutputExt" );
380 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
381 }
382 else if ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() )
383 {
384 lastExtPath = QStringLiteral( "/Processing/LastRasterOutputExt" );
385 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
386 }
387 else if ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() )
388 {
389 lastExtPath = QStringLiteral( "/Processing/LastPointCloudOutputExt" );
390 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
391 }
392 else if ( mParameter->type() == QgsProcessingParameterVectorTileDestination::typeName() )
393 {
394 lastExtPath = QStringLiteral( "/Processing/LastVectorTileOutputExt" );
395 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
396 }
397
398 // get default filter
399 const QStringList filters = fileFilter.split( QStringLiteral( ";;" ) );
400 QString lastFilter;
401 for ( const QString &f : filters )
402 {
403 if ( f.contains( QStringLiteral( "*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
404 {
405 lastFilter = f;
406 break;
407 }
408 }
409
410 QString path;
411 if ( settings.contains( QStringLiteral( "/Processing/LastOutputPath" ) ) )
412 path = settings.value( QStringLiteral( "/Processing/LastOutputPath" ) ).toString();
413 else
414 path = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
415
416 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "dontconfirmoverwrite" ), false ).toBool();
417
418 QString filename = QFileDialog::getSaveFileName( this, tr( "Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
419 if ( !filename.isEmpty() )
420 {
421 mUseTemporary = false;
422 mUseRemapping = false;
423 filename = QgsFileUtils::addExtensionFromFilter( filename, lastFilter );
424
425 leText->setText( filename );
426 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
427 if ( !lastExtPath.isEmpty() )
428 settings.setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
429
430 emit skipOutputChanged( false );
431 emit destinationChanged();
432 }
433 // return dialog focus on Mac
434 activateWindow();
435 raise();
436}
437
438void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
439{
440 QgsSettings settings;
441 QString lastPath = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QString() ).toString();
442 if ( lastPath.isEmpty() )
443 lastPath = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
444
445 QString filename = QFileDialog::getSaveFileName( this, tr( "Save to GeoPackage" ), lastPath, tr( "GeoPackage files (*.gpkg);;All files (*.*)" ), nullptr, QFileDialog::DontConfirmOverwrite );
446 // return dialog focus on Mac
447 activateWindow();
448 raise();
449
450 if ( filename.isEmpty() )
451 return;
452
453 const QString layerName = QInputDialog::getText( this, tr( "Save to GeoPackage" ), tr( "Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
454 if ( layerName.isEmpty() )
455 return;
456
457 mUseTemporary = false;
458 mUseRemapping = false;
459
460 filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "gpkg" ) );
461
462 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
463
465 uri.setTable( layerName );
466 uri.setDatabase( filename );
467
468 QString geomColumn;
469 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast<const QgsProcessingParameterFeatureSink *>( mParameter ) )
470 {
471 if ( sink->hasGeometry() )
472 geomColumn = QStringLiteral( "geom" );
473 }
474 uri.setGeometryColumn( geomColumn );
475
476 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
477
478 emit skipOutputChanged( false );
479 emit destinationChanged();
480}
481
482void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
483{
484 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
485 {
486 QgsNewDatabaseTableNameWidget *widget = new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral( "postgres" ) << QStringLiteral( "mssql" ) << QStringLiteral( "ogr" ) << QStringLiteral( "hana" ) << QStringLiteral( "spatialite" ) << QStringLiteral( "oracle" ), this );
487 widget->setPanelTitle( tr( "Save “%1” to Database Table" ).arg( mParameter->description() ) );
488 widget->setAcceptButtonVisible( true );
489
490 panel->openPanel( widget );
491
492 auto changed = [=] {
493 mUseTemporary = false;
494 mUseRemapping = false;
495
496 QString geomColumn;
497 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast<const QgsProcessingParameterFeatureSink *>( mParameter ) )
498 {
499 if ( sink->hasGeometry() )
500 geomColumn = widget->dataProviderKey() == QLatin1String( "oracle" ) ? QStringLiteral( "GEOM" ) : QStringLiteral( "geom" );
501 }
502
503 if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
504 {
506 uri.setTable( widget->table() );
507 uri.setDatabase( widget->schema() );
508 uri.setGeometryColumn( geomColumn );
509 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
510 }
511 else
512 {
513 QgsDataSourceUri uri( widget->uri() );
514 uri.setGeometryColumn( geomColumn );
515 leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
516 }
517
518 emit skipOutputChanged( false );
519 emit destinationChanged();
520 };
521
522 connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [=] { changed(); } );
523 connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [=] { changed(); } );
524 connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [=] { changed(); } );
525 connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [=] { changed(); } );
526 connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [=] {
527 changed();
528 widget->acceptPanel();
529 } );
530 }
531}
532
533void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
534{
535 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
536 {
538 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
539
540 panel->openPanel( widget );
541
542 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [=]( const QgsMimeDataUtils::Uri & ) {
543 widget->acceptPanel();
544 } );
545 connect( widget, &QgsPanelWidget::panelAccepted, this, [=]() {
546 if ( widget->uri().uri.isEmpty() )
547 setValue( QVariant() );
548 else
549 {
550 // get fields for destination
551 std::unique_ptr<QgsVectorLayer> dest = std::make_unique<QgsVectorLayer>( widget->uri().uri, QString(), widget->uri().providerKey );
552 if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
553 setAppendDestination( widget->uri().uri, dest->fields() );
554 else
555 setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
556 }
557 } );
558 }
559}
560
561
562void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
563{
564 const QgsProcessingAlgorithm *alg = mParameter->algorithm();
565 QVariantMap props;
566 if ( mParametersGenerator )
567 props = mParametersGenerator->createProcessingParameters();
568 props.insert( mParameter->name(), uri );
569
570 const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties>() );
572 {
573 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
574 {
575 // get mapping from fields output by algorithm to destination fields
576 QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
577 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
578 if ( !mRemapDefinition.fieldMap().isEmpty() )
579 widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
580
581 panel->openPanel( widget );
582
583 connect( widget, &QgsPanelWidget::panelAccepted, this, [=]() {
586 remap.setSourceCrs( outputProps.crs );
587 remap.setFieldMap( widget->fieldPropertyMap() );
588 remap.setDestinationFields( destFields );
589 def.setRemappingDefinition( remap );
590 setValue( def );
591 } );
592 }
593 }
594}
595
596void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
597{
598 QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
599 if ( dialog.exec() )
600 {
601 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( dialog.encoding() );
602
603 QgsSettings settings;
604 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
605
606 emit destinationChanged();
607 }
608}
609
610void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
611{
612 mUseTemporary = text.isEmpty();
613 mUseRemapping = false;
614 emit destinationChanged();
615}
616
617
618QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
619{
621 for ( const QgsMimeDataUtils::Uri &u : uriList )
622 {
623 if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
625 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
626 && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
627 {
628 return u.uri;
629 }
630 else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
631 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
632 && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
633 {
634 return u.uri;
635 }
636 else if ( ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName()
637 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
638 && u.layerType == QLatin1String( "pointcloud" ) && ( u.providerKey == QLatin1String( "ept" ) || u.providerKey == QLatin1String( "pdal" ) ) )
639 {
640 return u.uri;
641 }
642#if 0
643 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
644 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
645 && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
646 return u.uri;
647
648#endif
649 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
650 && u.layerType == QLatin1String( "directory" ) )
651 {
652 return u.uri;
653 }
654 }
655 if ( !uriList.isEmpty() )
656 return QString();
657
658 // files dragged from file explorer, outside of QGIS
659 QStringList rawPaths;
660 if ( data->hasUrls() )
661 {
662 const QList<QUrl> urls = data->urls();
663 rawPaths.reserve( urls.count() );
664 for ( const QUrl &url : urls )
665 {
666 const QString local = url.toLocalFile();
667 if ( !rawPaths.contains( local ) )
668 rawPaths.append( local );
669 }
670 }
671 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
672 rawPaths.append( data->text() );
673
674 for ( const QString &path : std::as_const( rawPaths ) )
675 {
676 QFileInfo file( path );
677 if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() || mParameter->type() == QgsProcessingParameterRasterDestination::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() || mParameter->type() == QgsProcessingParameterFileDestination::typeName() || mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() ) )
678 {
679 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
680 return path;
681 }
682 else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
683 return path;
684 }
685
686 return QString();
687}
688
689void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
690{
691 if ( !( event->possibleActions() & Qt::CopyAction ) )
692 return;
693
694 const QString path = mimeDataToPath( event->mimeData() );
695 if ( !path.isEmpty() )
696 {
697 // dragged an acceptable path, phew
698 event->setDropAction( Qt::CopyAction );
699 event->accept();
700 leText->setHighlighted( true );
701 }
702}
703
704void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
705{
706 QWidget::dragLeaveEvent( event );
707 if ( leText->isHighlighted() )
708 {
709 event->accept();
710 leText->setHighlighted( false );
711 }
712}
713
714void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
715{
716 if ( !( event->possibleActions() & Qt::CopyAction ) )
717 return;
718
719 const QString path = mimeDataToPath( event->mimeData() );
720 if ( !path.isEmpty() )
721 {
722 // dropped an acceptable path, phew
723 setFocus( Qt::MouseFocusReason );
724 event->setDropAction( Qt::CopyAction );
725 event->accept();
726 setValue( path );
727 }
728 leText->setHighlighted( false );
729}
730
@ Available
Properties are available.
@ Vector
Vector layer.
@ Optional
Parameter is optional.
The QgsDataSourceSelectWidget class embeds the browser view to select an existing data source.
void itemTriggered(const QgsMimeDataUtils::Uri &uri)
Emitted when an item is triggered, e.g.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
Class for storing the component parts of a RDBMS data source URI (e.g.
void setTable(const QString &table)
Sets table to table.
void setGeometryColumn(const QString &geometryColumn)
Sets geometry column name to geometryColumn.
QString uri(bool expandAuthConfig=true) const
Returns the complete URI as a string.
void setDatabase(const QString &database)
Sets the URI database name.
A dialog which presents the user with a choice of file encodings.
The QgsFieldMappingWidget class creates a mapping from one set of QgsFields to another,...
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
Container of fields for a vector layer.
Definition qgsfields.h:46
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
static QString addExtensionFromFilter(const QString &fileName, const QString &filter)
Ensures that a fileName ends with an extension from the specified filter string.
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
The QgsNewDatabaseTableNameWidget class embeds the browser view to select a DB schema and a new table...
QString table() const
Returns the current name of the new table.
QString dataProviderKey() const
Returns the currently selected data item provider key.
void setAcceptButtonVisible(bool visible)
Sets whether the optional "Ok"/accept button should be visible.
void providerKeyChanged(const QString &providerKey)
This signal is emitted when the selects a data provider or a schema name that has a different data pr...
QString schema() const
Returns the currently selected schema or file path (in case of filesystem-based DBs like spatialite o...
void tableNameChanged(const QString &tableName)
This signal is emitted when the user enters a table name.
void accepted()
Emitted when the OK/accept button is clicked.
void validationChanged(bool isValid)
This signal is emitted whenever the validation status of the widget changes.
QString uri() const
Returns the (possibly blank) string representation of the new table data source URI.
void schemaNameChanged(const QString &schemaName)
This signal is emitted when the user selects a schema (or file path for filesystem-based DBs like spa...
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void acceptPanel()
Accept the panel.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Abstract base class for processing algorithms.
virtual QgsProcessingAlgorithm::VectorProperties sinkProperties(const QString &sink, const QVariantMap &parameters, QgsProcessingContext &context, const QMap< QString, QgsProcessingAlgorithm::VectorProperties > &sourceProperties) const
Returns the vector properties which will be used for the sink with matching name.
Contains information about the context in which a processing algorithm is executed.
Base class for all parameter definitions which represent file or layer destinations,...
Encapsulates settings relating to a feature sink or output raster layer for a processing algorithm.
QgsProperty sink
Sink/layer definition.
bool useRemapping() const
Returns true if the output uses a remapping definition.
QgsRemappingSinkDefinition remappingDefinition() const
Returns the output remapping definition, if useRemapping() is true.
QVariantMap createOptions
Map of optional sink/layer creation options, which are passed to the underlying provider when creatin...
void setRemappingDefinition(const QgsRemappingSinkDefinition &definition)
Sets the remapping definition to use when adding features to the output layer.
A feature sink output for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
Contains settings which reflect the context in which a Processing parameter widget is shown,...
QgsBrowserGuiModel * browserModel() const
Returns the browser model associated with the widget.
An interface for objects which can create sets of parameter values for processing algorithms.
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri()
static QString resolveDefaultEncoding(const QString &defaultEncoding="System")
Returns the default encoding.
static bool decodeProviderKeyAndUri(const QString &string, QString &providerKey, QString &uri)
Decodes a provider key and layer uri from an encoded string, for use with encodeProviderKeyAndUri()
static const QString TEMPORARY_OUTPUT
Constant used to indicate that a Processing algorithm output should be a temporary layer/file.
QVariant staticValue() const
Returns the current static value for the property.
Defines the parameters used to remap features when creating a QgsRemappingProxyFeatureSink.
void setFieldMap(const QMap< QString, QgsProperty > &map)
Sets the field mapping, which defines how to map the values from incoming features to destination fie...
void setDestinationFields(const QgsFields &fields)
Sets the fields for the destination sink.
void setSourceCrs(const QgsCoordinateReferenceSystem &source)
Sets the source crs used for reprojecting incoming features to the sink's destination CRS.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QString uri
Identifier of the data source recognized by its providerKey.
QString providerKey
For "vector" / "raster" type: provider id.
Properties of a vector source or sink used in an algorithm.
QgsCoordinateReferenceSystem crs
Coordinate Reference System.
Qgis::ProcessingPropertyAvailability availability
Availability of the properties. By default properties are not available.