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