29#include <QInputDialog>
37QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget(
const QgsProcessingDestinationParameter *param,
bool defaultSelection, QWidget *parent )
40 , mDefaultSelection( defaultSelection )
42 Q_ASSERT( mParameter );
46 leText->setClearButtonEnabled(
false );
48 connect( leText, &QLineEdit::textEdited,
this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
50 mMenu =
new QMenu(
this );
51 connect( mMenu, &QMenu::aboutToShow,
this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
52 mSelectButton->setMenu( mMenu );
53 mSelectButton->setPopupMode( QToolButton::InstantPopup );
57 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
59 if ( !mParameter->defaultValueForGui().isValid() )
63 setValue( QVariant() );
69 setValue( mParameter->defaultValueForGui() );
72 setToolTip( mParameter->toolTip() );
74 setAcceptDrops(
true );
75 leText->setAcceptDrops(
false );
78bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
const
80 return leText->text().isEmpty() && !mUseTemporary;
83void QgsProcessingLayerOutputDestinationWidget::setValue(
const QVariant &value )
85 const bool prevSkip = outputIsSkipped();
86 mUseRemapping =
false;
87 if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) )
100 else if ( value.userType() == QMetaType::type(
"QgsProcessingOutputLayerDefinition" ) )
109 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
111 mUseTemporary =
false;
113 emit skipOutputChanged(
false );
114 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
115 emit destinationChanged();
119 mEncoding = def.
createOptions.value( QStringLiteral(
"fileEncoding" ) ).toString();
123 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
124 leText->setText( value.toString() );
125 mUseTemporary =
false;
127 emit skipOutputChanged(
false );
131 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
132 emit destinationChanged();
136 if ( prev.userType() != QMetaType::type(
"QgsProcessingOutputLayerDefinition" ) ||
138 emit destinationChanged();
144QVariant QgsProcessingLayerOutputDestinationWidget::value()
const
152 else if ( mUseTemporary && !mDefaultSelection )
158 key = leText->text();
167 && !key.startsWith( QLatin1String(
"memory:" ) )
168 && !key.startsWith( QLatin1String(
"ogr:" ) )
169 && !key.startsWith( QLatin1String(
"postgres:" ) )
170 && !key.startsWith( QLatin1String(
"postgis:" ) )
174 QString folder = QFileInfo( key ).path();
178 QString defaultFolder = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
179 key = QDir( defaultFolder ).filePath( key );
189 value.createOptions.insert( QStringLiteral(
"fileEncoding" ), mEncoding );
191 value.setRemappingDefinition( mRemapDefinition );
207 mParametersGenerator = generator;
210void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
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 );
218 connect(
this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged,
this, [ = ](
bool skipped )
220 bool enabled = !skipped;
221 mOpenAfterRunningCheck->setEnabled( enabled );
222 mOpenAfterRunningCheck->setChecked( enabled );
226bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
228 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
231void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
235 if ( !mDefaultSelection )
239 QAction *actionSkipOutput =
new QAction( tr(
"Skip Output" ),
this );
240 connect( actionSkipOutput, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
241 mMenu->addAction( actionSkipOutput );
244 QAction *actionSaveToTemp =
nullptr;
248 actionSaveToTemp =
new QAction( tr(
"Create Temporary Layer" ),
this );
252 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary Directory" ),
this );
256 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary File" ),
this );
259 connect( actionSaveToTemp, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
260 mMenu->addAction( actionSaveToTemp );
263 QAction *actionSaveToFile =
nullptr;
266 actionSaveToFile =
new QAction( tr(
"Save to Directory…" ),
this );
267 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
271 actionSaveToFile =
new QAction( tr(
"Save to File…" ),
this );
272 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
274 mMenu->addAction( actionSaveToFile );
278 QAction *actionSaveToGpkg =
new QAction( tr(
"Save to GeoPackage…" ),
this );
279 connect( actionSaveToGpkg, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
280 mMenu->addAction( actionSaveToGpkg );
282 QAction *actionSaveToDatabase =
new QAction( tr(
"Save to Database Table…" ),
this );
283 connect( actionSaveToDatabase, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
284 mMenu->addAction( actionSaveToDatabase );
286 if ( mParameter->algorithm() && qgis::down_cast< const QgsProcessingParameterFeatureSink * >( mParameter )->supportsAppend() )
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 );
294 QAction *editMappingAction =
new QAction( tr(
"Edit Field Mapping…" ),
this );
295 connect( editMappingAction, &QAction::triggered,
this, [ = ]
297 setAppendDestination( value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
299 mMenu->addAction( editMappingAction );
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 );
313void QgsProcessingLayerOutputDestinationWidget::skipOutput()
315 leText->setPlaceholderText( tr(
"[Skip output]" ) );
317 mUseTemporary =
false;
318 mUseRemapping =
false;
320 emit skipOutputChanged(
true );
321 emit destinationChanged();
324void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
326 const bool prevSkip = outputIsSkipped();
330 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
334 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
338 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
345 mUseTemporary =
true;
346 mUseRemapping =
false;
348 emit skipOutputChanged(
false );
349 emit destinationChanged();
352void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
354 QString lastDir = leText->text();
356 if ( lastDir.isEmpty() )
357 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
359 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::Options() );
360 if ( !dirName.isEmpty() )
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();
371void QgsProcessingLayerOutputDestinationWidget::selectFile()
373 const QString fileFilter = mParameter->createFileFilter();
381 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
382 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString() ;
386 lastExtPath = QStringLiteral(
"/Processing/LastRasterOutputExt" );
387 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
391 lastExtPath = QStringLiteral(
"/Processing/LastPointCloudOutputExt" );
392 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
396 lastExtPath = QStringLiteral(
"/Processing/LastVectorTileOutputExt" );
397 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
401 const QStringList filters = fileFilter.split( QStringLiteral(
";;" ) );
403 for (
const QString &f : filters )
405 if ( f.contains( QStringLiteral(
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
413 if ( settings.
contains( QStringLiteral(
"/Processing/LastOutputPath" ) ) )
414 path = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ) ).toString();
416 path = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
418 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral(
"widget_wrapper" ) ).toMap().value( QStringLiteral(
"dontconfirmoverwrite" ),
false ).toBool();
420 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
421 if ( !filename.isEmpty() )
423 mUseTemporary =
false;
424 mUseRemapping =
false;
427 leText->setText( filename );
428 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
429 if ( !lastExtPath.isEmpty() )
430 settings.
setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
432 emit skipOutputChanged(
false );
433 emit destinationChanged();
440void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
443 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
444 if ( lastPath.isEmpty() )
445 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
447 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
452 if ( filename.isEmpty() )
455 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
456 if ( layerName.isEmpty() )
459 mUseTemporary =
false;
460 mUseRemapping =
false;
464 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
473 if ( sink->hasGeometry() )
474 geomColumn = QStringLiteral(
"geom" );
478 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
480 emit skipOutputChanged(
false );
481 emit destinationChanged();
484void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
490 << QStringLiteral(
"mssql" )
491 << QStringLiteral(
"ogr" )
492 << QStringLiteral(
"hana" )
493 << QStringLiteral(
"spatialite" )
494 << QStringLiteral(
"oracle" ),
this );
495 widget->
setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
498 panel->openPanel( widget );
502 mUseTemporary =
false;
503 mUseRemapping =
false;
508 if ( sink->hasGeometry() )
509 geomColumn = widget->
dataProviderKey() == QLatin1String(
"oracle" ) ? QStringLiteral(
"GEOM" ) : QStringLiteral(
"geom" );
518 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
527 emit skipOutputChanged(
false );
528 emit destinationChanged();
543void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
548 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
550 panel->openPanel( widget );
558 if ( widget->
uri().
uri.isEmpty() )
559 setValue( QVariant() );
563 std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
565 setAppendDestination( widget->
uri().
uri, dest->fields() );
574void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
578 if ( mParametersGenerator )
579 props = mParametersGenerator->createProcessingParameters();
580 props.insert( mParameter->name(), uri );
589 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
590 if ( !mRemapDefinition.fieldMap().isEmpty() )
593 panel->openPanel( widget );
609void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
617 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
619 emit destinationChanged();
623void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
625 mUseTemporary = text.isEmpty();
626 mUseRemapping =
false;
627 emit destinationChanged();
631QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
639 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
645 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
651 && u.layerType == QLatin1String(
"pointcloud" ) && ( u.providerKey == QLatin1String(
"ept" ) || u.providerKey == QLatin1String(
"pdal" ) ) )
656 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
658 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
663 && u.layerType == QLatin1String(
"directory" ) )
668 if ( !uriList.isEmpty() )
672 QStringList rawPaths;
673 if ( data->hasUrls() )
675 const QList< QUrl > urls = data->urls();
676 rawPaths.reserve( urls.count() );
677 for (
const QUrl &url : urls )
679 const QString local = url.toLocalFile();
680 if ( !rawPaths.contains( local ) )
681 rawPaths.append( local );
684 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
685 rawPaths.append( data->text() );
687 for (
const QString &path : std::as_const( rawPaths ) )
689 QFileInfo file( path );
707void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
709 if ( !( event->possibleActions() & Qt::CopyAction ) )
712 const QString path = mimeDataToPath( event->mimeData() );
713 if ( !path.isEmpty() )
716 event->setDropAction( Qt::CopyAction );
718 leText->setHighlighted(
true );
722void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
724 QWidget::dragLeaveEvent( event );
725 if ( leText->isHighlighted() )
728 leText->setHighlighted(
false );
732void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
734 if ( !( event->possibleActions() & Qt::CopyAction ) )
737 const QString path = mimeDataToPath( event->mimeData() );
738 if ( !path.isEmpty() )
741 setFocus( Qt::MouseFocusReason );
742 event->setDropAction( Qt::CopyAction );
746 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 QString resolveDefaultEncoding(const QString &defaultEncoding="System")
Returns the default encoding.
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.