32#include <QInputDialog>
38#include "moc_qgsprocessingoutputdestinationwidget.cpp"
42QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget(
const QgsProcessingDestinationParameter *param,
bool defaultSelection, QWidget *parent )
45 , mDefaultSelection( defaultSelection )
47 Q_ASSERT( mParameter );
51 leText->setClearButtonEnabled(
false );
53 connect( leText, &QLineEdit::textEdited,
this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
55 mMenu =
new QMenu(
this );
56 connect( mMenu, &QMenu::aboutToShow,
this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
57 mSelectButton->setMenu( mMenu );
58 mSelectButton->setPopupMode( QToolButton::InstantPopup );
62 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
64 if ( !mParameter->defaultValueForGui().isValid() )
68 setValue( QVariant() );
74 setValue( mParameter->defaultValueForGui() );
77 setToolTip( mParameter->toolTip() );
79 setAcceptDrops(
true );
80 leText->setAcceptDrops(
false );
83bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
const
85 return leText->text().isEmpty() && !mUseTemporary;
88void QgsProcessingLayerOutputDestinationWidget::setValue(
const QVariant &value )
90 const bool prevSkip = outputIsSkipped();
91 mUseRemapping =
false;
92 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
105 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
114 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
116 mUseTemporary =
false;
118 emit skipOutputChanged(
false );
119 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
120 emit destinationChanged();
124 mEncoding = def.
createOptions.value( QStringLiteral(
"fileEncoding" ) ).toString();
128 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
129 leText->setText( value.toString() );
130 mUseTemporary =
false;
132 emit skipOutputChanged(
false );
136 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
137 emit destinationChanged();
142 emit destinationChanged();
148QVariant QgsProcessingLayerOutputDestinationWidget::value()
const
156 else if ( mUseTemporary && !mDefaultSelection )
162 key = leText->text();
171 && !key.startsWith( QLatin1String(
"memory:" ) )
172 && !key.startsWith( QLatin1String(
"ogr:" ) )
173 && !key.startsWith( QLatin1String(
"postgres:" ) )
174 && !key.startsWith( QLatin1String(
"postgis:" ) )
178 QString folder = QFileInfo( key ).path();
182 QString defaultFolder = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QStringLiteral(
"%1/processing" ).arg( QDir::homePath() ) ).toString();
183 QDir destDir( defaultFolder );
184 if ( !destDir.exists() && !QDir().mkpath( defaultFolder ) )
186 QgsDebugError( QStringLiteral(
"Can't create output folder '%1'" ).arg( defaultFolder ) );
188 key = destDir.filePath( key );
198 value.createOptions.insert( QStringLiteral(
"fileEncoding" ), mEncoding );
200 value.setRemappingDefinition( mRemapDefinition );
216 mParametersGenerator = generator;
219void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
221 Q_ASSERT( mOpenAfterRunningCheck ==
nullptr );
222 mOpenAfterRunningCheck =
new QCheckBox( tr(
"Open output file after running algorithm" ) );
223 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
224 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
225 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
227 connect(
this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged,
this, [
this](
bool skipped ) {
228 bool enabled = !skipped;
229 mOpenAfterRunningCheck->setEnabled( enabled );
230 mOpenAfterRunningCheck->setChecked( enabled );
234bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
236 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
239void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
243 if ( !mDefaultSelection )
247 QAction *actionSkipOutput =
new QAction( tr(
"Skip Output" ),
this );
248 connect( actionSkipOutput, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
249 mMenu->addAction( actionSkipOutput );
252 QAction *actionSaveToTemp =
nullptr;
256 actionSaveToTemp =
new QAction( tr(
"Create Temporary Layer" ),
this );
260 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary Directory" ),
this );
264 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary File" ),
this );
267 connect( actionSaveToTemp, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
268 mMenu->addAction( actionSaveToTemp );
271 QAction *actionSaveToFile =
nullptr;
274 actionSaveToFile =
new QAction( tr(
"Save to Directory…" ),
this );
275 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
279 actionSaveToFile =
new QAction( tr(
"Save to File…" ),
this );
280 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
282 mMenu->addAction( actionSaveToFile );
286 QAction *actionSaveToGpkg =
new QAction( tr(
"Save to GeoPackage…" ),
this );
287 connect( actionSaveToGpkg, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
288 mMenu->addAction( actionSaveToGpkg );
290 QAction *actionSaveToDatabase =
new QAction( tr(
"Save to Database Table…" ),
this );
291 connect( actionSaveToDatabase, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
292 mMenu->addAction( actionSaveToDatabase );
294 if ( mParameter->algorithm() && qgis::down_cast<const QgsProcessingParameterFeatureSink *>( mParameter )->supportsAppend() )
296 mMenu->addSeparator();
297 QAction *actionAppendToLayer =
new QAction( tr(
"Append to Layer…" ),
this );
298 connect( actionAppendToLayer, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
299 mMenu->addAction( actionAppendToLayer );
302 QAction *editMappingAction =
new QAction( tr(
"Edit Field Mapping…" ),
this );
303 connect( editMappingAction, &QAction::triggered,
this, [
this] {
304 setAppendDestination( value().value<QgsProcessingOutputLayerDefinition>().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
306 mMenu->addAction( editMappingAction );
313 mMenu->addSeparator();
314 QAction *actionSetEncoding =
new QAction( tr(
"Change File Encoding (%1)…" ).arg( mEncoding ),
this );
315 connect( actionSetEncoding, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
316 mMenu->addAction( actionSetEncoding );
320void QgsProcessingLayerOutputDestinationWidget::skipOutput()
322 leText->setPlaceholderText( tr(
"[Skip output]" ) );
324 mUseTemporary =
false;
325 mUseRemapping =
false;
327 emit skipOutputChanged(
true );
328 emit destinationChanged();
331void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
333 const bool prevSkip = outputIsSkipped();
337 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
341 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
345 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
352 mUseTemporary =
true;
353 mUseRemapping =
false;
355 emit skipOutputChanged(
false );
356 emit destinationChanged();
359void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
361 QString lastDir = leText->text();
363 if ( lastDir.isEmpty() )
364 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
366 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::Options() );
367 if ( !dirName.isEmpty() )
369 leText->setText( QDir::toNativeSeparators( dirName ) );
370 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), dirName );
371 mUseTemporary =
false;
372 mUseRemapping =
false;
373 emit skipOutputChanged(
false );
374 emit destinationChanged();
378void QgsProcessingLayerOutputDestinationWidget::selectFile()
380 const QString fileFilter = mParameter->createFileFilter();
388 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
389 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
393 lastExtPath = QStringLiteral(
"/Processing/LastRasterOutputExt" );
394 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
398 lastExtPath = QStringLiteral(
"/Processing/LastPointCloudOutputExt" );
399 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
403 lastExtPath = QStringLiteral(
"/Processing/LastVectorTileOutputExt" );
404 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
408 const QStringList filters = fileFilter.split( QStringLiteral(
";;" ) );
410 for (
const QString &f : filters )
412 if ( f.contains( QStringLiteral(
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
420 if ( settings.
contains( QStringLiteral(
"/Processing/LastOutputPath" ) ) )
421 path = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ) ).toString();
423 path = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
425 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral(
"widget_wrapper" ) ).toMap().value( QStringLiteral(
"dontconfirmoverwrite" ),
false ).toBool();
427 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
428 if ( !filename.isEmpty() )
430 mUseTemporary =
false;
431 mUseRemapping =
false;
434 leText->setText( filename );
435 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
436 if ( !lastExtPath.isEmpty() )
437 settings.
setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
439 emit skipOutputChanged(
false );
440 emit destinationChanged();
447void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
450 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
451 if ( lastPath.isEmpty() )
452 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
454 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
459 if ( filename.isEmpty() )
462 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
463 if ( layerName.isEmpty() )
466 mUseTemporary =
false;
467 mUseRemapping =
false;
471 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
480 if ( sink->hasGeometry() )
481 geomColumn = QStringLiteral(
"geom" );
485 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
487 emit skipOutputChanged(
false );
488 emit destinationChanged();
491void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
495 QgsNewDatabaseTableNameWidget *widget =
new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral(
"postgres" ) << QStringLiteral(
"mssql" ) << QStringLiteral(
"ogr" ) << QStringLiteral(
"hana" ) << QStringLiteral(
"spatialite" ) << QStringLiteral(
"oracle" ),
this );
496 widget->
setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
499 panel->openPanel( widget );
501 auto changed = [
this, 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();
542void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
547 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
549 panel->openPanel( widget );
555 if ( widget->
uri().
uri.isEmpty() )
556 setValue( QVariant() );
560 auto dest = std::make_unique<QgsVectorLayer>( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
562 setAppendDestination( widget->
uri().
uri, dest->fields() );
571void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
575 if ( mParametersGenerator )
576 props = mParametersGenerator->createProcessingParameters();
577 props.insert( mParameter->name(), uri );
586 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
587 if ( !mRemapDefinition.fieldMap().isEmpty() )
590 panel->openPanel( widget );
605void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
613 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
615 emit destinationChanged();
619void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
621 mUseTemporary = text.isEmpty();
622 mUseRemapping =
false;
623 emit destinationChanged();
627QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
635 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
641 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
647 && u.layerType == QLatin1String(
"pointcloud" ) && ( u.providerKey == QLatin1String(
"ept" ) || u.providerKey == QLatin1String(
"pdal" ) ) )
652 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
654 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
659 && u.layerType == QLatin1String(
"directory" ) )
664 if ( !uriList.isEmpty() )
668 QStringList rawPaths;
669 if ( data->hasUrls() )
671 const QList<QUrl> urls = data->urls();
672 rawPaths.reserve( urls.count() );
673 for (
const QUrl &url : urls )
675 const QString local = url.toLocalFile();
676 if ( !rawPaths.contains( local ) )
677 rawPaths.append( local );
680 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
681 rawPaths.append( data->text() );
683 for (
const QString &path : std::as_const( rawPaths ) )
685 QFileInfo file( path );
698void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
700 if ( !( event->possibleActions() & Qt::CopyAction ) )
703 const QString path = mimeDataToPath( event->mimeData() );
704 if ( !path.isEmpty() )
707 event->setDropAction( Qt::CopyAction );
709 leText->setHighlighted(
true );
713void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
715 QWidget::dragLeaveEvent( event );
716 if ( leText->isHighlighted() )
719 leText->setHighlighted(
false );
723void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
725 if ( !( event->possibleActions() & Qt::CopyAction ) )
728 const QString path = mimeDataToPath( event->mimeData() );
729 if ( !path.isEmpty() )
732 setFocus( Qt::MouseFocusReason );
733 event->setDropAction( Qt::CopyAction );
737 leText->setHighlighted(
false );
@ Available
Properties are available.
@ Optional
Parameter is optional.
Stores the component parts of a 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.
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.
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.
Stores settings for use within QGIS.
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.
#define QgsDebugError(str)
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.
Qgis::ProcessingPropertyAvailability availability
Availability of the properties. By default properties are not available.