31 #include <QFileDialog>
32 #include <QInputDialog>
37 QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget(
const QgsProcessingDestinationParameter *param,
bool defaultSelection, QWidget *parent )
40 , mDefaultSelection( defaultSelection )
42 Q_ASSERT( mParameter );
46 leText->setClearButtonEnabled(
false );
48 connect( leText, &QLineEdit::textEdited,
this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
50 mMenu =
new QMenu(
this );
51 connect( mMenu, &QMenu::aboutToShow,
this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
52 mSelectButton->setMenu( mMenu );
53 mSelectButton->setPopupMode( QToolButton::InstantPopup );
56 mEncoding = settings.
value( QStringLiteral(
"/Processing/encoding" ), QStringLiteral(
"System" ) ).toString();
58 if ( !mParameter->defaultValue().isValid() )
62 setValue( QVariant() );
68 setValue( mParameter->defaultValue() );
71 setToolTip( mParameter->toolTip() );
73 setAcceptDrops(
true );
74 leText->setAcceptDrops(
false );
77 bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped()
const
79 return leText->text().isEmpty() && !mUseTemporary;
82 void QgsProcessingLayerOutputDestinationWidget::setValue(
const QVariant &value )
84 const bool prevSkip = outputIsSkipped();
85 mUseRemapping =
false;
86 if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) )
108 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
110 mUseTemporary =
false;
112 emit skipOutputChanged(
false );
113 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
114 emit destinationChanged();
118 mEncoding = def.
createOptions.value( QStringLiteral(
"fileEncoding" ) ).toString();
122 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
123 leText->setText( value.toString() );
124 mUseTemporary =
false;
126 emit skipOutputChanged(
false );
130 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
131 emit destinationChanged();
137 emit destinationChanged();
143 QVariant QgsProcessingLayerOutputDestinationWidget::value()
const
151 else if ( mUseTemporary && !mDefaultSelection )
157 key = leText->text();
166 && !key.startsWith( QLatin1String(
"memory:" ) )
167 && !key.startsWith( QLatin1String(
"ogr:" ) )
168 && !key.startsWith( QLatin1String(
"postgres:" ) )
169 && !key.startsWith( QLatin1String(
"postgis:" ) )
173 QString folder = QFileInfo( key ).path();
177 QString defaultFolder = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
178 key = QDir( defaultFolder ).filePath( key );
188 value.createOptions.insert( QStringLiteral(
"fileEncoding" ), mEncoding );
190 value.setRemappingDefinition( mRemapDefinition );
206 mParametersGenerator = generator;
209 void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
211 Q_ASSERT( mOpenAfterRunningCheck ==
nullptr );
212 mOpenAfterRunningCheck =
new QCheckBox( tr(
"Open output file after running algorithm" ) );
213 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
214 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
215 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
217 connect(
this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged,
this, [ = ](
bool skipped )
219 bool enabled = !skipped;
220 mOpenAfterRunningCheck->setEnabled( enabled );
221 mOpenAfterRunningCheck->setChecked( enabled );
225 bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning()
const
227 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
230 void 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 );
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, [ = ]
296 setAppendDestination( value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
298 mMenu->addAction( editMappingAction );
305 mMenu->addSeparator();
306 QAction *actionSetEncoding =
new QAction( tr(
"Change File Encoding (%1)…" ).arg( mEncoding ),
this );
307 connect( actionSetEncoding, &QAction::triggered,
this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
308 mMenu->addAction( actionSetEncoding );
312 void QgsProcessingLayerOutputDestinationWidget::skipOutput()
314 leText->setPlaceholderText( tr(
"[Skip output]" ) );
316 mUseTemporary =
false;
317 mUseRemapping =
false;
319 emit skipOutputChanged(
true );
320 emit destinationChanged();
323 void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
325 const bool prevSkip = outputIsSkipped();
329 leText->setPlaceholderText( tr(
"[Create temporary layer]" ) );
333 leText->setPlaceholderText( tr(
"[Save to temporary folder]" ) );
337 leText->setPlaceholderText( tr(
"[Save to temporary file]" ) );
344 mUseTemporary =
true;
345 mUseRemapping =
false;
347 emit skipOutputChanged(
false );
348 emit destinationChanged();
351 void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
353 QString lastDir = leText->text();
355 if ( lastDir.isEmpty() )
356 lastDir = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QDir::homePath() ).toString();
358 const QString dirName = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), lastDir, QFileDialog::ShowDirsOnly );
359 if ( !dirName.isEmpty() )
361 leText->setText( QDir::toNativeSeparators( dirName ) );
362 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), dirName );
363 mUseTemporary =
false;
364 mUseRemapping =
false;
365 emit skipOutputChanged(
false );
366 emit destinationChanged();
370 void QgsProcessingLayerOutputDestinationWidget::selectFile()
372 const QString fileFilter = mParameter->createFileFilter();
380 lastExtPath = QStringLiteral(
"/Processing/LastVectorOutputExt" );
381 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString() ;
385 lastExtPath = QStringLiteral(
"/Processing/LastRasterOutputExt" );
386 lastExt = settings.
value( lastExtPath, QStringLiteral(
".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
390 const QStringList filters = fileFilter.split( QStringLiteral(
";;" ) );
392 for (
const QString &f : filters )
394 if ( f.contains( QStringLiteral(
"*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
402 if ( settings.
contains( QStringLiteral(
"/Processing/LastOutputPath" ) ) )
403 path = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ) ).toString();
405 path = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
407 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral(
"widget_wrapper" ) ).toMap().value( QStringLiteral(
"dontconfirmoverwrite" ),
false ).toBool();
409 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
410 if ( !filename.isEmpty() )
412 mUseTemporary =
false;
413 mUseRemapping =
false;
416 leText->setText( filename );
417 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
418 if ( !lastExtPath.isEmpty() )
419 settings.
setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
421 emit skipOutputChanged(
false );
422 emit destinationChanged();
426 void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
429 QString lastPath = settings.
value( QStringLiteral(
"/Processing/LastOutputPath" ), QString() ).toString();
430 if ( lastPath.isEmpty() )
431 lastPath = settings.
value( QStringLiteral(
"/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
433 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save to GeoPackage" ), lastPath, tr(
"GeoPackage files (*.gpkg);;All files (*.*)" ),
nullptr, QFileDialog::DontConfirmOverwrite );
435 if ( filename.isEmpty() )
438 const QString layerName = QInputDialog::getText(
this, tr(
"Save to GeoPackage" ), tr(
"Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
439 if ( layerName.isEmpty() )
442 mUseTemporary =
false;
443 mUseRemapping =
false;
447 settings.
setValue( QStringLiteral(
"/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
456 if ( sink->hasGeometry() )
457 geomColumn = QStringLiteral(
"geom" );
461 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
463 emit skipOutputChanged(
false );
464 emit destinationChanged();
467 void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
472 QgsNewDatabaseTableNameWidget *widget =
new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral(
"postgres" )
473 << QStringLiteral(
"mssql" )
474 << QStringLiteral(
"ogr" )
475 << QStringLiteral(
"spatialite" ),
this );
476 widget->setPanelTitle( tr(
"Save “%1” to Database Table" ).arg( mParameter->description() ) );
477 widget->setAcceptButtonVisible(
true );
479 panel->openPanel( widget );
483 mUseTemporary =
false;
484 mUseRemapping =
false;
489 if ( sink->hasGeometry() )
490 geomColumn = QStringLiteral(
"geom" );
493 if ( widget->dataProviderKey() == QLatin1String(
"ogr" ) )
499 leText->setText( QStringLiteral(
"ogr:%1" ).arg( uri.
uri() ) );
508 emit skipOutputChanged(
false );
509 emit destinationChanged();
519 widget->acceptPanel();
524 void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
529 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
531 panel->openPanel( widget );
539 if ( widget->
uri().
uri.isEmpty() )
540 setValue( QVariant() );
544 std::unique_ptr< QgsVectorLayer > dest = qgis::make_unique< QgsVectorLayer >( widget->
uri().
uri, QString(), widget->
uri().
providerKey );
546 setAppendDestination( widget->
uri().
uri, dest->fields() );
555 void QgsProcessingLayerOutputDestinationWidget::setAppendDestination(
const QString &uri,
const QgsFields &destFields )
559 if ( mParametersGenerator )
560 props = mParametersGenerator->createProcessingParameters();
561 props.insert( mParameter->name(), uri );
570 widget->
setPanelTitle( tr(
"Append \"%1\" to Layer" ).arg( mParameter->description() ) );
571 if ( !mRemapDefinition.fieldMap().isEmpty() )
574 panel->openPanel( widget );
590 void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
595 mEncoding = dialog.encoding();
597 settings.
setValue( QStringLiteral(
"/Processing/encoding" ), mEncoding );
598 emit destinationChanged();
602 void QgsProcessingLayerOutputDestinationWidget::textChanged(
const QString &text )
604 mUseTemporary = text.isEmpty();
605 mUseRemapping =
false;
606 emit destinationChanged();
610 QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath(
const QMimeData *data )
618 && u.layerType == QLatin1String(
"vector" ) && u.providerKey == QLatin1String(
"ogr" ) )
624 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
629 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
634 && u.layerType == QLatin1String(
"directory" ) )
639 if ( !uriList.isEmpty() )
643 QStringList rawPaths;
644 if ( data->hasUrls() )
646 const QList< QUrl > urls = data->urls();
647 rawPaths.reserve( urls.count() );
648 for (
const QUrl &url : urls )
650 const QString local = url.toLocalFile();
651 if ( !rawPaths.contains( local ) )
652 rawPaths.append( local );
655 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
656 rawPaths.append( data->text() );
658 for (
const QString &path : qgis::as_const( rawPaths ) )
660 QFileInfo file( path );
677 void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
679 if ( !( event->possibleActions() & Qt::CopyAction ) )
682 const QString path = mimeDataToPath( event->mimeData() );
683 if ( !path.isEmpty() )
686 event->setDropAction( Qt::CopyAction );
688 leText->setHighlighted(
true );
692 void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
694 QWidget::dragLeaveEvent( event );
695 if ( leText->isHighlighted() )
698 leText->setHighlighted(
false );
702 void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
704 if ( !( event->possibleActions() & Qt::CopyAction ) )
707 const QString path = mimeDataToPath( event->mimeData() );
708 if ( !path.isEmpty() )
711 setFocus( Qt::MouseFocusReason );
712 event->setDropAction( Qt::CopyAction );
716 leText->setHighlighted(
false );