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