QGIS API Documentation 3.41.0-Master (3440c17df1d)
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>() ||
138 !( prev.value< QgsProcessingOutputLayerDefinition >() == QgsProcessingLayerOutputDestinationWidget::value().value< QgsProcessingOutputLayerDefinition >() ) )
139 emit destinationChanged();
140 }
141 }
142 }
143}
144
145QVariant QgsProcessingLayerOutputDestinationWidget::value() const
146{
147 QgsSettings settings;
148 QString key;
149 if ( mUseTemporary && mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
150 {
152 }
153 else if ( mUseTemporary && !mDefaultSelection )
154 {
156 }
157 else
158 {
159 key = leText->text();
160 }
161
162 if ( key.isEmpty() && mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
163 return QVariant();
164
165 QString provider;
166 QString uri;
167 if ( !key.isEmpty() && key != QgsProcessing::TEMPORARY_OUTPUT
168 && !key.startsWith( QLatin1String( "memory:" ) )
169 && !key.startsWith( QLatin1String( "ogr:" ) )
170 && !key.startsWith( QLatin1String( "postgres:" ) )
171 && !key.startsWith( QLatin1String( "postgis:" ) )
172 && !QgsProcessingUtils::decodeProviderKeyAndUri( key, provider, uri ) )
173 {
174 // output should be a file path
175 QString folder = QFileInfo( key ).path();
176 if ( folder == '.' )
177 {
178 // output name does not include a folder - use default
179 QString defaultFolder = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
180 key = QDir( defaultFolder ).filePath( key );
181 }
182 }
183
184 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
185 return key;
186 else if ( mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
187 return key;
188
190 value.createOptions.insert( QStringLiteral( "fileEncoding" ), mEncoding );
191 if ( mUseRemapping )
192 value.setRemappingDefinition( mRemapDefinition );
193 return value;
194}
195
196void QgsProcessingLayerOutputDestinationWidget::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
197{
198 mBrowserModel = context.browserModel();
199}
200
201void QgsProcessingLayerOutputDestinationWidget::setContext( QgsProcessingContext *context )
202{
203 mContext = context;
204}
205
206void QgsProcessingLayerOutputDestinationWidget::registerProcessingParametersGenerator( QgsProcessingParametersGenerator *generator )
207{
208 mParametersGenerator = generator;
209}
210
211void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
212{
213 Q_ASSERT( mOpenAfterRunningCheck == nullptr );
214 mOpenAfterRunningCheck = new QCheckBox( tr( "Open output file after running algorithm" ) );
215 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
216 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
217 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
218
219 connect( this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged, this, [ = ]( bool skipped )
220 {
221 bool enabled = !skipped;
222 mOpenAfterRunningCheck->setEnabled( enabled );
223 mOpenAfterRunningCheck->setChecked( enabled );
224 } );
225}
226
227bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning() const
228{
229 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
230}
231
232void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
233{
234 mMenu->clear();
235
236 if ( !mDefaultSelection )
237 {
238 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
239 {
240 QAction *actionSkipOutput = new QAction( tr( "Skip Output" ), this );
241 connect( actionSkipOutput, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
242 mMenu->addAction( actionSkipOutput );
243 }
244
245 QAction *actionSaveToTemp = nullptr;
246 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
247 {
248 // use memory layers for temporary layers if supported
249 actionSaveToTemp = new QAction( tr( "Create Temporary Layer" ), this );
250 }
251 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
252 {
253 actionSaveToTemp = new QAction( tr( "Save to a Temporary Directory" ), this );
254 }
255 else
256 {
257 actionSaveToTemp = new QAction( tr( "Save to a Temporary File" ), this );
258 }
259
260 connect( actionSaveToTemp, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
261 mMenu->addAction( actionSaveToTemp );
262 }
263
264 QAction *actionSaveToFile = nullptr;
265 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
266 {
267 actionSaveToFile = new QAction( tr( "Save to Directory…" ), this );
268 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
269 }
270 else
271 {
272 actionSaveToFile = new QAction( tr( "Save to File…" ), this );
273 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
274 }
275 mMenu->addAction( actionSaveToFile );
276
277 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
278 {
279 QAction *actionSaveToGpkg = new QAction( tr( "Save to GeoPackage…" ), this );
280 connect( actionSaveToGpkg, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
281 mMenu->addAction( actionSaveToGpkg );
282
283 QAction *actionSaveToDatabase = new QAction( tr( "Save to Database Table…" ), this );
284 connect( actionSaveToDatabase, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
285 mMenu->addAction( actionSaveToDatabase );
286
287 if ( mParameter->algorithm() && qgis::down_cast< const QgsProcessingParameterFeatureSink * >( mParameter )->supportsAppend() )
288 {
289 mMenu->addSeparator();
290 QAction *actionAppendToLayer = new QAction( tr( "Append to Layer…" ), this );
291 connect( actionAppendToLayer, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
292 mMenu->addAction( actionAppendToLayer );
293 if ( mUseRemapping )
294 {
295 QAction *editMappingAction = new QAction( tr( "Edit Field Mapping…" ), this );
296 connect( editMappingAction, &QAction::triggered, this, [ = ]
297 {
298 setAppendDestination( value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
299 } );
300 mMenu->addAction( editMappingAction );
301 }
302 }
303 }
304
305 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
306 {
307 mMenu->addSeparator();
308 QAction *actionSetEncoding = new QAction( tr( "Change File Encoding (%1)…" ).arg( mEncoding ), this );
309 connect( actionSetEncoding, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
310 mMenu->addAction( actionSetEncoding );
311 }
312}
313
314void QgsProcessingLayerOutputDestinationWidget::skipOutput()
315{
316 leText->setPlaceholderText( tr( "[Skip output]" ) );
317 leText->clear();
318 mUseTemporary = false;
319 mUseRemapping = false;
320
321 emit skipOutputChanged( true );
322 emit destinationChanged();
323}
324
325void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
326{
327 const bool prevSkip = outputIsSkipped();
328
329 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
330 {
331 leText->setPlaceholderText( tr( "[Create temporary layer]" ) );
332 }
333 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
334 {
335 leText->setPlaceholderText( tr( "[Save to temporary folder]" ) );
336 }
337 else
338 {
339 leText->setPlaceholderText( tr( "[Save to temporary file]" ) );
340 }
341 leText->clear();
342
343 if ( mUseTemporary )
344 return;
345
346 mUseTemporary = true;
347 mUseRemapping = false;
348 if ( prevSkip )
349 emit skipOutputChanged( false );
350 emit destinationChanged();
351}
352
353void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
354{
355 QString lastDir = leText->text();
356 QgsSettings settings;
357 if ( lastDir.isEmpty() )
358 lastDir = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QDir::homePath() ).toString();
359
360 const QString dirName = QFileDialog::getExistingDirectory( this, tr( "Select Directory" ), lastDir, QFileDialog::Options() );
361 if ( !dirName.isEmpty() )
362 {
363 leText->setText( QDir::toNativeSeparators( dirName ) );
364 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), dirName );
365 mUseTemporary = false;
366 mUseRemapping = false;
367 emit skipOutputChanged( false );
368 emit destinationChanged();
369 }
370}
371
372void QgsProcessingLayerOutputDestinationWidget::selectFile()
373{
374 const QString fileFilter = mParameter->createFileFilter();
375
376 QgsSettings settings;
377
378 QString lastExtPath;
379 QString lastExt;
380 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() )
381 {
382 lastExtPath = QStringLiteral( "/Processing/LastVectorOutputExt" );
383 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString() ;
384 }
385 else if ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() )
386 {
387 lastExtPath = QStringLiteral( "/Processing/LastRasterOutputExt" );
388 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
389 }
390 else if ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() )
391 {
392 lastExtPath = QStringLiteral( "/Processing/LastPointCloudOutputExt" );
393 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
394 }
395 else if ( mParameter->type() == QgsProcessingParameterVectorTileDestination::typeName() )
396 {
397 lastExtPath = QStringLiteral( "/Processing/LastVectorTileOutputExt" );
398 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
399 }
400
401 // get default filter
402 const QStringList filters = fileFilter.split( QStringLiteral( ";;" ) );
403 QString lastFilter;
404 for ( const QString &f : filters )
405 {
406 if ( f.contains( QStringLiteral( "*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
407 {
408 lastFilter = f;
409 break;
410 }
411 }
412
413 QString path;
414 if ( settings.contains( QStringLiteral( "/Processing/LastOutputPath" ) ) )
415 path = settings.value( QStringLiteral( "/Processing/LastOutputPath" ) ).toString();
416 else
417 path = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
418
419 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "dontconfirmoverwrite" ), false ).toBool();
420
421 QString filename = QFileDialog::getSaveFileName( this, tr( "Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
422 if ( !filename.isEmpty() )
423 {
424 mUseTemporary = false;
425 mUseRemapping = false;
426 filename = QgsFileUtils::addExtensionFromFilter( filename, lastFilter );
427
428 leText->setText( filename );
429 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
430 if ( !lastExtPath.isEmpty() )
431 settings.setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
432
433 emit skipOutputChanged( false );
434 emit destinationChanged();
435 }
436 // return dialog focus on Mac
437 activateWindow();
438 raise();
439}
440
441void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
442{
443 QgsSettings settings;
444 QString lastPath = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QString() ).toString();
445 if ( lastPath.isEmpty() )
446 lastPath = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
447
448 QString filename = QFileDialog::getSaveFileName( this, tr( "Save to GeoPackage" ), lastPath, tr( "GeoPackage files (*.gpkg);;All files (*.*)" ), nullptr, QFileDialog::DontConfirmOverwrite );
449 // return dialog focus on Mac
450 activateWindow();
451 raise();
452
453 if ( filename.isEmpty() )
454 return;
455
456 const QString layerName = QInputDialog::getText( this, tr( "Save to GeoPackage" ), tr( "Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
457 if ( layerName.isEmpty() )
458 return;
459
460 mUseTemporary = false;
461 mUseRemapping = false;
462
463 filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "gpkg" ) );
464
465 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
466
468 uri.setTable( layerName );
469 uri.setDatabase( filename );
470
471 QString geomColumn;
472 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
473 {
474 if ( sink->hasGeometry() )
475 geomColumn = QStringLiteral( "geom" );
476 }
477 uri.setGeometryColumn( geomColumn );
478
479 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
480
481 emit skipOutputChanged( false );
482 emit destinationChanged();
483}
484
485void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
486{
487 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
488 {
489
490 QgsNewDatabaseTableNameWidget *widget = new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral( "postgres" )
491 << QStringLiteral( "mssql" )
492 << QStringLiteral( "ogr" )
493 << QStringLiteral( "hana" )
494 << QStringLiteral( "spatialite" )
495 << QStringLiteral( "oracle" ), this );
496 widget->setPanelTitle( tr( "Save “%1” to Database Table" ).arg( mParameter->description() ) );
497 widget->setAcceptButtonVisible( true );
498
499 panel->openPanel( widget );
500
501 auto changed = [ = ]
502 {
503 mUseTemporary = false;
504 mUseRemapping = false;
505
506 QString geomColumn;
507 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
508 {
509 if ( sink->hasGeometry() )
510 geomColumn = widget->dataProviderKey() == QLatin1String( "oracle" ) ? QStringLiteral( "GEOM" ) : QStringLiteral( "geom" );
511 }
512
513 if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
514 {
516 uri.setTable( widget->table() );
517 uri.setDatabase( widget->schema() );
518 uri.setGeometryColumn( geomColumn );
519 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
520 }
521 else
522 {
523 QgsDataSourceUri uri( widget->uri() );
524 uri.setGeometryColumn( geomColumn );
525 leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
526 }
527
528 emit skipOutputChanged( false );
529 emit destinationChanged();
530 };
531
532 connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [ = ] { changed(); } );
533 connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [ = ] { changed(); } );
534 connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [ = ] { changed(); } );
535 connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [ = ] { changed(); } );
536 connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [ = ]
537 {
538 changed();
539 widget->acceptPanel();
540 } );
541 }
542}
543
544void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
545{
546 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
547 {
549 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
550
551 panel->openPanel( widget );
552
553 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
554 {
555 widget->acceptPanel();
556 } );
557 connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
558 {
559 if ( widget->uri().uri.isEmpty() )
560 setValue( QVariant() );
561 else
562 {
563 // get fields for destination
564 std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->uri().uri, QString(), widget->uri().providerKey );
565 if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
566 setAppendDestination( widget->uri().uri, dest->fields() );
567 else
568 setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
569 }
570 } );
571 }
572}
573
574
575void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
576{
577 const QgsProcessingAlgorithm *alg = mParameter->algorithm();
578 QVariantMap props;
579 if ( mParametersGenerator )
580 props = mParametersGenerator->createProcessingParameters();
581 props.insert( mParameter->name(), uri );
582
583 const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties >() );
585 {
586 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
587 {
588 // get mapping from fields output by algorithm to destination fields
589 QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
590 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
591 if ( !mRemapDefinition.fieldMap().isEmpty() )
592 widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
593
594 panel->openPanel( widget );
595
596 connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
597 {
600 remap.setSourceCrs( outputProps.crs );
601 remap.setFieldMap( widget->fieldPropertyMap() );
602 remap.setDestinationFields( destFields );
603 def.setRemappingDefinition( remap );
604 setValue( def );
605 } );
606 }
607 }
608}
609
610void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
611{
612 QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
613 if ( dialog.exec() )
614 {
615 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( dialog.encoding() );
616
617 QgsSettings settings;
618 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
619
620 emit destinationChanged();
621 }
622}
623
624void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
625{
626 mUseTemporary = text.isEmpty();
627 mUseRemapping = false;
628 emit destinationChanged();
629}
630
631
632QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
633{
635 for ( const QgsMimeDataUtils::Uri &u : uriList )
636 {
637 if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
639 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
640 && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
641 {
642 return u.uri;
643 }
644 else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
645 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
646 && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
647 {
648 return u.uri;
649 }
650 else if ( ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName()
651 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
652 && u.layerType == QLatin1String( "pointcloud" ) && ( u.providerKey == QLatin1String( "ept" ) || u.providerKey == QLatin1String( "pdal" ) ) )
653 {
654 return u.uri;
655 }
656#if 0
657 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
658 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
659 && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
660 return u.uri;
661
662#endif
663 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
664 && u.layerType == QLatin1String( "directory" ) )
665 {
666 return u.uri;
667 }
668 }
669 if ( !uriList.isEmpty() )
670 return QString();
671
672 // files dragged from file explorer, outside of QGIS
673 QStringList rawPaths;
674 if ( data->hasUrls() )
675 {
676 const QList< QUrl > urls = data->urls();
677 rawPaths.reserve( urls.count() );
678 for ( const QUrl &url : urls )
679 {
680 const QString local = url.toLocalFile();
681 if ( !rawPaths.contains( local ) )
682 rawPaths.append( local );
683 }
684 }
685 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
686 rawPaths.append( data->text() );
687
688 for ( const QString &path : std::as_const( rawPaths ) )
689 {
690 QFileInfo file( path );
691 if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
695 || mParameter->type() == QgsProcessingParameterFileDestination::typeName()
696 || mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() ) )
697 {
698 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
699 return path;
700 }
701 else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
702 return path;
703 }
704
705 return QString();
706}
707
708void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
709{
710 if ( !( event->possibleActions() & Qt::CopyAction ) )
711 return;
712
713 const QString path = mimeDataToPath( event->mimeData() );
714 if ( !path.isEmpty() )
715 {
716 // dragged an acceptable path, phew
717 event->setDropAction( Qt::CopyAction );
718 event->accept();
719 leText->setHighlighted( true );
720 }
721}
722
723void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
724{
725 QWidget::dragLeaveEvent( event );
726 if ( leText->isHighlighted() )
727 {
728 event->accept();
729 leText->setHighlighted( false );
730 }
731}
732
733void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
734{
735 if ( !( event->possibleActions() & Qt::CopyAction ) )
736 return;
737
738 const QString path = mimeDataToPath( event->mimeData() );
739 if ( !path.isEmpty() )
740 {
741 // dropped an acceptable path, phew
742 setFocus( Qt::MouseFocusReason );
743 event->setDropAction( Qt::CopyAction );
744 event->accept();
745 setValue( path );
746 }
747 leText->setHighlighted( false );
748}
749
@ 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.