32#include <QInputDialog>
40QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget(
const QgsProcessingDestinationParameter *param,
bool defaultSelection, QWidget *parent )
43 , mDefaultSelection( defaultSelection )
45 Q_ASSERT( mParameter );
49 leText->setClearButtonEnabled(
false );
51 connect( leText, &QLineEdit::textEdited,
this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
53 mMenu =
new QMenu(
this );
54 connect( mMenu, &QMenu::aboutToShow,
this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
55 mSelectButton->setMenu( mMenu );
56 mSelectButton->setPopupMode( QToolButton::InstantPopup );
60 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
62 if ( !mParameter->defaultValueForGui().isValid() )
66 setValue( QVariant() );
72 setValue( mParameter->defaultValueForGui() );
75 setToolTip( mParameter->toolTip() );
77 setAcceptDrops(
true );
78 leText->setAcceptDrops(
false );
81bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
const
83 return leText->text().isEmpty() && !mUseTemporary;
86void QgsProcessingLayerOutputDestinationWidget::setValue(
const QVariant &value )
88 const bool prevSkip = outputIsSkipped();
89 mUseRemapping =
false;
90 if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) )
103 else if ( value.userType() == QMetaType::type(
"QgsProcessingOutputLayerDefinition" ) )
112 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
114 mUseTemporary =
false;
116 emit skipOutputChanged(
false );
117 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
118 emit destinationChanged();
122 mEncoding = def.
createOptions.value( QStringLiteral(
"fileEncoding" ) ).toString();
126 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
127 leText->setText( value.toString() );
128 mUseTemporary =
false;
130 emit skipOutputChanged(
false );
134 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
135 emit destinationChanged();
139 if ( prev.userType() != QMetaType::type(
"QgsProcessingOutputLayerDefinition" ) ||
141 emit destinationChanged();
147QVariant QgsProcessingLayerOutputDestinationWidget::value()
const
155 else if ( mUseTemporary && !mDefaultSelection )
161 key = leText->text();
170 && !key.startsWith( QLatin1String(
"memory:" ) )
171 && !key.startsWith( QLatin1String(
"ogr:" ) )
172 && !key.startsWith( QLatin1String(
"postgres:" ) )
173 && !key.startsWith( QLatin1String(
"postgis:" ) )
177 QString folder = QFileInfo( key ).path();
181 QString defaultFolder = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
182 key = QDir( defaultFolder ).filePath( key );
192 value.createOptions.insert( QStringLiteral(
"fileEncoding" ), mEncoding );
194 value.setRemappingDefinition( mRemapDefinition );
210 mParametersGenerator = generator;
213void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
215 Q_ASSERT( mOpenAfterRunningCheck ==
nullptr );
216 mOpenAfterRunningCheck =
new QCheckBox( tr(
"Open output file after running algorithm" ) );
217 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
218 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
219 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
221 connect(
this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged,
this, [ = ](
bool skipped )
223 bool enabled = !skipped;
224 mOpenAfterRunningCheck->setEnabled( enabled );
225 mOpenAfterRunningCheck->setChecked( enabled );
229bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
231 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
234void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
238 if ( !mDefaultSelection )
242 QAction *actionSkipOutput =
new QAction( tr(
"Skip Output" ),
this );
243 connect( actionSkipOutput, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
244 mMenu->addAction( actionSkipOutput );
247 QAction *actionSaveToTemp =
nullptr;
251 actionSaveToTemp =
new QAction( tr(
"Create Temporary Layer" ),
this );
255 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary Directory" ),
this );
259 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary File" ),
this );
262 connect( actionSaveToTemp, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
263 mMenu->addAction( actionSaveToTemp );
266 QAction *actionSaveToFile =
nullptr;
269 actionSaveToFile =
new QAction( tr(
"Save to Directory…" ),
this );
270 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
274 actionSaveToFile =
new QAction( tr(
"Save to File…" ),
this );
275 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
277 mMenu->addAction( actionSaveToFile );
281 QAction *actionSaveToGpkg =
new QAction( tr(
"Save to GeoPackage…" ),
this );
282 connect( actionSaveToGpkg, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
283 mMenu->addAction( actionSaveToGpkg );
285 QAction *actionSaveToDatabase =
new QAction( tr(
"Save to Database Table…" ),
this );
286 connect( actionSaveToDatabase, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
287 mMenu->addAction( actionSaveToDatabase );
289 if ( mParameter->algorithm() && qgis::down_cast< const QgsProcessingParameterFeatureSink * >( mParameter )->supportsAppend() )
291 mMenu->addSeparator();
292 QAction *actionAppendToLayer =
new QAction( tr(
"Append to Layer…" ),
this );
293 connect( actionAppendToLayer, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
294 mMenu->addAction( actionAppendToLayer );
297 QAction *editMappingAction =
new QAction( tr(
"Edit Field Mapping…" ),
this );
298 connect( editMappingAction, &QAction::triggered,
this, [ = ]
300 setAppendDestination( value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
302 mMenu->addAction( editMappingAction );
309 mMenu->addSeparator();
310 QAction *actionSetEncoding =
new QAction( tr(
"Change File Encoding (%1)…" ).arg( mEncoding ),
this );
311 connect( actionSetEncoding, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
312 mMenu->addAction( actionSetEncoding );
316void QgsProcessingLayerOutputDestinationWidget::skipOutput()
318 leText->setPlaceholderText( tr(
"[Skip output]" ) );
320 mUseTemporary =
false;
321 mUseRemapping =
false;
323 emit skipOutputChanged(
true );
324 emit destinationChanged();
327void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
329 const bool prevSkip = outputIsSkipped();
333 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
337 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
341 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
348 mUseTemporary =
true;
349 mUseRemapping =
false;
351 emit skipOutputChanged(
false );
352 emit destinationChanged();
355void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
357 QString lastDir = leText->text();
359 if ( lastDir.isEmpty() )
360 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
362 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::Options() );
363 if ( !dirName.isEmpty() )
365 leText->setText( QDir::toNativeSeparators( dirName ) );
366 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), dirName );
367 mUseTemporary =
false;
368 mUseRemapping =
false;
369 emit skipOutputChanged(
false );
370 emit destinationChanged();
374void QgsProcessingLayerOutputDestinationWidget::selectFile()
376 const QString fileFilter = mParameter->createFileFilter();
384 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
385 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString() ;
389 lastExtPath = QStringLiteral(
"/Processing/LastRasterOutputExt" );
390 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
394 lastExtPath = QStringLiteral(
"/Processing/LastPointCloudOutputExt" );
395 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
399 const QStringList filters = fileFilter.split( QStringLiteral(
";;" ) );
401 for (
const QString &f : filters )
403 if ( f.contains( QStringLiteral(
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
411 if ( settings.
contains( QStringLiteral(
"/Processing/LastOutputPath" ) ) )
412 path = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ) ).toString();
414 path = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
416 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral(
"widget_wrapper" ) ).toMap().value( QStringLiteral(
"dontconfirmoverwrite" ),
false ).toBool();
418 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
419 if ( !filename.isEmpty() )
421 mUseTemporary =
false;
422 mUseRemapping =
false;
425 leText->setText( filename );
426 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
427 if ( !lastExtPath.isEmpty() )
428 settings.
setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
430 emit skipOutputChanged(
false );
431 emit destinationChanged();
435void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
438 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
439 if ( lastPath.isEmpty() )
440 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
442 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
444 if ( filename.isEmpty() )
447 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
448 if ( layerName.isEmpty() )
451 mUseTemporary =
false;
452 mUseRemapping =
false;
456 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
465 if ( sink->hasGeometry() )
466 geomColumn = QStringLiteral(
"geom" );
470 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
472 emit skipOutputChanged(
false );
473 emit destinationChanged();
476void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
482 << QStringLiteral(
"mssql" )
483 << QStringLiteral(
"ogr" )
484 << QStringLiteral(
"hana" )
485 << QStringLiteral(
"spatialite" )
486 << QStringLiteral(
"oracle" ),
this );
487 widget->
setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
490 panel->openPanel( widget );
494 mUseTemporary =
false;
495 mUseRemapping =
false;
500 if ( sink->hasGeometry() )
501 geomColumn = widget->
dataProviderKey() == QLatin1String(
"oracle" ) ? QStringLiteral(
"GEOM" ) : QStringLiteral(
"geom" );
510 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
519 emit skipOutputChanged(
false );
520 emit destinationChanged();
535void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
540 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
542 panel->openPanel( widget );
550 if ( widget->
uri().
uri.isEmpty() )
551 setValue( QVariant() );
555 std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
557 setAppendDestination( widget->
uri().
uri, dest->fields() );
566void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
570 if ( mParametersGenerator )
571 props = mParametersGenerator->createProcessingParameters();
572 props.insert( mParameter->name(), uri );
581 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
582 if ( !mRemapDefinition.fieldMap().isEmpty() )
585 panel->openPanel( widget );
601void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
609 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
611 emit destinationChanged();
615void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
617 mUseTemporary = text.isEmpty();
618 mUseRemapping =
false;
619 emit destinationChanged();
623QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
631 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
637 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
643 && u.layerType == QLatin1String(
"pointcloud" ) && ( u.providerKey == QLatin1String(
"ept" ) || u.providerKey == QLatin1String(
"pdal" ) ) )
648 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
650 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
655 && u.layerType == QLatin1String(
"directory" ) )
660 if ( !uriList.isEmpty() )
664 QStringList rawPaths;
665 if ( data->hasUrls() )
667 const QList< QUrl > urls = data->urls();
668 rawPaths.reserve( urls.count() );
669 for (
const QUrl &url : urls )
671 const QString local = url.toLocalFile();
672 if ( !rawPaths.contains( local ) )
673 rawPaths.append( local );
676 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
677 rawPaths.append( data->text() );
679 for (
const QString &path : std::as_const( rawPaths ) )
681 QFileInfo file( path );
699void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
701 if ( !( event->possibleActions() & Qt::CopyAction ) )
704 const QString path = mimeDataToPath( event->mimeData() );
705 if ( !path.isEmpty() )
708 event->setDropAction( Qt::CopyAction );
710 leText->setHighlighted(
true );
714void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
716 QWidget::dragLeaveEvent( event );
717 if ( leText->isHighlighted() )
720 leText->setHighlighted(
false );
724void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
726 if ( !( event->possibleActions() & Qt::CopyAction ) )
729 const QString path = mimeDataToPath( event->mimeData() );
730 if ( !path.isEmpty() )
733 setFocus( Qt::MouseFocusReason );
734 event->setDropAction( Qt::CopyAction );
738 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.
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.
@ VectorLayer
Vector layer.
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.