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 );
201 if ( !mFormat.isEmpty() )
202 value.setFormat( mFormat );
218 mParametersGenerator = generator;
221void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
223 Q_ASSERT( mOpenAfterRunningCheck ==
nullptr );
224 mOpenAfterRunningCheck =
new QCheckBox( tr(
"Open output file after running algorithm" ) );
225 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
226 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
227 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
229 connect(
this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged,
this, [
this](
bool skipped ) {
230 bool enabled = !skipped;
231 mOpenAfterRunningCheck->setEnabled( enabled );
232 mOpenAfterRunningCheck->setChecked( enabled );
236bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
238 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
241void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
245 if ( !mDefaultSelection )
249 QAction *actionSkipOutput =
new QAction( tr(
"Skip Output" ),
this );
250 connect( actionSkipOutput, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
251 mMenu->addAction( actionSkipOutput );
254 QAction *actionSaveToTemp =
nullptr;
258 actionSaveToTemp =
new QAction( tr(
"Create Temporary Layer" ),
this );
262 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary Directory" ),
this );
266 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary File" ),
this );
269 connect( actionSaveToTemp, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
270 mMenu->addAction( actionSaveToTemp );
273 QAction *actionSaveToFile =
nullptr;
276 actionSaveToFile =
new QAction( tr(
"Save to Directory…" ),
this );
277 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
281 actionSaveToFile =
new QAction( tr(
"Save to File…" ),
this );
282 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
284 mMenu->addAction( actionSaveToFile );
288 QAction *actionSaveToGpkg =
new QAction( tr(
"Save to GeoPackage…" ),
this );
289 connect( actionSaveToGpkg, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
290 mMenu->addAction( actionSaveToGpkg );
292 QAction *actionSaveToDatabase =
new QAction( tr(
"Save to Database Table…" ),
this );
293 connect( actionSaveToDatabase, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
294 mMenu->addAction( actionSaveToDatabase );
296 if ( mParameter->algorithm() && qgis::down_cast<const QgsProcessingParameterFeatureSink *>( mParameter )->supportsAppend() )
298 mMenu->addSeparator();
299 QAction *actionAppendToLayer =
new QAction( tr(
"Append to Layer…" ),
this );
300 connect( actionAppendToLayer, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
301 mMenu->addAction( actionAppendToLayer );
304 QAction *editMappingAction =
new QAction( tr(
"Edit Field Mapping…" ),
this );
305 connect( editMappingAction, &QAction::triggered,
this, [
this] {
306 setAppendDestination( value().value<QgsProcessingOutputLayerDefinition>().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
308 mMenu->addAction( editMappingAction );
315 mMenu->addSeparator();
316 QAction *actionSetEncoding =
new QAction( tr(
"Change File Encoding (%1)…" ).arg( mEncoding ),
this );
317 connect( actionSetEncoding, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
318 mMenu->addAction( actionSetEncoding );
322void QgsProcessingLayerOutputDestinationWidget::skipOutput()
324 leText->setPlaceholderText( tr(
"[Skip output]" ) );
326 mUseTemporary =
false;
327 mUseRemapping =
false;
329 emit skipOutputChanged(
true );
330 emit destinationChanged();
333void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
335 const bool prevSkip = outputIsSkipped();
339 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
343 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
347 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
354 mUseTemporary =
true;
355 mUseRemapping =
false;
357 emit skipOutputChanged(
false );
358 emit destinationChanged();
361void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
363 QString lastDir = leText->text();
365 if ( lastDir.isEmpty() )
366 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
368 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::Options() );
369 if ( !dirName.isEmpty() )
371 leText->setText( QDir::toNativeSeparators( dirName ) );
372 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), dirName );
373 mUseTemporary =
false;
374 mUseRemapping =
false;
375 emit skipOutputChanged(
false );
376 emit destinationChanged();
380void QgsProcessingLayerOutputDestinationWidget::selectFile()
382 const QString fileFilter = mParameter->createFileFilter();
388 QString lastFormatPath;
392 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
393 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
399 lastFormatPath = QStringLiteral(
"/Processing/LastRasterOutputFormat" );
404 lastExtPath = QStringLiteral(
"/Processing/LastPointCloudOutputExt" );
405 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
409 lastExtPath = QStringLiteral(
"/Processing/LastVectorTileOutputExt" );
410 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
414 const QStringList filters = fileFilter.split( QStringLiteral(
";;" ) );
416 for (
const QString &f : filters )
418 if ( !lastFormat.isEmpty() && f.contains( lastFormat, Qt::CaseInsensitive ) )
423 else if ( !lastExt.isEmpty() && f.contains( QStringLiteral(
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
431 if ( settings.
contains( QStringLiteral(
"/Processing/LastOutputPath" ) ) )
432 path = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ) ).toString();
434 path = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
436 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral(
"widget_wrapper" ) ).toMap().value( QStringLiteral(
"dontconfirmoverwrite" ),
false ).toBool();
438 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
439 if ( !filename.isEmpty() )
441 mUseTemporary =
false;
442 mUseRemapping =
false;
445 int spacePos =
static_cast<int>( lastFilter.indexOf(
' ' ) );
448 mFormat = lastFilter.left( spacePos );
453 leText->setText( filename );
454 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
455 if ( !lastFormatPath.isEmpty() && !mFormat.isEmpty() )
456 settings.
setValue( lastFormatPath, mFormat );
457 else if ( !lastExtPath.isEmpty() )
458 settings.
setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
460 emit skipOutputChanged(
false );
461 emit destinationChanged();
468void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
471 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
472 if ( lastPath.isEmpty() )
473 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
475 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
480 if ( filename.isEmpty() )
483 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
484 if ( layerName.isEmpty() )
487 mUseTemporary =
false;
488 mUseRemapping =
false;
492 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
501 if ( sink->hasGeometry() )
502 geomColumn = QStringLiteral(
"geom" );
506 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
508 emit skipOutputChanged(
false );
509 emit destinationChanged();
512void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
516 QgsNewDatabaseTableNameWidget *widget =
new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral(
"postgres" ) << QStringLiteral(
"mssql" ) << QStringLiteral(
"ogr" ) << QStringLiteral(
"hana" ) << QStringLiteral(
"spatialite" ) << QStringLiteral(
"oracle" ),
this );
517 widget->
setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
520 panel->openPanel( widget );
522 auto changed = [
this, widget] {
523 mUseTemporary =
false;
524 mUseRemapping =
false;
529 if ( sink->hasGeometry() )
530 geomColumn = widget->
dataProviderKey() == QLatin1String(
"oracle" ) ? QStringLiteral(
"GEOM" ) : QStringLiteral(
"geom" );
539 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
548 emit skipOutputChanged(
false );
549 emit destinationChanged();
563void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
568 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
570 panel->openPanel( widget );
576 if ( widget->
uri().
uri.isEmpty() )
577 setValue( QVariant() );
581 auto dest = std::make_unique<QgsVectorLayer>( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
583 setAppendDestination( widget->
uri().
uri, dest->fields() );
592void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
596 if ( mParametersGenerator )
597 props = mParametersGenerator->createProcessingParameters();
598 props.insert( mParameter->name(), uri );
607 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
608 if ( !mRemapDefinition.fieldMap().isEmpty() )
611 panel->openPanel( widget );
626void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
634 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
636 emit destinationChanged();
640void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
642 mUseTemporary = text.isEmpty();
643 mUseRemapping =
false;
644 emit destinationChanged();
648QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
656 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
662 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
668 && u.layerType == QLatin1String(
"pointcloud" ) && ( u.providerKey == QLatin1String(
"ept" ) || u.providerKey == QLatin1String(
"pdal" ) ) )
673 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
675 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
680 && u.layerType == QLatin1String(
"directory" ) )
685 if ( !uriList.isEmpty() )
689 QStringList rawPaths;
690 if ( data->hasUrls() )
692 const QList<QUrl> urls = data->urls();
693 rawPaths.reserve( urls.count() );
694 for (
const QUrl &url : urls )
696 const QString local = url.toLocalFile();
697 if ( !rawPaths.contains( local ) )
698 rawPaths.append( local );
701 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
702 rawPaths.append( data->text() );
704 for (
const QString &path : std::as_const( rawPaths ) )
706 QFileInfo file( path );
719void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
721 if ( !( event->possibleActions() & Qt::CopyAction ) )
724 const QString path = mimeDataToPath( event->mimeData() );
725 if ( !path.isEmpty() )
728 event->setDropAction( Qt::CopyAction );
730 leText->setHighlighted(
true );
734void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
736 QWidget::dragLeaveEvent( event );
737 if ( leText->isHighlighted() )
740 leText->setHighlighted(
false );
744void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
746 if ( !( event->possibleActions() & Qt::CopyAction ) )
749 const QString path = mimeDataToPath( event->mimeData() );
750 if ( !path.isEmpty() )
753 setFocus( Qt::MouseFocusReason );
754 event->setDropAction( Qt::CopyAction );
758 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.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
QString defaultFileFormat() const
Returns the default file format for destination file paths associated with this parameter.
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.