QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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::ShowDirsOnly );
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 
390  // get default filter
391  const QStringList filters = fileFilter.split( QStringLiteral( ";;" ) );
392  QString lastFilter;
393  for ( const QString &f : filters )
394  {
395  if ( f.contains( QStringLiteral( "*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
396  {
397  lastFilter = f;
398  break;
399  }
400  }
401 
402  QString path;
403  if ( settings.contains( QStringLiteral( "/Processing/LastOutputPath" ) ) )
404  path = settings.value( QStringLiteral( "/Processing/LastOutputPath" ) ).toString();
405  else
406  path = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
407 
408  const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "dontconfirmoverwrite" ), false ).toBool();
409 
410  QString filename = QFileDialog::getSaveFileName( this, tr( "Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
411  if ( !filename.isEmpty() )
412  {
413  mUseTemporary = false;
414  mUseRemapping = false;
415  filename = QgsFileUtils::addExtensionFromFilter( filename, lastFilter );
416 
417  leText->setText( filename );
418  settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
419  if ( !lastExtPath.isEmpty() )
420  settings.setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
421 
422  emit skipOutputChanged( false );
423  emit destinationChanged();
424  }
425 }
426 
427 void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
428 {
429  QgsSettings settings;
430  QString lastPath = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QString() ).toString();
431  if ( lastPath.isEmpty() )
432  lastPath = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
433 
434  QString filename = QFileDialog::getSaveFileName( this, tr( "Save to GeoPackage" ), lastPath, tr( "GeoPackage files (*.gpkg);;All files (*.*)" ), nullptr, QFileDialog::DontConfirmOverwrite );
435 
436  if ( filename.isEmpty() )
437  return;
438 
439  const QString layerName = QInputDialog::getText( this, tr( "Save to GeoPackage" ), tr( "Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
440  if ( layerName.isEmpty() )
441  return;
442 
443  mUseTemporary = false;
444  mUseRemapping = false;
445 
446  filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "gpkg" ) );
447 
448  settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
449 
450  QgsDataSourceUri uri;
451  uri.setTable( layerName );
452  uri.setDatabase( filename );
453 
454  QString geomColumn;
455  if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
456  {
457  if ( sink->hasGeometry() )
458  geomColumn = QStringLiteral( "geom" );
459  }
460  uri.setGeometryColumn( geomColumn );
461 
462  leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
463 
464  emit skipOutputChanged( false );
465  emit destinationChanged();
466 }
467 
468 void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
469 {
470  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
471  {
472 
473  QgsNewDatabaseTableNameWidget *widget = new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral( "postgres" )
474  << QStringLiteral( "mssql" )
475  << QStringLiteral( "ogr" )
476  << QStringLiteral( "hana" )
477  << QStringLiteral( "spatialite" ), this );
478  widget->setPanelTitle( tr( "Save “%1” to Database Table" ).arg( mParameter->description() ) );
479  widget->setAcceptButtonVisible( true );
480 
481  panel->openPanel( widget );
482 
483  auto changed = [ = ]
484  {
485  mUseTemporary = false;
486  mUseRemapping = false;
487 
488  QString geomColumn;
489  if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
490  {
491  if ( sink->hasGeometry() )
492  geomColumn = QStringLiteral( "geom" );
493  }
494 
495  if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
496  {
497  QgsDataSourceUri uri;
498  uri.setTable( widget->table() );
499  uri.setDatabase( widget->schema() );
500  uri.setGeometryColumn( geomColumn );
501  leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
502  }
503  else
504  {
505  QgsDataSourceUri uri( widget->uri() );
506  uri.setGeometryColumn( geomColumn );
507  leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
508  }
509 
510  emit skipOutputChanged( false );
511  emit destinationChanged();
512  };
513 
514  connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [ = ] { changed(); } );
515  connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [ = ] { changed(); } );
516  connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [ = ] { changed(); } );
517  connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [ = ] { changed(); } );
518  connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [ = ]
519  {
520  changed();
521  widget->acceptPanel();
522  } );
523  }
524 }
525 
526 void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
527 {
528  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
529  {
531  widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
532 
533  panel->openPanel( widget );
534 
535  connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
536  {
537  widget->acceptPanel();
538  } );
539  connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
540  {
541  if ( widget->uri().uri.isEmpty() )
542  setValue( QVariant() );
543  else
544  {
545  // get fields for destination
546  std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->uri().uri, QString(), widget->uri().providerKey );
547  if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
548  setAppendDestination( widget->uri().uri, dest->fields() );
549  else
550  setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
551  }
552  } );
553  }
554 }
555 
556 
557 void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
558 {
559  const QgsProcessingAlgorithm *alg = mParameter->algorithm();
560  QVariantMap props;
561  if ( mParametersGenerator )
562  props = mParametersGenerator->createProcessingParameters();
563  props.insert( mParameter->name(), uri );
564 
565  const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties >() );
566  if ( outputProps.availability == QgsProcessingAlgorithm::Available )
567  {
568  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
569  {
570  // get mapping from fields output by algorithm to destination fields
571  QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
572  widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
573  if ( !mRemapDefinition.fieldMap().isEmpty() )
574  widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
575 
576  panel->openPanel( widget );
577 
578  connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
579  {
582  remap.setSourceCrs( outputProps.crs );
583  remap.setFieldMap( widget->fieldPropertyMap() );
584  remap.setDestinationFields( destFields );
585  def.setRemappingDefinition( remap );
586  setValue( def );
587  } );
588  }
589  }
590 }
591 
592 void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
593 {
594  QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
595  if ( dialog.exec() )
596  {
597  mEncoding = dialog.encoding();
598  QgsSettings settings;
599  settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
600  emit destinationChanged();
601  }
602 }
603 
604 void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
605 {
606  mUseTemporary = text.isEmpty();
607  mUseRemapping = false;
608  emit destinationChanged();
609 }
610 
611 
612 QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
613 {
615  for ( const QgsMimeDataUtils::Uri &u : uriList )
616  {
617  if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
618  || mParameter->type() == QgsProcessingParameterVectorDestination::typeName()
619  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
620  && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
621  {
622  return u.uri;
623  }
624  else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
625  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
626  && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
627  return u.uri;
628 #if 0
629  else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
630  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
631  && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
632  return u.uri;
633 
634 #endif
635  else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
636  && u.layerType == QLatin1String( "directory" ) )
637  {
638  return u.uri;
639  }
640  }
641  if ( !uriList.isEmpty() )
642  return QString();
643 
644  // files dragged from file explorer, outside of QGIS
645  QStringList rawPaths;
646  if ( data->hasUrls() )
647  {
648  const QList< QUrl > urls = data->urls();
649  rawPaths.reserve( urls.count() );
650  for ( const QUrl &url : urls )
651  {
652  const QString local = url.toLocalFile();
653  if ( !rawPaths.contains( local ) )
654  rawPaths.append( local );
655  }
656  }
657  if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
658  rawPaths.append( data->text() );
659 
660  for ( const QString &path : std::as_const( rawPaths ) )
661  {
662  QFileInfo file( path );
663  if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
664  || mParameter->type() == QgsProcessingParameterVectorDestination::typeName()
665  || mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
666  || mParameter->type() == QgsProcessingParameterVectorDestination::typeName()
667  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() ) )
668  {
669  // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
670  return path;
671  }
672  else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
673  return path;
674  }
675 
676  return QString();
677 }
678 
679 void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
680 {
681  if ( !( event->possibleActions() & Qt::CopyAction ) )
682  return;
683 
684  const QString path = mimeDataToPath( event->mimeData() );
685  if ( !path.isEmpty() )
686  {
687  // dragged an acceptable path, phew
688  event->setDropAction( Qt::CopyAction );
689  event->accept();
690  leText->setHighlighted( true );
691  }
692 }
693 
694 void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
695 {
696  QWidget::dragLeaveEvent( event );
697  if ( leText->isHighlighted() )
698  {
699  event->accept();
700  leText->setHighlighted( false );
701  }
702 }
703 
704 void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
705 {
706  if ( !( event->possibleActions() & Qt::CopyAction ) )
707  return;
708 
709  const QString path = mimeDataToPath( event->mimeData() );
710  if ( !path.isEmpty() )
711  {
712  // dropped an acceptable path, phew
713  setFocus( Qt::MouseFocusReason );
714  event->setDropAction( Qt::CopyAction );
715  event->accept();
716  setValue( path );
717  }
718  leText->setHighlighted( false );
719 }
720 
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.
Contains settings which reflect the context in which a Processing parameter widget is shown,...
QgsBrowserGuiModel * browserModel() const
Returns the browser model associated with the widget.
An interface for objects which can create sets of parameter values for processing algorithms.
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri()
static bool decodeProviderKeyAndUri(const QString &string, QString &providerKey, QString &uri)
Decodes a provider key and layer uri from an encoded string, for use with encodeProviderKeyAndUri()
static const QString TEMPORARY_OUTPUT
Constant used to indicate that a Processing algorithm output should be a temporary layer/file.
QVariant staticValue() const
Returns the current static value for the property.
Defines the parameters used to remap features when creating a QgsRemappingProxyFeatureSink.
void setFieldMap(const QMap< QString, QgsProperty > &map)
Sets the field mapping, which defines how to map the values from incoming features to destination fie...
void setDestinationFields(const QgsFields &fields)
Sets the fields for the destination sink.
void setSourceCrs(const QgsCoordinateReferenceSystem &source)
Sets the source crs used for reprojecting incoming features to the sink's destination CRS.
This class is a composition of two QSettings instances:
Definition: qgssettings.h: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.
const QString & typeName
QString uri
Identifier of the data source recognized by its providerKey.
QString providerKey
For "vector" / "raster" type: provider id.
Properties of a vector source or sink used in an algorithm.
QgsCoordinateReferenceSystem crs
Coordinate Reference System.
QgsProcessingAlgorithm::PropertyAvailability availability
Availability of the properties. By default properties are not available.