17#include "moc_qgsprocessingoutputdestinationwidget.cpp"
30#include <QInputDialog>
38QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget(
const QgsProcessingDestinationParameter *param,
bool defaultSelection, QWidget *parent )
41 , mDefaultSelection( defaultSelection )
43 Q_ASSERT( mParameter );
47 leText->setClearButtonEnabled(
false );
49 connect( leText, &QLineEdit::textEdited,
this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
51 mMenu =
new QMenu(
this );
52 connect( mMenu, &QMenu::aboutToShow,
this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
53 mSelectButton->setMenu( mMenu );
54 mSelectButton->setPopupMode( QToolButton::InstantPopup );
58 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
60 if ( !mParameter->defaultValueForGui().isValid() )
64 setValue( QVariant() );
70 setValue( mParameter->defaultValueForGui() );
73 setToolTip( mParameter->toolTip() );
75 setAcceptDrops(
true );
76 leText->setAcceptDrops(
false );
79bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
const
81 return leText->text().isEmpty() && !mUseTemporary;
84void QgsProcessingLayerOutputDestinationWidget::setValue(
const QVariant &value )
86 const bool prevSkip = outputIsSkipped();
87 mUseRemapping =
false;
88 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
101 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
110 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
112 mUseTemporary =
false;
114 emit skipOutputChanged(
false );
115 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
116 emit destinationChanged();
120 mEncoding = def.
createOptions.value( QStringLiteral(
"fileEncoding" ) ).toString();
124 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
125 leText->setText( value.toString() );
126 mUseTemporary =
false;
128 emit skipOutputChanged(
false );
132 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
133 emit destinationChanged();
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 ) {
219 bool enabled = !skipped;
220 mOpenAfterRunningCheck->setEnabled( enabled );
221 mOpenAfterRunningCheck->setChecked( enabled );
225bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
227 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
230void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
234 if ( !mDefaultSelection )
238 QAction *actionSkipOutput =
new QAction( tr(
"Skip Output" ),
this );
239 connect( actionSkipOutput, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
240 mMenu->addAction( actionSkipOutput );
243 QAction *actionSaveToTemp =
nullptr;
247 actionSaveToTemp =
new QAction( tr(
"Create Temporary Layer" ),
this );
251 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary Directory" ),
this );
255 actionSaveToTemp =
new QAction( tr(
"Save to a Temporary File" ),
this );
258 connect( actionSaveToTemp, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
259 mMenu->addAction( actionSaveToTemp );
262 QAction *actionSaveToFile =
nullptr;
265 actionSaveToFile =
new QAction( tr(
"Save to Directory…" ),
this );
266 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
270 actionSaveToFile =
new QAction( tr(
"Save to File…" ),
this );
271 connect( actionSaveToFile, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
273 mMenu->addAction( actionSaveToFile );
277 QAction *actionSaveToGpkg =
new QAction( tr(
"Save to GeoPackage…" ),
this );
278 connect( actionSaveToGpkg, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
279 mMenu->addAction( actionSaveToGpkg );
281 QAction *actionSaveToDatabase =
new QAction( tr(
"Save to Database Table…" ),
this );
282 connect( actionSaveToDatabase, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
283 mMenu->addAction( actionSaveToDatabase );
285 if ( mParameter->algorithm() && qgis::down_cast<const QgsProcessingParameterFeatureSink *>( mParameter )->supportsAppend() )
287 mMenu->addSeparator();
288 QAction *actionAppendToLayer =
new QAction( tr(
"Append to Layer…" ),
this );
289 connect( actionAppendToLayer, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
290 mMenu->addAction( actionAppendToLayer );
293 QAction *editMappingAction =
new QAction( tr(
"Edit Field Mapping…" ),
this );
294 connect( editMappingAction, &QAction::triggered,
this, [=] {
295 setAppendDestination( value().value<QgsProcessingOutputLayerDefinition>().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
297 mMenu->addAction( editMappingAction );
304 mMenu->addSeparator();
305 QAction *actionSetEncoding =
new QAction( tr(
"Change File Encoding (%1)…" ).arg( mEncoding ),
this );
306 connect( actionSetEncoding, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
307 mMenu->addAction( actionSetEncoding );
311void QgsProcessingLayerOutputDestinationWidget::skipOutput()
313 leText->setPlaceholderText( tr(
"[Skip output]" ) );
315 mUseTemporary =
false;
316 mUseRemapping =
false;
318 emit skipOutputChanged(
true );
319 emit destinationChanged();
322void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
324 const bool prevSkip = outputIsSkipped();
328 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
332 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
336 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
343 mUseTemporary =
true;
344 mUseRemapping =
false;
346 emit skipOutputChanged(
false );
347 emit destinationChanged();
350void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
352 QString lastDir = leText->text();
354 if ( lastDir.isEmpty() )
355 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
357 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::Options() );
358 if ( !dirName.isEmpty() )
360 leText->setText( QDir::toNativeSeparators( dirName ) );
361 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), dirName );
362 mUseTemporary =
false;
363 mUseRemapping =
false;
364 emit skipOutputChanged(
false );
365 emit destinationChanged();
369void QgsProcessingLayerOutputDestinationWidget::selectFile()
371 const QString fileFilter = mParameter->createFileFilter();
379 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
380 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
384 lastExtPath = QStringLiteral(
"/Processing/LastRasterOutputExt" );
385 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
389 lastExtPath = QStringLiteral(
"/Processing/LastPointCloudOutputExt" );
390 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
394 lastExtPath = QStringLiteral(
"/Processing/LastVectorTileOutputExt" );
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();
438void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
441 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
442 if ( lastPath.isEmpty() )
443 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
445 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
450 if ( filename.isEmpty() )
453 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
454 if ( layerName.isEmpty() )
457 mUseTemporary =
false;
458 mUseRemapping =
false;
462 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
471 if ( sink->hasGeometry() )
472 geomColumn = QStringLiteral(
"geom" );
476 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
478 emit skipOutputChanged(
false );
479 emit destinationChanged();
482void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
486 QgsNewDatabaseTableNameWidget *widget =
new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral(
"postgres" ) << QStringLiteral(
"mssql" ) << QStringLiteral(
"ogr" ) << QStringLiteral(
"hana" ) << QStringLiteral(
"spatialite" ) << QStringLiteral(
"oracle" ),
this );
487 widget->
setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
490 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();
533void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
538 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
540 panel->openPanel( widget );
546 if ( widget->
uri().
uri.isEmpty() )
547 setValue( QVariant() );
551 std::unique_ptr<QgsVectorLayer> dest = std::make_unique<QgsVectorLayer>( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
553 setAppendDestination( widget->
uri().
uri, dest->fields() );
562void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
566 if ( mParametersGenerator )
567 props = mParametersGenerator->createProcessingParameters();
568 props.insert( mParameter->name(), uri );
577 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
578 if ( !mRemapDefinition.fieldMap().isEmpty() )
581 panel->openPanel( widget );
596void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
604 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
606 emit destinationChanged();
610void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
612 mUseTemporary = text.isEmpty();
613 mUseRemapping =
false;
614 emit destinationChanged();
618QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
626 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
632 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
638 && u.layerType == QLatin1String(
"pointcloud" ) && ( u.providerKey == QLatin1String(
"ept" ) || u.providerKey == QLatin1String(
"pdal" ) ) )
643 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
645 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
650 && u.layerType == QLatin1String(
"directory" ) )
655 if ( !uriList.isEmpty() )
659 QStringList rawPaths;
660 if ( data->hasUrls() )
662 const QList<QUrl> urls = data->urls();
663 rawPaths.reserve( urls.count() );
664 for (
const QUrl &url : urls )
666 const QString local = url.toLocalFile();
667 if ( !rawPaths.contains( local ) )
668 rawPaths.append( local );
671 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
672 rawPaths.append( data->text() );
674 for (
const QString &path : std::as_const( rawPaths ) )
676 QFileInfo file( path );
689void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
691 if ( !( event->possibleActions() & Qt::CopyAction ) )
694 const QString path = mimeDataToPath( event->mimeData() );
695 if ( !path.isEmpty() )
698 event->setDropAction( Qt::CopyAction );
700 leText->setHighlighted(
true );
704void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
706 QWidget::dragLeaveEvent( event );
707 if ( leText->isHighlighted() )
710 leText->setHighlighted(
false );
714void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
716 if ( !( event->possibleActions() & Qt::CopyAction ) )
719 const QString path = mimeDataToPath( event->mimeData() );
720 if ( !path.isEmpty() )
723 setFocus( Qt::MouseFocusReason );
724 event->setDropAction( Qt::CopyAction );
728 leText->setHighlighted(
false );
@ Available
Properties are available.
@ Optional
Parameter is optional.
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.
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.
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.
Qgis::ProcessingPropertyAvailability availability
Availability of the properties. By default properties are not available.