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