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, [
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, [
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 );
 
  492    auto changed = [
this, 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        auto 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.
 
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.
 
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.