QGIS API Documentation 3.99.0-Master (752b475928d)
Loading...
Searching...
No Matches
qgsprocessingoutputdestinationwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingmatrixparameterdialog.cpp
3 ------------------------------------
4 Date : February 2019
5 Copyright : (C) 2019 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsapplication.h"
20#include "qgsdatasourceuri.h"
23#include "qgsfileutils.h"
28#include "qgssettings.h"
29
30#include <QCheckBox>
31#include <QFileDialog>
32#include <QInputDialog>
33#include <QLocale>
34#include <QMenu>
35#include <QTextCodec>
36#include <QUrl>
37
38#include "moc_qgsprocessingoutputdestinationwidget.cpp"
39
41
42QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWidget( const QgsProcessingDestinationParameter *param, bool defaultSelection, QWidget *parent )
43 : QWidget( parent )
44 , mParameter( param )
45 , mDefaultSelection( defaultSelection )
46{
47 Q_ASSERT( mParameter );
48
49 setupUi( this );
50
51 leText->setClearButtonEnabled( false );
52
53 connect( leText, &QLineEdit::textEdited, this, &QgsProcessingLayerOutputDestinationWidget::textChanged );
54
55 mMenu = new QMenu( this );
56 connect( mMenu, &QMenu::aboutToShow, this, &QgsProcessingLayerOutputDestinationWidget::menuAboutToShow );
57 mSelectButton->setMenu( mMenu );
58 mSelectButton->setPopupMode( QToolButton::InstantPopup );
59
60 QgsSettings settings;
61 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( settings.value( QStringLiteral( "/Processing/encoding" ), QStringLiteral( "System" ) ).toString() );
62 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
63
64 if ( !mParameter->defaultValueForGui().isValid() )
65 {
66 // no default value -- we default to either skipping the output or a temporary output, depending on the createByDefault value
67 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional && !mParameter->createByDefault() )
68 setValue( QVariant() );
69 else
71 }
72 else
73 {
74 setValue( mParameter->defaultValueForGui() );
75 }
76
77 setToolTip( mParameter->toolTip() );
78
79 setAcceptDrops( true );
80 leText->setAcceptDrops( false );
81}
82
83bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped() const
84{
85 return leText->text().isEmpty() && !mUseTemporary;
86}
87
88void QgsProcessingLayerOutputDestinationWidget::setValue( const QVariant &value )
89{
90 const bool prevSkip = outputIsSkipped();
91 mUseRemapping = false;
92 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
93 {
94 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
95 skipOutput();
96 else
97 saveToTemporary();
98 }
99 else
100 {
101 if ( value.toString() == QLatin1String( "memory:" ) || value.toString() == QgsProcessing::TEMPORARY_OUTPUT )
102 {
103 saveToTemporary();
104 }
105 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
106 {
108 if ( def.sink.staticValue().toString() == QLatin1String( "memory:" ) || def.sink.staticValue().toString() == QgsProcessing::TEMPORARY_OUTPUT || def.sink.staticValue().toString().isEmpty() )
109 {
110 saveToTemporary();
111 }
112 else
113 {
114 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
115 leText->setText( def.sink.staticValue().toString() );
116 mUseTemporary = false;
117 if ( prevSkip )
118 emit skipOutputChanged( false );
119 if ( prev != QgsProcessingLayerOutputDestinationWidget::value() )
120 emit destinationChanged();
121 }
122 mUseRemapping = def.useRemapping();
123 mRemapDefinition = def.remappingDefinition();
124 mEncoding = def.createOptions.value( QStringLiteral( "fileEncoding" ) ).toString();
125 }
126 else
127 {
128 const QVariant prev = QgsProcessingLayerOutputDestinationWidget::value();
129 leText->setText( value.toString() );
130 mUseTemporary = false;
131 if ( prevSkip )
132 emit skipOutputChanged( false );
133
134 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
135 {
136 if ( prev.toString() != QgsProcessingLayerOutputDestinationWidget::value().toString() )
137 emit destinationChanged();
138 }
139 else
140 {
141 if ( prev.userType() != qMetaTypeId<QgsProcessingOutputLayerDefinition>() || !( prev.value<QgsProcessingOutputLayerDefinition>() == QgsProcessingLayerOutputDestinationWidget::value().value<QgsProcessingOutputLayerDefinition>() ) )
142 emit destinationChanged();
143 }
144 }
145 }
146}
147
148QVariant QgsProcessingLayerOutputDestinationWidget::value() const
149{
150 QgsSettings settings;
151 QString key;
152 if ( mUseTemporary && mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
153 {
155 }
156 else if ( mUseTemporary && !mDefaultSelection )
157 {
159 }
160 else
161 {
162 key = leText->text();
163 }
164
165 if ( key.isEmpty() && mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
166 return QVariant();
167
168 QString provider;
169 QString uri;
170 if ( !key.isEmpty() && key != QgsProcessing::TEMPORARY_OUTPUT
171 && !key.startsWith( QLatin1String( "memory:" ) )
172 && !key.startsWith( QLatin1String( "ogr:" ) )
173 && !key.startsWith( QLatin1String( "postgres:" ) )
174 && !key.startsWith( QLatin1String( "postgis:" ) )
175 && !QgsProcessingUtils::decodeProviderKeyAndUri( key, provider, uri ) )
176 {
177 // output should be a file path
178 QString folder = QFileInfo( key ).path();
179 if ( folder == '.' )
180 {
181 // output name does not include a folder - use default
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 ) )
185 {
186 QgsDebugError( QStringLiteral( "Can't create output folder '%1'" ).arg( defaultFolder ) );
187 }
188 key = destDir.filePath( key );
189 }
190 }
191
192 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
193 return key;
194 else if ( mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
195 return key;
196
198 value.createOptions.insert( QStringLiteral( "fileEncoding" ), mEncoding );
199 if ( mUseRemapping )
200 value.setRemappingDefinition( mRemapDefinition );
201 if ( !mFormat.isEmpty() )
202 value.setFormat( mFormat );
203 return value;
204}
205
206void QgsProcessingLayerOutputDestinationWidget::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
207{
208 mBrowserModel = context.browserModel();
209}
210
211void QgsProcessingLayerOutputDestinationWidget::setContext( QgsProcessingContext *context )
212{
213 mContext = context;
214}
215
216void QgsProcessingLayerOutputDestinationWidget::registerProcessingParametersGenerator( QgsProcessingParametersGenerator *generator )
217{
218 mParametersGenerator = generator;
219}
220
221void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
222{
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 );
228
229 connect( this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged, this, [this]( bool skipped ) {
230 bool enabled = !skipped;
231 mOpenAfterRunningCheck->setEnabled( enabled );
232 mOpenAfterRunningCheck->setChecked( enabled );
233 } );
234}
235
236bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning() const
237{
238 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
239}
240
241void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
242{
243 mMenu->clear();
244
245 if ( !mDefaultSelection )
246 {
247 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
248 {
249 QAction *actionSkipOutput = new QAction( tr( "Skip Output" ), this );
250 connect( actionSkipOutput, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
251 mMenu->addAction( actionSkipOutput );
252 }
253
254 QAction *actionSaveToTemp = nullptr;
255 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
256 {
257 // use memory layers for temporary layers if supported
258 actionSaveToTemp = new QAction( tr( "Create Temporary Layer" ), this );
259 }
260 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
261 {
262 actionSaveToTemp = new QAction( tr( "Save to a Temporary Directory" ), this );
263 }
264 else
265 {
266 actionSaveToTemp = new QAction( tr( "Save to a Temporary File" ), this );
267 }
268
269 connect( actionSaveToTemp, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
270 mMenu->addAction( actionSaveToTemp );
271 }
272
273 QAction *actionSaveToFile = nullptr;
274 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
275 {
276 actionSaveToFile = new QAction( tr( "Save to Directory…" ), this );
277 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
278 }
279 else
280 {
281 actionSaveToFile = new QAction( tr( "Save to File…" ), this );
282 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
283 }
284 mMenu->addAction( actionSaveToFile );
285
286 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
287 {
288 QAction *actionSaveToGpkg = new QAction( tr( "Save to GeoPackage…" ), this );
289 connect( actionSaveToGpkg, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
290 mMenu->addAction( actionSaveToGpkg );
291
292 QAction *actionSaveToDatabase = new QAction( tr( "Save to Database Table…" ), this );
293 connect( actionSaveToDatabase, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
294 mMenu->addAction( actionSaveToDatabase );
295
296 if ( mParameter->algorithm() && qgis::down_cast<const QgsProcessingParameterFeatureSink *>( mParameter )->supportsAppend() )
297 {
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 );
302 if ( mUseRemapping )
303 {
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() );
307 } );
308 mMenu->addAction( editMappingAction );
309 }
310 }
311 }
312
313 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
314 {
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 );
319 }
320}
321
322void QgsProcessingLayerOutputDestinationWidget::skipOutput()
323{
324 leText->setPlaceholderText( tr( "[Skip output]" ) );
325 leText->clear();
326 mUseTemporary = false;
327 mUseRemapping = false;
328
329 emit skipOutputChanged( true );
330 emit destinationChanged();
331}
332
333void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
334{
335 const bool prevSkip = outputIsSkipped();
336
337 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
338 {
339 leText->setPlaceholderText( tr( "[Create temporary layer]" ) );
340 }
341 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
342 {
343 leText->setPlaceholderText( tr( "[Save to temporary folder]" ) );
344 }
345 else
346 {
347 leText->setPlaceholderText( tr( "[Save to temporary file]" ) );
348 }
349 leText->clear();
350
351 if ( mUseTemporary )
352 return;
353
354 mUseTemporary = true;
355 mUseRemapping = false;
356 if ( prevSkip )
357 emit skipOutputChanged( false );
358 emit destinationChanged();
359}
360
361void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
362{
363 QString lastDir = leText->text();
364 QgsSettings settings;
365 if ( lastDir.isEmpty() )
366 lastDir = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QDir::homePath() ).toString();
367
368 const QString dirName = QFileDialog::getExistingDirectory( this, tr( "Select Directory" ), lastDir, QFileDialog::Options() );
369 if ( !dirName.isEmpty() )
370 {
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();
377 }
378}
379
380void QgsProcessingLayerOutputDestinationWidget::selectFile()
381{
382 const QString fileFilter = mParameter->createFileFilter();
383
384 QgsSettings settings;
385
386 QString lastExtPath;
387 QString lastExt;
388 QString lastFormatPath;
389 QString lastFormat;
390 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() )
391 {
392 lastExtPath = QStringLiteral( "/Processing/LastVectorOutputExt" );
393 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
394 }
395 else if ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() )
396 {
397 const QgsProcessingParameterRasterDestination *dest = dynamic_cast<const QgsProcessingParameterRasterDestination *>( mParameter );
398 Q_ASSERT( dest );
399 lastFormatPath = QStringLiteral( "/Processing/LastRasterOutputFormat" );
400 lastFormat = settings.value( lastFormatPath, dest->defaultFileFormat() ).toString();
401 }
402 else if ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() )
403 {
404 lastExtPath = QStringLiteral( "/Processing/LastPointCloudOutputExt" );
405 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
406 }
407 else if ( mParameter->type() == QgsProcessingParameterVectorTileDestination::typeName() )
408 {
409 lastExtPath = QStringLiteral( "/Processing/LastVectorTileOutputExt" );
410 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
411 }
412
413 // get default filter
414 const QStringList filters = fileFilter.split( QStringLiteral( ";;" ) );
415 QString lastFilter;
416 for ( const QString &f : filters )
417 {
418 if ( !lastFormat.isEmpty() && f.contains( lastFormat, Qt::CaseInsensitive ) )
419 {
420 lastFilter = f;
421 break;
422 }
423 else if ( !lastExt.isEmpty() && f.contains( QStringLiteral( "*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
424 {
425 lastFilter = f;
426 break;
427 }
428 }
429
430 QString path;
431 if ( settings.contains( QStringLiteral( "/Processing/LastOutputPath" ) ) )
432 path = settings.value( QStringLiteral( "/Processing/LastOutputPath" ) ).toString();
433 else
434 path = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
435
436 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "dontconfirmoverwrite" ), false ).toBool();
437
438 QString filename = QFileDialog::getSaveFileName( this, tr( "Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
439 if ( !filename.isEmpty() )
440 {
441 mUseTemporary = false;
442 mUseRemapping = false;
443 if ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() )
444 {
445 int spacePos = static_cast<int>( lastFilter.indexOf( ' ' ) );
446 if ( spacePos > 0 )
447 {
448 mFormat = lastFilter.left( spacePos );
449 }
450 }
451 filename = QgsFileUtils::addExtensionFromFilter( filename, lastFilter );
452
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() );
459
460 emit skipOutputChanged( false );
461 emit destinationChanged();
462 }
463 // return dialog focus on Mac
464 activateWindow();
465 raise();
466}
467
468void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
469{
470 QgsSettings settings;
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();
474
475 QString filename = QFileDialog::getSaveFileName( this, tr( "Save to GeoPackage" ), lastPath, tr( "GeoPackage files (*.gpkg);;All files (*.*)" ), nullptr, QFileDialog::DontConfirmOverwrite );
476 // return dialog focus on Mac
477 activateWindow();
478 raise();
479
480 if ( filename.isEmpty() )
481 return;
482
483 const QString layerName = QInputDialog::getText( this, tr( "Save to GeoPackage" ), tr( "Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
484 if ( layerName.isEmpty() )
485 return;
486
487 mUseTemporary = false;
488 mUseRemapping = false;
489
490 filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "gpkg" ) );
491
492 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
493
495 uri.setTable( layerName );
496 uri.setDatabase( filename );
497
498 QString geomColumn;
499 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast<const QgsProcessingParameterFeatureSink *>( mParameter ) )
500 {
501 if ( sink->hasGeometry() )
502 geomColumn = QStringLiteral( "geom" );
503 }
504 uri.setGeometryColumn( geomColumn );
505
506 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
507
508 emit skipOutputChanged( false );
509 emit destinationChanged();
510}
511
512void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
513{
514 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
515 {
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() ) );
518 widget->setAcceptButtonVisible( true );
519
520 panel->openPanel( widget );
521
522 auto changed = [this, widget] {
523 mUseTemporary = false;
524 mUseRemapping = false;
525
526 QString geomColumn;
527 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast<const QgsProcessingParameterFeatureSink *>( mParameter ) )
528 {
529 if ( sink->hasGeometry() )
530 geomColumn = widget->dataProviderKey() == QLatin1String( "oracle" ) ? QStringLiteral( "GEOM" ) : QStringLiteral( "geom" );
531 }
532
533 if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
534 {
536 uri.setTable( widget->table() );
537 uri.setDatabase( widget->schema() );
538 uri.setGeometryColumn( geomColumn );
539 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
540 }
541 else
542 {
543 QgsDataSourceUri uri( widget->uri() );
544 uri.setGeometryColumn( geomColumn );
545 leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
546 }
547
548 emit skipOutputChanged( false );
549 emit destinationChanged();
550 };
551
552 connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [changed] { changed(); } );
553 connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [changed] { changed(); } );
554 connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [changed] { changed(); } );
555 connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [changed] { changed(); } );
556 connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [changed, widget] {
557 changed();
558 widget->acceptPanel();
559 } );
560 }
561}
562
563void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
564{
565 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
566 {
568 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
569
570 panel->openPanel( widget );
571
572 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [widget]( const QgsMimeDataUtils::Uri & ) {
573 widget->acceptPanel();
574 } );
575 connect( widget, &QgsPanelWidget::panelAccepted, this, [this, widget]() {
576 if ( widget->uri().uri.isEmpty() )
577 setValue( QVariant() );
578 else
579 {
580 // get fields for destination
581 auto dest = std::make_unique<QgsVectorLayer>( widget->uri().uri, QString(), widget->uri().providerKey );
582 if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
583 setAppendDestination( widget->uri().uri, dest->fields() );
584 else
585 setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
586 }
587 } );
588 }
589}
590
591
592void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
593{
594 const QgsProcessingAlgorithm *alg = mParameter->algorithm();
595 QVariantMap props;
596 if ( mParametersGenerator )
597 props = mParametersGenerator->createProcessingParameters();
598 props.insert( mParameter->name(), uri );
599
600 const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties>() );
602 {
603 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
604 {
605 // get mapping from fields output by algorithm to destination fields
606 QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
607 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
608 if ( !mRemapDefinition.fieldMap().isEmpty() )
609 widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
610
611 panel->openPanel( widget );
612
613 connect( widget, &QgsPanelWidget::panelAccepted, this, [this, outputProps, widget, destFields, uri]() {
616 remap.setSourceCrs( outputProps.crs );
617 remap.setFieldMap( widget->fieldPropertyMap() );
618 remap.setDestinationFields( destFields );
619 def.setRemappingDefinition( remap );
620 setValue( def );
621 } );
622 }
623 }
624}
625
626void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
627{
628 QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
629 if ( dialog.exec() )
630 {
631 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( dialog.encoding() );
632
633 QgsSettings settings;
634 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
635
636 emit destinationChanged();
637 }
638}
639
640void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
641{
642 mUseTemporary = text.isEmpty();
643 mUseRemapping = false;
644 emit destinationChanged();
645}
646
647
648QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
649{
651 for ( const QgsMimeDataUtils::Uri &u : uriList )
652 {
653 if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
655 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
656 && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
657 {
658 return u.uri;
659 }
660 else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
661 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
662 && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
663 {
664 return u.uri;
665 }
666 else if ( ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName()
667 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
668 && u.layerType == QLatin1String( "pointcloud" ) && ( u.providerKey == QLatin1String( "ept" ) || u.providerKey == QLatin1String( "pdal" ) ) )
669 {
670 return u.uri;
671 }
672#if 0
673 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
674 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
675 && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
676 return u.uri;
677
678#endif
679 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
680 && u.layerType == QLatin1String( "directory" ) )
681 {
682 return u.uri;
683 }
684 }
685 if ( !uriList.isEmpty() )
686 return QString();
687
688 // files dragged from file explorer, outside of QGIS
689 QStringList rawPaths;
690 if ( data->hasUrls() )
691 {
692 const QList<QUrl> urls = data->urls();
693 rawPaths.reserve( urls.count() );
694 for ( const QUrl &url : urls )
695 {
696 const QString local = url.toLocalFile();
697 if ( !rawPaths.contains( local ) )
698 rawPaths.append( local );
699 }
700 }
701 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
702 rawPaths.append( data->text() );
703
704 for ( const QString &path : std::as_const( rawPaths ) )
705 {
706 QFileInfo file( path );
707 if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() || mParameter->type() == QgsProcessingParameterRasterDestination::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() || mParameter->type() == QgsProcessingParameterFileDestination::typeName() || mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() ) )
708 {
709 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
710 return path;
711 }
712 else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
713 return path;
714 }
715
716 return QString();
717}
718
719void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
720{
721 if ( !( event->possibleActions() & Qt::CopyAction ) )
722 return;
723
724 const QString path = mimeDataToPath( event->mimeData() );
725 if ( !path.isEmpty() )
726 {
727 // dragged an acceptable path, phew
728 event->setDropAction( Qt::CopyAction );
729 event->accept();
730 leText->setHighlighted( true );
731 }
732}
733
734void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
735{
736 QWidget::dragLeaveEvent( event );
737 if ( leText->isHighlighted() )
738 {
739 event->accept();
740 leText->setHighlighted( false );
741 }
742}
743
744void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
745{
746 if ( !( event->possibleActions() & Qt::CopyAction ) )
747 return;
748
749 const QString path = mimeDataToPath( event->mimeData() );
750 if ( !path.isEmpty() )
751 {
752 // dropped an acceptable path, phew
753 setFocus( Qt::MouseFocusReason );
754 event->setDropAction( Qt::CopyAction );
755 event->accept();
756 setValue( path );
757 }
758 leText->setHighlighted( false );
759}
760
@ Available
Properties are available.
Definition qgis.h:3644
@ Vector
Vector layer.
Definition qgis.h:191
@ Optional
Parameter is optional.
Definition qgis.h:3765
Embeds the browser view to select an existing data source.
void itemTriggered(const QgsMimeDataUtils::Uri &uri)
Emitted when an item is triggered, e.g.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
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.
Creates a mapping from one set of QgsFields to another, for each set of "destination" fields an expre...
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
Container of fields for a vector layer.
Definition qgsfields.h:46
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)
A widget which embeds the browser view to select a DB schema and a new table name.
QString table() const
Returns the current name of the new table.
QString dataProviderKey() const
Returns the currently selected data item provider key.
void setAcceptButtonVisible(bool visible)
Sets whether the optional "Ok"/accept button should be visible.
void providerKeyChanged(const QString &providerKey)
This signal is emitted when the selects a data provider or a schema name that has a different data pr...
QString schema() const
Returns the currently selected schema or file path (in case of filesystem-based DBs like spatialite o...
void tableNameChanged(const QString &tableName)
This signal is emitted when the user enters a table name.
void accepted()
Emitted when the OK/accept button is clicked.
void validationChanged(bool isValid)
This signal is emitted whenever the validation status of the widget changes.
QString uri() const
Returns the (possibly blank) string representation of the new table data source URI.
void schemaNameChanged(const QString &schemaName)
This signal is emitted when the user selects a schema (or file path for filesystem-based DBs like spa...
Base class for any widget that can be shown as an inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void acceptPanel()
Accept the panel.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Abstract base class for processing algorithms.
virtual QgsProcessingAlgorithm::VectorProperties sinkProperties(const QString &sink, const QVariantMap &parameters, 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.
Definition qgssettings.h:65
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)
Definition qgslogger.h:57
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.