QGIS API Documentation 3.99.0-Master (26c88405ac0)
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 return value;
202}
203
204void QgsProcessingLayerOutputDestinationWidget::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
205{
206 mBrowserModel = context.browserModel();
207}
208
209void QgsProcessingLayerOutputDestinationWidget::setContext( QgsProcessingContext *context )
210{
211 mContext = context;
212}
213
214void QgsProcessingLayerOutputDestinationWidget::registerProcessingParametersGenerator( QgsProcessingParametersGenerator *generator )
215{
216 mParametersGenerator = generator;
217}
218
219void QgsProcessingLayerOutputDestinationWidget::addOpenAfterRunningOption()
220{
221 Q_ASSERT( mOpenAfterRunningCheck == nullptr );
222 mOpenAfterRunningCheck = new QCheckBox( tr( "Open output file after running algorithm" ) );
223 mOpenAfterRunningCheck->setChecked( !outputIsSkipped() );
224 mOpenAfterRunningCheck->setEnabled( !outputIsSkipped() );
225 gridLayout->addWidget( mOpenAfterRunningCheck, 1, 0, 1, 2 );
226
227 connect( this, &QgsProcessingLayerOutputDestinationWidget::skipOutputChanged, this, [this]( bool skipped ) {
228 bool enabled = !skipped;
229 mOpenAfterRunningCheck->setEnabled( enabled );
230 mOpenAfterRunningCheck->setChecked( enabled );
231 } );
232}
233
234bool QgsProcessingLayerOutputDestinationWidget::openAfterRunning() const
235{
236 return mOpenAfterRunningCheck && mOpenAfterRunningCheck->isChecked();
237}
238
239void QgsProcessingLayerOutputDestinationWidget::menuAboutToShow()
240{
241 mMenu->clear();
242
243 if ( !mDefaultSelection )
244 {
245 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
246 {
247 QAction *actionSkipOutput = new QAction( tr( "Skip Output" ), this );
248 connect( actionSkipOutput, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::skipOutput );
249 mMenu->addAction( actionSkipOutput );
250 }
251
252 QAction *actionSaveToTemp = nullptr;
253 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
254 {
255 // use memory layers for temporary layers if supported
256 actionSaveToTemp = new QAction( tr( "Create Temporary Layer" ), this );
257 }
258 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
259 {
260 actionSaveToTemp = new QAction( tr( "Save to a Temporary Directory" ), this );
261 }
262 else
263 {
264 actionSaveToTemp = new QAction( tr( "Save to a Temporary File" ), this );
265 }
266
267 connect( actionSaveToTemp, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToTemporary );
268 mMenu->addAction( actionSaveToTemp );
269 }
270
271 QAction *actionSaveToFile = nullptr;
272 if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
273 {
274 actionSaveToFile = new QAction( tr( "Save to Directory…" ), this );
275 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectDirectory );
276 }
277 else
278 {
279 actionSaveToFile = new QAction( tr( "Save to File…" ), this );
280 connect( actionSaveToFile, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectFile );
281 }
282 mMenu->addAction( actionSaveToFile );
283
284 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
285 {
286 QAction *actionSaveToGpkg = new QAction( tr( "Save to GeoPackage…" ), this );
287 connect( actionSaveToGpkg, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToGeopackage );
288 mMenu->addAction( actionSaveToGpkg );
289
290 QAction *actionSaveToDatabase = new QAction( tr( "Save to Database Table…" ), this );
291 connect( actionSaveToDatabase, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::saveToDatabase );
292 mMenu->addAction( actionSaveToDatabase );
293
294 if ( mParameter->algorithm() && qgis::down_cast<const QgsProcessingParameterFeatureSink *>( mParameter )->supportsAppend() )
295 {
296 mMenu->addSeparator();
297 QAction *actionAppendToLayer = new QAction( tr( "Append to Layer…" ), this );
298 connect( actionAppendToLayer, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::appendToLayer );
299 mMenu->addAction( actionAppendToLayer );
300 if ( mUseRemapping )
301 {
302 QAction *editMappingAction = new QAction( tr( "Edit Field Mapping…" ), this );
303 connect( editMappingAction, &QAction::triggered, this, [this] {
304 setAppendDestination( value().value<QgsProcessingOutputLayerDefinition>().sink.staticValue().toString(), mRemapDefinition.destinationFields() );
305 } );
306 mMenu->addAction( editMappingAction );
307 }
308 }
309 }
310
311 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() )
312 {
313 mMenu->addSeparator();
314 QAction *actionSetEncoding = new QAction( tr( "Change File Encoding (%1)…" ).arg( mEncoding ), this );
315 connect( actionSetEncoding, &QAction::triggered, this, &QgsProcessingLayerOutputDestinationWidget::selectEncoding );
316 mMenu->addAction( actionSetEncoding );
317 }
318}
319
320void QgsProcessingLayerOutputDestinationWidget::skipOutput()
321{
322 leText->setPlaceholderText( tr( "[Skip output]" ) );
323 leText->clear();
324 mUseTemporary = false;
325 mUseRemapping = false;
326
327 emit skipOutputChanged( true );
328 emit destinationChanged();
329}
330
331void QgsProcessingLayerOutputDestinationWidget::saveToTemporary()
332{
333 const bool prevSkip = outputIsSkipped();
334
335 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() && mParameter->supportsNonFileBasedOutput() )
336 {
337 leText->setPlaceholderText( tr( "[Create temporary layer]" ) );
338 }
339 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() )
340 {
341 leText->setPlaceholderText( tr( "[Save to temporary folder]" ) );
342 }
343 else
344 {
345 leText->setPlaceholderText( tr( "[Save to temporary file]" ) );
346 }
347 leText->clear();
348
349 if ( mUseTemporary )
350 return;
351
352 mUseTemporary = true;
353 mUseRemapping = false;
354 if ( prevSkip )
355 emit skipOutputChanged( false );
356 emit destinationChanged();
357}
358
359void QgsProcessingLayerOutputDestinationWidget::selectDirectory()
360{
361 QString lastDir = leText->text();
362 QgsSettings settings;
363 if ( lastDir.isEmpty() )
364 lastDir = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QDir::homePath() ).toString();
365
366 const QString dirName = QFileDialog::getExistingDirectory( this, tr( "Select Directory" ), lastDir, QFileDialog::Options() );
367 if ( !dirName.isEmpty() )
368 {
369 leText->setText( QDir::toNativeSeparators( dirName ) );
370 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), dirName );
371 mUseTemporary = false;
372 mUseRemapping = false;
373 emit skipOutputChanged( false );
374 emit destinationChanged();
375 }
376}
377
378void QgsProcessingLayerOutputDestinationWidget::selectFile()
379{
380 const QString fileFilter = mParameter->createFileFilter();
381
382 QgsSettings settings;
383
384 QString lastExtPath;
385 QString lastExt;
386 if ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() )
387 {
388 lastExtPath = QStringLiteral( "/Processing/LastVectorOutputExt" );
389 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
390 }
391 else if ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() )
392 {
393 lastExtPath = QStringLiteral( "/Processing/LastRasterOutputExt" );
394 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
395 }
396 else if ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName() )
397 {
398 lastExtPath = QStringLiteral( "/Processing/LastPointCloudOutputExt" );
399 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
400 }
401 else if ( mParameter->type() == QgsProcessingParameterVectorTileDestination::typeName() )
402 {
403 lastExtPath = QStringLiteral( "/Processing/LastVectorTileOutputExt" );
404 lastExt = settings.value( lastExtPath, QStringLiteral( ".%1" ).arg( mParameter->defaultFileExtension() ) ).toString();
405 }
406
407 // get default filter
408 const QStringList filters = fileFilter.split( QStringLiteral( ";;" ) );
409 QString lastFilter;
410 for ( const QString &f : filters )
411 {
412 if ( f.contains( QStringLiteral( "*.%1" ).arg( lastExt ), Qt::CaseInsensitive ) )
413 {
414 lastFilter = f;
415 break;
416 }
417 }
418
419 QString path;
420 if ( settings.contains( QStringLiteral( "/Processing/LastOutputPath" ) ) )
421 path = settings.value( QStringLiteral( "/Processing/LastOutputPath" ) ).toString();
422 else
423 path = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ) ).toString();
424
425 const bool dontConfirmOverwrite = mParameter->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "dontconfirmoverwrite" ), false ).toBool();
426
427 QString filename = QFileDialog::getSaveFileName( this, tr( "Save file" ), path, fileFilter, &lastFilter, dontConfirmOverwrite ? QFileDialog::Options( QFileDialog::DontConfirmOverwrite ) : QFileDialog::Options() );
428 if ( !filename.isEmpty() )
429 {
430 mUseTemporary = false;
431 mUseRemapping = false;
432 filename = QgsFileUtils::addExtensionFromFilter( filename, lastFilter );
433
434 leText->setText( filename );
435 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
436 if ( !lastExtPath.isEmpty() )
437 settings.setValue( lastExtPath, QFileInfo( filename ).suffix().toLower() );
438
439 emit skipOutputChanged( false );
440 emit destinationChanged();
441 }
442 // return dialog focus on Mac
443 activateWindow();
444 raise();
445}
446
447void QgsProcessingLayerOutputDestinationWidget::saveToGeopackage()
448{
449 QgsSettings settings;
450 QString lastPath = settings.value( QStringLiteral( "/Processing/LastOutputPath" ), QString() ).toString();
451 if ( lastPath.isEmpty() )
452 lastPath = settings.value( QStringLiteral( "/Processing/Configuration/OUTPUTS_FOLDER" ), QString() ).toString();
453
454 QString filename = QFileDialog::getSaveFileName( this, tr( "Save to GeoPackage" ), lastPath, tr( "GeoPackage files (*.gpkg);;All files (*.*)" ), nullptr, QFileDialog::DontConfirmOverwrite );
455 // return dialog focus on Mac
456 activateWindow();
457 raise();
458
459 if ( filename.isEmpty() )
460 return;
461
462 const QString layerName = QInputDialog::getText( this, tr( "Save to GeoPackage" ), tr( "Layer name" ), QLineEdit::Normal, mParameter->name().toLower() );
463 if ( layerName.isEmpty() )
464 return;
465
466 mUseTemporary = false;
467 mUseRemapping = false;
468
469 filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "gpkg" ) );
470
471 settings.setValue( QStringLiteral( "/Processing/LastOutputPath" ), QFileInfo( filename ).path() );
472
474 uri.setTable( layerName );
475 uri.setDatabase( filename );
476
477 QString geomColumn;
478 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast<const QgsProcessingParameterFeatureSink *>( mParameter ) )
479 {
480 if ( sink->hasGeometry() )
481 geomColumn = QStringLiteral( "geom" );
482 }
483 uri.setGeometryColumn( geomColumn );
484
485 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
486
487 emit skipOutputChanged( false );
488 emit destinationChanged();
489}
490
491void QgsProcessingLayerOutputDestinationWidget::saveToDatabase()
492{
493 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
494 {
495 QgsNewDatabaseTableNameWidget *widget = new QgsNewDatabaseTableNameWidget( mBrowserModel, QStringList() << QStringLiteral( "postgres" ) << QStringLiteral( "mssql" ) << QStringLiteral( "ogr" ) << QStringLiteral( "hana" ) << QStringLiteral( "spatialite" ) << QStringLiteral( "oracle" ), this );
496 widget->setPanelTitle( tr( "Save “%1” to Database Table" ).arg( mParameter->description() ) );
497 widget->setAcceptButtonVisible( true );
498
499 panel->openPanel( widget );
500
501 auto changed = [this, widget] {
502 mUseTemporary = false;
503 mUseRemapping = false;
504
505 QString geomColumn;
506 if ( const QgsProcessingParameterFeatureSink *sink = dynamic_cast<const QgsProcessingParameterFeatureSink *>( mParameter ) )
507 {
508 if ( sink->hasGeometry() )
509 geomColumn = widget->dataProviderKey() == QLatin1String( "oracle" ) ? QStringLiteral( "GEOM" ) : QStringLiteral( "geom" );
510 }
511
512 if ( widget->dataProviderKey() == QLatin1String( "ogr" ) )
513 {
515 uri.setTable( widget->table() );
516 uri.setDatabase( widget->schema() );
517 uri.setGeometryColumn( geomColumn );
518 leText->setText( QStringLiteral( "ogr:%1" ).arg( uri.uri() ) );
519 }
520 else
521 {
522 QgsDataSourceUri uri( widget->uri() );
523 uri.setGeometryColumn( geomColumn );
524 leText->setText( QgsProcessingUtils::encodeProviderKeyAndUri( widget->dataProviderKey(), uri.uri() ) );
525 }
526
527 emit skipOutputChanged( false );
528 emit destinationChanged();
529 };
530
531 connect( widget, &QgsNewDatabaseTableNameWidget::tableNameChanged, this, [changed] { changed(); } );
532 connect( widget, &QgsNewDatabaseTableNameWidget::schemaNameChanged, this, [changed] { changed(); } );
533 connect( widget, &QgsNewDatabaseTableNameWidget::validationChanged, this, [changed] { changed(); } );
534 connect( widget, &QgsNewDatabaseTableNameWidget::providerKeyChanged, this, [changed] { changed(); } );
535 connect( widget, &QgsNewDatabaseTableNameWidget::accepted, this, [changed, widget] {
536 changed();
537 widget->acceptPanel();
538 } );
539 }
540}
541
542void QgsProcessingLayerOutputDestinationWidget::appendToLayer()
543{
544 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
545 {
547 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
548
549 panel->openPanel( widget );
550
551 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [widget]( const QgsMimeDataUtils::Uri & ) {
552 widget->acceptPanel();
553 } );
554 connect( widget, &QgsPanelWidget::panelAccepted, this, [this, widget]() {
555 if ( widget->uri().uri.isEmpty() )
556 setValue( QVariant() );
557 else
558 {
559 // get fields for destination
560 auto dest = std::make_unique<QgsVectorLayer>( widget->uri().uri, QString(), widget->uri().providerKey );
561 if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
562 setAppendDestination( widget->uri().uri, dest->fields() );
563 else
564 setAppendDestination( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), dest->fields() );
565 }
566 } );
567 }
568}
569
570
571void QgsProcessingLayerOutputDestinationWidget::setAppendDestination( const QString &uri, const QgsFields &destFields )
572{
573 const QgsProcessingAlgorithm *alg = mParameter->algorithm();
574 QVariantMap props;
575 if ( mParametersGenerator )
576 props = mParametersGenerator->createProcessingParameters();
577 props.insert( mParameter->name(), uri );
578
579 const QgsProcessingAlgorithm::VectorProperties outputProps = alg->sinkProperties( mParameter->name(), props, *mContext, QMap<QString, QgsProcessingAlgorithm::VectorProperties>() );
581 {
582 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
583 {
584 // get mapping from fields output by algorithm to destination fields
585 QgsFieldMappingWidget *widget = new QgsFieldMappingWidget( nullptr, outputProps.fields, destFields );
586 widget->setPanelTitle( tr( "Append \"%1\" to Layer" ).arg( mParameter->description() ) );
587 if ( !mRemapDefinition.fieldMap().isEmpty() )
588 widget->setFieldPropertyMap( mRemapDefinition.fieldMap() );
589
590 panel->openPanel( widget );
591
592 connect( widget, &QgsPanelWidget::panelAccepted, this, [this, outputProps, widget, destFields, uri]() {
595 remap.setSourceCrs( outputProps.crs );
596 remap.setFieldMap( widget->fieldPropertyMap() );
597 remap.setDestinationFields( destFields );
598 def.setRemappingDefinition( remap );
599 setValue( def );
600 } );
601 }
602 }
603}
604
605void QgsProcessingLayerOutputDestinationWidget::selectEncoding()
606{
607 QgsEncodingSelectionDialog dialog( this, tr( "File encoding" ), mEncoding );
608 if ( dialog.exec() )
609 {
610 mEncoding = QgsProcessingUtils::resolveDefaultEncoding( dialog.encoding() );
611
612 QgsSettings settings;
613 settings.setValue( QStringLiteral( "/Processing/encoding" ), mEncoding );
614
615 emit destinationChanged();
616 }
617}
618
619void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text )
620{
621 mUseTemporary = text.isEmpty();
622 mUseRemapping = false;
623 emit destinationChanged();
624}
625
626
627QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data )
628{
630 for ( const QgsMimeDataUtils::Uri &u : uriList )
631 {
632 if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName()
634 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
635 && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
636 {
637 return u.uri;
638 }
639 else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName()
640 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
641 && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
642 {
643 return u.uri;
644 }
645 else if ( ( mParameter->type() == QgsProcessingParameterPointCloudDestination::typeName()
646 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
647 && u.layerType == QLatin1String( "pointcloud" ) && ( u.providerKey == QLatin1String( "ept" ) || u.providerKey == QLatin1String( "pdal" ) ) )
648 {
649 return u.uri;
650 }
651#if 0
652 else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName()
653 || mParameter->type() == QgsProcessingParameterFileDestination::typeName() )
654 && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
655 return u.uri;
656
657#endif
658 else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName()
659 && u.layerType == QLatin1String( "directory" ) )
660 {
661 return u.uri;
662 }
663 }
664 if ( !uriList.isEmpty() )
665 return QString();
666
667 // files dragged from file explorer, outside of QGIS
668 QStringList rawPaths;
669 if ( data->hasUrls() )
670 {
671 const QList<QUrl> urls = data->urls();
672 rawPaths.reserve( urls.count() );
673 for ( const QUrl &url : urls )
674 {
675 const QString local = url.toLocalFile();
676 if ( !rawPaths.contains( local ) )
677 rawPaths.append( local );
678 }
679 }
680 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
681 rawPaths.append( data->text() );
682
683 for ( const QString &path : std::as_const( rawPaths ) )
684 {
685 QFileInfo file( path );
686 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() ) )
687 {
688 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
689 return path;
690 }
691 else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) )
692 return path;
693 }
694
695 return QString();
696}
697
698void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event )
699{
700 if ( !( event->possibleActions() & Qt::CopyAction ) )
701 return;
702
703 const QString path = mimeDataToPath( event->mimeData() );
704 if ( !path.isEmpty() )
705 {
706 // dragged an acceptable path, phew
707 event->setDropAction( Qt::CopyAction );
708 event->accept();
709 leText->setHighlighted( true );
710 }
711}
712
713void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event )
714{
715 QWidget::dragLeaveEvent( event );
716 if ( leText->isHighlighted() )
717 {
718 event->accept();
719 leText->setHighlighted( false );
720 }
721}
722
723void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event )
724{
725 if ( !( event->possibleActions() & Qt::CopyAction ) )
726 return;
727
728 const QString path = mimeDataToPath( event->mimeData() );
729 if ( !path.isEmpty() )
730 {
731 // dropped an acceptable path, phew
732 setFocus( Qt::MouseFocusReason );
733 event->setDropAction( Qt::CopyAction );
734 event->accept();
735 setValue( path );
736 }
737 leText->setHighlighted( false );
738}
739
@ 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.
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.