QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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  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" ), this );
483  widget->setPanelTitle( tr( "Save “%1” to Database Table" ).arg( mParameter->description() ) );
484  widget->setAcceptButtonVisible( true );
485 
486  panel->openPanel( widget );
487 
488  auto changed = [ = ]
489  {
490  mUseTemporary = false;
491  mUseRemapping = false;
492 
493  QString geomColumn;
494  if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast< const QgsProcessingParameterFeatureSink * >( mParameter ) )
495  {
496  if ( sink->hasGeometry() )
497  geomColumn = QStringLiteral( "geom" );
498  }
499 
500  if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
501  {
502  QgsDataSourceUri uri;
503  uri.setTable( widget->table() );
504  uri.setDatabase( widget->schema() );
505  uri.setGeometryColumn( geomColumn );
506  leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
507  }
508  else
509  {
510  QgsDataSourceUri uri( widget->uri() );
511  uri.setGeometryColumn( geomColumn );
512  leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
513  }
514 
515  emit skipOutputChanged( false );
516  emit destinationChanged();
517  };
518 
519  connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [ = ] { changed(); } );
520  connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [ = ] { changed(); } );
521  connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [ = ] { changed(); } );
522  connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [ = ] { changed(); } );
523  connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [ = ]
524  {
525  changed();
526  widget->acceptPanel();
527  } );
528  }
529 }
530 
531 void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
532 {
533  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
534  {
536  widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
537 
538  panel->openPanel( widget );
539 
540  connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
541  {
542  widget->acceptPanel();
543  } );
544  connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
545  {
546  if ( widget->uri().uri.isEmpty() )
547  setValue( QVariant() );
548  else
549  {
550  // get fields for destination
551  std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->uri().uri, QString(), widget->uri().providerKey );
552  if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
553  setAppendDestination( widget->uri().uri, dest->fields() );
554  else
555  setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
556  }
557  } );
558  }
559 }
560 
561 
562 void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
563 {
564  const QgsProcessingAlgorithm *alg = mParameter->algorithm();
565  QVariantMap props;
566  if ( mParametersGenerator )
567  props = mParametersGenerator->createProcessingParameters();
568  props.insert( mParameter->name(), uri );
569 
570  const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties >() );
571  if ( outputProps.availability == QgsProcessingAlgorithm::Available )
572  {
573  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
574  {
575  // get mapping from fields output by algorithm to destination fields
576  QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
577  widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
578  if ( !mRemapDefinition.fieldMap().isEmpty() )
579  widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
580 
581  panel->openPanel( widget );
582 
583  connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
584  {
587  remap.setSourceCrs( outputProps.crs );
588  remap.setFieldMap( widget->fieldPropertyMap() );
589  remap.setDestinationFields( destFields );
590  def.setRemappingDefinition( remap );
591  setValue( def );
592  } );
593  }
594  }
595 }
596 
597 void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
598 {
599  QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
600  if ( dialog.exec() )
601  {
602  mEncoding = dialog.encoding();
603  QgsSettings settings;
604  settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
605  emit destinationChanged();
606  }
607 }
608 
609 void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
610 {
611  mUseTemporary = text.isEmpty();
612  mUseRemapping = false;
613  emit destinationChanged();
614 }
615 
616 
617 QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
618 {
620  for ( const QgsMimeDataUtils::Uri &u : uriList )
621  {
622  if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
623  || mParameter->type() == QgsProcessingParameterVectorDestination::typeName()
624  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
625  && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
626  {
627  return u.uri;
628  }
629  else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
630  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
631  && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
632  {
633  return u.uri;
634  }
635  else if ( ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName()
636  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
637  && u.layerType == QLatin1String( "pointcloud" ) && ( u.providerKey == QLatin1String( "ept" ) || u.providerKey == QLatin1String( "pdal" ) ) )
638  {
639  return u.uri;
640  }
641 #if 0
642  else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
643  || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
644  && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
645  return u.uri;
646 
647 #endif
648  else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
649  && u.layerType == QLatin1String( "directory" ) )
650  {
651  return u.uri;
652  }
653  }
654  if ( !uriList.isEmpty() )
655  return QString();
656 
657  // files dragged from file explorer, outside of QGIS
658  QStringList rawPaths;
659  if ( data->hasUrls() )
660  {
661  const QList< QUrl > urls = data->urls();
662  rawPaths.reserve( urls.count() );
663  for ( const QUrl &url : urls )
664  {
665  const QString local = url.toLocalFile();
666  if ( !rawPaths.contains( local ) )
667  rawPaths.append( local );
668  }
669  }
670  if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
671  rawPaths.append( data->text() );
672 
673  for ( const QString &path : std::as_const( rawPaths ) )
674  {
675  QFileInfo file( path );
676  if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
677  || mParameter->type() == QgsProcessingParameterVectorDestination::typeName()
678  || mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
679  || mParameter->type() == QgsProcessingParameterVectorDestination::typeName()
680  || mParameter->type() == QgsProcessingParameterFileDestination::typeName()
681  || mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() ) )
682  {
683  // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
684  return path;
685  }
686  else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
687  return path;
688  }
689 
690  return QString();
691 }
692 
693 void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
694 {
695  if ( !( event->possibleActions() & Qt::CopyAction ) )
696  return;
697 
698  const QString path = mimeDataToPath( event->mimeData() );
699  if ( !path.isEmpty() )
700  {
701  // dragged an acceptable path, phew
702  event->setDropAction( Qt::CopyAction );
703  event->accept();
704  leText->setHighlighted( true );
705  }
706 }
707 
708 void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
709 {
710  QWidget::dragLeaveEvent( event );
711  if ( leText->isHighlighted() )
712  {
713  event->accept();
714  leText->setHighlighted( false );
715  }
716 }
717 
718 void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
719 {
720  if ( !( event->possibleActions() & Qt::CopyAction ) )
721  return;
722 
723  const QString path = mimeDataToPath( event->mimeData() );
724  if ( !path.isEmpty() )
725  {
726  // dropped an acceptable path, phew
727  setFocus( Qt::MouseFocusReason );
728  event->setDropAction( Qt::CopyAction );
729  event->accept();
730  setValue( path );
731  }
732  leText->setHighlighted( false );
733 }
734 
The QgsDataSourceSelectWidget class embeds the browser view to select an existing data source.
void itemTriggered(const QgsMimeDataUtils::Uri &uri)
Emitted when an item is triggered, e.g.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
Class for storing the component parts of a RDBMS data source URI (e.g.
void setTable(const QString &table)
Sets table to table.
void setGeometryColumn(const QString &geometryColumn)
Sets geometry column name to geometryColumn.
QString uri(bool expandAuthConfig=true) const
Returns the complete URI as a string.
void setDatabase(const QString &database)
Sets the URI database name.
A dialog which presents the user with a choice of file encodings.
The QgsFieldMappingWidget class creates a mapping from one set of QgsFields to another,...
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
Container of fields for a vector layer.
Definition: qgsfields.h:45
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
static QString addExtensionFromFilter(const QString &fileName, const QString &filter)
Ensures that a fileName ends with an extension from the specified filter string.
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
The QgsNewDatabaseTableNameWidget class embeds the browser view to select a DB schema and a new table...
QString table() const
Returns the current name of the new table.
QString dataProviderKey() const
Returns the currently selected data item provider key.
void setAcceptButtonVisible(bool visible)
Sets whether the optional "Ok"/accept button should be visible.
void providerKeyChanged(const QString &providerKey)
This signal is emitted when the selects a data provider or a schema name that has a different data pr...
QString schema() const
Returns the currently selected schema or file path (in case of filesystem-based DBs like spatialite o...
void tableNameChanged(const QString &tableName)
This signal is emitted when the user enters a table name.
void accepted()
Emitted when the OK/accept button is clicked.
void validationChanged(bool isValid)
This signal is emitted whenever the validation status of the widget changes.
QString uri() const
Returns the (possibly blank) string representation of the new table data source URI.
void schemaNameChanged(const QString &schemaName)
This signal is emitted when the user selects a schema (or file path for filesystem-based DBs like spa...
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void acceptPanel()
Accept the panel.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Abstract base class for processing algorithms.
@ Available
Properties are available.
virtual QgsProcessingAlgorithm::VectorProperties sinkProperties(const QString &sink, const QVariantMap &parameters, QgsProcessingContext &context, const QMap< QString, QgsProcessingAlgorithm::VectorProperties > &sourceProperties) const
Returns the vector properties which will be used for the sink with matching name.
Contains information about the context in which a processing algorithm is executed.
Base class for all parameter definitions which represent file or layer destinations,...
Encapsulates settings relating to a feature sink or output raster layer for a processing algorithm.
QgsProperty sink
Sink/layer definition.
bool useRemapping() const
Returns true if the output uses a remapping definition.
QgsRemappingSinkDefinition remappingDefinition() const
Returns the output remapping definition, if useRemapping() is true.
QVariantMap createOptions
Map of optional sink/layer creation options, which are passed to the underlying provider when creatin...
void setRemappingDefinition(const QgsRemappingSinkDefinition &definition)
Sets the remapping definition to use when adding features to the output layer.
A feature sink output for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
Contains settings which reflect the context in which a Processing parameter widget is shown,...
QgsBrowserGuiModel * browserModel() const
Returns the browser model associated with the widget.
An interface for objects which can create sets of parameter values for processing algorithms.
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri()
static bool decodeProviderKeyAndUri(const QString &string, QString &providerKey, QString &uri)
Decodes a provider key and layer uri from an encoded string, for use with encodeProviderKeyAndUri()
static const QString TEMPORARY_OUTPUT
Constant used to indicate that a Processing algorithm output should be a temporary layer/file.
QVariant staticValue() const
Returns the current static value for the property.
Defines the parameters used to remap features when creating a QgsRemappingProxyFeatureSink.
void setFieldMap(const QMap< QString, QgsProperty > &map)
Sets the field mapping, which defines how to map the values from incoming features to destination fie...
void setDestinationFields(const QgsFields &fields)
Sets the fields for the destination sink.
void setSourceCrs(const QgsCoordinateReferenceSystem &source)
Sets the source crs used for reprojecting incoming features to the sink's destination CRS.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
@ VectorLayer
Vector layer.
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.