QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
19#include "qgssettings.h"
20#include "qgsfileutils.h"
21#include "qgsdatasourceuri.h"
27#include <QMenu>
28#include <QFileDialog>
29#include <QInputDialog>
30#include <QCheckBox>
31#include <QLocale>
32#include <QTextCodec>
33#include <QUrl>
34
36
37QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget( const QgsProcessingDestinationParameter *param, bool defaultSelection, QWidget *parent )
38 : QWidget( parent )
39 , mParameter( param )
40 , mDefaultSelection( defaultSelection )
41{
42 Q_ASSERT( mParameter );
43
44 setupUi( this );
45
46 leText->setClearButtonEnabled( false );
47
48 connect( leText, &QLineEdit::textEdited, this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
49
50 mMenu = new QMenu( this );
51 connect( mMenu, &QMenu::aboutToShow, this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
52 mSelectButton->setMenu( mMenu );
53 mSelectButton->setPopupMode( QToolButton::InstantPopup );
54
55 QgsSettings settings;
56 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( settings.value( QStringLiteral( "/Processing/encoding" ), QStringLiteral( "System" ) ).toString() );
57 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
58
59 if ( !mParameter->defaultValueForGui().isValid() )
60 {
61 // no default value -- we default to either skipping the output or a temporary output, depending on the createByDefault value
62 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional && !mParameter->createByDefault() )
63 setValue( QVariant() );
64 else
66 }
67 else
68 {
69 setValue( mParameter->defaultValueForGui() );
70 }
71
72 setToolTip( mParameter->toolTip() );
73
74 setAcceptDrops( true );
75 leText->setAcceptDrops( false );
76}
77
78bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped() const
79{
80 return leText->text().isEmpty() && !mUseTemporary;
81}
82
83void QgsProcessingLayerOutputDestinationWidget::setValue( const QVariant &value )
84{
85 const bool prevSkip = outputIsSkipped();
86 mUseRemapping = false;
87 if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) )
88 {
89 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
90 skipOutput();
91 else
92 saveToTemporary();
93 }
94 else
95 {
96 if ( value.toString() == QLatin1String( "memory:" ) || value.toString() == QgsProcessing::TEMPORARY_OUTPUT )
97 {
98 saveToTemporary();
99 }
100 else if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
101 {
103 if ( def.sink.staticValue().toString() == QLatin1String( "memory:" ) || def.sink.staticValue().toString() == QgsProcessing::TEMPORARY_OUTPUT || def.sink.staticValue().toString().isEmpty() )
104 {
105 saveToTemporary();
106 }
107 else
108 {
109 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
110 leText->setText( def.sink.staticValue().toString() );
111 mUseTemporary = false;
112 if ( prevSkip )
113 emit skipOutputChanged( false );
114 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
115 emit destinationChanged();
116 }
117 mUseRemapping = def.useRemapping();
118 mRemapDefinition = def.remappingDefinition();
119 mEncoding = def.createOptions.value( QStringLiteral( "fileEncoding" ) ).toString();
120 }
121 else
122 {
123 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
124 leText->setText( value.toString() );
125 mUseTemporary = false;
126 if ( prevSkip )
127 emit skipOutputChanged( false );
128
129 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
130 {
131 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
132 emit destinationChanged();
133 }
134 else
135 {
136 if ( prev.userType() != QMetaType::type( "QgsProcessingOutputLayerDefinition" ) ||
137 !( 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 {
220 bool enabled = !skipped;
221 mOpenAfterRunningCheck->setEnabled( enabled );
222 mOpenAfterRunningCheck->setChecked( enabled );
223 } );
224}
225
226bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning() const
227{
228 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
229}
230
231void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
232{
233 mMenu->clear();
234
235 if ( !mDefaultSelection )
236 {
237 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
238 {
239 QAction *actionSkipOutput = new QAction( tr( "Skip Output" ), this );
240 connect( actionSkipOutput, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
241 mMenu->addAction( actionSkipOutput );
242 }
243
244 QAction *actionSaveToTemp = nullptr;
245 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
246 {
247 // use memory layers for temporary layers if supported
248 actionSaveToTemp = new QAction( tr( "Create Temporary Layer" ), this );
249 }
250 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
251 {
252 actionSaveToTemp = new QAction( tr( "Save to a Temporary Directory" ), this );
253 }
254 else
255 {
256 actionSaveToTemp = new QAction( tr( "Save to a Temporary File" ), this );
257 }
258
259 connect( actionSaveToTemp, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
260 mMenu->addAction( actionSaveToTemp );
261 }
262
263 QAction *actionSaveToFile = nullptr;
264 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
265 {
266 actionSaveToFile = new QAction( tr( "Save to Directory…" ), this );
267 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
268 }
269 else
270 {
271 actionSaveToFile = new QAction( tr( "Save to File…" ), this );
272 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
273 }
274 mMenu->addAction( actionSaveToFile );
275
276 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
277 {
278 QAction *actionSaveToGpkg = new QAction( tr( "Save to GeoPackage…" ), this );
279 connect( actionSaveToGpkg, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
280 mMenu->addAction( actionSaveToGpkg );
281
282 QAction *actionSaveToDatabase = new QAction( tr( "Save to Database Table…" ), this );
283 connect( actionSaveToDatabase, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
284 mMenu->addAction( actionSaveToDatabase );
285
286 if ( mParameter->algorithm() && qgis::down_cast< const QgsProcessingParameterFeatureSink * >( mParameter )->supportsAppend() )
287 {
288 mMenu->addSeparator();
289 QAction *actionAppendToLayer = new QAction( tr( "Append to Layer…" ), this );
290 connect( actionAppendToLayer, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
291 mMenu->addAction( actionAppendToLayer );
292 if ( mUseRemapping )
293 {
294 QAction *editMappingAction = new QAction( tr( "Edit Field Mapping…" ), this );
295 connect( editMappingAction, &QAction::triggered, this, [ = ]
296 {
297 setAppendDestination( value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
298 } );
299 mMenu->addAction( editMappingAction );
300 }
301 }
302 }
303
304 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
305 {
306 mMenu->addSeparator();
307 QAction *actionSetEncoding = new QAction( tr( "Change File Encoding (%1)…" ).arg( mEncoding ), this );
308 connect( actionSetEncoding, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
309 mMenu->addAction( actionSetEncoding );
310 }
311}
312
313void QgsProcessingLayerOutputDestinationWidget::skipOutput()
314{
315 leText->setPlaceholderText( tr( "[Skip output]" ) );
316 leText->clear();
317 mUseTemporary = false;
318 mUseRemapping = false;
319
320 emit skipOutputChanged( true );
321 emit destinationChanged();
322}
323
324void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
325{
326 const bool prevSkip = outputIsSkipped();
327
328 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
329 {
330 leText->setPlaceholderText( tr( "[Create temporary layer]" ) );
331 }
332 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
333 {
334 leText->setPlaceholderText( tr( "[Save to temporary folder]" ) );
335 }
336 else
337 {
338 leText->setPlaceholderText( tr( "[Save to temporary file]" ) );
339 }
340 leText->clear();
341
342 if ( mUseTemporary )
343 return;
344
345 mUseTemporary = true;
346 mUseRemapping = false;
347 if ( prevSkip )
348 emit skipOutputChanged( false );
349 emit destinationChanged();
350}
351
352void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
353{
354 QString lastDir = leText->text();
355 QgsSettings settings;
356 if ( lastDir.isEmpty() )
357 lastDir = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QDir::homePath() ).toString();
358
359 const QString dirName = QFileDialog::getExistingDirectory( this, tr( "Select Directory" ), lastDir, QFileDialog::Options() );
360 if ( !dirName.isEmpty() )
361 {
362 leText->setText( QDir::toNativeSeparators( dirName ) );
363 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), dirName );
364 mUseTemporary = false;
365 mUseRemapping = false;
366 emit skipOutputChanged( false );
367 emit destinationChanged();
368 }
369}
370
371void QgsProcessingLayerOutputDestinationWidget::selectFile()
372{
373 const QString fileFilter = mParameter->createFileFilter();
374
375 QgsSettings settings;
376
377 QString lastExtPath;
378 QString lastExt;
379 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() )
380 {
381 lastExtPath = QStringLiteral( "/Processing/LastVectorOutputExt" );
382 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString() ;
383 }
384 else if ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() )
385 {
386 lastExtPath = QStringLiteral( "/Processing/LastRasterOutputExt" );
387 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
388 }
389 else if ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() )
390 {
391 lastExtPath = QStringLiteral( "/Processing/LastPointCloudOutputExt" );
392 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
393 }
394 else if ( mParameter->type() == QgsProcessingParameterVectorTileDestination::typeName() )
395 {
396 lastExtPath = QStringLiteral( "/Processing/LastVectorTileOutputExt" );
397 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
398 }
399
400 // get default filter
401 const QStringList filters = fileFilter.split( QStringLiteral( ";;" ) );
402 QString lastFilter;
403 for ( const QString &f : filters )
404 {
405 if ( f.contains( QStringLiteral( "*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
406 {
407 lastFilter = f;
408 break;
409 }
410 }
411
412 QString path;
413 if ( settings.contains( QStringLiteral( "/Processing/LastOutputPath" ) ) )
414 path = settings.value( QStringLiteral( "/Processing/LastOutputPath" ) ).toString();
415 else
416 path = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
417
418 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "dontconfirmoverwrite" ), false ).toBool();
419
420 QString filename = QFileDialog::getSaveFileName( this, tr( "Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
421 if ( !filename.isEmpty() )
422 {
423 mUseTemporary = false;
424 mUseRemapping = false;
425 filename = QgsFileUtils::addExtensionFromFilter( filename, lastFilter );
426
427 leText->setText( filename );
428 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
429 if ( !lastExtPath.isEmpty() )
430 settings.setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
431
432 emit skipOutputChanged( false );
433 emit destinationChanged();
434 }
435 // return dialog focus on Mac
436 activateWindow();
437 raise();
438}
439
440void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
441{
442 QgsSettings settings;
443 QString lastPath = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QString() ).toString();
444 if ( lastPath.isEmpty() )
445 lastPath = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
446
447 QString filename = QFileDialog::getSaveFileName( this, tr( "Save to GeoPackage" ), lastPath, tr( "GeoPackage files (*.gpkg);;All files (*.*)" ), nullptr, QFileDialog::DontConfirmOverwrite );
448 // return dialog focus on Mac
449 activateWindow();
450 raise();
451
452 if ( filename.isEmpty() )
453 return;
454
455 const QString layerName = QInputDialog::getText( this, tr( "Save to GeoPackage" ), tr( "Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
456 if ( layerName.isEmpty() )
457 return;
458
459 mUseTemporary = false;
460 mUseRemapping = false;
461
462 filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "gpkg" ) );
463
464 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
465
467 uri.setTable( layerName );
468 uri.setDatabase( filename );
469
470 QString geomColumn;
471 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
472 {
473 if ( sink->hasGeometry() )
474 geomColumn = QStringLiteral( "geom" );
475 }
476 uri.setGeometryColumn( geomColumn );
477
478 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
479
480 emit skipOutputChanged( false );
481 emit destinationChanged();
482}
483
484void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
485{
486 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
487 {
488
489 QgsNewDatabaseTableNameWidget *widget = new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral( "postgres" )
490 << QStringLiteral( "mssql" )
491 << QStringLiteral( "ogr" )
492 << QStringLiteral( "hana" )
493 << QStringLiteral( "spatialite" )
494 << QStringLiteral( "oracle" ), this );
495 widget->setPanelTitle( tr( "Save “%1” to Database Table" ).arg( mParameter->description() ) );
496 widget->setAcceptButtonVisible( true );
497
498 panel->openPanel( widget );
499
500 auto changed = [ = ]
501 {
502 mUseTemporary = false;
503 mUseRemapping = false;
504
505 QString geomColumn;
506 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
507 {
508 if ( sink->hasGeometry() )
509 geomColumn = widget->dataProviderKey() == QLatin1String( "oracle" ) ? QStringLiteral( "GEOM" ) : QStringLiteral( "geom" );
510 }
511
512 if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
513 {
515 uri.setTable( widget->table() );
516 uri.setDatabase( widget->schema() );
517 uri.setGeometryColumn( geomColumn );
518 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
519 }
520 else
521 {
522 QgsDataSourceUri uri( widget->uri() );
523 uri.setGeometryColumn( geomColumn );
524 leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
525 }
526
527 emit skipOutputChanged( false );
528 emit destinationChanged();
529 };
530
531 connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [ = ] { changed(); } );
532 connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [ = ] { changed(); } );
533 connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [ = ] { changed(); } );
534 connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [ = ] { changed(); } );
535 connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [ = ]
536 {
537 changed();
538 widget->acceptPanel();
539 } );
540 }
541}
542
543void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
544{
545 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
546 {
548 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
549
550 panel->openPanel( widget );
551
552 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
553 {
554 widget->acceptPanel();
555 } );
556 connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
557 {
558 if ( widget->uri().uri.isEmpty() )
559 setValue( QVariant() );
560 else
561 {
562 // get fields for destination
563 std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->uri().uri, QString(), widget->uri().providerKey );
564 if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
565 setAppendDestination( widget->uri().uri, dest->fields() );
566 else
567 setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
568 }
569 } );
570 }
571}
572
573
574void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
575{
576 const QgsProcessingAlgorithm *alg = mParameter->algorithm();
577 QVariantMap props;
578 if ( mParametersGenerator )
579 props = mParametersGenerator->createProcessingParameters();
580 props.insert( mParameter->name(), uri );
581
582 const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties >() );
584 {
585 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
586 {
587 // get mapping from fields output by algorithm to destination fields
588 QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
589 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
590 if ( !mRemapDefinition.fieldMap().isEmpty() )
591 widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
592
593 panel->openPanel( widget );
594
595 connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
596 {
599 remap.setSourceCrs( outputProps.crs );
600 remap.setFieldMap( widget->fieldPropertyMap() );
601 remap.setDestinationFields( destFields );
602 def.setRemappingDefinition( remap );
603 setValue( def );
604 } );
605 }
606 }
607}
608
609void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
610{
611 QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
612 if ( dialog.exec() )
613 {
614 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( dialog.encoding() );
615
616 QgsSettings settings;
617 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
618
619 emit destinationChanged();
620 }
621}
622
623void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
624{
625 mUseTemporary = text.isEmpty();
626 mUseRemapping = false;
627 emit destinationChanged();
628}
629
630
631QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
632{
634 for ( const QgsMimeDataUtils::Uri &u : uriList )
635 {
636 if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
638 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
639 && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
640 {
641 return u.uri;
642 }
643 else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
644 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
645 && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
646 {
647 return u.uri;
648 }
649 else if ( ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName()
650 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
651 && u.layerType == QLatin1String( "pointcloud" ) && ( u.providerKey == QLatin1String( "ept" ) || u.providerKey == QLatin1String( "pdal" ) ) )
652 {
653 return u.uri;
654 }
655#if 0
656 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
657 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
658 && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
659 return u.uri;
660
661#endif
662 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
663 && u.layerType == QLatin1String( "directory" ) )
664 {
665 return u.uri;
666 }
667 }
668 if ( !uriList.isEmpty() )
669 return QString();
670
671 // files dragged from file explorer, outside of QGIS
672 QStringList rawPaths;
673 if ( data->hasUrls() )
674 {
675 const QList< QUrl > urls = data->urls();
676 rawPaths.reserve( urls.count() );
677 for ( const QUrl &url : urls )
678 {
679 const QString local = url.toLocalFile();
680 if ( !rawPaths.contains( local ) )
681 rawPaths.append( local );
682 }
683 }
684 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
685 rawPaths.append( data->text() );
686
687 for ( const QString &path : std::as_const( rawPaths ) )
688 {
689 QFileInfo file( path );
690 if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
694 || mParameter->type() == QgsProcessingParameterFileDestination::typeName()
695 || mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() ) )
696 {
697 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
698 return path;
699 }
700 else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
701 return path;
702 }
703
704 return QString();
705}
706
707void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
708{
709 if ( !( event->possibleActions() & Qt::CopyAction ) )
710 return;
711
712 const QString path = mimeDataToPath( event->mimeData() );
713 if ( !path.isEmpty() )
714 {
715 // dragged an acceptable path, phew
716 event->setDropAction( Qt::CopyAction );
717 event->accept();
718 leText->setHighlighted( true );
719 }
720}
721
722void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
723{
724 QWidget::dragLeaveEvent( event );
725 if ( leText->isHighlighted() )
726 {
727 event->accept();
728 leText->setHighlighted( false );
729 }
730}
731
732void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
733{
734 if ( !( event->possibleActions() & Qt::CopyAction ) )
735 return;
736
737 const QString path = mimeDataToPath( event->mimeData() );
738 if ( !path.isEmpty() )
739 {
740 // dropped an acceptable path, phew
741 setFocus( Qt::MouseFocusReason );
742 event->setDropAction( Qt::CopyAction );
743 event->accept();
744 setValue( path );
745 }
746 leText->setHighlighted( false );
747}
748
@ 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:45
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.
const QString & typeName
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.