29#include <QInputDialog>
35QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget(
const QgsProcessingDestinationParameter *param,
bool defaultSelection, QWidget *parent )
38 , mDefaultSelection( defaultSelection )
40 Q_ASSERT( mParameter );
44 leText->setClearButtonEnabled(
false );
46 connect( leText, &QLineEdit::textEdited,
this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
48 mMenu =
new QMenu(
this );
49 connect( mMenu, &QMenu::aboutToShow,
this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
50 mSelectButton->setMenu( mMenu );
51 mSelectButton->setPopupMode( QToolButton::InstantPopup );
54 mEncoding = settings.
value( QStringLiteral(
"/Processing/encoding" ), QStringLiteral(
"System" ) ).toString();
56 if ( !mParameter->defaultValueForGui().isValid() )
60 setValue( QVariant() );
66 setValue( mParameter->defaultValueForGui() );
69 setToolTip( mParameter->toolTip() );
71 setAcceptDrops(
true );
72 leText->setAcceptDrops(
false );
75bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
const
77 return leText->text().isEmpty() && !mUseTemporary;
80void QgsProcessingLayerOutputDestinationWidget::setValue(
const QVariant &value )
82 const bool prevSkip = outputIsSkipped();
83 mUseRemapping =
false;
84 if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) )
97 else if ( value.userType() == QMetaType::type(
"QgsProcessingOutputLayerDefinition" ) )
106 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
108 mUseTemporary =
false;
110 emit skipOutputChanged(
false );
111 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
112 emit destinationChanged();
116 mEncoding = def.
createOptions.value( QStringLiteral(
"fileEncoding" ) ).toString();
120 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
121 leText->setText( value.toString() );
122 mUseTemporary =
false;
124 emit skipOutputChanged(
false );
128 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
129 emit destinationChanged();
133 if ( prev.userType() != QMetaType::type(
"QgsProcessingOutputLayerDefinition" ) ||
135 emit destinationChanged();
141QVariant QgsProcessingLayerOutputDestinationWidget::value()
const
149 else if ( mUseTemporary && !mDefaultSelection )
155 key = leText->text();
164 && !key.startsWith( QLatin1String(
"memory:" ) )
165 && !key.startsWith( QLatin1String(
"ogr:" ) )
166 && !key.startsWith( QLatin1String(
"postgres:" ) )
167 && !key.startsWith( QLatin1String(
"postgis:" ) )
171 QString folder = QFileInfo( key ).path();
175 QString defaultFolder = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
176 key = QDir( defaultFolder ).filePath( key );
186 value.createOptions.insert( QStringLiteral(
"fileEncoding" ), mEncoding );
188 value.setRemappingDefinition( mRemapDefinition );
204 mParametersGenerator = generator;
207void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
209 Q_ASSERT( mOpenAfterRunningCheck ==
nullptr );
210 mOpenAfterRunningCheck =
new QCheckBox( tr(
"Open output file after running algorithm" ) );
211 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
212 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
213 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
215 connect(
this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged,
this, [ = ](
bool skipped )
217 bool enabled = !skipped;
218 mOpenAfterRunningCheck->setEnabled( enabled );
219 mOpenAfterRunningCheck->setChecked( enabled );
223bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
225 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
228void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
232 if ( !mDefaultSelection )
236 QAction *actionSkipOutput =
new QAction( tr(
"Skip Output" ),
this );
237 connect( actionSkipOutput, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
238 mMenu->addAction( actionSkipOutput );
241 QAction *actionSaveToTemp =
nullptr;
245 actionSaveToTemp =
new QAction( tr(
"Create Temporary Layer" ),
this );
249 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary Directory" ),
this );
253 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary File" ),
this );
256 connect( actionSaveToTemp, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
257 mMenu->addAction( actionSaveToTemp );
260 QAction *actionSaveToFile =
nullptr;
263 actionSaveToFile =
new QAction( tr(
"Save to Directory…" ),
this );
264 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
268 actionSaveToFile =
new QAction( tr(
"Save to File…" ),
this );
269 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
271 mMenu->addAction( actionSaveToFile );
275 QAction *actionSaveToGpkg =
new QAction( tr(
"Save to GeoPackage…" ),
this );
276 connect( actionSaveToGpkg, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
277 mMenu->addAction( actionSaveToGpkg );
279 QAction *actionSaveToDatabase =
new QAction( tr(
"Save to Database Table…" ),
this );
280 connect( actionSaveToDatabase, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
281 mMenu->addAction( actionSaveToDatabase );
283 if ( mParameter->algorithm() && qgis::down_cast< const QgsProcessingParameterFeatureSink * >( mParameter )->supportsAppend() )
285 mMenu->addSeparator();
286 QAction *actionAppendToLayer =
new QAction( tr(
"Append to Layer…" ),
this );
287 connect( actionAppendToLayer, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
288 mMenu->addAction( actionAppendToLayer );
291 QAction *editMappingAction =
new QAction( tr(
"Edit Field Mapping…" ),
this );
292 connect( editMappingAction, &QAction::triggered,
this, [ = ]
294 setAppendDestination( value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
296 mMenu->addAction( editMappingAction );
303 mMenu->addSeparator();
304 QAction *actionSetEncoding =
new QAction( tr(
"Change File Encoding (%1)…" ).arg( mEncoding ),
this );
305 connect( actionSetEncoding, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
306 mMenu->addAction( actionSetEncoding );
310void QgsProcessingLayerOutputDestinationWidget::skipOutput()
312 leText->setPlaceholderText( tr(
"[Skip output]" ) );
314 mUseTemporary =
false;
315 mUseRemapping =
false;
317 emit skipOutputChanged(
true );
318 emit destinationChanged();
321void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
323 const bool prevSkip = outputIsSkipped();
327 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
331 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
335 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
342 mUseTemporary =
true;
343 mUseRemapping =
false;
345 emit skipOutputChanged(
false );
346 emit destinationChanged();
349void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
351 QString lastDir = leText->text();
353 if ( lastDir.isEmpty() )
354 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
356 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::Options() );
357 if ( !dirName.isEmpty() )
359 leText->setText( QDir::toNativeSeparators( dirName ) );
360 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), dirName );
361 mUseTemporary =
false;
362 mUseRemapping =
false;
363 emit skipOutputChanged(
false );
364 emit destinationChanged();
368void QgsProcessingLayerOutputDestinationWidget::selectFile()
370 const QString fileFilter = mParameter->createFileFilter();
378 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
379 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString() ;
383 lastExtPath = QStringLiteral(
"/Processing/LastRasterOutputExt" );
384 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
388 lastExtPath = QStringLiteral(
"/Processing/LastPointCloudOutputExt" );
389 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
393 lastExtPath = QStringLiteral(
"/Processing/LastVectorTileOutputExt" );
394 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
398 const QStringList filters = fileFilter.split( QStringLiteral(
";;" ) );
400 for (
const QString &f : filters )
402 if ( f.contains( QStringLiteral(
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
410 if ( settings.
contains( QStringLiteral(
"/Processing/LastOutputPath" ) ) )
411 path = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ) ).toString();
413 path = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
415 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral(
"widget_wrapper" ) ).toMap().value( QStringLiteral(
"dontconfirmoverwrite" ),
false ).toBool();
417 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
418 if ( !filename.isEmpty() )
420 mUseTemporary =
false;
421 mUseRemapping =
false;
424 leText->setText( filename );
425 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
426 if ( !lastExtPath.isEmpty() )
427 settings.
setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
429 emit skipOutputChanged(
false );
430 emit destinationChanged();
434void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
437 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
438 if ( lastPath.isEmpty() )
439 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
441 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
443 if ( filename.isEmpty() )
446 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
447 if ( layerName.isEmpty() )
450 mUseTemporary =
false;
451 mUseRemapping =
false;
455 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
464 if ( sink->hasGeometry() )
465 geomColumn = QStringLiteral(
"geom" );
469 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
471 emit skipOutputChanged(
false );
472 emit destinationChanged();
475void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
481 << QStringLiteral(
"mssql" )
482 << QStringLiteral(
"ogr" )
483 << QStringLiteral(
"hana" )
484 << QStringLiteral(
"spatialite" )
485 << QStringLiteral(
"oracle" ),
this );
486 widget->
setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
489 panel->openPanel( widget );
493 mUseTemporary =
false;
494 mUseRemapping =
false;
499 if ( sink->hasGeometry() )
500 geomColumn = widget->
dataProviderKey() == QLatin1String(
"oracle" ) ? QStringLiteral(
"GEOM" ) : QStringLiteral(
"geom" );
509 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
518 emit skipOutputChanged(
false );
519 emit destinationChanged();
534void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
539 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
541 panel->openPanel( widget );
549 if ( widget->
uri().
uri.isEmpty() )
550 setValue( QVariant() );
554 std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
556 setAppendDestination( widget->
uri().
uri, dest->fields() );
565void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
569 if ( mParametersGenerator )
570 props = mParametersGenerator->createProcessingParameters();
571 props.insert( mParameter->name(), uri );
580 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
581 if ( !mRemapDefinition.fieldMap().isEmpty() )
584 panel->openPanel( widget );
600void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
605 mEncoding = dialog.encoding();
607 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
608 emit destinationChanged();
612void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
614 mUseTemporary = text.isEmpty();
615 mUseRemapping =
false;
616 emit destinationChanged();
620QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
628 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
634 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
640 && u.layerType == QLatin1String(
"pointcloud" ) && ( u.providerKey == QLatin1String(
"ept" ) || u.providerKey == QLatin1String(
"pdal" ) ) )
647 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
652 && u.layerType == QLatin1String(
"directory" ) )
657 if ( !uriList.isEmpty() )
661 QStringList rawPaths;
662 if ( data->hasUrls() )
664 const QList< QUrl > urls = data->urls();
665 rawPaths.reserve( urls.count() );
666 for (
const QUrl &url : urls )
668 const QString local = url.toLocalFile();
669 if ( !rawPaths.contains( local ) )
670 rawPaths.append( local );
673 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
674 rawPaths.append( data->text() );
676 for (
const QString &path : std::as_const( rawPaths ) )
678 QFileInfo file( path );
696void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
698 if ( !( event->possibleActions() & Qt::CopyAction ) )
701 const QString path = mimeDataToPath( event->mimeData() );
702 if ( !path.isEmpty() )
705 event->setDropAction( Qt::CopyAction );
707 leText->setHighlighted(
true );
711void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
713 QWidget::dragLeaveEvent( event );
714 if ( leText->isHighlighted() )
717 leText->setHighlighted(
false );
721void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
723 if ( !( event->possibleActions() & Qt::CopyAction ) )
726 const QString path = mimeDataToPath( event->mimeData() );
727 if ( !path.isEmpty() )
730 setFocus( Qt::MouseFocusReason );
731 event->setDropAction( Qt::CopyAction );
735 leText->setHighlighted(
false );
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.
Container of fields for a vector layer.
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)
Abstract base class for processing algorithms.
@ Available
Properties are available.
virtual QgsProcessingAlgorithm::VectorProperties sinkProperties(const QString &sink, const QVariantMap ¶meters, 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.
@ FlagOptional
Parameter is optional.
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.
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:
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.
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.