22 #include "qgssettings.h" 
   31 #include <QFileDialog> 
   32 #include <QInputDialog> 
   38 QgsProcessingLayerOutputDestinationWidget::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 );
 
   57   mEncoding = settings.value( QStringLiteral( 
"/Processing/encoding" ), QStringLiteral( 
"System" ) ).toString();
 
   59   if ( !mParameter->defaultValueForGui().isValid() )
 
   63       setValue( QVariant() );
 
   69     setValue( mParameter->defaultValueForGui() );
 
   72   setToolTip( mParameter->toolTip() );
 
   74   setAcceptDrops( 
true );
 
   75   leText->setAcceptDrops( 
false );
 
   78 bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
 const 
   80   return leText->text().isEmpty() && !mUseTemporary;
 
   83 void QgsProcessingLayerOutputDestinationWidget::setValue( 
const QVariant &value )
 
   85   const bool prevSkip = outputIsSkipped();
 
   86   mUseRemapping = 
false;
 
   87   if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) )
 
  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();
 
  138           emit destinationChanged();
 
  144 QVariant QgsProcessingLayerOutputDestinationWidget::value()
 const 
  146   QgsSettings settings;
 
  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;
 
  210 void 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 );
 
  226 bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
 const 
  228   return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
 
  231 void 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 );
 
  313 void QgsProcessingLayerOutputDestinationWidget::skipOutput()
 
  315   leText->setPlaceholderText( tr( 
"[Skip output]" ) );
 
  317   mUseTemporary = 
false;
 
  318   mUseRemapping = 
false;
 
  320   emit skipOutputChanged( 
true );
 
  321   emit destinationChanged();
 
  324 void 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();
 
  352 void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
 
  354   QString lastDir = leText->text();
 
  355   QgsSettings settings;
 
  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::ShowDirsOnly );
 
  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();
 
  371 void QgsProcessingLayerOutputDestinationWidget::selectFile()
 
  373   const QString fileFilter = mParameter->createFileFilter();
 
  375   QgsSettings settings;
 
  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   const QStringList filters = fileFilter.split( QStringLiteral( 
";;" ) );
 
  393   for ( 
const QString &f : filters )
 
  395     if ( f.contains( QStringLiteral( 
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
 
  403   if ( settings.contains( QStringLiteral( 
"/Processing/LastOutputPath" ) ) )
 
  404     path = settings.value( QStringLiteral( 
"/Processing/LastOutputPath" ) ).toString();
 
  406     path = settings.value( QStringLiteral( 
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
 
  408   const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( 
"widget_wrapper" ) ).toMap().value( QStringLiteral( 
"dontconfirmoverwrite" ), 
false ).toBool();
 
  410   QString filename = QFileDialog::getSaveFileName( 
this, tr( 
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
 
  411   if ( !filename.isEmpty() )
 
  413     mUseTemporary = 
false;
 
  414     mUseRemapping = 
false;
 
  417     leText->setText( filename );
 
  418     settings.setValue( QStringLiteral( 
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
 
  419     if ( !lastExtPath.isEmpty() )
 
  420       settings.setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
 
  422     emit skipOutputChanged( 
false );
 
  423     emit destinationChanged();
 
  427 void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
 
  429   QgsSettings settings;
 
  430   QString lastPath = settings.value( QStringLiteral( 
"/Processing/LastOutputPath" ), QString() ).toString();
 
  431   if ( lastPath.isEmpty() )
 
  432     lastPath = settings.value( QStringLiteral( 
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
 
  434   QString filename =  QFileDialog::getSaveFileName( 
this, tr( 
"Save to GeoPackage" ), lastPath, tr( 
"GeoPackage files (*.gpkg);;All files (*.*)" ), 
nullptr, QFileDialog::DontConfirmOverwrite );
 
  436   if ( filename.isEmpty() )
 
  439   const QString layerName = QInputDialog::getText( 
this, tr( 
"Save to GeoPackage" ), tr( 
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
 
  440   if ( layerName.isEmpty() )
 
  443   mUseTemporary = 
false;
 
  444   mUseRemapping = 
false;
 
  448   settings.setValue( QStringLiteral( 
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
 
  457     if ( sink->hasGeometry() )
 
  458       geomColumn = QStringLiteral( 
"geom" );
 
  462   leText->setText( QStringLiteral( 
"ogr:%1" ).arg( uri.
uri() ) );
 
  464   emit skipOutputChanged( 
false );
 
  465   emit destinationChanged();
 
  468 void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
 
  474         << QStringLiteral( 
"mssql" )
 
  475         << QStringLiteral( 
"ogr" )
 
  476         << QStringLiteral( 
"hana" )
 
  477         << QStringLiteral( 
"spatialite" ), 
this );
 
  478     widget->
setPanelTitle( tr( 
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
 
  481     panel->openPanel( widget );
 
  485       mUseTemporary = 
false;
 
  486       mUseRemapping = 
false;
 
  491         if ( sink->hasGeometry() )
 
  492           geomColumn = QStringLiteral( 
"geom" );
 
  501         leText->setText( QStringLiteral( 
"ogr:%1" ).arg( uri.
uri() ) );
 
  510       emit skipOutputChanged( 
false );
 
  511       emit destinationChanged();
 
  526 void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
 
  531     widget->
setPanelTitle( tr( 
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
 
  533     panel->openPanel( widget );
 
  541       if ( widget->
uri().
uri.isEmpty() )
 
  542         setValue( QVariant() );
 
  546         std::unique_ptr< QgsVectorLayer > dest = std::make_unique< QgsVectorLayer >( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
 
  548           setAppendDestination( widget->
uri().
uri, dest->fields() );
 
  557 void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( 
const QString &uri, 
const QgsFields &destFields )
 
  561   if ( mParametersGenerator )
 
  562     props = mParametersGenerator->createProcessingParameters();
 
  563   props.insert( mParameter->name(), uri );
 
  572       widget->
setPanelTitle( tr( 
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
 
  573       if ( !mRemapDefinition.fieldMap().isEmpty() )
 
  576       panel->openPanel( widget );
 
  592 void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
 
  597     mEncoding = dialog.encoding();
 
  598     QgsSettings settings;
 
  599     settings.setValue( QStringLiteral( 
"/Processing/encoding" ), mEncoding );
 
  600     emit destinationChanged();
 
  604 void QgsProcessingLayerOutputDestinationWidget::textChanged( 
const QString &text )
 
  606   mUseTemporary = text.isEmpty();
 
  607   mUseRemapping = 
false;
 
  608   emit destinationChanged();
 
  612 QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( 
const QMimeData *data )
 
  620          && u.layerType == QLatin1String( 
"vector" ) && u.providerKey == QLatin1String( 
"ogr" ) )
 
  626               && u.layerType == QLatin1String( 
"raster" ) && u.providerKey == QLatin1String( 
"gdal" ) )
 
  631               && u.layerType == QLatin1String( 
"mesh" ) && u.providerKey == QLatin1String( 
"mdal" ) )
 
  636               && u.layerType == QLatin1String( 
"directory" ) )
 
  641   if ( !uriList.isEmpty() )
 
  645   QStringList rawPaths;
 
  646   if ( data->hasUrls() )
 
  648     const QList< QUrl > urls = data->urls();
 
  649     rawPaths.reserve( urls.count() );
 
  650     for ( 
const QUrl &url : urls )
 
  652       const QString local =  url.toLocalFile();
 
  653       if ( !rawPaths.contains( local ) )
 
  654         rawPaths.append( local );
 
  657   if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
 
  658     rawPaths.append( data->text() );
 
  660   for ( 
const QString &path : std::as_const( rawPaths ) )
 
  662     QFileInfo file( path );
 
  679 void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
 
  681   if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  684   const QString path = mimeDataToPath( event->mimeData() );
 
  685   if ( !path.isEmpty() )
 
  688     event->setDropAction( Qt::CopyAction );
 
  690     leText->setHighlighted( 
true );
 
  694 void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
 
  696   QWidget::dragLeaveEvent( event );
 
  697   if ( leText->isHighlighted() )
 
  700     leText->setHighlighted( 
false );
 
  704 void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
 
  706   if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  709   const QString path = mimeDataToPath( event->mimeData() );
 
  710   if ( !path.isEmpty() )
 
  713     setFocus( Qt::MouseFocusReason );
 
  714     event->setDropAction( Qt::CopyAction );
 
  718   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.
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 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.
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.