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