QGIS API Documentation 4.3.0-Master (bed6b413c04)
Loading...
Searching...
No Matches
qgsprocessingwidgetwrapperimpl.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingwidgetwrapperimpl.cpp
3 ---------------------
4 begin : August 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <memory>
21
23#include "qgsannotationlayer.h"
24#include "qgsapplication.h"
25#include "qgsauthconfigselect.h"
27#include "qgscolorbutton.h"
31#include "qgsdatetimeedit.h"
33#include "qgsdoublespinbox.h"
34#include "qgsdoublevalidator.h"
38#include "qgsextentwidget.h"
39#include "qgsfieldcombobox.h"
41#include "qgsfileutils.h"
42#include "qgsfilewidget.h"
43#include "qgsfilterlineedit.h"
44#include "qgsgeometrywidget.h"
45#include "qgslayoutcombobox.h"
47#include "qgslayoutmanager.h"
48#include "qgsmapcanvas.h"
49#include "qgsmaplayercombobox.h"
50#include "qgsmapmouseevent.h"
52#include "qgsmessagebar.h"
54#include "qgspointcloudlayer.h"
55#include "qgsprintlayout.h"
72#include "qgsproject.h"
76#include "qgsscalewidget.h"
77#include "qgssettings.h"
78#include "qgssnapindicator.h"
79#include "qgsspinbox.h"
80#include "qgsunittypes.h"
81
82#include <QButtonGroup>
83#include <QCheckBox>
84#include <QComboBox>
85#include <QFileDialog>
86#include <QHBoxLayout>
87#include <QInputDialog>
88#include <QLabel>
89#include <QLineEdit>
90#include <QMenu>
91#include <QMessageBox>
92#include <QPlainTextEdit>
93#include <QRadioButton>
94#include <QString>
95#include <QToolButton>
96#include <QVBoxLayout>
97
98#include "moc_qgsprocessingwidgetwrapperimpl.cpp"
99
100using namespace Qt::StringLiterals;
101
103
104//
105// QgsProcessingBooleanWidgetWrapper
106//
107
108
109QgsProcessingBooleanParameterDefinitionWidget::QgsProcessingBooleanParameterDefinitionWidget(
110 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
111)
112 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
113{
114 QVBoxLayout *vlayout = new QVBoxLayout();
115 vlayout->setContentsMargins( 0, 0, 0, 0 );
116
117 mDefaultCheckBox = new QCheckBox( tr( "Checked" ) );
118 if ( const QgsProcessingParameterBoolean *boolParam = dynamic_cast<const QgsProcessingParameterBoolean *>( definition ) )
119 mDefaultCheckBox->setChecked( QgsProcessingParameters::parameterAsBool( boolParam, boolParam->defaultValueForGui(), context ) );
120 else
121 mDefaultCheckBox->setChecked( false );
122 vlayout->addWidget( mDefaultCheckBox );
123 connect( mDefaultCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
124
125 setLayout( vlayout );
126}
127
128QgsProcessingParameterDefinition *QgsProcessingBooleanParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
129{
130 auto param = std::make_unique<QgsProcessingParameterBoolean>( name, description, mDefaultCheckBox->isChecked() );
131 param->setFlags( flags );
132 return param.release();
133}
134
135
136QgsProcessingBooleanWidgetWrapper::QgsProcessingBooleanWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
137 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
138{}
139
140QWidget *QgsProcessingBooleanWidgetWrapper::createWidget()
141{
142 switch ( type() )
143 {
145 {
146 QString description = parameterDefinition()->description();
147 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
148 description = QObject::tr( "%1 [optional]" ).arg( description );
149
150 mCheckBox = new QCheckBox( description );
151 mCheckBox->setToolTip( parameterDefinition()->toolTip() );
152
153 connect( mCheckBox, &QCheckBox::toggled, this, [this] { emit widgetValueHasChanged( this ); } );
154 return mCheckBox;
155 }
156
159 {
160 mComboBox = new QComboBox();
161 mComboBox->addItem( tr( "Yes" ), true );
162 mComboBox->addItem( tr( "No" ), false );
163 mComboBox->setToolTip( parameterDefinition()->toolTip() );
164
165 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
166
167 return mComboBox;
168 }
169 }
170 return nullptr;
171}
172
173QLabel *QgsProcessingBooleanWidgetWrapper::createLabel()
174{
175 // avoid creating labels in standard dialogs
176 if ( type() == Qgis::ProcessingMode::Standard )
177 return nullptr;
178 else
180}
181
182void QgsProcessingBooleanWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
183{
184 switch ( type() )
185 {
187 {
188 const bool v = QgsProcessingParameters::parameterAsBool( parameterDefinition(), value, context );
189 mCheckBox->setChecked( v );
190 break;
191 }
192
195 {
196 const bool v = QgsProcessingParameters::parameterAsBool( parameterDefinition(), value, context );
197 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
198 break;
199 }
200 }
201}
202
203QVariant QgsProcessingBooleanWidgetWrapper::widgetValue() const
204{
205 switch ( type() )
206 {
208 return mCheckBox->isChecked();
209
212 return mComboBox->currentData();
213 }
214 return QVariant();
215}
216
217QString QgsProcessingBooleanWidgetWrapper::parameterType() const
218{
220}
221
222QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingBooleanWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
223{
224 return new QgsProcessingBooleanWidgetWrapper( parameter, type );
225}
226
227QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingBooleanWidgetWrapper::createParameterDefinitionWidget(
229)
230{
231 return new QgsProcessingBooleanParameterDefinitionWidget( context, widgetContext, definition, algorithm );
232}
233
234
235//
236// QgsProcessingCrsWidgetWrapper
237//
238
239QgsProcessingCrsParameterDefinitionWidget::QgsProcessingCrsParameterDefinitionWidget(
240 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
241)
242 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
243{
244 QVBoxLayout *vlayout = new QVBoxLayout();
245 vlayout->setContentsMargins( 0, 0, 0, 0 );
246
247 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
248
249 mCrsSelector = new QgsProjectionSelectionWidget();
250
251 // possibly we should expose this for parameter by parameter control
252 mCrsSelector->setShowAccuracyWarnings( true );
253
254 if ( const QgsProcessingParameterCrs *crsParam = dynamic_cast<const QgsProcessingParameterCrs *>( definition ) )
255 mCrsSelector->setCrs( QgsProcessingParameters::parameterAsCrs( crsParam, crsParam->defaultValueForGui(), context ) );
256 else
257 mCrsSelector->setCrs( QgsCoordinateReferenceSystem( u"EPSG:4326"_s ) );
258
259 vlayout->addWidget( mCrsSelector );
260 setLayout( vlayout );
261}
262
263QgsProcessingParameterDefinition *QgsProcessingCrsParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
264{
265 auto param = std::make_unique<QgsProcessingParameterCrs>( name, description, mCrsSelector->crs().authid() );
266 param->setFlags( flags );
267 return param.release();
268}
269
270QgsProcessingCrsWidgetWrapper::QgsProcessingCrsWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
271 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
272{}
273
274QWidget *QgsProcessingCrsWidgetWrapper::createWidget()
275{
276 Q_ASSERT( mProjectionSelectionWidget == nullptr );
277 mProjectionSelectionWidget = new QgsProjectionSelectionWidget();
278 mProjectionSelectionWidget->setToolTip( parameterDefinition()->toolTip() );
279
280 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
281 mProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
282 else
283 mProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, false );
284
285 connect( mProjectionSelectionWidget, &QgsProjectionSelectionWidget::crsChanged, this, [this] { emit widgetValueHasChanged( this ); } );
286
287 switch ( type() )
288 {
291 {
292 return mProjectionSelectionWidget;
293 }
294
296 {
297 QWidget *w = new QWidget();
298 w->setToolTip( parameterDefinition()->toolTip() );
299
300 QVBoxLayout *vl = new QVBoxLayout();
301 vl->setContentsMargins( 0, 0, 0, 0 );
302 w->setLayout( vl );
303
304 mUseProjectCrsCheckBox = new QCheckBox( tr( "Use project CRS" ) );
305 mUseProjectCrsCheckBox->setToolTip( tr( "Always use the current project CRS when running the model" ) );
306 vl->addWidget( mUseProjectCrsCheckBox );
307 connect( mUseProjectCrsCheckBox, &QCheckBox::toggled, mProjectionSelectionWidget, &QgsProjectionSelectionWidget::setDisabled );
308 connect( mUseProjectCrsCheckBox, &QCheckBox::toggled, this, [this] { emit widgetValueHasChanged( this ); } );
309
310 vl->addWidget( mProjectionSelectionWidget );
311
312 return w;
313 }
314 }
315 return nullptr;
316}
317
318void QgsProcessingCrsWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
319{
320 if ( mUseProjectCrsCheckBox )
321 {
322 if ( value.toString().compare( "ProjectCrs"_L1, Qt::CaseInsensitive ) == 0 )
323 {
324 mUseProjectCrsCheckBox->setChecked( true );
325 return;
326 }
327 else
328 {
329 mUseProjectCrsCheckBox->setChecked( false );
330 }
331 }
332
333 const QgsCoordinateReferenceSystem v = QgsProcessingParameters::parameterAsCrs( parameterDefinition(), value, context );
334 if ( mProjectionSelectionWidget )
335 mProjectionSelectionWidget->setCrs( v );
336}
337
338QVariant QgsProcessingCrsWidgetWrapper::widgetValue() const
339{
340 if ( mUseProjectCrsCheckBox && mUseProjectCrsCheckBox->isChecked() )
341 return u"ProjectCrs"_s;
342 else if ( mProjectionSelectionWidget )
343 return mProjectionSelectionWidget->crs().isValid() ? mProjectionSelectionWidget->crs() : QVariant();
344 else
345 return QVariant();
346}
347
348QString QgsProcessingCrsWidgetWrapper::modelerExpressionFormatString() const
349{
350 return tr( "string as EPSG code, WKT or PROJ format, or a string identifying a map layer" );
351}
352
353QString QgsProcessingCrsWidgetWrapper::parameterType() const
354{
356}
357
358QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingCrsWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
359{
360 return new QgsProcessingCrsWidgetWrapper( parameter, type );
361}
362
363QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingCrsWidgetWrapper::createParameterDefinitionWidget(
365)
366{
367 return new QgsProcessingCrsParameterDefinitionWidget( context, widgetContext, definition, algorithm );
368}
369
370
371//
372// QgsProcessingStringWidgetWrapper
373//
374
375
376QgsProcessingStringParameterDefinitionWidget::QgsProcessingStringParameterDefinitionWidget(
377 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
378)
379 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
380{
381 QVBoxLayout *vlayout = new QVBoxLayout();
382 vlayout->setContentsMargins( 0, 0, 0, 0 );
383
384 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
385
386 mDefaultLineEdit = new QLineEdit();
387 if ( const QgsProcessingParameterString *stringParam = dynamic_cast<const QgsProcessingParameterString *>( definition ) )
388 mDefaultLineEdit->setText( QgsProcessingParameters::parameterAsString( stringParam, stringParam->defaultValueForGui(), context ) );
389 vlayout->addWidget( mDefaultLineEdit );
390
391 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
392
393 mMultiLineCheckBox = new QCheckBox( tr( "Multiline input" ) );
394 if ( const QgsProcessingParameterString *stringParam = dynamic_cast<const QgsProcessingParameterString *>( definition ) )
395 mMultiLineCheckBox->setChecked( stringParam->multiLine() );
396 vlayout->addWidget( mMultiLineCheckBox );
397
398 connect( mMultiLineCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
399
400 setLayout( vlayout );
401}
402
403QgsProcessingParameterDefinition *QgsProcessingStringParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
404{
405 auto param = std::make_unique<QgsProcessingParameterString>( name, description, mDefaultLineEdit->text(), mMultiLineCheckBox->isChecked() );
406 param->setFlags( flags );
407 return param.release();
408}
409
410
411QgsProcessingStringWidgetWrapper::QgsProcessingStringWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
412 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
413{}
414
415QWidget *QgsProcessingStringWidgetWrapper::createWidget()
416{
417 const QVariantMap metadata = parameterDefinition()->metadata();
418 const QVariant valueHintsVariant = metadata.value( u"widget_wrapper"_s ).toMap().value( u"value_hints"_s );
419
420 if ( valueHintsVariant.isValid() )
421 {
422 const QVariantList valueList = valueHintsVariant.toList();
423 mComboBox = new QComboBox();
424 mComboBox->setToolTip( parameterDefinition()->toolTip() );
425
426 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
427 {
428 mComboBox->addItem( QString() );
429 }
430 for ( const QVariant &entry : valueList )
431 {
432 mComboBox->addItem( entry.toString(), entry.toString() );
433 }
434 mComboBox->setCurrentIndex( 0 );
435
436 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { emit widgetValueHasChanged( this ); } );
437 return mComboBox;
438 }
439 else
440 {
441 switch ( type() )
442 {
445 {
446 if ( static_cast<const QgsProcessingParameterString *>( parameterDefinition() )->multiLine() )
447 {
448 mPlainTextEdit = new QPlainTextEdit();
449 mPlainTextEdit->setToolTip( parameterDefinition()->toolTip() );
450
451 connect( mPlainTextEdit, &QPlainTextEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
452 return mPlainTextEdit;
453 }
454 else
455 {
456 mLineEdit = new QLineEdit();
457 mLineEdit->setToolTip( parameterDefinition()->toolTip() );
458
459 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
460 return mLineEdit;
461 }
462 }
463
465 {
466 mLineEdit = new QLineEdit();
467 mLineEdit->setToolTip( parameterDefinition()->toolTip() );
468
469 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
470 return mLineEdit;
471 }
472 }
473 }
474
475 return nullptr;
476}
477
478void QgsProcessingStringWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
479{
480 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
481 if ( mLineEdit )
482 mLineEdit->setText( v );
483 if ( mPlainTextEdit )
484 mPlainTextEdit->setPlainText( v );
485 if ( mComboBox )
486 {
487 int index = -1;
488 if ( !value.isValid() )
489 index = mComboBox->findData( QVariant() );
490 else
491 index = mComboBox->findData( v );
492
493 if ( index >= 0 )
494 mComboBox->setCurrentIndex( index );
495 else
496 mComboBox->setCurrentIndex( 0 );
497 }
498}
499
500QVariant QgsProcessingStringWidgetWrapper::widgetValue() const
501{
502 if ( mLineEdit )
503 return mLineEdit->text();
504 else if ( mPlainTextEdit )
505 return mPlainTextEdit->toPlainText();
506 else if ( mComboBox )
507 return mComboBox->currentData();
508 else
509 return QVariant();
510}
511
512QString QgsProcessingStringWidgetWrapper::parameterType() const
513{
515}
516
517QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingStringWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
518{
519 return new QgsProcessingStringWidgetWrapper( parameter, type );
520}
521
522QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingStringWidgetWrapper::createParameterDefinitionWidget(
524)
525{
526 return new QgsProcessingStringParameterDefinitionWidget( context, widgetContext, definition, algorithm );
527}
528
529
530//
531// QgsProcessingAuthConfigWidgetWrapper
532//
533
534QgsProcessingAuthConfigWidgetWrapper::QgsProcessingAuthConfigWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
535 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
536{}
537
538QWidget *QgsProcessingAuthConfigWidgetWrapper::createWidget()
539{
540 switch ( type() )
541 {
545 {
546 mAuthConfigSelect = new QgsAuthConfigSelect();
547 mAuthConfigSelect->setToolTip( parameterDefinition()->toolTip() );
548
549 connect( mAuthConfigSelect, &QgsAuthConfigSelect::selectedConfigIdChanged, this, [this] { emit widgetValueHasChanged( this ); } );
550 return mAuthConfigSelect;
551 }
552 }
553 return nullptr;
554}
555
556void QgsProcessingAuthConfigWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
557{
558 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
559 if ( mAuthConfigSelect )
560 mAuthConfigSelect->setConfigId( v );
561}
562
563QVariant QgsProcessingAuthConfigWidgetWrapper::widgetValue() const
564{
565 if ( mAuthConfigSelect )
566 return mAuthConfigSelect->configId();
567 else
568 return QVariant();
569}
570
571QString QgsProcessingAuthConfigWidgetWrapper::parameterType() const
572{
574}
575
576QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAuthConfigWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
577{
578 return new QgsProcessingAuthConfigWidgetWrapper( parameter, type );
579}
580
581//
582// QgsProcessingNumericWidgetWrapper
583//
584
585QgsProcessingNumberParameterDefinitionWidget::QgsProcessingNumberParameterDefinitionWidget(
586 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
587)
588 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
589{
590 QVBoxLayout *vlayout = new QVBoxLayout();
591 vlayout->setContentsMargins( 0, 0, 0, 0 );
592
593 vlayout->addWidget( new QLabel( tr( "Number type" ) ) );
594
595 mTypeComboBox = new QComboBox();
596 mTypeComboBox->addItem( tr( "Float" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Double ) );
597 mTypeComboBox->addItem( tr( "Integer" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Integer ) );
598 vlayout->addWidget( mTypeComboBox );
599
600 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
601 mMinLineEdit = new QLineEdit();
602 vlayout->addWidget( mMinLineEdit );
603
604 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
605 mMaxLineEdit = new QLineEdit();
606 vlayout->addWidget( mMaxLineEdit );
607
608 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
609 mDefaultLineEdit = new QLineEdit();
610 vlayout->addWidget( mDefaultLineEdit );
611
612 if ( const QgsProcessingParameterNumber *numberParam = dynamic_cast<const QgsProcessingParameterNumber *>( definition ) )
613 {
614 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( numberParam->dataType() ) ) );
615
616 if ( !qgsDoubleNear( numberParam->maximum(), std::numeric_limits<double>::max() ) )
617 {
618 mMaxLineEdit->setText( QLocale().toString( numberParam->maximum() ) );
619 }
620 else
621 {
622 mMaxLineEdit->clear();
623 }
624
625 if ( !qgsDoubleNear( numberParam->minimum(), std::numeric_limits<double>::lowest() ) )
626 {
627 mMinLineEdit->setText( QLocale().toString( numberParam->minimum() ) );
628 }
629 else
630 {
631 mMinLineEdit->clear();
632 }
633
634 mDefaultLineEdit->setText( numberParam->defaultValueForGui().toString() );
635 }
636
637 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
638 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
639 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
640 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
641
642 setLayout( vlayout );
643}
644
645QgsProcessingParameterDefinition *QgsProcessingNumberParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
646{
647 bool ok;
648 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
649
650 Qgis::ProcessingNumberParameterType dataType = static_cast<Qgis::ProcessingNumberParameterType>( mTypeComboBox->currentData().toInt() );
651 auto param = std::make_unique<QgsProcessingParameterNumber>( name, description, dataType, ok ? val : QVariant() );
652
653 if ( !mMinLineEdit->text().trimmed().isEmpty() )
654 {
655 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
656 if ( ok )
657 {
658 param->setMinimum( val );
659 }
660 }
661
662 if ( !mMaxLineEdit->text().trimmed().isEmpty() )
663 {
664 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
665 if ( ok )
666 {
667 param->setMaximum( val );
668 }
669 }
670
671 param->setFlags( flags );
672 return param.release();
673}
674
675QgsProcessingNumericWidgetWrapper::QgsProcessingNumericWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
676 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
677{}
678
679QWidget *QgsProcessingNumericWidgetWrapper::createWidget()
680{
681 const QgsProcessingParameterNumber *numberDef = static_cast<const QgsProcessingParameterNumber *>( parameterDefinition() );
682 const QVariantMap metadata = numberDef->metadata();
683 const int decimals = metadata.value( u"widget_wrapper"_s ).toMap().value( u"decimals"_s, 6 ).toInt();
684 switch ( type() )
685 {
689 {
690 // lots of duplicate code here -- but there's no common interface between QSpinBox/QDoubleSpinBox which would allow us to avoid this
691 QAbstractSpinBox *spinBox = nullptr;
692 switch ( numberDef->dataType() )
693 {
695 mDoubleSpinBox = new QgsDoubleSpinBox();
696 mDoubleSpinBox->setExpressionsEnabled( true );
697 mDoubleSpinBox->setDecimals( decimals );
698
699 // guess reasonable step value for double spin boxes
700 if ( !qgsDoubleNear( numberDef->maximum(), std::numeric_limits<double>::max() ) && !qgsDoubleNear( numberDef->minimum(), std::numeric_limits<double>::lowest() + 1 ) )
701 {
702 double singleStep = calculateStep( numberDef->minimum(), numberDef->maximum() );
703 singleStep = std::max( singleStep, std::pow( 10, -decimals ) );
704 mDoubleSpinBox->setSingleStep( singleStep );
705 }
706
707 spinBox = mDoubleSpinBox;
708 break;
709
711 mSpinBox = new QgsSpinBox();
712 mSpinBox->setExpressionsEnabled( true );
713 spinBox = mSpinBox;
714 break;
715 }
716 spinBox->setToolTip( parameterDefinition()->toolTip() );
717
718 double max = 999999999;
719 if ( !qgsDoubleNear( numberDef->maximum(), std::numeric_limits<double>::max() ) )
720 {
721 max = numberDef->maximum();
722 }
723 double min = -999999999;
724 if ( !qgsDoubleNear( numberDef->minimum(), std::numeric_limits<double>::lowest() ) )
725 {
726 min = numberDef->minimum();
727 }
728 if ( mDoubleSpinBox )
729 {
730 mDoubleSpinBox->setMinimum( min );
731 mDoubleSpinBox->setMaximum( max );
732 }
733 else
734 {
735 mSpinBox->setMinimum( static_cast<int>( min ) );
736 mSpinBox->setMaximum( static_cast<int>( max ) );
737 }
738
740 {
741 mAllowingNull = true;
742 if ( mDoubleSpinBox )
743 {
744 mDoubleSpinBox->setShowClearButton( true );
745 const double min = mDoubleSpinBox->minimum() - mDoubleSpinBox->singleStep();
746 mDoubleSpinBox->setMinimum( min );
747 mDoubleSpinBox->setValue( min );
748 }
749 else
750 {
751 mSpinBox->setShowClearButton( true );
752 const int min = mSpinBox->minimum() - 1;
753 mSpinBox->setMinimum( min );
754 mSpinBox->setValue( min );
755 }
756 spinBox->setSpecialValueText( tr( "Not set" ) );
757 }
758 else
759 {
760 if ( numberDef->defaultValueForGui().isValid() )
761 {
762 // if default value for parameter, we clear to that
763 bool ok = false;
764 if ( mDoubleSpinBox )
765 {
766 double defaultVal = numberDef->defaultValueForGui().toDouble( &ok );
767 if ( ok )
768 mDoubleSpinBox->setClearValue( defaultVal );
769 }
770 else
771 {
772 int intVal = numberDef->defaultValueForGui().toInt( &ok );
773 if ( ok )
774 mSpinBox->setClearValue( intVal );
775 }
776 }
777 else if ( !qgsDoubleNear( numberDef->minimum(), std::numeric_limits<double>::lowest() ) )
778 {
779 // otherwise we clear to the minimum, if it's set
780 if ( mDoubleSpinBox )
781 mDoubleSpinBox->setClearValue( numberDef->minimum() );
782 else
783 mSpinBox->setClearValue( static_cast<int>( numberDef->minimum() ) );
784 }
785 else
786 {
787 // last resort, we clear to 0
788 if ( mDoubleSpinBox )
789 {
790 mDoubleSpinBox->setValue( 0 );
791 mDoubleSpinBox->setClearValue( 0 );
792 }
793 else
794 {
795 mSpinBox->setValue( 0 );
796 mSpinBox->setClearValue( 0 );
797 }
798 }
799 }
800
801 if ( mDoubleSpinBox )
802 connect( mDoubleSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
803 else if ( mSpinBox )
804 connect( mSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
805
806 return spinBox;
807 }
808 }
809 return nullptr;
810}
811
812void QgsProcessingNumericWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
813{
814 if ( mDoubleSpinBox )
815 {
816 if ( mAllowingNull && !value.isValid() )
817 mDoubleSpinBox->clear();
818 else
819 {
820 const double v = QgsProcessingParameters::parameterAsDouble( parameterDefinition(), value, context );
821 mDoubleSpinBox->setValue( v );
822 }
823 }
824 else if ( mSpinBox )
825 {
826 if ( mAllowingNull && !value.isValid() )
827 mSpinBox->clear();
828 else
829 {
830 const int v = QgsProcessingParameters::parameterAsInt( parameterDefinition(), value, context );
831 mSpinBox->setValue( v );
832 }
833 }
834}
835
836QVariant QgsProcessingNumericWidgetWrapper::widgetValue() const
837{
838 if ( mDoubleSpinBox )
839 {
840 if ( mAllowingNull && qgsDoubleNear( mDoubleSpinBox->value(), mDoubleSpinBox->minimum() ) )
841 return QVariant();
842 else
843 return mDoubleSpinBox->value();
844 }
845 else if ( mSpinBox )
846 {
847 if ( mAllowingNull && mSpinBox->value() == mSpinBox->minimum() )
848 return QVariant();
849 else
850 return mSpinBox->value();
851 }
852 else
853 return QVariant();
854}
855
856double QgsProcessingNumericWidgetWrapper::calculateStep( const double minimum, const double maximum )
857{
858 const double valueRange = maximum - minimum;
859 if ( valueRange <= 1.0 )
860 {
861 const double step = valueRange / 10.0;
862 // round to 1 significant figure
863 return qgsRound( step, -std::floor( std::log( step ) ) );
864 }
865 else
866 {
867 return 1.0;
868 }
869}
870
871QString QgsProcessingNumericWidgetWrapper::parameterType() const
872{
874}
875
876QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingNumericWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
877{
878 return new QgsProcessingNumericWidgetWrapper( parameter, type );
879}
880
881QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingNumericWidgetWrapper::createParameterDefinitionWidget(
883)
884{
885 return new QgsProcessingNumberParameterDefinitionWidget( context, widgetContext, definition, algorithm );
886}
887
888//
889// QgsProcessingDistanceWidgetWrapper
890//
891
892QgsProcessingDistanceParameterDefinitionWidget::QgsProcessingDistanceParameterDefinitionWidget(
893 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
894)
895 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
896{
897 QVBoxLayout *vlayout = new QVBoxLayout();
898 vlayout->setContentsMargins( 0, 0, 0, 0 );
899
900 vlayout->addWidget( new QLabel( tr( "Linked input" ) ) );
901
902 mParentLayerComboBox = new QComboBox();
903
904 QString initialParent;
905 if ( const QgsProcessingParameterDistance *distParam = dynamic_cast<const QgsProcessingParameterDistance *>( definition ) )
906 initialParent = distParam->parentParameterName();
907
908 if ( auto *lModel = widgetContext.model() )
909 {
910 // populate combo box with other model input choices
911 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
912 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
913 {
914 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
915 {
916 mParentLayerComboBox->addItem( definition->description(), definition->name() );
917 if ( !initialParent.isEmpty() && initialParent == definition->name() )
918 {
919 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
920 }
921 }
922 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
923 {
924 mParentLayerComboBox->addItem( definition->description(), definition->name() );
925 if ( !initialParent.isEmpty() && initialParent == definition->name() )
926 {
927 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
928 }
929 }
930 else if ( const QgsProcessingParameterMapLayer *definition = dynamic_cast<const QgsProcessingParameterMapLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
931 {
932 mParentLayerComboBox->addItem( definition->description(), definition->name() );
933 if ( !initialParent.isEmpty() && initialParent == definition->name() )
934 {
935 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
936 }
937 }
938 else if ( const QgsProcessingParameterCrs *definition = dynamic_cast<const QgsProcessingParameterCrs *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
939 {
940 mParentLayerComboBox->addItem( definition->description(), definition->name() );
941 if ( !initialParent.isEmpty() && initialParent == definition->name() )
942 {
943 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
944 }
945 }
946 }
947 }
948
949 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
950 {
951 // if no parent candidates found, we just add the existing one as a placeholder
952 mParentLayerComboBox->addItem( initialParent, initialParent );
953 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
954 }
955
956 vlayout->addWidget( mParentLayerComboBox );
957 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
958
959 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
960 mMinLineEdit = new QLineEdit();
961 vlayout->addWidget( mMinLineEdit );
962
963 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
964 mMaxLineEdit = new QLineEdit();
965 vlayout->addWidget( mMaxLineEdit );
966
967 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
968 mDefaultLineEdit = new QLineEdit();
969 vlayout->addWidget( mDefaultLineEdit );
970
971 if ( const QgsProcessingParameterDistance *distParam = dynamic_cast<const QgsProcessingParameterDistance *>( definition ) )
972 {
973 mMinLineEdit->setText( QLocale().toString( distParam->minimum() ) );
974 mMaxLineEdit->setText( QLocale().toString( distParam->maximum() ) );
975 mDefaultLineEdit->setText( distParam->defaultValueForGui().toString() );
976 }
977 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
978 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
979 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
980
981 setLayout( vlayout );
982}
983
984QgsProcessingParameterDefinition *QgsProcessingDistanceParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
985{
986 bool ok;
987 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
988
989 auto param = std::make_unique<QgsProcessingParameterDistance>( name, description, ok ? val : QVariant(), mParentLayerComboBox->currentData().toString() );
990
991 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
992 if ( ok )
993 {
994 param->setMinimum( val );
995 }
996
997 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
998 if ( ok )
999 {
1000 param->setMaximum( val );
1001 }
1002
1003 param->setFlags( flags );
1004 return param.release();
1005}
1006
1007QgsProcessingDistanceWidgetWrapper::QgsProcessingDistanceWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1008 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1009{}
1010
1011QString QgsProcessingDistanceWidgetWrapper::parameterType() const
1012{
1014}
1015
1016QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDistanceWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1017{
1018 return new QgsProcessingDistanceWidgetWrapper( parameter, type );
1019}
1020
1021QWidget *QgsProcessingDistanceWidgetWrapper::createWidget()
1022{
1023 const QgsProcessingParameterDistance *distanceDef = static_cast<const QgsProcessingParameterDistance *>( parameterDefinition() );
1024
1025 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1026 switch ( type() )
1027 {
1029 {
1030 mLabel = new QLabel();
1031 mUnitsCombo = new QComboBox();
1032
1033 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Meters ), static_cast<int>( Qgis::DistanceUnit::Meters ) );
1034 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Kilometers ), static_cast<int>( Qgis::DistanceUnit::Kilometers ) );
1035 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Feet ), static_cast<int>( Qgis::DistanceUnit::Feet ) );
1036 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Yards ), static_cast<int>( Qgis::DistanceUnit::Yards ) );
1037 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Miles ), static_cast<int>( Qgis::DistanceUnit::Miles ) );
1039 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Centimeters ), static_cast<int>( Qgis::DistanceUnit::Centimeters ) );
1040 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Millimeters ), static_cast<int>( Qgis::DistanceUnit::Millimeters ) );
1041 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Inches ), static_cast<int>( Qgis::DistanceUnit::Inches ) );
1042
1043 const int labelMargin = static_cast<int>( std::round( mUnitsCombo->fontMetrics().horizontalAdvance( 'X' ) ) );
1044 QHBoxLayout *layout = new QHBoxLayout();
1045 layout->addWidget( spin, 1 );
1046 layout->insertSpacing( 1, labelMargin / 2 );
1047 layout->insertWidget( 2, mLabel );
1048 layout->insertWidget( 3, mUnitsCombo );
1049
1050 // bit of fiddlyness here -- we want the initial spacing to only be visible
1051 // when the warning label is shown, so it's embedded inside mWarningLabel
1052 // instead of outside it
1053 mWarningLabel = new QWidget();
1054 QHBoxLayout *warningLayout = new QHBoxLayout();
1055 warningLayout->setContentsMargins( 0, 0, 0, 0 );
1056 QLabel *warning = new QLabel();
1057 QIcon icon = QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
1058 const int size = static_cast<int>( std::max( 24.0, spin->minimumSize().height() * 0.5 ) );
1059 warning->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
1060 warning->setToolTip( tr( "Distance is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results." ) );
1061 warningLayout->insertSpacing( 0, labelMargin / 2 );
1062 warningLayout->insertWidget( 1, warning );
1063 mWarningLabel->setLayout( warningLayout );
1064 layout->insertWidget( 4, mWarningLabel );
1065
1066 QWidget *w = new QWidget();
1067 layout->setContentsMargins( 0, 0, 0, 0 );
1068 w->setLayout( layout );
1069
1070 setUnits( distanceDef->defaultUnit() );
1071
1072 return w;
1073 }
1074
1077 return spin;
1078 }
1079 return nullptr;
1080}
1081
1082void QgsProcessingDistanceWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
1083{
1084 QgsProcessingNumericWidgetWrapper::postInitialize( wrappers );
1085 switch ( type() )
1086 {
1088 {
1089 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
1090 {
1091 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDistance *>( parameterDefinition() )->parentParameterName() )
1092 {
1093 setUnitParameterValue( wrapper->parameterValue(), wrapper );
1094 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setUnitParameterValue( wrapper->parameterValue(), wrapper ); } );
1095 break;
1096 }
1097 }
1098 break;
1099 }
1100
1103 break;
1104 }
1105}
1106
1107void QgsProcessingDistanceWidgetWrapper::setUnitParameterValue( const QVariant &value, const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
1108{
1110
1111 // evaluate value to layer
1112 QgsProcessingContext *context = nullptr;
1113 std::unique_ptr<QgsProcessingContext> tmpContext;
1114 if ( mProcessingContextGenerator )
1115 context = mProcessingContextGenerator->processingContext();
1116
1117 if ( !context )
1118 {
1119 tmpContext = std::make_unique<QgsProcessingContext>();
1120 context = tmpContext.get();
1121 }
1122
1123 const QgsCoordinateReferenceSystem crs = wrapper ? QgsProcessingParameters::parameterAsCrs( wrapper->parameterDefinition(), wrapper->parameterValue(), *context )
1124 : QgsProcessingUtils::variantToCrs( value, *context );
1125 if ( crs.isValid() )
1126 {
1127 units = crs.mapUnits();
1128 }
1129
1130 setUnits( units );
1131}
1132
1133void QgsProcessingDistanceWidgetWrapper::setUnits( Qgis::DistanceUnit units )
1134{
1135 mLabel->setText( QgsUnitTypes::toString( units ) );
1137 {
1138 mUnitsCombo->hide();
1139 mLabel->show();
1140 }
1141 else
1142 {
1143 // Only reset the combo if the base units have changed
1144 // or we might loose any user-set unit when changing layer.
1145 // See issue #61470
1146 if ( mBaseUnit != units )
1147 {
1148 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( static_cast<int>( units ) ) );
1149 }
1150
1151 mUnitsCombo->show();
1152 mLabel->hide();
1153 }
1154 mWarningLabel->setVisible( units == Qgis::DistanceUnit::Degrees );
1155 mBaseUnit = units;
1156}
1157
1158QVariant QgsProcessingDistanceWidgetWrapper::widgetValue() const
1159{
1160 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1161 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo && mUnitsCombo->isVisible() )
1162 {
1163 Qgis::DistanceUnit displayUnit = static_cast<Qgis::DistanceUnit>( mUnitsCombo->currentData().toInt() );
1164 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1165 }
1166 else
1167 {
1168 return val;
1169 }
1170}
1171
1172QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDistanceWidgetWrapper::createParameterDefinitionWidget(
1174)
1175{
1176 return new QgsProcessingDistanceParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1177}
1178
1179
1180//
1181// QgsProcessingAreaParameterDefinitionWidget
1182//
1183
1184QgsProcessingAreaParameterDefinitionWidget::QgsProcessingAreaParameterDefinitionWidget(
1185 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1186)
1187 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1188{
1189 QVBoxLayout *vlayout = new QVBoxLayout();
1190 vlayout->setContentsMargins( 0, 0, 0, 0 );
1191
1192 vlayout->addWidget( new QLabel( tr( "Linked input" ) ) );
1193
1194 mParentLayerComboBox = new QComboBox();
1195
1196 QString initialParent;
1197 if ( const QgsProcessingParameterArea *areaParam = dynamic_cast<const QgsProcessingParameterArea *>( definition ) )
1198 initialParent = areaParam->parentParameterName();
1199
1200 if ( auto *lModel = widgetContext.model() )
1201 {
1202 // populate combo box with other model input choices
1203 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
1204 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
1205 {
1206 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1207 {
1208 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1209 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1210 {
1211 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1212 }
1213 }
1214 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1215 {
1216 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1217 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1218 {
1219 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1220 }
1221 }
1222 else if ( const QgsProcessingParameterMapLayer *definition = dynamic_cast<const QgsProcessingParameterMapLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1223 {
1224 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1225 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1226 {
1227 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1228 }
1229 }
1230 else if ( const QgsProcessingParameterCrs *definition = dynamic_cast<const QgsProcessingParameterCrs *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1231 {
1232 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1233 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1234 {
1235 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1236 }
1237 }
1238 }
1239 }
1240
1241 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
1242 {
1243 // if no parent candidates found, we just add the existing one as a placeholder
1244 mParentLayerComboBox->addItem( initialParent, initialParent );
1245 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1246 }
1247
1248 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1249
1250 vlayout->addWidget( mParentLayerComboBox );
1251
1252 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
1253 mMinLineEdit = new QLineEdit();
1254 vlayout->addWidget( mMinLineEdit );
1255
1256 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
1257 mMaxLineEdit = new QLineEdit();
1258 vlayout->addWidget( mMaxLineEdit );
1259
1260 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1261 mDefaultLineEdit = new QLineEdit();
1262 vlayout->addWidget( mDefaultLineEdit );
1263
1264 if ( const QgsProcessingParameterArea *areaParam = dynamic_cast<const QgsProcessingParameterArea *>( definition ) )
1265 {
1266 mMinLineEdit->setText( QLocale().toString( areaParam->minimum() ) );
1267 mMaxLineEdit->setText( QLocale().toString( areaParam->maximum() ) );
1268 mDefaultLineEdit->setText( areaParam->defaultValueForGui().toString() );
1269 }
1270
1271 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1272 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1273 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1274
1275 setLayout( vlayout );
1276}
1277
1278QgsProcessingParameterDefinition *QgsProcessingAreaParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1279{
1280 bool ok;
1281 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
1282
1283 auto param = std::make_unique<QgsProcessingParameterArea>( name, description, ok ? val : QVariant(), mParentLayerComboBox->currentData().toString() );
1284
1285 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
1286 if ( ok )
1287 {
1288 param->setMinimum( val );
1289 }
1290
1291 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
1292 if ( ok )
1293 {
1294 param->setMaximum( val );
1295 }
1296
1297 param->setFlags( flags );
1298 return param.release();
1299}
1300
1301
1302//
1303// QgsProcessingAreaWidgetWrapper
1304//
1305
1306QgsProcessingAreaWidgetWrapper::QgsProcessingAreaWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1307 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1308{}
1309
1310QString QgsProcessingAreaWidgetWrapper::parameterType() const
1311{
1313}
1314
1315QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAreaWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1316{
1317 return new QgsProcessingAreaWidgetWrapper( parameter, type );
1318}
1319
1320QWidget *QgsProcessingAreaWidgetWrapper::createWidget()
1321{
1322 const QgsProcessingParameterArea *areaDef = static_cast<const QgsProcessingParameterArea *>( parameterDefinition() );
1323
1324 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1325 switch ( type() )
1326 {
1328 {
1329 mLabel = new QLabel();
1330 mUnitsCombo = new QComboBox();
1331
1332 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareMeters ), QVariant::fromValue( Qgis::AreaUnit::SquareMeters ) );
1333 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareKilometers ), QVariant::fromValue( Qgis::AreaUnit::SquareKilometers ) );
1334 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareFeet ), QVariant::fromValue( Qgis::AreaUnit::SquareFeet ) );
1335 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareYards ), QVariant::fromValue( Qgis::AreaUnit::SquareYards ) );
1336 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareMiles ), QVariant::fromValue( Qgis::AreaUnit::SquareMiles ) );
1337 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::Hectares ), QVariant::fromValue( Qgis::AreaUnit::Hectares ) );
1338 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::Acres ), QVariant::fromValue( Qgis::AreaUnit::Acres ) );
1340 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareDegrees ), QVariant::fromValue( Qgis::AreaUnit::SquareDegrees ) );
1341 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareCentimeters ), QVariant::fromValue( Qgis::AreaUnit::SquareCentimeters ) );
1342 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareMillimeters ), QVariant::fromValue( Qgis::AreaUnit::SquareMillimeters ) );
1343 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareInches ), QVariant::fromValue( Qgis::AreaUnit::SquareInches ) );
1344
1345 const int labelMargin = static_cast<int>( std::round( mUnitsCombo->fontMetrics().horizontalAdvance( 'X' ) ) );
1346 QHBoxLayout *layout = new QHBoxLayout();
1347 layout->addWidget( spin, 1 );
1348 layout->insertSpacing( 1, labelMargin / 2 );
1349 layout->insertWidget( 2, mLabel );
1350 layout->insertWidget( 3, mUnitsCombo );
1351
1352 // bit of fiddlyness here -- we want the initial spacing to only be visible
1353 // when the warning label is shown, so it's embedded inside mWarningLabel
1354 // instead of outside it
1355 mWarningLabel = new QWidget();
1356 QHBoxLayout *warningLayout = new QHBoxLayout();
1357 warningLayout->setContentsMargins( 0, 0, 0, 0 );
1358 QLabel *warning = new QLabel();
1359 QIcon icon = QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
1360 const int size = static_cast<int>( std::max( 24.0, spin->minimumSize().height() * 0.5 ) );
1361 warning->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
1362 warning->setToolTip( tr( "Area is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results." ) );
1363 warningLayout->insertSpacing( 0, labelMargin / 2 );
1364 warningLayout->insertWidget( 1, warning );
1365 mWarningLabel->setLayout( warningLayout );
1366 layout->insertWidget( 4, mWarningLabel );
1367
1368 QWidget *w = new QWidget();
1369 layout->setContentsMargins( 0, 0, 0, 0 );
1370 w->setLayout( layout );
1371
1372 setUnits( areaDef->defaultUnit() );
1373
1374 return w;
1375 }
1376
1379 return spin;
1380 }
1381 return nullptr;
1382}
1383
1384void QgsProcessingAreaWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
1385{
1386 QgsProcessingNumericWidgetWrapper::postInitialize( wrappers );
1387 switch ( type() )
1388 {
1390 {
1391 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
1392 {
1393 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterArea *>( parameterDefinition() )->parentParameterName() )
1394 {
1395 setUnitParameterValue( wrapper->parameterValue(), wrapper );
1396 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setUnitParameterValue( wrapper->parameterValue(), wrapper ); } );
1397 break;
1398 }
1399 }
1400 break;
1401 }
1402
1405 break;
1406 }
1407}
1408
1409void QgsProcessingAreaWidgetWrapper::setUnitParameterValue( const QVariant &value, const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
1410{
1412
1413 // evaluate value to layer
1414 QgsProcessingContext *context = nullptr;
1415 std::unique_ptr<QgsProcessingContext> tmpContext;
1416 if ( mProcessingContextGenerator )
1417 context = mProcessingContextGenerator->processingContext();
1418
1419 if ( !context )
1420 {
1421 tmpContext = std::make_unique<QgsProcessingContext>();
1422 context = tmpContext.get();
1423 }
1424
1425 const QgsCoordinateReferenceSystem crs = wrapper ? QgsProcessingParameters::parameterAsCrs( wrapper->parameterDefinition(), wrapper->parameterValue(), *context )
1426 : QgsProcessingUtils::variantToCrs( value, *context );
1427 if ( crs.isValid() )
1428 {
1429 units = QgsUnitTypes::distanceToAreaUnit( crs.mapUnits() );
1430 }
1431
1432 setUnits( units );
1433}
1434
1435void QgsProcessingAreaWidgetWrapper::setUnits( Qgis::AreaUnit units )
1436{
1437 mLabel->setText( QgsUnitTypes::toString( units ) );
1439 {
1440 mUnitsCombo->hide();
1441 mLabel->show();
1442 }
1443 else
1444 {
1445 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QVariant::fromValue( units ) ) );
1446 mUnitsCombo->show();
1447 mLabel->hide();
1448 }
1449 mWarningLabel->setVisible( units == Qgis::AreaUnit::SquareDegrees );
1450 mBaseUnit = units;
1451}
1452
1453QVariant QgsProcessingAreaWidgetWrapper::widgetValue() const
1454{
1455 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1456 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo && mUnitsCombo->isVisible() )
1457 {
1458 const Qgis::AreaUnit displayUnit = mUnitsCombo->currentData().value<Qgis::AreaUnit>();
1459 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1460 }
1461 else
1462 {
1463 return val;
1464 }
1465}
1466
1467QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAreaWidgetWrapper::createParameterDefinitionWidget(
1469)
1470{
1471 return new QgsProcessingAreaParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1472}
1473
1474
1475//
1476// QgsProcessingVolumeParameterDefinitionWidget
1477//
1478
1479QgsProcessingVolumeParameterDefinitionWidget::QgsProcessingVolumeParameterDefinitionWidget(
1480 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1481)
1482 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1483{
1484 QVBoxLayout *vlayout = new QVBoxLayout();
1485 vlayout->setContentsMargins( 0, 0, 0, 0 );
1486
1487 vlayout->addWidget( new QLabel( tr( "Linked input" ) ) );
1488
1489 mParentLayerComboBox = new QComboBox();
1490
1491 QString initialParent;
1492 if ( const QgsProcessingParameterVolume *volumeParam = dynamic_cast<const QgsProcessingParameterVolume *>( definition ) )
1493 initialParent = volumeParam->parentParameterName();
1494
1495 if ( auto *lModel = widgetContext.model() )
1496 {
1497 // populate combo box with other model input choices
1498 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
1499 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
1500 {
1501 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1502 {
1503 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1504 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1505 {
1506 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1507 }
1508 }
1509 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1510 {
1511 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1512 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1513 {
1514 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1515 }
1516 }
1517 else if ( const QgsProcessingParameterMapLayer *definition = dynamic_cast<const QgsProcessingParameterMapLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1518 {
1519 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1520 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1521 {
1522 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1523 }
1524 }
1525 else if ( const QgsProcessingParameterCrs *definition = dynamic_cast<const QgsProcessingParameterCrs *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1526 {
1527 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1528 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1529 {
1530 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1531 }
1532 }
1533 }
1534 }
1535
1536 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
1537 {
1538 // if no parent candidates found, we just add the existing one as a placeholder
1539 mParentLayerComboBox->addItem( initialParent, initialParent );
1540 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1541 }
1542
1543 vlayout->addWidget( mParentLayerComboBox );
1544
1545 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
1546 mMinLineEdit = new QLineEdit();
1547 vlayout->addWidget( mMinLineEdit );
1548
1549 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
1550 mMaxLineEdit = new QLineEdit();
1551 vlayout->addWidget( mMaxLineEdit );
1552
1553 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1554 mDefaultLineEdit = new QLineEdit();
1555 vlayout->addWidget( mDefaultLineEdit );
1556
1557 if ( const QgsProcessingParameterVolume *volumeParam = dynamic_cast<const QgsProcessingParameterVolume *>( definition ) )
1558 {
1559 mMinLineEdit->setText( QLocale().toString( volumeParam->minimum() ) );
1560 mMaxLineEdit->setText( QLocale().toString( volumeParam->maximum() ) );
1561 mDefaultLineEdit->setText( volumeParam->defaultValueForGui().toString() );
1562 }
1563
1564 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1565 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1566 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1567 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1568
1569 setLayout( vlayout );
1570}
1571
1572QgsProcessingParameterDefinition *QgsProcessingVolumeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1573{
1574 bool ok;
1575 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
1576
1577 auto param = std::make_unique<QgsProcessingParameterVolume>( name, description, ok ? val : QVariant(), mParentLayerComboBox->currentData().toString() );
1578
1579 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
1580 if ( ok )
1581 {
1582 param->setMinimum( val );
1583 }
1584
1585 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
1586 if ( ok )
1587 {
1588 param->setMaximum( val );
1589 }
1590
1591 param->setFlags( flags );
1592 return param.release();
1593}
1594
1595
1596//
1597// QgsProcessingVolumeWidgetWrapper
1598//
1599
1600QgsProcessingVolumeWidgetWrapper::QgsProcessingVolumeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1601 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1602{}
1603
1604QString QgsProcessingVolumeWidgetWrapper::parameterType() const
1605{
1607}
1608
1609QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVolumeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1610{
1611 return new QgsProcessingVolumeWidgetWrapper( parameter, type );
1612}
1613
1614QWidget *QgsProcessingVolumeWidgetWrapper::createWidget()
1615{
1616 const QgsProcessingParameterVolume *volumeDef = static_cast<const QgsProcessingParameterVolume *>( parameterDefinition() );
1617
1618 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1619 switch ( type() )
1620 {
1622 {
1623 mLabel = new QLabel();
1624 mUnitsCombo = new QComboBox();
1625
1626 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicMeters ), QVariant::fromValue( Qgis::VolumeUnit::CubicMeters ) );
1627 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicFeet ), QVariant::fromValue( Qgis::VolumeUnit::CubicFeet ) );
1628 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicYards ), QVariant::fromValue( Qgis::VolumeUnit::CubicYards ) );
1629 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::Barrel ), QVariant::fromValue( Qgis::VolumeUnit::Barrel ) );
1630 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicDecimeter ), QVariant::fromValue( Qgis::VolumeUnit::CubicDecimeter ) );
1631 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::Liters ), QVariant::fromValue( Qgis::VolumeUnit::Liters ) );
1632 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::GallonUS ), QVariant::fromValue( Qgis::VolumeUnit::GallonUS ) );
1633 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicInch ), QVariant::fromValue( Qgis::VolumeUnit::CubicInch ) );
1634 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicCentimeter ), QVariant::fromValue( Qgis::VolumeUnit::CubicCentimeter ) );
1635 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicDegrees ), QVariant::fromValue( Qgis::VolumeUnit::CubicDegrees ) );
1636
1637 const int labelMargin = static_cast<int>( std::round( mUnitsCombo->fontMetrics().horizontalAdvance( 'X' ) ) );
1638 QHBoxLayout *layout = new QHBoxLayout();
1639 layout->addWidget( spin, 1 );
1640 layout->insertSpacing( 1, labelMargin / 2 );
1641 layout->insertWidget( 2, mLabel );
1642 layout->insertWidget( 3, mUnitsCombo );
1643
1644 // bit of fiddlyness here -- we want the initial spacing to only be visible
1645 // when the warning label is shown, so it's embedded inside mWarningLabel
1646 // instead of outside it
1647 mWarningLabel = new QWidget();
1648 QHBoxLayout *warningLayout = new QHBoxLayout();
1649 warningLayout->setContentsMargins( 0, 0, 0, 0 );
1650 QLabel *warning = new QLabel();
1651 QIcon icon = QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
1652 const int size = static_cast<int>( std::max( 24.0, spin->minimumSize().height() * 0.5 ) );
1653 warning->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
1654 warning->setToolTip( tr( "Volume is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results." ) );
1655 warningLayout->insertSpacing( 0, labelMargin / 2 );
1656 warningLayout->insertWidget( 1, warning );
1657 mWarningLabel->setLayout( warningLayout );
1658 layout->insertWidget( 4, mWarningLabel );
1659
1660 QWidget *w = new QWidget();
1661 layout->setContentsMargins( 0, 0, 0, 0 );
1662 w->setLayout( layout );
1663
1664 setUnits( volumeDef->defaultUnit() );
1665
1666 return w;
1667 }
1668
1671 return spin;
1672 }
1673 return nullptr;
1674}
1675
1676void QgsProcessingVolumeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
1677{
1678 QgsProcessingNumericWidgetWrapper::postInitialize( wrappers );
1679 switch ( type() )
1680 {
1682 {
1683 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
1684 {
1685 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterVolume *>( parameterDefinition() )->parentParameterName() )
1686 {
1687 setUnitParameterValue( wrapper->parameterValue(), wrapper );
1688 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setUnitParameterValue( wrapper->parameterValue(), wrapper ); } );
1689 break;
1690 }
1691 }
1692 break;
1693 }
1694
1697 break;
1698 }
1699}
1700
1701void QgsProcessingVolumeWidgetWrapper::setUnitParameterValue( const QVariant &value, const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
1702{
1704
1705 // evaluate value to layer
1706 QgsProcessingContext *context = nullptr;
1707 std::unique_ptr<QgsProcessingContext> tmpContext;
1708 if ( mProcessingContextGenerator )
1709 context = mProcessingContextGenerator->processingContext();
1710
1711 if ( !context )
1712 {
1713 tmpContext = std::make_unique<QgsProcessingContext>();
1714 context = tmpContext.get();
1715 }
1716
1717 const QgsCoordinateReferenceSystem crs = wrapper ? QgsProcessingParameters::parameterAsCrs( wrapper->parameterDefinition(), wrapper->parameterValue(), *context )
1718 : QgsProcessingUtils::variantToCrs( value, *context );
1719 if ( crs.isValid() )
1720 {
1721 units = QgsUnitTypes::distanceToVolumeUnit( crs.mapUnits() );
1722 }
1723
1724 setUnits( units );
1725}
1726
1727void QgsProcessingVolumeWidgetWrapper::setUnits( Qgis::VolumeUnit units )
1728{
1729 mLabel->setText( QgsUnitTypes::toString( units ) );
1731 {
1732 mUnitsCombo->hide();
1733 mLabel->show();
1734 }
1735 else
1736 {
1737 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QVariant::fromValue( units ) ) );
1738 mUnitsCombo->show();
1739 mLabel->hide();
1740 }
1741 mWarningLabel->setVisible( units == Qgis::VolumeUnit::CubicDegrees );
1742 mBaseUnit = units;
1743}
1744
1745QVariant QgsProcessingVolumeWidgetWrapper::widgetValue() const
1746{
1747 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1748 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo && mUnitsCombo->isVisible() )
1749 {
1750 const Qgis::VolumeUnit displayUnit = mUnitsCombo->currentData().value<Qgis::VolumeUnit>();
1751 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1752 }
1753 else
1754 {
1755 return val;
1756 }
1757}
1758
1759QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingVolumeWidgetWrapper::createParameterDefinitionWidget(
1761)
1762{
1763 return new QgsProcessingVolumeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1764}
1765
1766
1767//
1768// QgsProcessingDurationWidgetWrapper
1769//
1770
1771QgsProcessingDurationParameterDefinitionWidget::QgsProcessingDurationParameterDefinitionWidget(
1772 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1773)
1774 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1775{
1776 QVBoxLayout *vlayout = new QVBoxLayout();
1777 vlayout->setContentsMargins( 0, 0, 0, 0 );
1778
1779 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
1780 mMinLineEdit = new QLineEdit();
1781 vlayout->addWidget( mMinLineEdit );
1782
1783 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
1784 mMaxLineEdit = new QLineEdit();
1785 vlayout->addWidget( mMaxLineEdit );
1786
1787 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1788 mDefaultLineEdit = new QLineEdit();
1789 vlayout->addWidget( mDefaultLineEdit );
1790
1791 vlayout->addWidget( new QLabel( tr( "Default unit type" ) ) );
1792
1793 mUnitsCombo = new QComboBox();
1795 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Seconds ), static_cast<int>( Qgis::TemporalUnit::Seconds ) );
1796 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Minutes ), static_cast<int>( Qgis::TemporalUnit::Minutes ) );
1797 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Hours ), static_cast<int>( Qgis::TemporalUnit::Hours ) );
1798 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Days ), static_cast<int>( Qgis::TemporalUnit::Days ) );
1799 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Weeks ), static_cast<int>( Qgis::TemporalUnit::Weeks ) );
1800 mUnitsCombo->addItem( tr( "years (365.25 days)" ), static_cast<int>( Qgis::TemporalUnit::Years ) );
1801 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Decades ), static_cast<int>( Qgis::TemporalUnit::Decades ) );
1802 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Centuries ), static_cast<int>( Qgis::TemporalUnit::Centuries ) );
1803 vlayout->addWidget( mUnitsCombo );
1804
1805 connect( mUnitsCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1806
1807 if ( const QgsProcessingParameterDuration *durationParam = dynamic_cast<const QgsProcessingParameterDuration *>( definition ) )
1808 {
1809 mMinLineEdit->setText( QLocale().toString( durationParam->minimum() ) );
1810 mMaxLineEdit->setText( QLocale().toString( durationParam->maximum() ) );
1811 mDefaultLineEdit->setText( durationParam->defaultValueForGui().toString() );
1812 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( static_cast<int>( durationParam->defaultUnit() ) ) );
1813 }
1814 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1815 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1816 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1817
1818 setLayout( vlayout );
1819}
1820
1821QgsProcessingParameterDefinition *QgsProcessingDurationParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1822{
1823 bool ok;
1824 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
1825
1826 auto param = std::make_unique<QgsProcessingParameterDuration>( name, description, ok ? val : QVariant() );
1827
1828 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
1829 if ( ok )
1830 {
1831 param->setMinimum( val );
1832 }
1833
1834 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
1835 if ( ok )
1836 {
1837 param->setMaximum( val );
1838 }
1839
1840 param->setDefaultUnit( static_cast<Qgis::TemporalUnit>( mUnitsCombo->currentData().toInt() ) );
1841
1842 param->setFlags( flags );
1843 return param.release();
1844}
1845
1846QgsProcessingDurationWidgetWrapper::QgsProcessingDurationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1847 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1848{}
1849
1850QString QgsProcessingDurationWidgetWrapper::parameterType() const
1851{
1853}
1854
1855QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDurationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1856{
1857 return new QgsProcessingDurationWidgetWrapper( parameter, type );
1858}
1859
1860QWidget *QgsProcessingDurationWidgetWrapper::createWidget()
1861{
1862 const QgsProcessingParameterDuration *durationDef = static_cast<const QgsProcessingParameterDuration *>( parameterDefinition() );
1863
1864 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1865 switch ( type() )
1866 {
1868 {
1869 mUnitsCombo = new QComboBox();
1870
1872 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Seconds ), static_cast<int>( Qgis::TemporalUnit::Seconds ) );
1873 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Minutes ), static_cast<int>( Qgis::TemporalUnit::Minutes ) );
1874 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Hours ), static_cast<int>( Qgis::TemporalUnit::Hours ) );
1875 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Days ), static_cast<int>( Qgis::TemporalUnit::Days ) );
1876 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Weeks ), static_cast<int>( Qgis::TemporalUnit::Weeks ) );
1877 mUnitsCombo->addItem( tr( "years (365.25 days)" ), static_cast<int>( Qgis::TemporalUnit::Years ) );
1878 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Decades ), static_cast<int>( Qgis::TemporalUnit::Decades ) );
1879 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Centuries ), static_cast<int>( Qgis::TemporalUnit::Centuries ) );
1880
1881 QHBoxLayout *layout = new QHBoxLayout();
1882 layout->addWidget( spin, 1 );
1883 layout->insertWidget( 1, mUnitsCombo );
1884
1885 QWidget *w = new QWidget();
1886 layout->setContentsMargins( 0, 0, 0, 0 );
1887 w->setLayout( layout );
1888
1889 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( static_cast<int>( durationDef->defaultUnit() ) ) );
1890 mUnitsCombo->show();
1891
1892 return w;
1893 }
1894
1897 return spin;
1898 }
1899 return nullptr;
1900}
1901
1902QLabel *QgsProcessingDurationWidgetWrapper::createLabel()
1903{
1905
1906 if ( type() == Qgis::ProcessingMode::Modeler )
1907 {
1908 label->setText( u"%1 [%2]"_s.arg( label->text(), QgsUnitTypes::toString( mBaseUnit ) ) );
1909 }
1910
1911 return label;
1912}
1913
1914QVariant QgsProcessingDurationWidgetWrapper::widgetValue() const
1915{
1916 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1917 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo )
1918 {
1919 Qgis::TemporalUnit displayUnit = static_cast<Qgis::TemporalUnit>( mUnitsCombo->currentData().toInt() );
1920 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1921 }
1922 else
1923 {
1924 return val;
1925 }
1926}
1927
1928void QgsProcessingDurationWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
1929{
1930 if ( mUnitsCombo )
1931 {
1932 Qgis::TemporalUnit displayUnit = static_cast<Qgis::TemporalUnit>( mUnitsCombo->currentData().toInt() );
1933 const QVariant val = value.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( mBaseUnit, displayUnit );
1934 QgsProcessingNumericWidgetWrapper::setWidgetValue( val, context );
1935 }
1936 else
1937 {
1938 QgsProcessingNumericWidgetWrapper::setWidgetValue( value, context );
1939 }
1940}
1941
1942QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDurationWidgetWrapper::createParameterDefinitionWidget(
1944)
1945{
1946 return new QgsProcessingDurationParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1947}
1948
1949//
1950// QgsProcessingScaleWidgetWrapper
1951//
1952
1953QgsProcessingScaleParameterDefinitionWidget::QgsProcessingScaleParameterDefinitionWidget(
1954 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1955)
1956 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1957{
1958 QVBoxLayout *vlayout = new QVBoxLayout();
1959 vlayout->setContentsMargins( 0, 0, 0, 0 );
1960
1961 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1962
1963 mDefaultLineEdit = new QLineEdit();
1964
1965 if ( const QgsProcessingParameterScale *scaleParam = dynamic_cast<const QgsProcessingParameterScale *>( definition ) )
1966 {
1967 mDefaultLineEdit->setText( scaleParam->defaultValueForGui().toString() );
1968 }
1969
1970 vlayout->addWidget( mDefaultLineEdit );
1971 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1972
1973 setLayout( vlayout );
1974}
1975
1976QgsProcessingParameterDefinition *QgsProcessingScaleParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1977{
1978 bool ok;
1979 double val = mDefaultLineEdit->text().toDouble( &ok );
1980 auto param = std::make_unique<QgsProcessingParameterScale>( name, description, ok ? val : QVariant() );
1981 param->setFlags( flags );
1982 return param.release();
1983}
1984
1985QgsProcessingScaleWidgetWrapper::QgsProcessingScaleWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1986 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1987{}
1988
1989QString QgsProcessingScaleWidgetWrapper::parameterType() const
1990{
1992}
1993
1994QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingScaleWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1995{
1996 return new QgsProcessingScaleWidgetWrapper( parameter, type );
1997}
1998
1999QWidget *QgsProcessingScaleWidgetWrapper::createWidget()
2000{
2001 const QgsProcessingParameterScale *scaleDef = static_cast<const QgsProcessingParameterScale *>( parameterDefinition() );
2002
2003 switch ( type() )
2004 {
2008 {
2009 mScaleWidget = new QgsScaleWidget( nullptr );
2011 mScaleWidget->setAllowNull( true );
2012
2013 mScaleWidget->setMapCanvas( widgetContext().mapCanvas() );
2014 mScaleWidget->setShowCurrentScaleButton( true );
2015
2016 mScaleWidget->setToolTip( parameterDefinition()->toolTip() );
2017 connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, [this]( double ) { emit widgetValueHasChanged( this ); } );
2018 return mScaleWidget;
2019 }
2020 }
2021 return nullptr;
2022}
2023
2024void QgsProcessingScaleWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
2025{
2026 if ( mScaleWidget )
2027 mScaleWidget->setMapCanvas( context.mapCanvas() );
2029}
2030
2031
2032QVariant QgsProcessingScaleWidgetWrapper::widgetValue() const
2033{
2034 return mScaleWidget && !mScaleWidget->isNull() ? QVariant( mScaleWidget->scale() ) : QVariant();
2035}
2036
2037void QgsProcessingScaleWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2038{
2039 if ( mScaleWidget )
2040 {
2041 if ( mScaleWidget->allowNull() && !value.isValid() )
2042 mScaleWidget->setNull();
2043 else
2044 {
2045 const double v = QgsProcessingParameters::parameterAsDouble( parameterDefinition(), value, context );
2046 mScaleWidget->setScale( v );
2047 }
2048 }
2049}
2050
2051QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingScaleWidgetWrapper::createParameterDefinitionWidget(
2053)
2054{
2055 return new QgsProcessingScaleParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2056}
2057
2058
2059//
2060// QgsProcessingRangeWidgetWrapper
2061//
2062
2063QgsProcessingRangeParameterDefinitionWidget::QgsProcessingRangeParameterDefinitionWidget(
2064 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2065)
2066 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2067{
2068 QVBoxLayout *vlayout = new QVBoxLayout();
2069 vlayout->setContentsMargins( 0, 0, 0, 0 );
2070
2071 vlayout->addWidget( new QLabel( tr( "Number type" ) ) );
2072
2073 mTypeComboBox = new QComboBox();
2074 mTypeComboBox->addItem( tr( "Float" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Double ) );
2075 mTypeComboBox->addItem( tr( "Integer" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Integer ) );
2076 vlayout->addWidget( mTypeComboBox );
2077
2078 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
2079 mMinLineEdit = new QLineEdit();
2080 vlayout->addWidget( mMinLineEdit );
2081
2082 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
2083 mMaxLineEdit = new QLineEdit();
2084 vlayout->addWidget( mMaxLineEdit );
2085
2086 if ( const QgsProcessingParameterRange *rangeParam = dynamic_cast<const QgsProcessingParameterRange *>( definition ) )
2087 {
2088 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( rangeParam->dataType() ) ) );
2089 const QList<double> range = QgsProcessingParameters::parameterAsRange( rangeParam, rangeParam->defaultValueForGui(), context );
2090 mMinLineEdit->setText( QLocale().toString( range.at( 0 ) ) );
2091 mMaxLineEdit->setText( QLocale().toString( range.at( 1 ) ) );
2092 }
2093
2094 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2095 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2096 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2097
2098 setLayout( vlayout );
2099}
2100
2101QgsProcessingParameterDefinition *QgsProcessingRangeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2102{
2103 QString defaultValue;
2104 if ( mMinLineEdit->text().isEmpty() )
2105 {
2106 defaultValue = u"None"_s;
2107 }
2108 else
2109 {
2110 bool ok;
2111 defaultValue = QString::number( QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok ) );
2112 if ( !ok )
2113 {
2114 defaultValue = u"None"_s;
2115 }
2116 }
2117
2118 if ( mMaxLineEdit->text().isEmpty() )
2119 {
2120 defaultValue += ",None"_L1;
2121 }
2122 else
2123 {
2124 bool ok;
2125 const double val { QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok ) };
2126 defaultValue += u",%1"_s.arg( ok ? QString::number( val ) : "None"_L1 );
2127 }
2128
2129 Qgis::ProcessingNumberParameterType dataType = static_cast<Qgis::ProcessingNumberParameterType>( mTypeComboBox->currentData().toInt() );
2130 auto param = std::make_unique<QgsProcessingParameterRange>( name, description, dataType, defaultValue );
2131 param->setFlags( flags );
2132 return param.release();
2133}
2134
2135
2136QgsProcessingRangeWidgetWrapper::QgsProcessingRangeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2137 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2138{}
2139
2140QWidget *QgsProcessingRangeWidgetWrapper::createWidget()
2141{
2142 const QgsProcessingParameterRange *rangeDef = static_cast<const QgsProcessingParameterRange *>( parameterDefinition() );
2143 switch ( type() )
2144 {
2148 {
2149 QHBoxLayout *layout = new QHBoxLayout();
2150
2151 mMinSpinBox = new QgsDoubleSpinBox();
2152 mMaxSpinBox = new QgsDoubleSpinBox();
2153
2154 mMinSpinBox->setExpressionsEnabled( true );
2155 mMinSpinBox->setShowClearButton( false );
2156 mMaxSpinBox->setExpressionsEnabled( true );
2157 mMaxSpinBox->setShowClearButton( false );
2158
2159 QLabel *minLabel = new QLabel( tr( "Min" ) );
2160 layout->addWidget( minLabel );
2161 layout->addWidget( mMinSpinBox, 1 );
2162
2163 QLabel *maxLabel = new QLabel( tr( "Max" ) );
2164 layout->addWidget( maxLabel );
2165 layout->addWidget( mMaxSpinBox, 1 );
2166
2167 QWidget *w = new QWidget();
2168 layout->setContentsMargins( 0, 0, 0, 0 );
2169 w->setLayout( layout );
2170
2172 {
2173 mMinSpinBox->setDecimals( 6 );
2174 mMaxSpinBox->setDecimals( 6 );
2175 }
2176 else
2177 {
2178 mMinSpinBox->setDecimals( 0 );
2179 mMaxSpinBox->setDecimals( 0 );
2180 }
2181
2182 mMinSpinBox->setMinimum( -99999999.999999 );
2183 mMaxSpinBox->setMinimum( -99999999.999999 );
2184 mMinSpinBox->setMaximum( 99999999.999999 );
2185 mMaxSpinBox->setMaximum( 99999999.999999 );
2186
2188 {
2189 mAllowingNull = true;
2190
2191 const double min = mMinSpinBox->minimum() - 1;
2192 mMinSpinBox->setMinimum( min );
2193 mMaxSpinBox->setMinimum( min );
2194 mMinSpinBox->setValue( min );
2195 mMaxSpinBox->setValue( min );
2196
2197 mMinSpinBox->setShowClearButton( true );
2198 mMaxSpinBox->setShowClearButton( true );
2199 mMinSpinBox->setSpecialValueText( tr( "Not set" ) );
2200 mMaxSpinBox->setSpecialValueText( tr( "Not set" ) );
2201 }
2202
2203 w->setToolTip( parameterDefinition()->toolTip() );
2204
2205 connect( mMinSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this]( const double v ) {
2206 mBlockChangedSignal++;
2207 if ( !mAllowingNull && v > mMaxSpinBox->value() )
2208 mMaxSpinBox->setValue( v );
2209 mBlockChangedSignal--;
2210
2211 if ( !mBlockChangedSignal )
2212 emit widgetValueHasChanged( this );
2213 } );
2214 connect( mMaxSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this]( const double v ) {
2215 mBlockChangedSignal++;
2216 if ( !mAllowingNull && v < mMinSpinBox->value() )
2217 mMinSpinBox->setValue( v );
2218 mBlockChangedSignal--;
2219
2220 if ( !mBlockChangedSignal )
2221 emit widgetValueHasChanged( this );
2222 } );
2223
2224 return w;
2225 }
2226 }
2227 return nullptr;
2228}
2229
2230void QgsProcessingRangeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2231{
2232 const QList<double> v = QgsProcessingParameters::parameterAsRange( parameterDefinition(), value, context );
2233 if ( mAllowingNull && v.empty() )
2234 {
2235 mMinSpinBox->clear();
2236 mMaxSpinBox->clear();
2237 }
2238 else
2239 {
2240 if ( v.empty() )
2241 return;
2242
2243 if ( mAllowingNull )
2244 {
2245 mBlockChangedSignal++;
2246 if ( std::isnan( v.at( 0 ) ) )
2247 mMinSpinBox->clear();
2248 else
2249 mMinSpinBox->setValue( v.at( 0 ) );
2250
2251 if ( v.count() >= 2 )
2252 {
2253 if ( std::isnan( v.at( 1 ) ) )
2254 mMaxSpinBox->clear();
2255 else
2256 mMaxSpinBox->setValue( v.at( 1 ) );
2257 }
2258 mBlockChangedSignal--;
2259 }
2260 else
2261 {
2262 mBlockChangedSignal++;
2263 mMinSpinBox->setValue( v.at( 0 ) );
2264 if ( v.count() >= 2 )
2265 mMaxSpinBox->setValue( v.at( 1 ) );
2266 mBlockChangedSignal--;
2267 }
2268 }
2269
2270 if ( !mBlockChangedSignal )
2271 emit widgetValueHasChanged( this );
2272}
2273
2274QVariant QgsProcessingRangeWidgetWrapper::widgetValue() const
2275{
2276 if ( mAllowingNull )
2277 {
2278 QString value;
2279 if ( qgsDoubleNear( mMinSpinBox->value(), mMinSpinBox->minimum() ) )
2280 value = u"None"_s;
2281 else
2282 value = QString::number( mMinSpinBox->value() );
2283
2284 if ( qgsDoubleNear( mMaxSpinBox->value(), mMaxSpinBox->minimum() ) )
2285 value += ",None"_L1;
2286 else
2287 value += u",%1"_s.arg( mMaxSpinBox->value() );
2288
2289 return value;
2290 }
2291 else
2292 return u"%1,%2"_s.arg( mMinSpinBox->value() ).arg( mMaxSpinBox->value() );
2293}
2294
2295QString QgsProcessingRangeWidgetWrapper::modelerExpressionFormatString() const
2296{
2297 return tr( "string as two comma delimited floats, e.g. '1,10'" );
2298}
2299
2300QString QgsProcessingRangeWidgetWrapper::parameterType() const
2301{
2303}
2304
2305QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingRangeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
2306{
2307 return new QgsProcessingRangeWidgetWrapper( parameter, type );
2308}
2309
2310QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingRangeWidgetWrapper::createParameterDefinitionWidget(
2312)
2313{
2314 return new QgsProcessingRangeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2315}
2316
2317
2318//
2319// QgsProcessingMatrixWidgetWrapper
2320//
2321
2322QgsProcessingMatrixParameterDefinitionWidget::QgsProcessingMatrixParameterDefinitionWidget(
2323 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2324)
2325 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2326{
2327 QVBoxLayout *vlayout = new QVBoxLayout();
2328 vlayout->setContentsMargins( 0, 0, 0, 0 );
2329
2330 mMatrixWidget = new QgsProcessingMatrixModelerWidget();
2331 if ( const QgsProcessingParameterMatrix *matrixParam = dynamic_cast<const QgsProcessingParameterMatrix *>( definition ) )
2332 {
2333 mMatrixWidget->setValue( matrixParam->headers(), matrixParam->defaultValueForGui() );
2334 mMatrixWidget->setFixedRows( matrixParam->hasFixedNumberRows() );
2335 }
2336 vlayout->addWidget( mMatrixWidget );
2337
2338 connect( mMatrixWidget, &QgsProcessingMatrixModelerWidget::changed, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2339
2340 setLayout( vlayout );
2341}
2342
2343QgsProcessingParameterDefinition *QgsProcessingMatrixParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2344{
2345 auto param = std::make_unique<QgsProcessingParameterMatrix>( name, description, 1, mMatrixWidget->fixedRows(), mMatrixWidget->headers(), mMatrixWidget->value() );
2346 param->setFlags( flags );
2347 return param.release();
2348}
2349
2350
2351QgsProcessingMatrixWidgetWrapper::QgsProcessingMatrixWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2352 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2353{}
2354
2355QWidget *QgsProcessingMatrixWidgetWrapper::createWidget()
2356{
2357 mMatrixWidget = new QgsProcessingMatrixParameterPanel( nullptr, dynamic_cast<const QgsProcessingParameterMatrix *>( parameterDefinition() ) );
2358 mMatrixWidget->setToolTip( parameterDefinition()->toolTip() );
2359
2360 connect( mMatrixWidget, &QgsProcessingMatrixParameterPanel::changed, this, [this] { emit widgetValueHasChanged( this ); } );
2361
2362 switch ( type() )
2363 {
2367 {
2368 return mMatrixWidget;
2369 }
2370 }
2371 return nullptr;
2372}
2373
2374void QgsProcessingMatrixWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2375{
2376 const QVariantList v = QgsProcessingParameters::parameterAsMatrix( parameterDefinition(), value, context );
2377 if ( mMatrixWidget )
2378 mMatrixWidget->setValue( v );
2379}
2380
2381QVariant QgsProcessingMatrixWidgetWrapper::widgetValue() const
2382{
2383 if ( mMatrixWidget )
2384 return mMatrixWidget->value().isEmpty() ? QVariant() : mMatrixWidget->value();
2385 else
2386 return QVariant();
2387}
2388
2389QString QgsProcessingMatrixWidgetWrapper::modelerExpressionFormatString() const
2390{
2391 return tr( "comma delimited string of values, or an array of values" );
2392}
2393
2394QString QgsProcessingMatrixWidgetWrapper::parameterType() const
2395{
2397}
2398
2399QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMatrixWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
2400{
2401 return new QgsProcessingMatrixWidgetWrapper( parameter, type );
2402}
2403
2404QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMatrixWidgetWrapper::createParameterDefinitionWidget(
2406)
2407{
2408 return new QgsProcessingMatrixParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2409}
2410
2411
2412//
2413// QgsProcessingFileWidgetWrapper
2414//
2415
2416
2417QgsProcessingFileParameterDefinitionWidget::QgsProcessingFileParameterDefinitionWidget(
2418 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2419)
2420 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2421{
2422 QVBoxLayout *vlayout = new QVBoxLayout();
2423 vlayout->setContentsMargins( 0, 0, 0, 0 );
2424
2425 vlayout->addWidget( new QLabel( tr( "Type" ) ) );
2426
2427 mTypeComboBox = new QComboBox();
2428 mTypeComboBox->addItem( tr( "File" ), static_cast<int>( Qgis::ProcessingFileParameterBehavior::File ) );
2429 mTypeComboBox->addItem( tr( "Folder" ), static_cast<int>( Qgis::ProcessingFileParameterBehavior::Folder ) );
2430 if ( const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( definition ) )
2431 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( fileParam->behavior() ) ) );
2432 else
2433 mTypeComboBox->setCurrentIndex( 0 );
2434 vlayout->addWidget( mTypeComboBox );
2435
2436 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2437
2438 vlayout->addWidget( new QLabel( tr( "File filter" ) ) );
2439
2440 mFilterComboBox = new QComboBox();
2441 mFilterComboBox->setEditable( true );
2442 // add some standard ones -- these also act as a demonstration of the required format
2443 mFilterComboBox->addItem( tr( "All Files (*.*)" ) );
2444 mFilterComboBox->addItem( tr( "CSV Files (*.csv)" ) );
2445 mFilterComboBox->addItem( tr( "HTML Files (*.html *.htm)" ) );
2446 mFilterComboBox->addItem( tr( "Text Files (*.txt)" ) );
2447 if ( const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( definition ) )
2448 mFilterComboBox->setCurrentText( fileParam->fileFilter() );
2449 else
2450 mFilterComboBox->setCurrentIndex( 0 );
2451 vlayout->addWidget( mFilterComboBox );
2452
2453 connect( mFilterComboBox, &QComboBox::currentTextChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2454
2455 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
2456
2457 mDefaultFileWidget = new QgsFileWidget();
2458 mDefaultFileWidget->lineEdit()->setShowClearButton( true );
2459 if ( const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( definition ) )
2460 {
2461 mDefaultFileWidget->setStorageMode( fileParam->behavior() == Qgis::ProcessingFileParameterBehavior::File ? QgsFileWidget::GetFile : QgsFileWidget::GetDirectory );
2462 mDefaultFileWidget->setFilePath( fileParam->defaultValueForGui().toString() );
2463 }
2464 else
2465 mDefaultFileWidget->setStorageMode( QgsFileWidget::GetFile );
2466 vlayout->addWidget( mDefaultFileWidget );
2468
2469 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
2470 Qgis::ProcessingFileParameterBehavior behavior = static_cast<Qgis::ProcessingFileParameterBehavior>( mTypeComboBox->currentData().toInt() );
2471 mFilterComboBox->setEnabled( behavior == Qgis::ProcessingFileParameterBehavior::File );
2472 mDefaultFileWidget->setStorageMode( behavior == Qgis::ProcessingFileParameterBehavior::File ? QgsFileWidget::GetFile : QgsFileWidget::GetDirectory );
2473 } );
2474 mFilterComboBox->setEnabled( static_cast<Qgis::ProcessingFileParameterBehavior>( mTypeComboBox->currentData().toInt() ) == Qgis::ProcessingFileParameterBehavior::File );
2475
2476
2477 setLayout( vlayout );
2478}
2479
2480QgsProcessingParameterDefinition *QgsProcessingFileParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2481{
2482 auto param = std::make_unique<QgsProcessingParameterFile>( name, description );
2483 param->setBehavior( static_cast<Qgis::ProcessingFileParameterBehavior>( mTypeComboBox->currentData().toInt() ) );
2484 if ( param->behavior() == Qgis::ProcessingFileParameterBehavior::File )
2485 param->setFileFilter( mFilterComboBox->currentText() );
2486 if ( !mDefaultFileWidget->filePath().isEmpty() )
2487 param->setDefaultValue( mDefaultFileWidget->filePath() );
2488 param->setFlags( flags );
2489 return param.release();
2490}
2491
2492
2493QgsProcessingFileWidgetWrapper::QgsProcessingFileWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2494 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2495{}
2496
2497QWidget *QgsProcessingFileWidgetWrapper::createWidget()
2498{
2499 const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( parameterDefinition() );
2500 if ( !fileParam )
2501 return nullptr;
2502
2503 switch ( type() )
2504 {
2508 {
2509 mFileWidget = new QgsFileWidget();
2510 mFileWidget->setToolTip( parameterDefinition()->toolTip() );
2511 mFileWidget->setDialogTitle( parameterDefinition()->description() );
2512
2513 mFileWidget->setDefaultRoot( QgsSettings().value( u"/Processing/LastInputPath"_s, QDir::homePath() ).toString() );
2514
2515 switch ( fileParam->behavior() )
2516 {
2518 mFileWidget->setStorageMode( QgsFileWidget::GetFile );
2519 if ( !fileParam->fileFilter().isEmpty() )
2520 mFileWidget->setFilter( fileParam->fileFilter() );
2521 else if ( !fileParam->extension().isEmpty() )
2522 mFileWidget->setFilter( tr( "%1 files" ).arg( fileParam->extension().toUpper() ) + u" (*."_s + fileParam->extension().toLower() + ')' );
2523 break;
2524
2526 mFileWidget->setStorageMode( QgsFileWidget::GetDirectory );
2527 break;
2528 }
2529
2530 connect( mFileWidget, &QgsFileWidget::fileChanged, this, [this]( const QString &path ) {
2531 QgsSettings().setValue( u"/Processing/LastInputPath"_s, QFileInfo( path ).canonicalPath() );
2532 emit widgetValueHasChanged( this );
2533 } );
2534 return mFileWidget;
2535 }
2536 }
2537 return nullptr;
2538}
2539
2540void QgsProcessingFileWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2541{
2542 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
2543 if ( mFileWidget )
2544 mFileWidget->setFilePath( v );
2545}
2546
2547QVariant QgsProcessingFileWidgetWrapper::widgetValue() const
2548{
2549 if ( mFileWidget )
2550 return mFileWidget->filePath();
2551 else
2552 return QVariant();
2553}
2554
2555QString QgsProcessingFileWidgetWrapper::modelerExpressionFormatString() const
2556{
2557 return tr( "string representing a path to a file or folder" );
2558}
2559
2560QString QgsProcessingFileWidgetWrapper::parameterType() const
2561{
2563}
2564
2565QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFileWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
2566{
2567 return new QgsProcessingFileWidgetWrapper( parameter, type );
2568}
2569
2570QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFileWidgetWrapper::createParameterDefinitionWidget(
2572)
2573{
2574 return new QgsProcessingFileParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2575}
2576
2577
2578//
2579// QgsProcessingExpressionWidgetWrapper
2580//
2581
2582QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParameterDefinitionWidget(
2583 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2584)
2585 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2586{
2587 QVBoxLayout *vlayout = new QVBoxLayout();
2588 vlayout->setContentsMargins( 0, 0, 0, 0 );
2589 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
2590
2591 mDefaultQgisLineEdit = new QgsExpressionLineEdit();
2592 mDefaultQgisLineEdit->registerExpressionContextGenerator( this );
2593
2594 mDefaultPointCloudLineEdit = new QgsProcessingPointCloudExpressionLineEdit();
2595 mDefaultRasterCalculatorLineEdit = new QgsProcessingRasterCalculatorExpressionLineEdit();
2596
2597 QStackedWidget *stackedWidget = new QStackedWidget();
2598 stackedWidget->addWidget( mDefaultQgisLineEdit );
2599 stackedWidget->addWidget( mDefaultPointCloudLineEdit );
2600 stackedWidget->addWidget( mDefaultRasterCalculatorLineEdit );
2601 vlayout->addWidget( stackedWidget );
2602
2604 connect( mDefaultPointCloudLineEdit, &QgsProcessingPointCloudExpressionLineEdit::expressionChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2605 connect( mDefaultRasterCalculatorLineEdit, &QgsProcessingRasterCalculatorExpressionLineEdit::expressionChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2606
2607 if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
2608 {
2609 const QString expr = QgsProcessingParameters::parameterAsExpression( expParam, expParam->defaultValueForGui(), context );
2610 mDefaultQgisLineEdit->setExpression( expr );
2611 mDefaultPointCloudLineEdit->setExpression( expr );
2612 }
2613
2614 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
2615
2616 mParentLayerComboBox = new QComboBox();
2617 vlayout->addWidget( mParentLayerComboBox );
2618
2619 vlayout->addWidget( new QLabel( tr( "Expression type" ) ) );
2620 mExpressionTypeComboBox = new QComboBox();
2621 mExpressionTypeComboBox->addItem( tr( "QGIS" ), static_cast<int>( Qgis::ExpressionType::Qgis ) );
2622 mExpressionTypeComboBox->addItem( tr( "Point Cloud" ), static_cast<int>( Qgis::ExpressionType::PointCloud ) );
2623 mExpressionTypeComboBox->addItem( tr( "Raster Calculator" ), static_cast<int>( Qgis::ExpressionType::RasterCalculator ) );
2624
2625 connect( mExpressionTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this, stackedWidget, definition, widgetContext]( int ) {
2626 mParentLayerComboBox->clear();
2627 mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
2628
2629 stackedWidget->setCurrentIndex( mExpressionTypeComboBox->currentIndex() > 0 ? mExpressionTypeComboBox->currentIndex() : 0 );
2630
2631 QString initialParent;
2632 if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
2633 initialParent = expParam->parentLayerParameterName();
2634
2635 Qgis::ExpressionType exprType = static_cast<Qgis::ExpressionType>( mExpressionTypeComboBox->currentData().toInt() );
2636
2637 if ( QgsProcessingModelAlgorithm *model = widgetContext.model() )
2638 {
2639 // populate combo box with other model input choices
2640 const QMap<QString, QgsProcessingModelParameter> components = model->parameterComponents();
2641 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
2642 {
2643 switch ( exprType )
2644 {
2646 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( model->parameterDefinition( it.value().parameterName() ) ) )
2647 {
2648 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2649 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2650 {
2651 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2652 }
2653 }
2654 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( model->parameterDefinition( it.value().parameterName() ) ) )
2655 {
2656 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2657 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2658 {
2659 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2660 }
2661 }
2662 break;
2664 if ( const QgsProcessingParameterPointCloudLayer *definition = dynamic_cast<const QgsProcessingParameterPointCloudLayer *>( model->parameterDefinition( it.value().parameterName() ) ) )
2665 {
2666 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2667 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2668 {
2669 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2670 }
2671 }
2672 break;
2674 if ( const QgsProcessingParameterMultipleLayers *definition = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( model->parameterDefinition( it.value().parameterName() ) ) )
2675 {
2676 if ( definition->layerType() != Qgis::ProcessingSourceType::Raster )
2677 {
2678 continue;
2679 }
2680 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2681 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2682 {
2683 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2684 }
2685 }
2686 break;
2687 }
2688 }
2689 }
2690
2691 if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
2692 {
2693 // if no parent candidates found, we just add the existing one as a placeholder
2694 mParentLayerComboBox->addItem( initialParent, initialParent );
2695 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2696 }
2697 } );
2698
2699 mExpressionTypeComboBox->setCurrentIndex( -1 );
2700 if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
2701 mExpressionTypeComboBox->setCurrentIndex( mExpressionTypeComboBox->findData( static_cast<int>( expParam->expressionType() ) ) );
2702 else
2703 mExpressionTypeComboBox->setCurrentIndex( 0 );
2704
2705 vlayout->addWidget( mExpressionTypeComboBox );
2706
2707 connect( mExpressionTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2708 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2709
2710 setLayout( vlayout );
2711}
2712
2713QgsProcessingParameterDefinition *QgsProcessingExpressionParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2714{
2715 Qgis::ExpressionType expressionType = static_cast<Qgis::ExpressionType>( mExpressionTypeComboBox->currentData().toInt() );
2716 QString expression;
2717 switch ( expressionType )
2718 {
2720 expression = mDefaultQgisLineEdit->expression();
2721 break;
2723 expression = mDefaultPointCloudLineEdit->expression();
2724 break;
2726 expression = mDefaultRasterCalculatorLineEdit->expression();
2727 break;
2728 }
2729 auto param = std::make_unique<QgsProcessingParameterExpression>( name, description, expression, mParentLayerComboBox->currentData().toString(), false, expressionType );
2730 param->setFlags( flags );
2731 return param.release();
2732}
2733
2734QgsProcessingExpressionWidgetWrapper::QgsProcessingExpressionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2735 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2736{}
2737
2738QWidget *QgsProcessingExpressionWidgetWrapper::createWidget()
2739{
2740 const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( parameterDefinition() );
2741 if ( !expParam )
2742 return nullptr;
2743
2744 switch ( type() )
2745 {
2749 {
2750 if ( expParam->parentLayerParameterName().isEmpty() )
2751 {
2752 mExpLineEdit = new QgsExpressionLineEdit();
2753 mExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
2754 mExpLineEdit->setExpressionDialogTitle( parameterDefinition()->description() );
2755 mExpLineEdit->registerExpressionContextGenerator( this );
2756 connect( mExpLineEdit, &QgsExpressionLineEdit::expressionChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
2757 return mExpLineEdit;
2758 }
2759 else
2760 {
2762 {
2763 mPointCloudExpLineEdit = new QgsProcessingPointCloudExpressionLineEdit();
2764 mPointCloudExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
2765 connect( mPointCloudExpLineEdit, &QgsProcessingPointCloudExpressionLineEdit::expressionChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
2766 return mPointCloudExpLineEdit;
2767 }
2768
2770 {
2771 mRasterCalculatorExpLineEdit = new QgsProcessingRasterCalculatorExpressionLineEdit();
2772 mRasterCalculatorExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
2773 if ( type() == Qgis::ProcessingMode::Modeler )
2774 {
2775 mRasterCalculatorExpLineEdit->setLayers( QVariantList() << "A" << "B" << "C" << "D" << "E" << "F" << "G" );
2776 }
2777 connect( mRasterCalculatorExpLineEdit, &QgsProcessingRasterCalculatorExpressionLineEdit::expressionChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
2778 return mRasterCalculatorExpLineEdit;
2779 }
2780
2781 // native QGIS expression
2782 if ( expParam->metadata().value( u"inlineEditor"_s ).toBool() )
2783 {
2784 mExpBuilderWidget = new QgsExpressionBuilderWidget();
2785 mExpBuilderWidget->setToolTip( parameterDefinition()->toolTip() );
2786 mExpBuilderWidget->init( createExpressionContext() );
2787 connect( mExpBuilderWidget, &QgsExpressionBuilderWidget::expressionParsed, this, [this]( bool changed ) {
2788 Q_UNUSED( changed );
2789 emit widgetValueHasChanged( this );
2790 } );
2791 return mExpBuilderWidget;
2792 }
2793 else
2794 {
2795 mFieldExpWidget = new QgsFieldExpressionWidget();
2796 mFieldExpWidget->setToolTip( parameterDefinition()->toolTip() );
2797 mFieldExpWidget->setExpressionDialogTitle( parameterDefinition()->description() );
2798 mFieldExpWidget->registerExpressionContextGenerator( this );
2800 mFieldExpWidget->setAllowEmptyFieldName( true );
2801
2802 connect( mFieldExpWidget, static_cast<void ( QgsFieldExpressionWidget::* )( const QString & )>( &QgsFieldExpressionWidget::fieldChanged ), this, [this]( const QString & ) {
2803 emit widgetValueHasChanged( this );
2804 } );
2805 return mFieldExpWidget;
2806 }
2807 }
2808 }
2809 }
2810 return nullptr;
2811}
2812
2813void QgsProcessingExpressionWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
2814{
2816 switch ( type() )
2817 {
2820 {
2821 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
2822 {
2823 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterExpression *>( parameterDefinition() )->parentLayerParameterName() )
2824 {
2825 setParentLayerWrapperValue( wrapper );
2826 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
2827 break;
2828 }
2829 }
2830 break;
2831 }
2832
2834 break;
2835 }
2836}
2837
2838void QgsProcessingExpressionWidgetWrapper::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
2839{
2841 if ( mExpBuilderWidget )
2842 {
2843 // we need to regenerate the expression context for use by this widget -- it doesn't fetch automatically on demand
2844 mExpBuilderWidget->setExpressionContext( createExpressionContext() );
2845 }
2846}
2847
2848void QgsProcessingExpressionWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
2849{
2850 // evaluate value to layer
2851 QgsProcessingContext *context = nullptr;
2852 std::unique_ptr<QgsProcessingContext> tmpContext;
2853 if ( mProcessingContextGenerator )
2854 context = mProcessingContextGenerator->processingContext();
2855
2856 if ( !context )
2857 {
2858 tmpContext = std::make_unique<QgsProcessingContext>();
2859 context = tmpContext.get();
2860 }
2861
2862 QVariant val = parentWrapper->parameterValue();
2863
2864 const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( parameterDefinition() );
2865 if ( !expParam )
2866 return;
2867
2868 switch ( expParam->expressionType() )
2869 {
2871 {
2872 if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
2873 {
2874 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
2875 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
2876 val = fromVar.source;
2877 }
2878
2880 if ( !layer )
2881 {
2882 if ( mFieldExpWidget )
2883 mFieldExpWidget->setLayer( nullptr );
2884 else if ( mExpBuilderWidget )
2885 mExpBuilderWidget->setLayer( nullptr );
2886 else if ( mExpLineEdit )
2887 mExpLineEdit->setLayer( nullptr );
2888 return;
2889 }
2890
2891 // need to grab ownership of layer if required - otherwise layer may be deleted when context
2892 // goes out of scope
2893 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
2894 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
2895 {
2896 mParentLayer = std::move( ownedLayer );
2897 layer = static_cast<QgsVectorLayer *>( mParentLayer.get() );
2898 }
2899 else
2900 {
2901 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
2902 }
2903
2904 if ( mFieldExpWidget )
2905 mFieldExpWidget->setLayer( layer );
2906 if ( mExpBuilderWidget )
2907 mExpBuilderWidget->setLayer( layer );
2908 else if ( mExpLineEdit )
2909 mExpLineEdit->setLayer( layer );
2910
2911 break;
2912 }
2914 {
2916 if ( !layer )
2917 {
2918 if ( mPointCloudExpLineEdit )
2919 mPointCloudExpLineEdit->setLayer( nullptr );
2920 return;
2921 }
2922
2923 // need to grab ownership of layer if required - otherwise layer may be deleted when context
2924 // goes out of scope
2925 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
2926 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::PointCloud )
2927 {
2928 mParentLayer = std::move( ownedLayer );
2929 layer = static_cast<QgsPointCloudLayer *>( mParentLayer.get() );
2930 }
2931 else
2932 {
2933 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
2934 }
2935
2936 if ( mPointCloudExpLineEdit )
2937 mPointCloudExpLineEdit->setLayer( layer );
2938
2939 break;
2940 }
2942 {
2944 if ( layers.isEmpty() )
2945 {
2946 if ( mRasterCalculatorExpLineEdit )
2947 {
2948 mRasterCalculatorExpLineEdit->setLayers( val.userType() == QMetaType::Type::QVariantList ? val.toList() : QVariantList() << val );
2949 }
2950 return;
2951 }
2952
2953 if ( mRasterCalculatorExpLineEdit )
2954 {
2955 QVariantList layersList;
2956 for ( QgsMapLayer *layer : layers )
2957 {
2958 layersList << layer->name();
2959 }
2960 mRasterCalculatorExpLineEdit->setLayers( layersList );
2961 }
2962
2963 break;
2964 }
2965 }
2966}
2967
2968void QgsProcessingExpressionWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2969{
2970 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
2971 if ( mFieldExpWidget )
2972 mFieldExpWidget->setExpression( v );
2973 else if ( mExpBuilderWidget )
2974 mExpBuilderWidget->setExpressionText( v );
2975 else if ( mExpLineEdit )
2976 mExpLineEdit->setExpression( v );
2977 else if ( mPointCloudExpLineEdit )
2978 mPointCloudExpLineEdit->setExpression( v );
2979 else if ( mRasterCalculatorExpLineEdit )
2980 mRasterCalculatorExpLineEdit->setExpression( v );
2981}
2982
2983QVariant QgsProcessingExpressionWidgetWrapper::widgetValue() const
2984{
2985 if ( mFieldExpWidget )
2986 return mFieldExpWidget->expression();
2987 if ( mExpBuilderWidget )
2988 return mExpBuilderWidget->expressionText();
2989 else if ( mExpLineEdit )
2990 return mExpLineEdit->expression();
2991 else if ( mPointCloudExpLineEdit )
2992 return mPointCloudExpLineEdit->expression();
2993 else if ( mRasterCalculatorExpLineEdit )
2994 return mRasterCalculatorExpLineEdit->expression();
2995 else
2996 return QVariant();
2997}
2998
2999QString QgsProcessingExpressionWidgetWrapper::modelerExpressionFormatString() const
3000{
3001 return tr( "string representation of an expression" );
3002}
3003
3004const QgsVectorLayer *QgsProcessingExpressionWidgetWrapper::linkedVectorLayer() const
3005{
3006 if ( mFieldExpWidget && mFieldExpWidget->layer() )
3007 return mFieldExpWidget->layer();
3008
3009 if ( mExpBuilderWidget && mExpBuilderWidget->layer() )
3010 return mExpBuilderWidget->layer();
3011
3013}
3014
3015QString QgsProcessingExpressionWidgetWrapper::parameterType() const
3016{
3018}
3019
3020QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExpressionWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3021{
3022 return new QgsProcessingExpressionWidgetWrapper( parameter, type );
3023}
3024
3025QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingExpressionWidgetWrapper::createParameterDefinitionWidget(
3027)
3028{
3029 return new QgsProcessingExpressionParameterDefinitionWidget( context, widgetContext, definition, algorithm );
3030}
3031
3032
3033//
3034// QgsProcessingEnumPanelWidget
3035//
3036
3037QgsProcessingEnumPanelWidget::QgsProcessingEnumPanelWidget( QWidget *parent, const QgsProcessingParameterEnum *param )
3038 : QWidget( parent )
3039 , mParam( param )
3040{
3041 QHBoxLayout *hl = new QHBoxLayout();
3042 hl->setContentsMargins( 0, 0, 0, 0 );
3043
3044 mLineEdit = new QLineEdit();
3045 mLineEdit->setEnabled( false );
3046 hl->addWidget( mLineEdit, 1 );
3047
3048 mToolButton = new QToolButton();
3049 mToolButton->setText( QString( QChar( 0x2026 ) ) );
3050 hl->addWidget( mToolButton );
3051
3052 setLayout( hl );
3053
3054 if ( mParam )
3055 {
3056 mLineEdit->setText( tr( "%1 options selected" ).arg( 0 ) );
3057 }
3058
3059 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingEnumPanelWidget::showDialog );
3060}
3061
3062void QgsProcessingEnumPanelWidget::setValue( const QVariant &value )
3063{
3064 if ( value.isValid() )
3065 {
3066 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
3067
3068 if ( mParam->usesStaticStrings() && mValue.count() == 1 && mValue.at( 0 ).toString().isEmpty() )
3069 mValue.clear();
3070 }
3071 else
3072 mValue.clear();
3073
3074 updateSummaryText();
3075 emit changed();
3076}
3077
3078void QgsProcessingEnumPanelWidget::showDialog()
3079{
3080 QVariantList availableOptions;
3081 if ( mParam )
3082 {
3083 availableOptions.reserve( mParam->options().size() );
3084
3085 if ( mParam->usesStaticStrings() )
3086 {
3087 for ( QString o : mParam->options() )
3088 {
3089 availableOptions << o;
3090 }
3091 }
3092 else
3093 {
3094 for ( int i = 0; i < mParam->options().count(); ++i )
3095 availableOptions << i;
3096 }
3097 }
3098
3099 const QStringList options = mParam ? mParam->options() : QStringList();
3101 if ( panel && panel->dockMode() )
3102 {
3103 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
3104 widget->setPanelTitle( mParam->description() );
3105
3106 if ( mParam->usesStaticStrings() )
3107 {
3108 widget->setValueFormatter( [options]( const QVariant &v ) -> QString {
3109 const QString i = v.toString();
3110 return options.contains( i ) ? i : QString();
3111 } );
3112 }
3113 else
3114 {
3115 widget->setValueFormatter( [options]( const QVariant &v ) -> QString {
3116 const int i = v.toInt();
3117 return options.size() > i ? options.at( i ) : QString();
3118 } );
3119 }
3120
3121 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
3122 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
3123 panel->openPanel( widget );
3124 }
3125 else
3126 {
3127 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
3128
3129 dlg.setValueFormatter( [options]( const QVariant &v ) -> QString {
3130 const int i = v.toInt();
3131 return options.size() > i ? options.at( i ) : QString();
3132 } );
3133 if ( dlg.exec() )
3134 {
3135 setValue( dlg.selectedOptions() );
3136 }
3137 }
3138}
3139
3140void QgsProcessingEnumPanelWidget::updateSummaryText()
3141{
3142 if ( !mParam )
3143 return;
3144
3145 if ( mValue.empty() )
3146 {
3147 mLineEdit->setText( tr( "%1 options selected" ).arg( 0 ) );
3148 }
3149 else
3150 {
3151 QStringList values;
3152 values.reserve( mValue.size() );
3153 if ( mParam->usesStaticStrings() )
3154 {
3155 for ( const QVariant &val : std::as_const( mValue ) )
3156 {
3157 values << val.toString();
3158 }
3159 }
3160 else
3161 {
3162 const QStringList options = mParam->options();
3163 for ( const QVariant &val : std::as_const( mValue ) )
3164 {
3165 const int i = val.toInt();
3166 values << ( options.size() > i ? options.at( i ) : QString() );
3167 }
3168 }
3169
3170 const QString concatenated = values.join( tr( "," ) );
3171 if ( concatenated.length() < 100 )
3172 mLineEdit->setText( concatenated );
3173 else
3174 mLineEdit->setText( tr( "%n option(s) selected", nullptr, mValue.count() ) );
3175 }
3176}
3177
3178
3179//
3180// QgsProcessingEnumCheckboxPanelWidget
3181//
3182QgsProcessingEnumCheckboxPanelWidget::QgsProcessingEnumCheckboxPanelWidget( QWidget *parent, const QgsProcessingParameterEnum *param, int columns )
3183 : QWidget( parent )
3184 , mParam( param )
3185 , mButtonGroup( new QButtonGroup( this ) )
3186 , mColumns( columns )
3187{
3188 mButtonGroup->setExclusive( !mParam->allowMultiple() );
3189
3190 QGridLayout *l = new QGridLayout();
3191 l->setContentsMargins( 0, 0, 0, 0 );
3192
3193 int rows = static_cast<int>( std::ceil( mParam->options().count() / static_cast<double>( mColumns ) ) );
3194 for ( int i = 0; i < mParam->options().count(); ++i )
3195 {
3196 QAbstractButton *button = nullptr;
3197 if ( mParam->allowMultiple() )
3198 button = new QCheckBox( mParam->options().at( i ) );
3199 else
3200 button = new QRadioButton( mParam->options().at( i ) );
3201
3202 connect( button, &QAbstractButton::toggled, this, [this] {
3203 if ( !mBlockChangedSignal )
3204 emit changed();
3205 } );
3206
3207 mButtons.insert( i, button );
3208
3209 mButtonGroup->addButton( button, i );
3210 l->addWidget( button, i % rows, i / rows );
3211 }
3212 l->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ), 0, mColumns );
3213 setLayout( l );
3214
3215 if ( mParam->allowMultiple() )
3216 {
3217 setContextMenuPolicy( Qt::CustomContextMenu );
3218 connect( this, &QWidget::customContextMenuRequested, this, &QgsProcessingEnumCheckboxPanelWidget::showPopupMenu );
3219 }
3220}
3221
3222QVariant QgsProcessingEnumCheckboxPanelWidget::value() const
3223{
3224 if ( mParam->allowMultiple() )
3225 {
3226 QVariantList value;
3227 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3228 {
3229 if ( it.value()->isChecked() )
3230 value.append( mParam->usesStaticStrings() ? mParam->options().at( it.key().toInt() ) : it.key() );
3231 }
3232 return value;
3233 }
3234 else
3235 {
3236 if ( mParam->usesStaticStrings() )
3237 return mButtonGroup->checkedId() >= 0 ? mParam->options().at( mButtonGroup->checkedId() ) : QVariant();
3238 else
3239 return mButtonGroup->checkedId() >= 0 ? mButtonGroup->checkedId() : QVariant();
3240 }
3241}
3242
3243void QgsProcessingEnumCheckboxPanelWidget::setValue( const QVariant &value )
3244{
3245 mBlockChangedSignal = true;
3246 if ( mParam->allowMultiple() )
3247 {
3248 QVariantList selected;
3249 if ( value.isValid() )
3250 selected = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
3251 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3252 {
3253 QVariant v = mParam->usesStaticStrings() ? mParam->options().at( it.key().toInt() ) : it.key();
3254 it.value()->setChecked( selected.contains( v ) );
3255 }
3256 }
3257 else
3258 {
3259 QVariant v = value;
3260 if ( v.userType() == QMetaType::Type::QVariantList )
3261 v = v.toList().value( 0 );
3262
3263 v = mParam->usesStaticStrings() ? static_cast< int >( mParam->options().indexOf( v.toString() ) ) : v;
3264 if ( mButtons.contains( v ) )
3265 mButtons.value( v )->setChecked( true );
3266 }
3267 mBlockChangedSignal = false;
3268 emit changed();
3269}
3270
3271void QgsProcessingEnumCheckboxPanelWidget::showPopupMenu()
3272{
3273 QMenu popupMenu;
3274 QAction *selectAllAction = new QAction( tr( "Select All" ), &popupMenu );
3275 connect( selectAllAction, &QAction::triggered, this, &QgsProcessingEnumCheckboxPanelWidget::selectAll );
3276 QAction *clearAllAction = new QAction( tr( "Clear Selection" ), &popupMenu );
3277 connect( clearAllAction, &QAction::triggered, this, &QgsProcessingEnumCheckboxPanelWidget::deselectAll );
3278 popupMenu.addAction( selectAllAction );
3279 popupMenu.addAction( clearAllAction );
3280 popupMenu.exec( QCursor::pos() );
3281}
3282
3283void QgsProcessingEnumCheckboxPanelWidget::selectAll()
3284{
3285 mBlockChangedSignal = true;
3286 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3287 it.value()->setChecked( true );
3288 mBlockChangedSignal = false;
3289 emit changed();
3290}
3291
3292void QgsProcessingEnumCheckboxPanelWidget::deselectAll()
3293{
3294 mBlockChangedSignal = true;
3295 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3296 it.value()->setChecked( false );
3297 mBlockChangedSignal = false;
3298 emit changed();
3299}
3300
3301
3302//
3303// QgsProcessingEnumWidgetWrapper
3304//
3305
3306QgsProcessingEnumParameterDefinitionWidget::QgsProcessingEnumParameterDefinitionWidget(
3307 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
3308)
3309 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
3310{
3311 QVBoxLayout *vlayout = new QVBoxLayout();
3312 vlayout->setContentsMargins( 0, 0, 0, 0 );
3313
3314 mEnumWidget = new QgsProcessingEnumModelerWidget();
3315 if ( const QgsProcessingParameterEnum *enumParam = dynamic_cast<const QgsProcessingParameterEnum *>( definition ) )
3316 {
3317 mEnumWidget->setAllowMultiple( enumParam->allowMultiple() );
3318 mEnumWidget->setOptions( enumParam->options() );
3319 mEnumWidget->setDefaultOptions( enumParam->defaultValueForGui() );
3320 }
3321 vlayout->addWidget( mEnumWidget );
3322
3323 connect( mEnumWidget, &QgsProcessingEnumModelerWidget::changed, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
3324
3325 setLayout( vlayout );
3326}
3327
3328QgsProcessingParameterDefinition *QgsProcessingEnumParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
3329{
3330 auto param = std::make_unique<QgsProcessingParameterEnum>( name, description, mEnumWidget->options(), mEnumWidget->allowMultiple(), mEnumWidget->defaultOptions() );
3331 param->setFlags( flags );
3332 return param.release();
3333}
3334
3335
3336QgsProcessingEnumWidgetWrapper::QgsProcessingEnumWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
3337 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
3338{}
3339
3340QWidget *QgsProcessingEnumWidgetWrapper::createWidget()
3341{
3342 const QgsProcessingParameterEnum *expParam = dynamic_cast<const QgsProcessingParameterEnum *>( parameterDefinition() );
3343 if ( !expParam )
3344 return nullptr;
3345
3346 switch ( type() )
3347 {
3349 {
3350 // checkbox panel only for use outside in standard gui!
3351 if ( expParam->metadata().value( u"widget_wrapper"_s ).toMap().value( u"useCheckBoxes"_s, false ).toBool() )
3352 {
3353 const int columns = expParam->metadata().value( u"widget_wrapper"_s ).toMap().value( u"columns"_s, 2 ).toInt();
3354 mCheckboxPanel = new QgsProcessingEnumCheckboxPanelWidget( nullptr, expParam, columns );
3355 mCheckboxPanel->setToolTip( parameterDefinition()->toolTip() );
3356 connect( mCheckboxPanel, &QgsProcessingEnumCheckboxPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
3357 return mCheckboxPanel;
3358 }
3359 }
3360 [[fallthrough]];
3363 {
3364 if ( expParam->allowMultiple() )
3365 {
3366 mPanel = new QgsProcessingEnumPanelWidget( nullptr, expParam );
3367 mPanel->setToolTip( parameterDefinition()->toolTip() );
3368 connect( mPanel, &QgsProcessingEnumPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
3369 return mPanel;
3370 }
3371 else
3372 {
3373 mComboBox = new QComboBox();
3374
3376 mComboBox->addItem( tr( "[Not selected]" ), QVariant() );
3377 const QStringList options = expParam->options();
3378 const QVariantList iconList = expParam->metadata().value( u"widget_wrapper"_s ).toMap().value( u"icons"_s ).toList();
3379 for ( int i = 0; i < options.count(); ++i )
3380 {
3381 const QIcon icon = iconList.value( i ).value<QIcon>();
3382
3383 if ( expParam->usesStaticStrings() )
3384 mComboBox->addItem( icon, options.at( i ), options.at( i ) );
3385 else
3386 mComboBox->addItem( icon, options.at( i ), i );
3387 }
3388
3389 mComboBox->setToolTip( parameterDefinition()->toolTip() );
3390 mComboBox->setSizeAdjustPolicy( QComboBox::AdjustToMinimumContentsLengthWithIcon );
3391 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { emit widgetValueHasChanged( this ); } );
3392 return mComboBox;
3393 }
3394 }
3395 }
3396 return nullptr;
3397}
3398
3399void QgsProcessingEnumWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
3400{
3401 if ( mComboBox )
3402 {
3403 if ( !value.isValid() )
3404 mComboBox->setCurrentIndex( mComboBox->findData( QVariant() ) );
3405 else
3406 {
3407 if ( auto *enumDef = dynamic_cast<const QgsProcessingParameterEnum *>( parameterDefinition() ); enumDef && enumDef->usesStaticStrings() )
3408 {
3409 const QString v = QgsProcessingParameters::parameterAsEnumString( parameterDefinition(), value, context );
3410 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
3411 }
3412 else
3413 {
3414 const int v = QgsProcessingParameters::parameterAsEnum( parameterDefinition(), value, context );
3415 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
3416 }
3417 }
3418 }
3419 else if ( mPanel || mCheckboxPanel )
3420 {
3421 QVariantList opts;
3422 if ( value.isValid() )
3423 {
3424 if ( auto *enumDef = dynamic_cast<const QgsProcessingParameterEnum *>( parameterDefinition() ); enumDef && enumDef->usesStaticStrings() )
3425 {
3426 const QStringList v = QgsProcessingParameters::parameterAsEnumStrings( parameterDefinition(), value, context );
3427 opts.reserve( v.size() );
3428 for ( QString i : v )
3429 opts << i;
3430 }
3431 else
3432 {
3433 const QList<int> v = QgsProcessingParameters::parameterAsEnums( parameterDefinition(), value, context );
3434 opts.reserve( v.size() );
3435 for ( int i : v )
3436 opts << i;
3437 }
3438 }
3439 if ( mPanel )
3440 mPanel->setValue( opts );
3441 else if ( mCheckboxPanel )
3442 mCheckboxPanel->setValue( opts );
3443 }
3444}
3445
3446QVariant QgsProcessingEnumWidgetWrapper::widgetValue() const
3447{
3448 if ( mComboBox )
3449 return mComboBox->currentData();
3450 else if ( mPanel )
3451 return mPanel->value();
3452 else if ( mCheckboxPanel )
3453 return mCheckboxPanel->value();
3454 else
3455 return QVariant();
3456}
3457
3458QString QgsProcessingEnumWidgetWrapper::modelerExpressionFormatString() const
3459{
3460 return tr( "selected option index (starting from 0), array of indices, or comma separated string of options (e.g. '1,3')" );
3461}
3462
3463QString QgsProcessingEnumWidgetWrapper::parameterType() const
3464{
3466}
3467
3468QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingEnumWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3469{
3470 return new QgsProcessingEnumWidgetWrapper( parameter, type );
3471}
3472
3473QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingEnumWidgetWrapper::createParameterDefinitionWidget(
3475)
3476{
3477 return new QgsProcessingEnumParameterDefinitionWidget( context, widgetContext, definition, algorithm );
3478}
3479
3480//
3481// QgsProcessingLayoutWidgetWrapper
3482//
3483
3484QgsProcessingLayoutWidgetWrapper::QgsProcessingLayoutWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
3485 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
3486{}
3487
3488QWidget *QgsProcessingLayoutWidgetWrapper::createWidget()
3489{
3490 const QgsProcessingParameterLayout *layoutParam = dynamic_cast<const QgsProcessingParameterLayout *>( parameterDefinition() );
3491 if ( !layoutParam )
3492 return nullptr;
3493
3494 switch ( type() )
3495 {
3498 {
3499 // combobox only for use outside modeler!
3500 mComboBox = new QgsLayoutComboBox( nullptr, widgetContext().project() ? widgetContext().project()->layoutManager() : nullptr );
3501 if ( layoutParam->flags() & Qgis::ProcessingParameterFlag::Optional )
3502 mComboBox->setAllowEmptyLayout( true );
3503 mComboBox->setFilters( QgsLayoutManagerProxyModel::FilterPrintLayouts );
3504
3505 mComboBox->setToolTip( parameterDefinition()->toolTip() );
3506 connect( mComboBox, &QgsLayoutComboBox::layoutChanged, this, [this]( QgsMasterLayoutInterface * ) { emit widgetValueHasChanged( this ); } );
3507 return mComboBox;
3508 }
3509
3511 {
3512 mPlainComboBox = new QComboBox();
3513 mPlainComboBox->setEditable( true );
3514 mPlainComboBox->setToolTip( tr( "Name of an existing print layout" ) );
3515 if ( widgetContext().project() )
3516 {
3517 const QList<QgsPrintLayout *> layouts = widgetContext().project()->layoutManager()->printLayouts();
3518 for ( const QgsPrintLayout *layout : layouts )
3519 mPlainComboBox->addItem( layout->name() );
3520 }
3521
3522 connect( mPlainComboBox, &QComboBox::currentTextChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
3523 return mPlainComboBox;
3524 }
3525 }
3526 return nullptr;
3527}
3528
3529void QgsProcessingLayoutWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
3530{
3531 if ( mComboBox )
3532 {
3533 if ( !value.isValid() )
3534 mComboBox->setCurrentLayout( nullptr );
3535 else
3536 {
3537 if ( QgsPrintLayout *l = QgsProcessingParameters::parameterAsLayout( parameterDefinition(), value, context ) )
3538 mComboBox->setCurrentLayout( l );
3539 else
3540 mComboBox->setCurrentLayout( nullptr );
3541 }
3542 }
3543 else if ( mPlainComboBox )
3544 {
3545 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
3546 mPlainComboBox->setCurrentText( v );
3547 }
3548}
3549
3550QVariant QgsProcessingLayoutWidgetWrapper::widgetValue() const
3551{
3552 if ( mComboBox )
3553 {
3554 const QgsMasterLayoutInterface *l = mComboBox->currentLayout();
3555 return l ? l->name() : QVariant();
3556 }
3557 else if ( mPlainComboBox )
3558 return mPlainComboBox->currentText().isEmpty() ? QVariant() : mPlainComboBox->currentText();
3559 else
3560 return QVariant();
3561}
3562
3563void QgsProcessingLayoutWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
3564{
3566 if ( mPlainComboBox && context.project() )
3567 {
3568 const QList<QgsPrintLayout *> layouts = widgetContext().project()->layoutManager()->printLayouts();
3569 for ( const QgsPrintLayout *layout : layouts )
3570 mPlainComboBox->addItem( layout->name() );
3571 }
3572}
3573
3574QString QgsProcessingLayoutWidgetWrapper::modelerExpressionFormatString() const
3575{
3576 return tr( "string representing the name of an existing print layout" );
3577}
3578
3579QString QgsProcessingLayoutWidgetWrapper::parameterType() const
3580{
3582}
3583
3584QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingLayoutWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3585{
3586 return new QgsProcessingLayoutWidgetWrapper( parameter, type );
3587}
3588
3589
3590//
3591// QgsProcessingLayoutItemWidgetWrapper
3592//
3593
3594
3595QgsProcessingLayoutItemParameterDefinitionWidget::QgsProcessingLayoutItemParameterDefinitionWidget(
3596 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
3597)
3598 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
3599{
3600 QVBoxLayout *vlayout = new QVBoxLayout();
3601 vlayout->setContentsMargins( 0, 0, 0, 0 );
3602
3603 vlayout->addWidget( new QLabel( tr( "Parent layout" ) ) );
3604
3605 mParentLayoutComboBox = new QComboBox();
3606 QString initialParent;
3607 if ( const QgsProcessingParameterLayoutItem *itemParam = dynamic_cast<const QgsProcessingParameterLayoutItem *>( definition ) )
3608 initialParent = itemParam->parentLayoutParameterName();
3609
3610 if ( auto *lModel = widgetContext.model() )
3611 {
3612 // populate combo box with other model input choices
3613 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
3614 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
3615 {
3616 if ( const QgsProcessingParameterLayout *definition = dynamic_cast<const QgsProcessingParameterLayout *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
3617 {
3618 mParentLayoutComboBox->addItem( definition->description(), definition->name() );
3619 if ( !initialParent.isEmpty() && initialParent == definition->name() )
3620 {
3621 mParentLayoutComboBox->setCurrentIndex( mParentLayoutComboBox->count() - 1 );
3622 }
3623 }
3624 }
3625 }
3626
3627 if ( mParentLayoutComboBox->count() == 0 && !initialParent.isEmpty() )
3628 {
3629 // if no parent candidates found, we just add the existing one as a placeholder
3630 mParentLayoutComboBox->addItem( initialParent, initialParent );
3631 mParentLayoutComboBox->setCurrentIndex( mParentLayoutComboBox->count() - 1 );
3632 }
3633
3634 vlayout->addWidget( mParentLayoutComboBox );
3635
3636 connect( mParentLayoutComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
3637
3638 setLayout( vlayout );
3639}
3640QgsProcessingParameterDefinition *QgsProcessingLayoutItemParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
3641{
3642 auto param = std::make_unique<QgsProcessingParameterLayoutItem>( name, description, QVariant(), mParentLayoutComboBox->currentData().toString() );
3643 param->setFlags( flags );
3644 return param.release();
3645}
3646
3647
3648QgsProcessingLayoutItemWidgetWrapper::QgsProcessingLayoutItemWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
3649 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
3650{}
3651
3652QWidget *QgsProcessingLayoutItemWidgetWrapper::createWidget()
3653{
3654 const QgsProcessingParameterLayoutItem *layoutParam = dynamic_cast<const QgsProcessingParameterLayoutItem *>( parameterDefinition() );
3655 if ( !layoutParam )
3656 return nullptr;
3657
3658 switch ( type() )
3659 {
3662 {
3663 // combobox only for use outside modeler!
3664 mComboBox = new QgsLayoutItemComboBox( nullptr, nullptr );
3665 if ( layoutParam->flags() & Qgis::ProcessingParameterFlag::Optional )
3666 mComboBox->setAllowEmptyItem( true );
3667 if ( layoutParam->itemType() >= 0 )
3668 mComboBox->setItemType( static_cast<QgsLayoutItemRegistry::ItemType>( layoutParam->itemType() ) );
3669
3670 mComboBox->setToolTip( parameterDefinition()->toolTip() );
3671 connect( mComboBox, &QgsLayoutItemComboBox::itemChanged, this, [this]( QgsLayoutItem * ) { emit widgetValueHasChanged( this ); } );
3672 return mComboBox;
3673 }
3674
3676 {
3677 mLineEdit = new QLineEdit();
3678 mLineEdit->setToolTip( tr( "UUID or ID of an existing print layout item" ) );
3679 connect( mLineEdit, &QLineEdit::textChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
3680 return mLineEdit;
3681 }
3682 }
3683 return nullptr;
3684}
3685
3686void QgsProcessingLayoutItemWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
3687{
3689 switch ( type() )
3690 {
3693 {
3694 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
3695 {
3696 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterLayoutItem *>( parameterDefinition() )->parentLayoutParameterName() )
3697 {
3698 setLayoutParameterValue( wrapper->parameterValue() );
3699 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setLayoutParameterValue( wrapper->parameterValue() ); } );
3700 break;
3701 }
3702 }
3703 break;
3704 }
3705
3707 break;
3708 }
3709}
3710
3711void QgsProcessingLayoutItemWidgetWrapper::setLayoutParameterValue( const QVariant &value )
3712{
3713 QgsPrintLayout *layout = nullptr;
3714
3715 // evaluate value to layout
3716 QgsProcessingContext *context = nullptr;
3717 std::unique_ptr<QgsProcessingContext> tmpContext;
3718 if ( mProcessingContextGenerator )
3719 context = mProcessingContextGenerator->processingContext();
3720
3721 if ( !context )
3722 {
3723 tmpContext = std::make_unique<QgsProcessingContext>();
3724 context = tmpContext.get();
3725 }
3726
3727 layout = QgsProcessingParameters::parameterAsLayout( parameterDefinition(), value, *context );
3728 setLayout( layout );
3729}
3730
3731void QgsProcessingLayoutItemWidgetWrapper::setLayout( QgsPrintLayout *layout )
3732{
3733 if ( mComboBox )
3734 mComboBox->setCurrentLayout( layout );
3735}
3736
3737void QgsProcessingLayoutItemWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
3738{
3739 if ( mComboBox )
3740 {
3741 if ( !value.isValid() )
3742 mComboBox->setItem( nullptr );
3743 else
3744 {
3745 QgsLayoutItem *item = QgsProcessingParameters::parameterAsLayoutItem( parameterDefinition(), value, context, qobject_cast<QgsPrintLayout *>( mComboBox->currentLayout() ) );
3746 mComboBox->setItem( item );
3747 }
3748 }
3749 else if ( mLineEdit )
3750 {
3751 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
3752 mLineEdit->setText( v );
3753 }
3754}
3755
3756QVariant QgsProcessingLayoutItemWidgetWrapper::widgetValue() const
3757{
3758 if ( mComboBox )
3759 {
3760 const QgsLayoutItem *i = mComboBox->currentItem();
3761 return i ? i->uuid() : QVariant();
3762 }
3763 else if ( mLineEdit )
3764 return mLineEdit->text().isEmpty() ? QVariant() : mLineEdit->text();
3765 else
3766 return QVariant();
3767}
3768
3769
3770QString QgsProcessingLayoutItemWidgetWrapper::modelerExpressionFormatString() const
3771{
3772 return tr( "string representing the UUID or ID of an existing print layout item" );
3773}
3774
3775QString QgsProcessingLayoutItemWidgetWrapper::parameterType() const
3776{
3778}
3779
3780QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingLayoutItemWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3781{
3782 return new QgsProcessingLayoutItemWidgetWrapper( parameter, type );
3783}
3784
3785QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingLayoutItemWidgetWrapper::createParameterDefinitionWidget(
3787)
3788{
3789 return new QgsProcessingLayoutItemParameterDefinitionWidget( context, widgetContext, definition, algorithm );
3790}
3791
3792//
3793// QgsProcessingPointMapTool
3794//
3795
3796QgsProcessingPointMapTool::QgsProcessingPointMapTool( QgsMapCanvas *canvas )
3797 : QgsMapTool( canvas )
3798{
3800 mSnapIndicator = std::make_unique<QgsSnapIndicator>( canvas );
3801}
3802
3803QgsProcessingPointMapTool::~QgsProcessingPointMapTool() = default;
3804
3805void QgsProcessingPointMapTool::deactivate()
3806{
3807 mSnapIndicator->setMatch( QgsPointLocator::Match() );
3809}
3810
3811void QgsProcessingPointMapTool::canvasMoveEvent( QgsMapMouseEvent *e )
3812{
3813 e->snapPoint();
3814 mSnapIndicator->setMatch( e->mapPointMatch() );
3815}
3816
3817void QgsProcessingPointMapTool::canvasPressEvent( QgsMapMouseEvent *e )
3818{
3819 if ( e->button() == Qt::LeftButton )
3820 {
3821 QgsPointXY point = e->snapPoint();
3822 emit clicked( point );
3823 emit complete();
3824 }
3825}
3826
3827void QgsProcessingPointMapTool::keyPressEvent( QKeyEvent *e )
3828{
3829 if ( e->key() == Qt::Key_Escape )
3830 {
3831 // Override default shortcut management in MapCanvas
3832 e->ignore();
3833 emit complete();
3834 }
3835}
3836
3837
3838//
3839// QgsProcessingPointPanel
3840//
3841
3842QgsProcessingPointPanel::QgsProcessingPointPanel( QWidget *parent )
3843 : QWidget( parent )
3844{
3845 QHBoxLayout *l = new QHBoxLayout();
3846 l->setContentsMargins( 0, 0, 0, 0 );
3847 mLineEdit = new QgsFilterLineEdit();
3848 mLineEdit->setShowClearButton( false );
3849 l->addWidget( mLineEdit, 1 );
3850 mButton = new QToolButton();
3851 mButton->setText( QString( QChar( 0x2026 ) ) );
3852 l->addWidget( mButton );
3853 setLayout( l );
3854
3855 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsProcessingPointPanel::changed );
3856 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsProcessingPointPanel::textChanged );
3857 connect( mButton, &QToolButton::clicked, this, &QgsProcessingPointPanel::selectOnCanvas );
3858 mButton->setVisible( false );
3859}
3860
3861QgsProcessingPointPanel::~QgsProcessingPointPanel()
3862{
3863 if ( mCanvas && mPrevTool && mCanvas->mapTool() == mTool.get() )
3864 {
3865 mCanvas->setMapTool( mPrevTool.get() );
3866 }
3867}
3868
3869void QgsProcessingPointPanel::setMapCanvas( QgsMapCanvas *canvas )
3870{
3871 mCanvas = canvas;
3872 if ( mAllowSelectOnCanvas )
3873 {
3874 mButton->setVisible( true );
3875
3876 mCrs = canvas->mapSettings().destinationCrs();
3877 mTool = std::make_unique<QgsProcessingPointMapTool>( mCanvas );
3878 connect( mTool.get(), &QgsProcessingPointMapTool::clicked, this, &QgsProcessingPointPanel::updatePoint );
3879 connect( mTool.get(), &QgsProcessingPointMapTool::complete, this, &QgsProcessingPointPanel::pointPicked );
3880 }
3881}
3882
3883void QgsProcessingPointPanel::setAllowNull( bool allowNull )
3884{
3885 mLineEdit->setShowClearButton( allowNull );
3886}
3887
3888void QgsProcessingPointPanel::setShowPointOnCanvas( bool show )
3889{
3890 if ( mShowPointOnCanvas == show )
3891 return;
3892
3893 mShowPointOnCanvas = show;
3894 if ( mShowPointOnCanvas )
3895 {
3896 updateRubberBand();
3897 }
3898 else
3899 {
3900 mMapPointRubberBand.reset();
3901 }
3902}
3903
3904void QgsProcessingPointPanel::setAllowSelectOnCanvas( bool allow )
3905{
3906 mAllowSelectOnCanvas = allow;
3907 mButton->setVisible( mAllowSelectOnCanvas && static_cast<bool>( mTool ) );
3908}
3909
3910QVariant QgsProcessingPointPanel::value() const
3911{
3912 return mLineEdit->showClearButton() && mLineEdit->text().trimmed().isEmpty() ? QVariant() : QVariant( mLineEdit->text() );
3913}
3914
3915void QgsProcessingPointPanel::clear()
3916{
3917 mLineEdit->clear();
3918}
3919
3920void QgsProcessingPointPanel::setValue( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs )
3921{
3922 mPoint = point;
3923 QString newText = u"%1,%2"_s.arg( QString::number( point.x(), 'f' ), QString::number( point.y(), 'f' ) );
3924
3925 mCrs = crs;
3926 if ( mCrs.isValid() )
3927 {
3928 newText += u" [%1]"_s.arg( mCrs.authid() );
3929 }
3930 mLineEdit->setText( newText );
3931 updateRubberBand();
3932}
3933
3934void QgsProcessingPointPanel::showEvent( QShowEvent * )
3935{
3936 if ( mFirstShow )
3937 {
3938 // we don't support select on canvas if the dialog is modal
3939 if ( QWidget *parentWindow = window() )
3940 {
3941 setAllowSelectOnCanvas( !parentWindow->isModal() );
3942 }
3943 mFirstShow = false;
3944 }
3945}
3946
3947void QgsProcessingPointPanel::selectOnCanvas()
3948{
3949 if ( !mCanvas )
3950 return;
3951
3952 mPrevTool = mCanvas->mapTool();
3953 mCanvas->setMapTool( mTool.get() );
3954
3955 emit toggleDialogVisibility( false );
3956}
3957
3958void QgsProcessingPointPanel::updatePoint( const QgsPointXY &point )
3959{
3960 setValue( point, mCanvas->mapSettings().destinationCrs() );
3961}
3962
3963void QgsProcessingPointPanel::pointPicked()
3964{
3965 if ( !mCanvas )
3966 return;
3967
3968 mCanvas->setMapTool( mPrevTool );
3969
3970 emit toggleDialogVisibility( true );
3971}
3972
3973void QgsProcessingPointPanel::textChanged( const QString &text )
3974{
3975 const thread_local QRegularExpression rx( u"^\\s*\\(?\\s*(.*?)\\s*,\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*\\)?\\s*$"_s );
3976
3977 const QRegularExpressionMatch match = rx.match( text );
3978 if ( match.hasMatch() )
3979 {
3980 bool xOk = false;
3981 const double x = match.captured( 1 ).toDouble( &xOk );
3982 bool yOk = false;
3983 const double y = match.captured( 2 ).toDouble( &yOk );
3984
3985 if ( xOk && yOk )
3986 {
3987 mPoint = QgsPointXY( x, y );
3988
3989 const QgsCoordinateReferenceSystem pointCrs( match.captured( 3 ) );
3990 if ( pointCrs.isValid() )
3991 {
3992 mCrs = pointCrs;
3993 }
3994 }
3995 else
3996 {
3997 mPoint = QgsPointXY();
3998 }
3999 }
4000 else
4001 {
4002 mPoint = QgsPointXY();
4003 }
4004
4005 updateRubberBand();
4006}
4007
4008void QgsProcessingPointPanel::updateRubberBand()
4009{
4010 if ( !mShowPointOnCanvas || !mCanvas )
4011 return;
4012
4013 if ( mPoint.isEmpty() )
4014 {
4015 mMapPointRubberBand.reset();
4016 return;
4017 }
4018
4019 if ( !mMapPointRubberBand )
4020 {
4021 mMapPointRubberBand = make_qobject_unique<QgsRubberBand>( mCanvas, Qgis::GeometryType::Point );
4022 mMapPointRubberBand->setZValue( 1000 );
4023 mMapPointRubberBand->setIcon( Qgis::RubberBandIconType::CrossX );
4024
4025 const double scaleFactor = mCanvas->fontMetrics().xHeight() * .4;
4026 mMapPointRubberBand->setWidth( scaleFactor );
4027 mMapPointRubberBand->setIconSize( scaleFactor * 5 );
4028
4029 mMapPointRubberBand->setSecondaryStrokeColor( QColor( 255, 255, 255, 100 ) );
4030 mMapPointRubberBand->setColor( QColor( 200, 0, 200 ) );
4031 }
4032
4033 mMapPointRubberBand->setToGeometry( QgsGeometry::fromPointXY( mPoint ), mCrs );
4034}
4035
4036
4037//
4038// QgsProcessingPointWidgetWrapper
4039//
4040
4041QgsProcessingPointParameterDefinitionWidget::QgsProcessingPointParameterDefinitionWidget(
4042 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4043)
4044 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4045{
4046 QVBoxLayout *vlayout = new QVBoxLayout();
4047 vlayout->setContentsMargins( 0, 0, 0, 0 );
4048
4049 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4050
4051 mDefaultLineEdit = new QLineEdit();
4052 mDefaultLineEdit->setToolTip( tr( "Point as 'x,y'" ) );
4053 mDefaultLineEdit->setPlaceholderText( tr( "Point as 'x,y'" ) );
4054 if ( const QgsProcessingParameterPoint *pointParam = dynamic_cast<const QgsProcessingParameterPoint *>( definition ) )
4055 {
4056 QgsPointXY point = QgsProcessingParameters::parameterAsPoint( pointParam, pointParam->defaultValueForGui(), context );
4057 mDefaultLineEdit->setText( u"%1,%2"_s.arg( QString::number( point.x(), 'f' ), QString::number( point.y(), 'f' ) ) );
4058 }
4059
4060 vlayout->addWidget( mDefaultLineEdit );
4061
4062 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4063
4064 setLayout( vlayout );
4065}
4066
4067QgsProcessingParameterDefinition *QgsProcessingPointParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4068{
4069 auto param = std::make_unique<QgsProcessingParameterPoint>( name, description, mDefaultLineEdit->text() );
4070 param->setFlags( flags );
4071 return param.release();
4072}
4073
4074QgsProcessingPointWidgetWrapper::QgsProcessingPointWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4075 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4076{}
4077
4078QWidget *QgsProcessingPointWidgetWrapper::createWidget()
4079{
4080 const QgsProcessingParameterPoint *pointParam = dynamic_cast<const QgsProcessingParameterPoint *>( parameterDefinition() );
4081 if ( !pointParam )
4082 return nullptr;
4083
4084 switch ( type() )
4085 {
4088 {
4089 mPanel = new QgsProcessingPointPanel( nullptr );
4090 if ( widgetContext().mapCanvas() )
4091 mPanel->setMapCanvas( widgetContext().mapCanvas() );
4092
4093 if ( pointParam->flags() & Qgis::ProcessingParameterFlag::Optional )
4094 mPanel->setAllowNull( true );
4095
4096 if ( type() == Qgis::ProcessingMode::Standard )
4097 mPanel->setShowPointOnCanvas( true );
4098
4099 mPanel->setToolTip( parameterDefinition()->toolTip() );
4100
4101 connect( mPanel, &QgsProcessingPointPanel::changed, this, [this] { emit widgetValueHasChanged( this ); } );
4102
4103 if ( mDialog )
4104 setDialog( mDialog ); // setup connections to panel - dialog was previously set before the widget was created
4105 return mPanel;
4106 }
4107
4109 {
4110 mLineEdit = new QLineEdit();
4111 mLineEdit->setToolTip( tr( "Point as 'x,y'" ) );
4112 connect( mLineEdit, &QLineEdit::textChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
4113 return mLineEdit;
4114 }
4115 }
4116 return nullptr;
4117}
4118
4119void QgsProcessingPointWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
4120{
4122 if ( mPanel && context.mapCanvas() )
4123 mPanel->setMapCanvas( context.mapCanvas() );
4124}
4125
4126void QgsProcessingPointWidgetWrapper::setDialog( QWidget *dialog )
4127{
4128 mDialog = dialog;
4129 if ( mPanel )
4130 {
4131 connect( mPanel, &QgsProcessingPointPanel::toggleDialogVisibility, mDialog, [this]( bool visible ) {
4132 if ( !visible )
4133 mDialog->showMinimized();
4134 else
4135 {
4136 mDialog->showNormal();
4137 mDialog->raise();
4138 mDialog->activateWindow();
4139 }
4140 } );
4141 }
4143}
4144
4145void QgsProcessingPointWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
4146{
4147 if ( mPanel )
4148 {
4149 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
4150 mPanel->clear();
4151 else
4152 {
4153 QgsPointXY p = QgsProcessingParameters::parameterAsPoint( parameterDefinition(), value, context );
4154 QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsPointCrs( parameterDefinition(), value, context );
4155 mPanel->setValue( p, crs );
4156 }
4157 }
4158 else if ( mLineEdit )
4159 {
4160 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
4161 mLineEdit->setText( v );
4162 }
4163}
4164
4165QVariant QgsProcessingPointWidgetWrapper::widgetValue() const
4166{
4167 if ( mPanel )
4168 {
4169 return mPanel->value();
4170 }
4171 else if ( mLineEdit )
4172 return mLineEdit->text().isEmpty() ? QVariant() : mLineEdit->text();
4173 else
4174 return QVariant();
4175}
4176
4177QString QgsProcessingPointWidgetWrapper::modelerExpressionFormatString() const
4178{
4179 return tr( "string of the format 'x,y' or a geometry value (centroid is used)" );
4180}
4181
4182QString QgsProcessingPointWidgetWrapper::parameterType() const
4183{
4185}
4186
4187QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4188{
4189 return new QgsProcessingPointWidgetWrapper( parameter, type );
4190}
4191
4192QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingPointWidgetWrapper::createParameterDefinitionWidget(
4194)
4195{
4196 return new QgsProcessingPointParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4197}
4198
4199
4200//
4201// QgsProcessingGeometryWidgetWrapper
4202//
4203
4204
4205QgsProcessingGeometryParameterDefinitionWidget::QgsProcessingGeometryParameterDefinitionWidget(
4206 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4207)
4208 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4209{
4210 QVBoxLayout *vlayout = new QVBoxLayout();
4211 vlayout->setContentsMargins( 0, 0, 0, 0 );
4212
4213 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4214
4215 mGeometryWidget = new QgsGeometryWidget();
4216 if ( const QgsProcessingParameterGeometry *geometryParam = dynamic_cast<const QgsProcessingParameterGeometry *>( definition ) )
4217 {
4218 QgsGeometry g = QgsProcessingParameters::parameterAsGeometry( geometryParam, geometryParam->defaultValueForGui(), context );
4219 if ( !g.isNull() )
4220 {
4221 mGeometryWidget->setGeometryValue( QgsReferencedGeometry( g, QgsCoordinateReferenceSystem() ) );
4222 }
4223 }
4224
4225 vlayout->addWidget( mGeometryWidget );
4226
4228
4229 setLayout( vlayout );
4230}
4231
4232QgsProcessingParameterDefinition *QgsProcessingGeometryParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4233{
4234 const QgsReferencedGeometry geometry = mGeometryWidget->geometryValue();
4235 auto param = std::make_unique<QgsProcessingParameterGeometry>( name, description, geometry.isEmpty() ? QVariant() : geometry.asWkt() );
4236 param->setFlags( flags );
4237 return param.release();
4238}
4239
4240QgsProcessingGeometryWidgetWrapper::QgsProcessingGeometryWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4241 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4242{}
4243
4244QWidget *QgsProcessingGeometryWidgetWrapper::createWidget()
4245{
4246 switch ( type() )
4247 {
4251 {
4252 mGeometryWidget = new QgsGeometryWidget();
4253 mGeometryWidget->setToolTip( parameterDefinition()->toolTip() );
4254 connect( mGeometryWidget, &QgsGeometryWidget::geometryValueChanged, this, [this]( const QgsReferencedGeometry & ) { emit widgetValueHasChanged( this ); } );
4255 return mGeometryWidget;
4256 }
4257 }
4258 return nullptr;
4259}
4260
4261void QgsProcessingGeometryWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
4262{
4263 if ( mGeometryWidget )
4264 {
4265 QgsGeometry g = QgsProcessingParameters::parameterAsGeometry( parameterDefinition(), value, context );
4266 if ( !g.isNull() )
4267 {
4268 mGeometryWidget->setGeometryValue( QgsReferencedGeometry( g, QgsCoordinateReferenceSystem() ) );
4269 }
4270 else
4271 {
4272 mGeometryWidget->clearGeometry();
4273 }
4274 }
4275}
4276
4277QVariant QgsProcessingGeometryWidgetWrapper::widgetValue() const
4278{
4279 if ( mGeometryWidget )
4280 {
4281 const QgsReferencedGeometry geometry = mGeometryWidget->geometryValue();
4282 return geometry.isEmpty() ? QVariant() : geometry.asWkt();
4283 }
4284 else
4285 {
4286 return QVariant();
4287 }
4288}
4289
4290QString QgsProcessingGeometryWidgetWrapper::modelerExpressionFormatString() const
4291{
4292 return tr( "string in the Well-Known-Text format or a geometry value" );
4293}
4294
4295QString QgsProcessingGeometryWidgetWrapper::parameterType() const
4296{
4298}
4299
4300QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingGeometryWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4301{
4302 return new QgsProcessingGeometryWidgetWrapper( parameter, type );
4303}
4304
4305QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingGeometryWidgetWrapper::createParameterDefinitionWidget(
4307)
4308{
4309 return new QgsProcessingGeometryParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4310}
4311
4312
4313//
4314// QgsProcessingColorWidgetWrapper
4315//
4316
4317
4318QgsProcessingColorParameterDefinitionWidget::QgsProcessingColorParameterDefinitionWidget(
4319 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4320)
4321 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4322{
4323 QVBoxLayout *vlayout = new QVBoxLayout();
4324 vlayout->setContentsMargins( 0, 0, 0, 0 );
4325
4326 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4327
4328 mDefaultColorButton = new QgsColorButton();
4329 mDefaultColorButton->setShowNull( true );
4330 mAllowOpacity = new QCheckBox( tr( "Allow opacity control" ) );
4331
4332 if ( const QgsProcessingParameterColor *colorParam = dynamic_cast<const QgsProcessingParameterColor *>( definition ) )
4333 {
4334 const QColor c = QgsProcessingParameters::parameterAsColor( colorParam, colorParam->defaultValueForGui(), context );
4335 if ( !c.isValid() )
4336 mDefaultColorButton->setToNull();
4337 else
4338 mDefaultColorButton->setColor( c );
4339 mAllowOpacity->setChecked( colorParam->opacityEnabled() );
4340 }
4341 else
4342 {
4343 mDefaultColorButton->setToNull();
4344 mAllowOpacity->setChecked( true );
4345 }
4346
4348 connect( mAllowOpacity, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4349
4350 vlayout->addWidget( mDefaultColorButton );
4351 vlayout->addWidget( mAllowOpacity );
4352 setLayout( vlayout );
4353}
4354
4355QgsProcessingParameterDefinition *QgsProcessingColorParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4356{
4357 auto param = std::make_unique<QgsProcessingParameterColor>( name, description, mDefaultColorButton->color(), mAllowOpacity->isChecked() );
4358 param->setFlags( flags );
4359 return param.release();
4360}
4361
4362QgsProcessingColorWidgetWrapper::QgsProcessingColorWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4363 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4364{}
4365
4366QWidget *QgsProcessingColorWidgetWrapper::createWidget()
4367{
4368 const QgsProcessingParameterColor *colorParam = dynamic_cast<const QgsProcessingParameterColor *>( parameterDefinition() );
4369 if ( !colorParam )
4370 return nullptr;
4371
4372 switch ( type() )
4373 {
4377 {
4378 mColorButton = new QgsColorButton( nullptr );
4379 mColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
4380
4381 if ( colorParam->flags() & Qgis::ProcessingParameterFlag::Optional )
4382 mColorButton->setShowNull( true );
4383
4384 mColorButton->setAllowOpacity( colorParam->opacityEnabled() );
4385 mColorButton->setToolTip( parameterDefinition()->toolTip() );
4386 mColorButton->setColorDialogTitle( parameterDefinition()->description() );
4387 if ( colorParam->defaultValueForGui().value<QColor>().isValid() )
4388 {
4389 mColorButton->setDefaultColor( colorParam->defaultValueForGui().value<QColor>() );
4390 }
4391
4392 connect( mColorButton, &QgsColorButton::colorChanged, this, [this] { emit widgetValueHasChanged( this ); } );
4393
4394 return mColorButton;
4395 }
4396 }
4397 return nullptr;
4398}
4399
4400void QgsProcessingColorWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
4401{
4402 if ( mColorButton )
4403 {
4404 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) || ( value.userType() == QMetaType::Type::QColor && !value.value<QColor>().isValid() ) )
4405 mColorButton->setToNull();
4406 else
4407 {
4408 const QColor c = QgsProcessingParameters::parameterAsColor( parameterDefinition(), value, context );
4409 if ( !c.isValid() && mColorButton->showNull() )
4410 mColorButton->setToNull();
4411 else
4412 mColorButton->setColor( c );
4413 }
4414 }
4415}
4416
4417QVariant QgsProcessingColorWidgetWrapper::widgetValue() const
4418{
4419 if ( mColorButton )
4420 return mColorButton->isNull() ? QVariant() : mColorButton->color();
4421 else
4422 return QVariant();
4423}
4424
4425QString QgsProcessingColorWidgetWrapper::modelerExpressionFormatString() const
4426{
4427 return tr( "color style string, e.g. #ff0000 or 255,0,0" );
4428}
4429
4430QString QgsProcessingColorWidgetWrapper::parameterType() const
4431{
4433}
4434
4435QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingColorWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4436{
4437 return new QgsProcessingColorWidgetWrapper( parameter, type );
4438}
4439
4440QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingColorWidgetWrapper::createParameterDefinitionWidget(
4442)
4443{
4444 return new QgsProcessingColorParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4445}
4446
4447
4448//
4449// QgsProcessingCoordinateOperationWidgetWrapper
4450//
4451
4452QgsProcessingCoordinateOperationParameterDefinitionWidget::QgsProcessingCoordinateOperationParameterDefinitionWidget(
4453 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4454)
4455 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4456{
4457 QVBoxLayout *vlayout = new QVBoxLayout();
4458 vlayout->setContentsMargins( 0, 0, 0, 0 );
4459
4460 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4461
4462 mDefaultLineEdit = new QLineEdit();
4463 if ( const QgsProcessingParameterCoordinateOperation *coordParam = dynamic_cast<const QgsProcessingParameterCoordinateOperation *>( definition ) )
4464 mDefaultLineEdit->setText( QgsProcessingParameters::parameterAsString( coordParam, coordParam->defaultValueForGui(), context ) );
4465 vlayout->addWidget( mDefaultLineEdit );
4466
4467 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4468
4469 mSourceParamComboBox = new QComboBox();
4470 mDestParamComboBox = new QComboBox();
4471 QString initialSource;
4472 QString initialDest;
4475 if ( const QgsProcessingParameterCoordinateOperation *itemParam = dynamic_cast<const QgsProcessingParameterCoordinateOperation *>( definition ) )
4476 {
4477 initialSource = itemParam->sourceCrsParameterName();
4478 initialDest = itemParam->destinationCrsParameterName();
4479 sourceCrs = QgsProcessingUtils::variantToCrs( itemParam->sourceCrs(), context );
4480 destCrs = QgsProcessingUtils::variantToCrs( itemParam->destinationCrs(), context );
4481 }
4482
4483 mSourceParamComboBox->addItem( QString(), QString() );
4484 mDestParamComboBox->addItem( QString(), QString() );
4485 if ( auto *lModel = widgetContext.model() )
4486 {
4487 // populate combo box with other model input choices
4488 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
4489 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
4490 {
4491 if ( definition && it->parameterName() == definition->name() )
4492 continue;
4493
4494 // TODO - we should probably filter this list?
4495 mSourceParamComboBox->addItem( it->parameterName(), it->parameterName() );
4496 mDestParamComboBox->addItem( it->parameterName(), it->parameterName() );
4497 if ( !initialSource.isEmpty() && initialSource == it->parameterName() )
4498 {
4499 mSourceParamComboBox->setCurrentIndex( mSourceParamComboBox->count() - 1 );
4500 }
4501 if ( !initialDest.isEmpty() && initialDest == it->parameterName() )
4502 {
4503 mDestParamComboBox->setCurrentIndex( mDestParamComboBox->count() - 1 );
4504 }
4505 }
4506 }
4507
4508 if ( mSourceParamComboBox->count() == 1 && !initialSource.isEmpty() )
4509 {
4510 // if no source candidates found, we just add the existing one as a placeholder
4511 mSourceParamComboBox->addItem( initialSource, initialSource );
4512 mSourceParamComboBox->setCurrentIndex( mSourceParamComboBox->count() - 1 );
4513 }
4514 if ( mDestParamComboBox->count() == 1 && !initialDest.isEmpty() )
4515 {
4516 // if no dest candidates found, we just add the existing one as a placeholder
4517 mDestParamComboBox->addItem( initialDest, initialDest );
4518 mDestParamComboBox->setCurrentIndex( mDestParamComboBox->count() - 1 );
4519 }
4520
4521 vlayout->addWidget( new QLabel( tr( "Source CRS parameter" ) ) );
4522 vlayout->addWidget( mSourceParamComboBox );
4523 vlayout->addWidget( new QLabel( tr( "Destination CRS parameter" ) ) );
4524 vlayout->addWidget( mDestParamComboBox );
4525
4526 connect( mSourceParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4527 connect( mDestParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4528
4529 mStaticSourceWidget = new QgsProjectionSelectionWidget();
4530 mStaticSourceWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
4531 mStaticSourceWidget->setCrs( sourceCrs );
4532 mStaticDestWidget = new QgsProjectionSelectionWidget();
4533 mStaticDestWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
4534 mStaticDestWidget->setCrs( destCrs );
4535
4536 vlayout->addWidget( new QLabel( tr( "Static source CRS" ) ) );
4537 vlayout->addWidget( mStaticSourceWidget );
4538 vlayout->addWidget( new QLabel( tr( "Static destination CRS" ) ) );
4539 vlayout->addWidget( mStaticDestWidget );
4540
4543
4544 setLayout( vlayout );
4545}
4546
4547QgsProcessingParameterDefinition *QgsProcessingCoordinateOperationParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4548{
4549 auto param = std::make_unique<QgsProcessingParameterCoordinateOperation>(
4550 name,
4551 description,
4552 mDefaultLineEdit->text(),
4553 mSourceParamComboBox->currentText(),
4554 mDestParamComboBox->currentText(),
4555 mStaticSourceWidget->crs().isValid() ? QVariant::fromValue( mStaticSourceWidget->crs() ) : QVariant(),
4556 mStaticDestWidget->crs().isValid() ? QVariant::fromValue( mStaticDestWidget->crs() ) : QVariant()
4557 );
4558 param->setFlags( flags );
4559 return param.release();
4560}
4561
4562QgsProcessingCoordinateOperationWidgetWrapper::QgsProcessingCoordinateOperationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4563 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4564{}
4565
4566QWidget *QgsProcessingCoordinateOperationWidgetWrapper::createWidget()
4567{
4568 const QgsProcessingParameterCoordinateOperation *coordParam = dynamic_cast<const QgsProcessingParameterCoordinateOperation *>( parameterDefinition() );
4569 if ( !coordParam )
4570 return nullptr;
4571
4573 mSourceCrs = QgsProcessingUtils::variantToCrs( coordParam->sourceCrs(), c );
4574 mDestCrs = QgsProcessingUtils::variantToCrs( coordParam->destinationCrs(), c );
4575 switch ( type() )
4576 {
4578 {
4579 mOperationWidget = new QgsCoordinateOperationWidget( nullptr );
4580 mOperationWidget->setShowMakeDefault( false );
4581 mOperationWidget->setShowFallbackOption( false );
4582 mOperationWidget->setToolTip( parameterDefinition()->toolTip() );
4583 mOperationWidget->setSourceCrs( mSourceCrs );
4584 mOperationWidget->setDestinationCrs( mDestCrs );
4585 mOperationWidget->setMapCanvas( mCanvas );
4586 if ( !coordParam->defaultValueForGui().toString().isEmpty() )
4587 {
4589 deets.proj = coordParam->defaultValueForGui().toString();
4590 mOperationWidget->setSelectedOperation( deets );
4591 }
4592
4593 connect( mOperationWidget, &QgsCoordinateOperationWidget::operationChanged, this, [this] { emit widgetValueHasChanged( this ); } );
4594
4595 return mOperationWidget;
4596 }
4597
4600 {
4601 mLineEdit = new QLineEdit();
4602 QHBoxLayout *layout = new QHBoxLayout();
4603 layout->addWidget( mLineEdit, 1 );
4604 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
4605
4606 QToolButton *button = new QToolButton();
4607 button->setText( QString( QChar( 0x2026 ) ) );
4608 connect( button, &QToolButton::clicked, this, [this, button] {
4609 QgsDatumTransformDialog dlg( mSourceCrs, mDestCrs, false, false, false, qMakePair( -1, -1 ), button, Qt::WindowFlags(), mLineEdit->text(), mCanvas );
4610 if ( dlg.exec() )
4611 {
4612 mLineEdit->setText( dlg.selectedDatumTransform().proj );
4613 emit widgetValueHasChanged( this );
4614 }
4615 } );
4616 layout->addWidget( button );
4617
4618 QWidget *w = new QWidget();
4619 layout->setContentsMargins( 0, 0, 0, 0 );
4620 w->setLayout( layout );
4621 return w;
4622 }
4623 }
4624 return nullptr;
4625}
4626
4627void QgsProcessingCoordinateOperationWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
4628{
4630 switch ( type() )
4631 {
4634 {
4635 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
4636 {
4637 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterCoordinateOperation *>( parameterDefinition() )->sourceCrsParameterName() )
4638 {
4639 setSourceCrsParameterValue( wrapper->parameterValue() );
4640 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setSourceCrsParameterValue( wrapper->parameterValue() ); } );
4641 }
4642 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterCoordinateOperation *>( parameterDefinition() )->destinationCrsParameterName() )
4643 {
4644 setDestinationCrsParameterValue( wrapper->parameterValue() );
4645 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setDestinationCrsParameterValue( wrapper->parameterValue() ); } );
4646 }
4647 }
4648 break;
4649 }
4650
4652 break;
4653 }
4654}
4655
4656void QgsProcessingCoordinateOperationWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
4657{
4658 mCanvas = context.mapCanvas();
4659 if ( mOperationWidget )
4660 mOperationWidget->setMapCanvas( context.mapCanvas() );
4661}
4662
4663void QgsProcessingCoordinateOperationWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
4664{
4665 if ( mOperationWidget )
4666 {
4667 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString ) )
4668 {
4670 deets.proj = value.toString();
4671 mOperationWidget->setSelectedOperation( deets );
4672 }
4673 }
4674 if ( mLineEdit )
4675 {
4676 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString ) )
4677 {
4678 mLineEdit->setText( value.toString() );
4679 }
4680 }
4681}
4682
4683QVariant QgsProcessingCoordinateOperationWidgetWrapper::widgetValue() const
4684{
4685 if ( mOperationWidget )
4686 return mOperationWidget->selectedOperation().proj;
4687 else if ( mLineEdit )
4688 return mLineEdit->text();
4689 else
4690 return QVariant();
4691}
4692
4693QString QgsProcessingCoordinateOperationWidgetWrapper::modelerExpressionFormatString() const
4694{
4695 return tr( "Proj coordinate operation string, e.g. '+proj=pipeline +step +inv...'" );
4696}
4697
4698void QgsProcessingCoordinateOperationWidgetWrapper::setSourceCrsParameterValue( const QVariant &value )
4699{
4700 QgsProcessingContext *context = nullptr;
4701 std::unique_ptr<QgsProcessingContext> tmpContext;
4702 if ( mProcessingContextGenerator )
4703 context = mProcessingContextGenerator->processingContext();
4704
4705 if ( !context )
4706 {
4707 tmpContext = std::make_unique<QgsProcessingContext>();
4708 context = tmpContext.get();
4709 }
4710
4711 mSourceCrs = QgsProcessingUtils::variantToCrs( value, *context );
4712 if ( mOperationWidget )
4713 {
4714 mOperationWidget->setSourceCrs( mSourceCrs );
4715 mOperationWidget->setSelectedOperationUsingContext( context->transformContext() );
4716 }
4717}
4718
4719void QgsProcessingCoordinateOperationWidgetWrapper::setDestinationCrsParameterValue( const QVariant &value )
4720{
4721 QgsProcessingContext *context = nullptr;
4722 std::unique_ptr<QgsProcessingContext> tmpContext;
4723 if ( mProcessingContextGenerator )
4724 context = mProcessingContextGenerator->processingContext();
4725
4726 if ( !context )
4727 {
4728 tmpContext = std::make_unique<QgsProcessingContext>();
4729 context = tmpContext.get();
4730 }
4731
4732 mDestCrs = QgsProcessingUtils::variantToCrs( value, *context );
4733 if ( mOperationWidget )
4734 {
4735 mOperationWidget->setDestinationCrs( mDestCrs );
4736 mOperationWidget->setSelectedOperationUsingContext( context->transformContext() );
4737 }
4738}
4739
4740QString QgsProcessingCoordinateOperationWidgetWrapper::parameterType() const
4741{
4743}
4744
4745QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingCoordinateOperationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4746{
4747 return new QgsProcessingCoordinateOperationWidgetWrapper( parameter, type );
4748}
4749
4750QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingCoordinateOperationWidgetWrapper::createParameterDefinitionWidget(
4752)
4753{
4754 return new QgsProcessingCoordinateOperationParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4755}
4756
4757
4758//
4759// QgsProcessingFieldPanelWidget
4760//
4761
4762QgsProcessingFieldPanelWidget::QgsProcessingFieldPanelWidget( QWidget *parent, const QgsProcessingParameterField *param )
4763 : QWidget( parent )
4764 , mParam( param )
4765{
4766 QHBoxLayout *hl = new QHBoxLayout();
4767 hl->setContentsMargins( 0, 0, 0, 0 );
4768
4769 mLineEdit = new QLineEdit();
4770 mLineEdit->setEnabled( false );
4771 hl->addWidget( mLineEdit, 1 );
4772
4773 mToolButton = new QToolButton();
4774 mToolButton->setText( QString( QChar( 0x2026 ) ) );
4775 hl->addWidget( mToolButton );
4776
4777 setLayout( hl );
4778
4779 if ( mParam )
4780 {
4781 mLineEdit->setText( tr( "%n field(s) selected", nullptr, 0 ) );
4782 }
4783
4784 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingFieldPanelWidget::showDialog );
4785}
4786
4787void QgsProcessingFieldPanelWidget::setFields( const QgsFields &fields )
4788{
4789 mFields = fields;
4790
4791 // ensure that after setting new fields the value does not contain fields
4792 // which are not available anymore, see https://github.com/qgis/QGIS/issues/39351
4793 QVariantList availableFields;
4794 for ( const QgsField &field : std::as_const( mFields ) )
4795 {
4796 availableFields << field.name();
4797 }
4798 QList<QVariant>::iterator it = std::remove_if( mValue.begin(), mValue.end(), [&availableFields]( const QVariant &value ) { return !availableFields.contains( value ); } );
4799 mValue.erase( it, mValue.end() );
4800
4801 updateSummaryText();
4802 emit changed();
4803}
4804
4805void QgsProcessingFieldPanelWidget::setValue( const QVariant &value )
4806{
4807 if ( value.isValid() )
4808 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
4809 else
4810 mValue.clear();
4811
4812 updateSummaryText();
4813 emit changed();
4814}
4815
4816void QgsProcessingFieldPanelWidget::showDialog()
4817{
4818 QVariantList availableOptions;
4819 availableOptions.reserve( mFields.size() );
4820 for ( const QgsField &field : std::as_const( mFields ) )
4821 {
4822 availableOptions << field.name();
4823 }
4824
4826 if ( panel && panel->dockMode() )
4827 {
4828 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
4829 widget->setPanelTitle( mParam->description() );
4830
4831 widget->setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
4832
4833 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
4834 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
4835 panel->openPanel( widget );
4836 }
4837 else
4838 {
4839 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
4840
4841 dlg.setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
4842 if ( dlg.exec() )
4843 {
4844 setValue( dlg.selectedOptions() );
4845 }
4846 }
4847}
4848
4849void QgsProcessingFieldPanelWidget::updateSummaryText()
4850{
4851 if ( !mParam )
4852 return;
4853
4854 if ( mValue.empty() )
4855 {
4856 mLineEdit->setText( tr( "%n field(s) selected", nullptr, 0 ) );
4857 }
4858 else
4859 {
4860 QStringList values;
4861 values.reserve( mValue.size() );
4862 for ( const QVariant &val : std::as_const( mValue ) )
4863 {
4864 values << val.toString();
4865 }
4866
4867 const QString concatenated = values.join( tr( "," ) );
4868 if ( concatenated.length() < 100 )
4869 mLineEdit->setText( concatenated );
4870 else
4871 mLineEdit->setText( tr( "%n field(s) selected", nullptr, mValue.count() ) );
4872 }
4873}
4874
4875
4876//
4877// QgsProcessingFieldWidgetWrapper
4878//
4879
4880QgsProcessingFieldParameterDefinitionWidget::QgsProcessingFieldParameterDefinitionWidget(
4881 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4882)
4883 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4884{
4885 QVBoxLayout *vlayout = new QVBoxLayout();
4886 vlayout->setContentsMargins( 0, 0, 0, 0 );
4887
4888 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
4889 mParentLayerComboBox = new QComboBox();
4890
4891 QString initialParent;
4892 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4893 initialParent = fieldParam->parentLayerParameterName();
4894
4895 if ( auto *lModel = widgetContext.model() )
4896 {
4897 // populate combo box with other model input choices
4898 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
4899 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
4900 {
4901 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
4902 {
4903 mParentLayerComboBox->addItem( definition->description(), definition->name() );
4904 if ( !initialParent.isEmpty() && initialParent == definition->name() )
4905 {
4906 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4907 }
4908 }
4909 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
4910 {
4911 mParentLayerComboBox->addItem( definition->description(), definition->name() );
4912 if ( !initialParent.isEmpty() && initialParent == definition->name() )
4913 {
4914 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4915 }
4916 }
4917 else if ( const QgsProcessingParameterMultipleLayers *definition = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
4918 {
4919 if ( definition->layerType() == Qgis::ProcessingSourceType::Vector )
4920 {
4921 mParentLayerComboBox->addItem( definition->description(), definition->name() );
4922 if ( !initialParent.isEmpty() && initialParent == definition->name() )
4923 {
4924 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4925 }
4926 }
4927 }
4928 }
4929 }
4930
4931 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
4932 {
4933 // if no parent candidates found, we just add the existing one as a placeholder
4934 mParentLayerComboBox->addItem( initialParent, initialParent );
4935 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4936 }
4937
4938 vlayout->addWidget( mParentLayerComboBox );
4939 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4940
4941 vlayout->addWidget( new QLabel( tr( "Allowed data type" ) ) );
4942 mDataTypeComboBox = new QComboBox();
4943 mDataTypeComboBox->addItem( tr( "Any" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Any ) );
4944 mDataTypeComboBox->addItem( tr( "Number" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Numeric ) );
4945 mDataTypeComboBox->addItem( tr( "String" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::String ) );
4946 mDataTypeComboBox->addItem( tr( "Date/time" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::DateTime ) );
4947 mDataTypeComboBox->addItem( tr( "Binary" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Binary ) );
4948 mDataTypeComboBox->addItem( tr( "Boolean" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Boolean ) );
4949 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4950 mDataTypeComboBox->setCurrentIndex( mDataTypeComboBox->findData( static_cast<int>( fieldParam->dataType() ) ) );
4951
4952 vlayout->addWidget( mDataTypeComboBox );
4953 connect( mDataTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4954
4955 mAllowMultipleCheckBox = new QCheckBox( tr( "Accept multiple fields" ) );
4956 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4957 mAllowMultipleCheckBox->setChecked( fieldParam->allowMultiple() );
4958
4959 vlayout->addWidget( mAllowMultipleCheckBox );
4960 connect( mDataTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4961
4962 connect( mAllowMultipleCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4963
4964 mDefaultToAllCheckBox = new QCheckBox( tr( "Select all fields by default" ) );
4965 mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() );
4966 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4967 mDefaultToAllCheckBox->setChecked( fieldParam->defaultToAllFields() );
4968
4969 vlayout->addWidget( mDefaultToAllCheckBox );
4970 connect( mDefaultToAllCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4971
4972 connect( mAllowMultipleCheckBox, &QCheckBox::stateChanged, this, [this] { mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() ); } );
4973
4974 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4975
4976 mDefaultLineEdit = new QLineEdit();
4977 mDefaultLineEdit->setToolTip( tr( "Default field name, or ; separated list of field names for multiple field parameters" ) );
4978 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4979 {
4980 const QStringList fields = QgsProcessingParameters::parameterAsStrings( fieldParam, fieldParam->defaultValueForGui(), context );
4981 mDefaultLineEdit->setText( fields.join( ';' ) );
4982 }
4983 vlayout->addWidget( mDefaultLineEdit );
4984
4985 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4986
4987 setLayout( vlayout );
4988}
4989
4990QgsProcessingParameterDefinition *QgsProcessingFieldParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4991{
4992 Qgis::ProcessingFieldParameterDataType dataType = static_cast<Qgis::ProcessingFieldParameterDataType>( mDataTypeComboBox->currentData().toInt() );
4993
4994 QVariant defaultValue;
4995 if ( !mDefaultLineEdit->text().trimmed().isEmpty() )
4996 {
4997 defaultValue = mDefaultLineEdit->text();
4998 }
4999 auto param = std::make_unique<
5000 QgsProcessingParameterField>( name, description, defaultValue, mParentLayerComboBox->currentData().toString(), dataType, mAllowMultipleCheckBox->isChecked(), false, mDefaultToAllCheckBox->isChecked() );
5001 param->setFlags( flags );
5002 return param.release();
5003}
5004
5005QgsProcessingFieldWidgetWrapper::QgsProcessingFieldWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5006 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5007{}
5008
5009QWidget *QgsProcessingFieldWidgetWrapper::createWidget()
5010{
5011 const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5012 if ( !fieldParam )
5013 return nullptr;
5014
5015 switch ( type() )
5016 {
5019 {
5020 if ( fieldParam->allowMultiple() )
5021 {
5022 mPanel = new QgsProcessingFieldPanelWidget( nullptr, fieldParam );
5023 mPanel->setToolTip( parameterDefinition()->toolTip() );
5024 connect( mPanel, &QgsProcessingFieldPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
5025 return mPanel;
5026 }
5027 else
5028 {
5029 mComboBox = new QgsFieldComboBox();
5030 mComboBox->setAllowEmptyFieldName( fieldParam->flags() & Qgis::ProcessingParameterFlag::Optional );
5031
5033 mComboBox->setFilters( QgsFieldProxyModel::Numeric );
5034 else if ( fieldParam->dataType() == Qgis::ProcessingFieldParameterDataType::String )
5035 mComboBox->setFilters( QgsFieldProxyModel::String );
5038 else if ( fieldParam->dataType() == Qgis::ProcessingFieldParameterDataType::Binary )
5039 mComboBox->setFilters( QgsFieldProxyModel::Binary );
5041 mComboBox->setFilters( QgsFieldProxyModel::Boolean );
5042
5043 mComboBox->setToolTip( parameterDefinition()->toolTip() );
5044 connect( mComboBox, &QgsFieldComboBox::fieldChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
5045 return mComboBox;
5046 }
5047 }
5048
5050 {
5051 mLineEdit = new QLineEdit();
5052 mLineEdit->setToolTip( QObject::tr( "Name of field (separate field names with ; for multiple field parameters)" ) );
5053 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
5054 return mLineEdit;
5055 }
5056 }
5057 return nullptr;
5058}
5059
5060void QgsProcessingFieldWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
5061{
5063 switch ( type() )
5064 {
5067 {
5068 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
5069 {
5070 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterField *>( parameterDefinition() )->parentLayerParameterName() )
5071 {
5072 setParentLayerWrapperValue( wrapper );
5073 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
5074 break;
5075 }
5076 }
5077 break;
5078 }
5079
5081 break;
5082 }
5083}
5084
5085void QgsProcessingFieldWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
5086{
5087 // evaluate value to layer
5088 QgsProcessingContext *context = nullptr;
5089 std::unique_ptr<QgsProcessingContext> tmpContext;
5090 if ( mProcessingContextGenerator )
5091 context = mProcessingContextGenerator->processingContext();
5092
5093 if ( !context )
5094 {
5095 tmpContext = std::make_unique<QgsProcessingContext>();
5096 context = tmpContext.get();
5097 }
5098
5099 QVariant value = parentWrapper->parameterValue();
5100
5101 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
5102 {
5103 // input is a QgsProcessingFeatureSourceDefinition - source from it.
5104 // this is normally discouraged, and algorithms should NEVER do this -- but in this case we can make
5105 // certain assumptions due to the fact that we're calling this outside of algorithm/model execution and all sources
5106 // should be real map layers at this stage
5107 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
5108 value = fromVar.source;
5109 }
5110
5111 bool valueSet = false;
5112 const QList<QgsMapLayer *> layers = QgsProcessingParameters::parameterAsLayerList( parentWrapper->parameterDefinition(), value, *context );
5113
5114 // several layers, populate with intersection of layers fields
5115 if ( layers.count() > 1 )
5116 {
5117 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layers.at( 0 ) );
5118 QgsFields fields = vlayer && vlayer->isValid() ? vlayer->fields() : QgsFields();
5119 const QList<QgsMapLayer *> remainingLayers = layers.mid( 1 );
5120 for ( QgsMapLayer *layer : remainingLayers )
5121 {
5122 if ( fields.isEmpty() )
5123 break;
5124
5125 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
5126 if ( !vlayer || !vlayer->isValid() )
5127 {
5128 fields = QgsFields();
5129 break;
5130 }
5131
5132 for ( int fieldIdx = fields.count() - 1; fieldIdx >= 0; fieldIdx-- )
5133 {
5134 if ( vlayer->fields().lookupField( fields.at( fieldIdx ).name() ) < 0 )
5135 fields.remove( fieldIdx );
5136 }
5137 }
5138
5139 if ( mComboBox )
5140 mComboBox->setFields( fields );
5141 else if ( mPanel )
5142 mPanel->setFields( filterFields( fields ) );
5143
5144 valueSet = true;
5145 }
5146
5147 // only one layer
5148 if ( !valueSet && !layers.isEmpty() && layers.at( 0 )->isValid() )
5149 {
5150 QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( layers.at( 0 ) );
5151
5152 // need to grab ownership of layer if required - otherwise layer may be deleted when context
5153 // goes out of scope
5154 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
5155 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
5156 {
5157 mParentLayer.reset( qobject_cast<QgsVectorLayer *>( ownedLayer.release() ) );
5158 layer = mParentLayer.get();
5159 }
5160 else
5161 {
5162 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
5163 }
5164
5165 if ( mComboBox )
5166 mComboBox->setLayer( layer );
5167 else if ( mPanel )
5168 mPanel->setFields( filterFields( layer->fields() ) );
5169
5170 valueSet = true;
5171 }
5172
5173 if ( !valueSet )
5174 {
5175 std::unique_ptr<QgsProcessingFeatureSource> source( QgsProcessingParameters::parameterAsSource( parentWrapper->parameterDefinition(), value, *context ) );
5176 if ( source )
5177 {
5178 const QgsFields fields = source->fields();
5179 if ( mComboBox )
5180 mComboBox->setFields( fields );
5181 else if ( mPanel )
5182 mPanel->setFields( filterFields( fields ) );
5183
5184 valueSet = true;
5185 }
5186 }
5187
5188 if ( !valueSet )
5189 {
5190 if ( mComboBox )
5191 mComboBox->setLayer( nullptr );
5192 else if ( mPanel )
5193 mPanel->setFields( QgsFields() );
5194
5195 if ( value.isValid() && widgetContext().messageBar() )
5196 {
5197 widgetContext().messageBar()->clearWidgets();
5198 widgetContext().messageBar()->pushMessage( QString(), QObject::tr( "Could not load selected layer/table. Dependent field could not be populated" ), Qgis::MessageLevel::Info );
5199 }
5200 return;
5201 }
5202
5203 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5204 if ( mPanel && fieldParam->defaultToAllFields() )
5205 {
5206 QVariantList val;
5207 val.reserve( mPanel->fields().size() );
5208 for ( const QgsField &field : mPanel->fields() )
5209 val << field.name();
5210 setWidgetValue( val, *context );
5211 }
5212 else if ( fieldParam->defaultValueForGui().isValid() )
5213 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
5214}
5215
5216void QgsProcessingFieldWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5217{
5218 if ( mComboBox )
5219 {
5220 if ( !value.isValid() )
5221 mComboBox->setField( QString() );
5222 else
5223 {
5224 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
5225 mComboBox->setField( v );
5226 }
5227 }
5228 else if ( mPanel )
5229 {
5230 QVariantList opts;
5231 if ( value.isValid() )
5232 {
5233 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
5234 opts.reserve( v.size() );
5235 for ( const QString &i : v )
5236 opts << i;
5237 }
5238 if ( mPanel )
5239 mPanel->setValue( opts );
5240 }
5241 else if ( mLineEdit )
5242 {
5243 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5244 if ( fieldParam->allowMultiple() )
5245 {
5246 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
5247 mLineEdit->setText( v.join( ';' ) );
5248 }
5249 else
5250 {
5251 mLineEdit->setText( QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context ) );
5252 }
5253 }
5254}
5255
5256QVariant QgsProcessingFieldWidgetWrapper::widgetValue() const
5257{
5258 if ( mComboBox )
5259 return mComboBox->currentField();
5260 else if ( mPanel )
5261 return mPanel->value();
5262 else if ( mLineEdit )
5263 {
5264 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5265 if ( fieldParam->allowMultiple() )
5266 {
5267 return mLineEdit->text().split( ';' );
5268 }
5269 else
5270 return mLineEdit->text();
5271 }
5272 else
5273 return QVariant();
5274}
5275
5276QString QgsProcessingFieldWidgetWrapper::modelerExpressionFormatString() const
5277{
5278 return tr( "selected field names as an array of names, or semicolon separated string of options (e.g. 'fid;place_name')" );
5279}
5280
5281const QgsVectorLayer *QgsProcessingFieldWidgetWrapper::linkedVectorLayer() const
5282{
5283 if ( mComboBox && mComboBox->layer() )
5284 return mComboBox->layer();
5285
5287}
5288
5289QgsFields QgsProcessingFieldWidgetWrapper::filterFields( const QgsFields &fields ) const
5290{
5291 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5292 QgsFields res;
5293 for ( const QgsField &f : fields )
5294 {
5295 switch ( fieldParam->dataType() )
5296 {
5298 res.append( f );
5299 break;
5300
5302 if ( f.isNumeric() )
5303 res.append( f );
5304 break;
5305
5307 if ( f.type() == QMetaType::Type::QString )
5308 res.append( f );
5309 break;
5310
5312 if ( f.type() == QMetaType::Type::QDate || f.type() == QMetaType::Type::QTime || f.type() == QMetaType::Type::QDateTime )
5313 res.append( f );
5314 break;
5315
5317 if ( f.type() == QMetaType::Type::QByteArray )
5318 res.append( f );
5319 break;
5320
5322 if ( f.type() == QMetaType::Type::Bool )
5323 res.append( f );
5324 break;
5325 }
5326 }
5327
5328 return res;
5329}
5330
5331QString QgsProcessingFieldWidgetWrapper::parameterType() const
5332{
5334}
5335
5336QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFieldWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5337{
5338 return new QgsProcessingFieldWidgetWrapper( parameter, type );
5339}
5340
5341QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFieldWidgetWrapper::createParameterDefinitionWidget(
5343)
5344{
5345 return new QgsProcessingFieldParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5346}
5347
5348//
5349// QgsProcessingMapThemeWidgetWrapper
5350//
5351
5352
5353QgsProcessingMapThemeParameterDefinitionWidget::QgsProcessingMapThemeParameterDefinitionWidget(
5354 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5355)
5356 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5357{
5358 QVBoxLayout *vlayout = new QVBoxLayout();
5359 vlayout->setContentsMargins( 0, 0, 0, 0 );
5360
5361 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
5362
5363 mDefaultComboBox = new QComboBox();
5364 mDefaultComboBox->addItem( QString(), QVariant( -1 ) );
5365
5366 const QStringList mapThemes = widgetContext.project() ? widgetContext.project()->mapThemeCollection()->mapThemes() : QgsProject::instance()->mapThemeCollection()->mapThemes();
5367 for ( const QString &theme : mapThemes )
5368 {
5369 mDefaultComboBox->addItem( QgsApplication::getThemeIcon( u"/mActionShowAllLayers.svg"_s ), theme, theme );
5370 }
5371 mDefaultComboBox->setEditable( true );
5372
5373 if ( const QgsProcessingParameterMapTheme *themeParam = dynamic_cast<const QgsProcessingParameterMapTheme *>( definition ) )
5374 {
5375 if ( themeParam->defaultValueForGui().isValid() )
5376 mDefaultComboBox->setCurrentText( QgsProcessingParameters::parameterAsString( themeParam, themeParam->defaultValueForGui(), context ) );
5377 else
5378 mDefaultComboBox->setCurrentIndex( mDefaultComboBox->findData( -1 ) );
5379 }
5380 else
5381 mDefaultComboBox->setCurrentIndex( mDefaultComboBox->findData( -1 ) );
5382
5383 vlayout->addWidget( mDefaultComboBox );
5384
5385 connect( mDefaultComboBox, &QComboBox::currentTextChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5386
5387 setLayout( vlayout );
5388}
5389
5390QgsProcessingParameterDefinition *QgsProcessingMapThemeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5391{
5392 QVariant defaultVal;
5393 if ( mDefaultComboBox->currentText().isEmpty() )
5394 defaultVal = QVariant();
5395 else
5396 defaultVal = mDefaultComboBox->currentText();
5397 auto param = std::make_unique<QgsProcessingParameterMapTheme>( name, description, defaultVal );
5398 param->setFlags( flags );
5399 return param.release();
5400}
5401
5402
5403QgsProcessingMapThemeWidgetWrapper::QgsProcessingMapThemeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5404 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5405{}
5406
5407QWidget *QgsProcessingMapThemeWidgetWrapper::createWidget()
5408{
5409 const QgsProcessingParameterMapTheme *themeParam = dynamic_cast<const QgsProcessingParameterMapTheme *>( parameterDefinition() );
5410 if ( !themeParam )
5411 return nullptr;
5412
5413 mComboBox = new QComboBox();
5414
5415 if ( themeParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5416 mComboBox->addItem( tr( "[Not selected]" ), QVariant( -1 ) );
5417
5418 const QStringList mapThemes = widgetContext().project() ? widgetContext().project()->mapThemeCollection()->mapThemes() : QgsProject::instance()->mapThemeCollection()->mapThemes();
5419 for ( const QString &theme : mapThemes )
5420 {
5421 mComboBox->addItem( QgsApplication::getThemeIcon( u"/mActionShowAllLayers.svg"_s ), theme, theme );
5422 }
5423
5424 switch ( type() )
5425 {
5428 break;
5429
5431 mComboBox->setEditable( true );
5432 break;
5433 }
5434
5435 mComboBox->setToolTip( parameterDefinition()->toolTip() );
5436 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { emit widgetValueHasChanged( this ); } );
5437
5438 return mComboBox;
5439}
5440
5441void QgsProcessingMapThemeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5442{
5443 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
5444
5445 if ( !value.isValid() )
5446 mComboBox->setCurrentIndex( mComboBox->findData( QVariant( -1 ) ) );
5447 else
5448 {
5449 if ( mComboBox->isEditable() && mComboBox->findData( v ) == -1 )
5450 {
5451 const QString prev = mComboBox->currentText();
5452 mComboBox->setCurrentText( v );
5453 if ( prev != v )
5454 emit widgetValueHasChanged( this );
5455 }
5456 else
5457 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
5458 }
5459}
5460
5461QVariant QgsProcessingMapThemeWidgetWrapper::widgetValue() const
5462{
5463 if ( mComboBox )
5464 return mComboBox->currentData().toInt() == -1 ? QVariant()
5465 : !mComboBox->currentData().isValid() && mComboBox->isEditable() ? mComboBox->currentText().isEmpty() ? QVariant() : QVariant( mComboBox->currentText() )
5466 : mComboBox->currentData();
5467 else
5468 return QVariant();
5469}
5470
5471QString QgsProcessingMapThemeWidgetWrapper::modelerExpressionFormatString() const
5472{
5473 return tr( "map theme as a string value (e.g. 'base maps')" );
5474}
5475
5476QString QgsProcessingMapThemeWidgetWrapper::parameterType() const
5477{
5479}
5480
5481QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMapThemeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5482{
5483 return new QgsProcessingMapThemeWidgetWrapper( parameter, type );
5484}
5485
5486QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMapThemeWidgetWrapper::createParameterDefinitionWidget(
5488)
5489{
5490 return new QgsProcessingMapThemeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5491}
5492
5493
5494//
5495// QgsProcessingDateTimeWidgetWrapper
5496//
5497
5498
5499QgsProcessingDateTimeParameterDefinitionWidget::QgsProcessingDateTimeParameterDefinitionWidget(
5500 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5501)
5502 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5503{
5504 QVBoxLayout *vlayout = new QVBoxLayout();
5505 vlayout->setContentsMargins( 0, 0, 0, 0 );
5506
5507 vlayout->addWidget( new QLabel( tr( "Type" ) ) );
5508
5509 mTypeComboBox = new QComboBox();
5510 mTypeComboBox->addItem( tr( "Date and Time" ), static_cast<int>( Qgis::ProcessingDateTimeParameterDataType::DateTime ) );
5511 mTypeComboBox->addItem( tr( "Date" ), static_cast<int>( Qgis::ProcessingDateTimeParameterDataType::Date ) );
5512 mTypeComboBox->addItem( tr( "Time" ), static_cast<int>( Qgis::ProcessingDateTimeParameterDataType::Time ) );
5513 if ( const QgsProcessingParameterDateTime *datetimeParam = dynamic_cast<const QgsProcessingParameterDateTime *>( definition ) )
5514 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( datetimeParam->dataType() ) ) );
5515 else
5516 mTypeComboBox->setCurrentIndex( 0 );
5517 vlayout->addWidget( mTypeComboBox );
5518
5519 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5520
5521 setLayout( vlayout );
5522}
5523
5524QgsProcessingParameterDefinition *QgsProcessingDateTimeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5525{
5526 auto param = std::make_unique<QgsProcessingParameterDateTime>( name, description );
5527 param->setDataType( static_cast<Qgis::ProcessingDateTimeParameterDataType>( mTypeComboBox->currentData().toInt() ) );
5528 param->setFlags( flags );
5529 return param.release();
5530}
5531
5532
5533QgsProcessingDateTimeWidgetWrapper::QgsProcessingDateTimeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5534 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5535{}
5536
5537QWidget *QgsProcessingDateTimeWidgetWrapper::createWidget()
5538{
5539 const QgsProcessingParameterDateTime *dateTimeParam = dynamic_cast<const QgsProcessingParameterDateTime *>( parameterDefinition() );
5540 if ( !dateTimeParam )
5541 return nullptr;
5542
5543 QgsDateTimeEdit *widget = nullptr;
5544 switch ( dateTimeParam->dataType() )
5545 {
5547 mDateTimeEdit = new QgsDateTimeEdit();
5548 widget = mDateTimeEdit;
5549 break;
5550
5552 mDateEdit = new QgsDateEdit();
5553 widget = mDateEdit;
5554 break;
5555
5557 mTimeEdit = new QgsTimeEdit();
5558 widget = mTimeEdit;
5559 break;
5560 }
5561 if ( !widget )
5562 return nullptr;
5563
5564 if ( dateTimeParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5565 {
5566 widget->setNullRepresentation( tr( "[Not selected]" ) );
5567 widget->setAllowNull( true );
5568 }
5569 else
5570 {
5571 widget->setAllowNull( false );
5572 }
5573 widget->setToolTip( parameterDefinition()->toolTip() );
5574
5575 if ( mDateTimeEdit )
5576 {
5577 connect( mDateTimeEdit, &QgsDateTimeEdit::valueChanged, this, [this]( const QDateTime & ) { emit widgetValueHasChanged( this ); } );
5578 }
5579 else if ( mDateEdit )
5580 {
5581 connect( mDateEdit, &QgsDateEdit::dateValueChanged, this, [this]( const QDate & ) { emit widgetValueHasChanged( this ); } );
5582 }
5583 else if ( mTimeEdit )
5584 {
5585 connect( mTimeEdit, &QgsTimeEdit::timeValueChanged, this, [this]( const QTime & ) { emit widgetValueHasChanged( this ); } );
5586 }
5587
5588 return widget;
5589}
5590
5591QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDateTimeWidgetWrapper::createParameterDefinitionWidget(
5593)
5594{
5595 return new QgsProcessingDateTimeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5596}
5597
5598void QgsProcessingDateTimeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5599{
5600 if ( mDateTimeEdit )
5601 {
5602 mDateTimeEdit->setDateTime( QgsProcessingParameters::parameterAsDateTime( parameterDefinition(), value, context ) );
5603 }
5604 else if ( mDateEdit )
5605 {
5606 mDateEdit->setDate( QgsProcessingParameters::parameterAsDate( parameterDefinition(), value, context ) );
5607 }
5608 else if ( mTimeEdit )
5609 {
5610 mTimeEdit->setTime( QgsProcessingParameters::parameterAsTime( parameterDefinition(), value, context ) );
5611 }
5612}
5613
5614QVariant QgsProcessingDateTimeWidgetWrapper::widgetValue() const
5615{
5616 if ( mDateTimeEdit )
5617 return !mDateTimeEdit->dateTime().isNull() && mDateTimeEdit->dateTime().isValid() ? QVariant( mDateTimeEdit->dateTime() ) : QVariant();
5618 else if ( mDateEdit )
5619 return !mDateEdit->date().isNull() && mDateEdit->date().isValid() ? QVariant( mDateEdit->date() ) : QVariant();
5620 else if ( mTimeEdit )
5621 return !mTimeEdit->time().isNull() && mTimeEdit->time().isValid() ? QVariant( mTimeEdit->time() ) : QVariant();
5622 else
5623 return QVariant();
5624}
5625
5626QString QgsProcessingDateTimeWidgetWrapper::modelerExpressionFormatString() const
5627{
5628 const QgsProcessingParameterDateTime *dateTimeParam = dynamic_cast<const QgsProcessingParameterDateTime *>( parameterDefinition() );
5629 if ( dateTimeParam )
5630 {
5631 switch ( dateTimeParam->dataType() )
5632 {
5634 return tr( "datetime value, or a ISO string representation of a datetime" );
5635
5637 return tr( "date value, or a ISO string representation of a date" );
5638
5640 return tr( "time value, or a ISO string representation of a time" );
5641 }
5642 }
5643 return QString();
5644}
5645
5646QString QgsProcessingDateTimeWidgetWrapper::parameterType() const
5647{
5649}
5650
5651QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDateTimeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5652{
5653 return new QgsProcessingDateTimeWidgetWrapper( parameter, type );
5654}
5655
5656
5657//
5658// QgsProcessingProviderConnectionWidgetWrapper
5659//
5660
5661QgsProcessingProviderConnectionParameterDefinitionWidget::QgsProcessingProviderConnectionParameterDefinitionWidget(
5662 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5663)
5664 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5665{
5666 const QgsProcessingParameterProviderConnection *connectionParam = dynamic_cast<const QgsProcessingParameterProviderConnection *>( definition );
5667
5668 QVBoxLayout *vlayout = new QVBoxLayout();
5669 vlayout->setContentsMargins( 0, 0, 0, 0 );
5670
5671 vlayout->addWidget( new QLabel( tr( "Provider" ) ) );
5672 mProviderComboBox = new QComboBox();
5673 mProviderComboBox->addItem( QObject::tr( "Postgres" ), u"postgres"_s );
5674 mProviderComboBox->addItem( QObject::tr( "GeoPackage" ), u"ogr"_s );
5675 mProviderComboBox->addItem( QObject::tr( "Spatialite" ), u"spatialite"_s );
5676
5677 vlayout->addWidget( mProviderComboBox );
5678
5679 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
5680
5681 mDefaultEdit = new QLineEdit();
5682 vlayout->addWidget( mDefaultEdit );
5683 setLayout( vlayout );
5684
5685 if ( connectionParam )
5686 {
5687 mProviderComboBox->setCurrentIndex( mProviderComboBox->findData( connectionParam->providerId() ) );
5688 mDefaultEdit->setText( connectionParam->defaultValueForGui().toString() );
5689 }
5690
5691 connect( mProviderComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5692 connect( mDefaultEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5693}
5694
5695QgsProcessingParameterDefinition *QgsProcessingProviderConnectionParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5696{
5697 QVariant defaultVal;
5698 if ( mDefaultEdit->text().isEmpty() )
5699 defaultVal = QVariant();
5700 else
5701 defaultVal = mDefaultEdit->text();
5702 auto param = std::make_unique<QgsProcessingParameterProviderConnection>( name, description, mProviderComboBox->currentData().toString(), defaultVal );
5703 param->setFlags( flags );
5704 return param.release();
5705}
5706
5707
5708QgsProcessingProviderConnectionWidgetWrapper::QgsProcessingProviderConnectionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5709 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5710{}
5711
5712QWidget *QgsProcessingProviderConnectionWidgetWrapper::createWidget()
5713{
5714 const QgsProcessingParameterProviderConnection *connectionParam = dynamic_cast<const QgsProcessingParameterProviderConnection *>( parameterDefinition() );
5715 if ( !connectionParam )
5716 return nullptr;
5717
5718 mProviderComboBox = new QgsProviderConnectionComboBox( connectionParam->providerId() );
5719 if ( connectionParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5720 mProviderComboBox->setAllowEmptyConnection( true );
5721
5722 switch ( type() )
5723 {
5726 break;
5728 mProviderComboBox->setEditable( true );
5729 break;
5730 }
5731
5732 mProviderComboBox->setToolTip( parameterDefinition()->toolTip() );
5733 connect( mProviderComboBox, &QgsProviderConnectionComboBox::currentTextChanged, this, [this]( const QString & ) {
5734 if ( mBlockSignals )
5735 return;
5736
5737 emit widgetValueHasChanged( this );
5738 } );
5739
5740 return mProviderComboBox;
5741}
5742
5743QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingProviderConnectionWidgetWrapper::createParameterDefinitionWidget(
5745)
5746{
5747 return new QgsProcessingProviderConnectionParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5748}
5749
5750void QgsProcessingProviderConnectionWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5751{
5752 const QString v = QgsProcessingParameters::parameterAsConnectionName( parameterDefinition(), value, context );
5753
5754 if ( !value.isValid() )
5755 mProviderComboBox->setCurrentIndex( -1 );
5756 else
5757 {
5758 if ( mProviderComboBox->isEditable() )
5759 {
5760 const QString prev = mProviderComboBox->currentText();
5761 mBlockSignals++;
5762 mProviderComboBox->setConnection( v );
5763 mProviderComboBox->setCurrentText( v );
5764
5765 mBlockSignals--;
5766 if ( prev != v )
5767 emit widgetValueHasChanged( this );
5768 }
5769 else
5770 mProviderComboBox->setConnection( v );
5771 }
5772}
5773
5774QVariant QgsProcessingProviderConnectionWidgetWrapper::widgetValue() const
5775{
5776 if ( mProviderComboBox )
5777 if ( mProviderComboBox->isEditable() )
5778 return mProviderComboBox->currentText().isEmpty() ? QVariant() : QVariant( mProviderComboBox->currentText() );
5779 else
5780 return mProviderComboBox->currentConnection().isEmpty() ? QVariant() : QVariant( mProviderComboBox->currentConnection() );
5781 else
5782 return QVariant();
5783}
5784
5785QString QgsProcessingProviderConnectionWidgetWrapper::modelerExpressionFormatString() const
5786{
5787 return tr( "connection name as a string value" );
5788}
5789
5790QString QgsProcessingProviderConnectionWidgetWrapper::parameterType() const
5791{
5793}
5794
5795QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingProviderConnectionWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5796{
5797 return new QgsProcessingProviderConnectionWidgetWrapper( parameter, type );
5798}
5799
5800
5801//
5802// QgsProcessingDatabaseSchemaWidgetWrapper
5803//
5804
5805QgsProcessingDatabaseSchemaParameterDefinitionWidget::QgsProcessingDatabaseSchemaParameterDefinitionWidget(
5806 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5807)
5808 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5809{
5810 const QgsProcessingParameterDatabaseSchema *schemaParam = dynamic_cast<const QgsProcessingParameterDatabaseSchema *>( definition );
5811
5812 QVBoxLayout *vlayout = new QVBoxLayout();
5813 vlayout->setContentsMargins( 0, 0, 0, 0 );
5814
5815 mConnectionParamComboBox = new QComboBox();
5816 QString initialConnection;
5817 if ( schemaParam )
5818 {
5819 initialConnection = schemaParam->parentConnectionParameterName();
5820 }
5821
5822 if ( auto *lModel = widgetContext.model() )
5823 {
5824 // populate combo box with other model input choices
5825 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
5826 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
5827 {
5828 if ( definition && it->parameterName() == definition->name() )
5829 continue;
5830
5831 if ( !dynamic_cast<const QgsProcessingParameterProviderConnection *>( lModel->parameterDefinition( it->parameterName() ) ) )
5832 continue;
5833
5834 mConnectionParamComboBox->addItem( it->parameterName(), it->parameterName() );
5835 if ( !initialConnection.isEmpty() && initialConnection == it->parameterName() )
5836 {
5837 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
5838 }
5839 }
5840 }
5841
5842 if ( mConnectionParamComboBox->count() == 0 && !initialConnection.isEmpty() )
5843 {
5844 // if no candidates found, we just add the existing one as a placeholder
5845 mConnectionParamComboBox->addItem( initialConnection, initialConnection );
5846 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
5847 }
5848
5849 vlayout->addWidget( new QLabel( tr( "Provider connection parameter" ) ) );
5850 vlayout->addWidget( mConnectionParamComboBox );
5851
5852 connect( mConnectionParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5853
5854 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
5855
5856 mDefaultEdit = new QLineEdit();
5857 vlayout->addWidget( mDefaultEdit );
5858 setLayout( vlayout );
5859
5860 if ( schemaParam )
5861 {
5862 mDefaultEdit->setText( schemaParam->defaultValueForGui().toString() );
5863 }
5864 connect( mDefaultEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5865}
5866
5867QgsProcessingParameterDefinition *QgsProcessingDatabaseSchemaParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5868{
5869 QVariant defaultVal;
5870 if ( mDefaultEdit->text().isEmpty() )
5871 defaultVal = QVariant();
5872 else
5873 defaultVal = mDefaultEdit->text();
5874 auto param = std::make_unique<QgsProcessingParameterDatabaseSchema>( name, description, mConnectionParamComboBox->currentData().toString(), defaultVal );
5875 param->setFlags( flags );
5876 return param.release();
5877}
5878
5879
5880QgsProcessingDatabaseSchemaWidgetWrapper::QgsProcessingDatabaseSchemaWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5881 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5882{}
5883
5884QWidget *QgsProcessingDatabaseSchemaWidgetWrapper::createWidget()
5885{
5886 const QgsProcessingParameterDatabaseSchema *schemaParam = dynamic_cast<const QgsProcessingParameterDatabaseSchema *>( parameterDefinition() );
5887 if ( !schemaParam )
5888 return nullptr;
5889
5890 mSchemaComboBox = new QgsDatabaseSchemaComboBox( QString(), QString() );
5891 if ( schemaParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5892 mSchemaComboBox->setAllowEmptySchema( true );
5893
5894 switch ( type() )
5895 {
5898 break;
5900 mSchemaComboBox->comboBox()->setEditable( true );
5901 break;
5902 }
5903
5904 mSchemaComboBox->setToolTip( parameterDefinition()->toolTip() );
5905 connect( mSchemaComboBox->comboBox(), &QComboBox::currentTextChanged, this, [this]( const QString & ) {
5906 if ( mBlockSignals )
5907 return;
5908
5909 emit widgetValueHasChanged( this );
5910 } );
5911
5912 return mSchemaComboBox;
5913}
5914
5915QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDatabaseSchemaWidgetWrapper::createParameterDefinitionWidget(
5917)
5918{
5919 return new QgsProcessingDatabaseSchemaParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5920}
5921
5922void QgsProcessingDatabaseSchemaWidgetWrapper::setParentConnectionWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
5923{
5924 // evaluate value to connection
5925 QgsProcessingContext *context = nullptr;
5926 std::unique_ptr<QgsProcessingContext> tmpContext;
5927 if ( mProcessingContextGenerator )
5928 context = mProcessingContextGenerator->processingContext();
5929
5930 if ( !context )
5931 {
5932 tmpContext = std::make_unique<QgsProcessingContext>();
5933 context = tmpContext.get();
5934 }
5935
5936 const QVariant value = parentWrapper->parameterValue();
5937 const QString connection = value.isValid() ? QgsProcessingParameters::parameterAsConnectionName( parentWrapper->parameterDefinition(), value, *context ) : QString();
5938
5939 if ( mSchemaComboBox )
5940 mSchemaComboBox->setConnectionName( connection, qgis::down_cast<const QgsProcessingParameterProviderConnection *>( parentWrapper->parameterDefinition() )->providerId() );
5941
5942 const QgsProcessingParameterDatabaseSchema *schemaParam = qgis::down_cast<const QgsProcessingParameterDatabaseSchema *>( parameterDefinition() );
5943 if ( schemaParam->defaultValueForGui().isValid() )
5944 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
5945}
5946
5947void QgsProcessingDatabaseSchemaWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5948{
5949 const QString v = QgsProcessingParameters::parameterAsSchema( parameterDefinition(), value, context );
5950
5951 if ( !value.isValid() )
5952 mSchemaComboBox->comboBox()->setCurrentIndex( -1 );
5953 else
5954 {
5955 if ( mSchemaComboBox->comboBox()->isEditable() )
5956 {
5957 const QString prev = mSchemaComboBox->comboBox()->currentText();
5958 mBlockSignals++;
5959 mSchemaComboBox->setSchema( v );
5960 mSchemaComboBox->comboBox()->setCurrentText( v );
5961
5962 mBlockSignals--;
5963 if ( prev != v )
5964 emit widgetValueHasChanged( this );
5965 }
5966 else
5967 mSchemaComboBox->setSchema( v );
5968 }
5969}
5970
5971QVariant QgsProcessingDatabaseSchemaWidgetWrapper::widgetValue() const
5972{
5973 if ( mSchemaComboBox )
5974 if ( mSchemaComboBox->comboBox()->isEditable() )
5975 return mSchemaComboBox->comboBox()->currentText().isEmpty() ? QVariant() : QVariant( mSchemaComboBox->comboBox()->currentText() );
5976 else
5977 return mSchemaComboBox->currentSchema().isEmpty() ? QVariant() : QVariant( mSchemaComboBox->currentSchema() );
5978 else
5979 return QVariant();
5980}
5981
5982QString QgsProcessingDatabaseSchemaWidgetWrapper::modelerExpressionFormatString() const
5983{
5984 return tr( "database schema name as a string value" );
5985}
5986
5987QString QgsProcessingDatabaseSchemaWidgetWrapper::parameterType() const
5988{
5990}
5991
5992QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDatabaseSchemaWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5993{
5994 return new QgsProcessingDatabaseSchemaWidgetWrapper( parameter, type );
5995}
5996
5997void QgsProcessingDatabaseSchemaWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
5998{
6000 switch ( type() )
6001 {
6004 {
6005 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
6006 {
6007 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDatabaseSchema *>( parameterDefinition() )->parentConnectionParameterName() )
6008 {
6009 setParentConnectionWrapperValue( wrapper );
6010 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentConnectionWrapperValue( wrapper ); } );
6011 break;
6012 }
6013 }
6014 break;
6015 }
6016
6018 break;
6019 }
6020}
6021
6022
6023//
6024// QgsProcessingDatabaseTableWidgetWrapper
6025//
6026
6027QgsProcessingDatabaseTableParameterDefinitionWidget::QgsProcessingDatabaseTableParameterDefinitionWidget(
6028 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6029)
6030 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6031{
6032 const QgsProcessingParameterDatabaseTable *tableParam = dynamic_cast<const QgsProcessingParameterDatabaseTable *>( definition );
6033
6034 QVBoxLayout *vlayout = new QVBoxLayout();
6035 vlayout->setContentsMargins( 0, 0, 0, 0 );
6036
6037 mConnectionParamComboBox = new QComboBox();
6038 mSchemaParamComboBox = new QComboBox();
6039 QString initialConnection;
6040 QString initialSchema;
6041 if ( tableParam )
6042 {
6043 initialConnection = tableParam->parentConnectionParameterName();
6044 initialSchema = tableParam->parentSchemaParameterName();
6045 }
6046
6047 if ( auto *lModel = widgetContext.model() )
6048 {
6049 // populate combo box with other model input choices
6050 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
6051 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
6052 {
6053 if ( definition && it->parameterName() == definition->name() )
6054 continue;
6055
6056 if ( dynamic_cast<const QgsProcessingParameterProviderConnection *>( lModel->parameterDefinition( it->parameterName() ) ) )
6057 {
6058 mConnectionParamComboBox->addItem( it->parameterName(), it->parameterName() );
6059 if ( !initialConnection.isEmpty() && initialConnection == it->parameterName() )
6060 {
6061 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
6062 }
6063 }
6064 else if ( dynamic_cast<const QgsProcessingParameterDatabaseSchema *>( lModel->parameterDefinition( it->parameterName() ) ) )
6065 {
6066 mSchemaParamComboBox->addItem( it->parameterName(), it->parameterName() );
6067 if ( !initialConnection.isEmpty() && initialConnection == it->parameterName() )
6068 {
6069 mSchemaParamComboBox->setCurrentIndex( mSchemaParamComboBox->count() - 1 );
6070 }
6071 }
6072 }
6073 }
6074
6075 if ( mConnectionParamComboBox->count() == 0 && !initialConnection.isEmpty() )
6076 {
6077 // if no candidates found, we just add the existing one as a placeholder
6078 mConnectionParamComboBox->addItem( initialConnection, initialConnection );
6079 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
6080 }
6081
6082 if ( mSchemaParamComboBox->count() == 0 && !initialSchema.isEmpty() )
6083 {
6084 // if no candidates found, we just add the existing one as a placeholder
6085 mSchemaParamComboBox->addItem( initialSchema, initialSchema );
6086 mSchemaParamComboBox->setCurrentIndex( mSchemaParamComboBox->count() - 1 );
6087 }
6088
6089 vlayout->addWidget( new QLabel( tr( "Provider connection parameter" ) ) );
6090 vlayout->addWidget( mConnectionParamComboBox );
6091
6092 vlayout->addWidget( new QLabel( tr( "Database schema parameter" ) ) );
6093 vlayout->addWidget( mSchemaParamComboBox );
6094
6095 connect( mConnectionParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6096 connect( mSchemaParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6097
6098 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
6099
6100 mDefaultEdit = new QLineEdit();
6101 vlayout->addWidget( mDefaultEdit );
6102 setLayout( vlayout );
6103
6104 if ( tableParam )
6105 {
6106 mDefaultEdit->setText( tableParam->defaultValueForGui().toString() );
6107 }
6108 connect( mDefaultEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6109}
6110
6111QgsProcessingParameterDefinition *QgsProcessingDatabaseTableParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6112{
6113 QVariant defaultVal;
6114 if ( mDefaultEdit->text().isEmpty() )
6115 defaultVal = QVariant();
6116 else
6117 defaultVal = mDefaultEdit->text();
6118 auto param = std::make_unique<QgsProcessingParameterDatabaseTable>( name, description, mConnectionParamComboBox->currentData().toString(), mSchemaParamComboBox->currentData().toString(), defaultVal );
6119 param->setFlags( flags );
6120 return param.release();
6121}
6122
6123
6124QgsProcessingDatabaseTableWidgetWrapper::QgsProcessingDatabaseTableWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6125 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
6126{}
6127
6128QWidget *QgsProcessingDatabaseTableWidgetWrapper::createWidget()
6129{
6130 const QgsProcessingParameterDatabaseTable *tableParam = dynamic_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() );
6131 if ( !tableParam )
6132 return nullptr;
6133
6134 mTableComboBox = new QgsDatabaseTableComboBox( QString(), QString() );
6135 if ( tableParam->flags() & Qgis::ProcessingParameterFlag::Optional )
6136 mTableComboBox->setAllowEmptyTable( true );
6137
6138 if ( type() == Qgis::ProcessingMode::Modeler || tableParam->allowNewTableNames() )
6139 mTableComboBox->comboBox()->setEditable( true );
6140
6141 mTableComboBox->setToolTip( parameterDefinition()->toolTip() );
6142 connect( mTableComboBox->comboBox(), &QComboBox::currentTextChanged, this, [this]( const QString & ) {
6143 if ( mBlockSignals )
6144 return;
6145
6146 emit widgetValueHasChanged( this );
6147 } );
6148
6149 return mTableComboBox;
6150}
6151
6152QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDatabaseTableWidgetWrapper::createParameterDefinitionWidget(
6154)
6155{
6156 return new QgsProcessingDatabaseTableParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6157}
6158
6159void QgsProcessingDatabaseTableWidgetWrapper::setParentConnectionWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
6160{
6161 // evaluate value to connection
6162 QgsProcessingContext *context = nullptr;
6163 std::unique_ptr<QgsProcessingContext> tmpContext;
6164 if ( mProcessingContextGenerator )
6165 context = mProcessingContextGenerator->processingContext();
6166
6167 if ( !context )
6168 {
6169 tmpContext = std::make_unique<QgsProcessingContext>();
6170 context = tmpContext.get();
6171 }
6172
6173 QVariant value = parentWrapper->parameterValue();
6174 mConnection = value.isValid() ? QgsProcessingParameters::parameterAsConnectionName( parentWrapper->parameterDefinition(), value, *context ) : QString();
6175 mProvider = qgis::down_cast<const QgsProcessingParameterProviderConnection *>( parentWrapper->parameterDefinition() )->providerId();
6176 if ( mTableComboBox && !mSchema.isEmpty() )
6177 {
6178 mTableComboBox->setSchema( mSchema );
6179 mTableComboBox->setConnectionName( mConnection, mProvider );
6180
6181 const QgsProcessingParameterDatabaseTable *tableParam = qgis::down_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() );
6182 if ( tableParam->defaultValueForGui().isValid() )
6183 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
6184 }
6185}
6186
6187void QgsProcessingDatabaseTableWidgetWrapper::setParentSchemaWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
6188{
6189 // evaluate value to schema
6190 QgsProcessingContext *context = nullptr;
6191 std::unique_ptr<QgsProcessingContext> tmpContext;
6192 if ( mProcessingContextGenerator )
6193 context = mProcessingContextGenerator->processingContext();
6194
6195 if ( !context )
6196 {
6197 tmpContext = std::make_unique<QgsProcessingContext>();
6198 context = tmpContext.get();
6199 }
6200
6201 QVariant value = parentWrapper->parameterValue();
6202 mSchema = value.isValid() ? QgsProcessingParameters::parameterAsSchema( parentWrapper->parameterDefinition(), value, *context ) : QString();
6203
6204 if ( mTableComboBox && !mSchema.isEmpty() && !mConnection.isEmpty() )
6205 {
6206 mTableComboBox->setSchema( mSchema );
6207 mTableComboBox->setConnectionName( mConnection, mProvider );
6208
6209 const QgsProcessingParameterDatabaseTable *tableParam = static_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() );
6210 if ( tableParam->defaultValueForGui().isValid() )
6211 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
6212 }
6213}
6214
6215void QgsProcessingDatabaseTableWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
6216{
6217 const QString v = QgsProcessingParameters::parameterAsDatabaseTableName( parameterDefinition(), value, context );
6218
6219 if ( !value.isValid() )
6220 mTableComboBox->comboBox()->setCurrentIndex( -1 );
6221 else
6222 {
6223 if ( mTableComboBox->comboBox()->isEditable() )
6224 {
6225 const QString prev = mTableComboBox->comboBox()->currentText();
6226 mBlockSignals++;
6227 mTableComboBox->setTable( v );
6228 mTableComboBox->comboBox()->setCurrentText( v );
6229
6230 mBlockSignals--;
6231 if ( prev != v )
6232 emit widgetValueHasChanged( this );
6233 }
6234 else
6235 mTableComboBox->setTable( v );
6236 }
6237}
6238
6239QVariant QgsProcessingDatabaseTableWidgetWrapper::widgetValue() const
6240{
6241 if ( mTableComboBox )
6242 if ( mTableComboBox->comboBox()->isEditable() )
6243 return mTableComboBox->comboBox()->currentText().isEmpty() ? QVariant() : QVariant( mTableComboBox->comboBox()->currentText() );
6244 else
6245 return mTableComboBox->currentTable().isEmpty() ? QVariant() : QVariant( mTableComboBox->currentTable() );
6246 else
6247 return QVariant();
6248}
6249
6250QString QgsProcessingDatabaseTableWidgetWrapper::modelerExpressionFormatString() const
6251{
6252 return tr( "database table name as a string value" );
6253}
6254
6255QString QgsProcessingDatabaseTableWidgetWrapper::parameterType() const
6256{
6258}
6259
6260QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDatabaseTableWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6261{
6262 return new QgsProcessingDatabaseTableWidgetWrapper( parameter, type );
6263}
6264
6265void QgsProcessingDatabaseTableWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
6266{
6268 switch ( type() )
6269 {
6272 {
6273 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
6274 {
6275 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() )->parentConnectionParameterName() )
6276 {
6277 setParentConnectionWrapperValue( wrapper );
6278 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentConnectionWrapperValue( wrapper ); } );
6279 }
6280 else if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() )->parentSchemaParameterName() )
6281 {
6282 setParentSchemaWrapperValue( wrapper );
6283 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentSchemaWrapperValue( wrapper ); } );
6284 }
6285 }
6286 break;
6287 }
6288
6290 break;
6291 }
6292}
6293
6294
6295//
6296// QgsProcessingExtentWidgetWrapper
6297//
6298
6299QgsProcessingExtentParameterDefinitionWidget::QgsProcessingExtentParameterDefinitionWidget(
6300 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6301)
6302 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6303{
6304 QVBoxLayout *vlayout = new QVBoxLayout();
6305 vlayout->setContentsMargins( 0, 0, 0, 0 );
6306
6307 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
6308
6309 mDefaultWidget = new QgsExtentWidget();
6310 mDefaultWidget->setNullValueAllowed( true, tr( "Not set" ) );
6311 if ( const QgsProcessingParameterExtent *extentParam = dynamic_cast<const QgsProcessingParameterExtent *>( definition ) )
6312 {
6313 if ( extentParam->defaultValueForGui().isValid() )
6314 {
6315 QgsRectangle rect = QgsProcessingParameters::parameterAsExtent( extentParam, extentParam->defaultValueForGui(), context );
6316 QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsExtentCrs( extentParam, extentParam->defaultValueForGui(), context );
6317 mDefaultWidget->setCurrentExtent( rect, crs );
6318 mDefaultWidget->setOutputExtentFromCurrent();
6319 }
6320 else
6321 {
6322 mDefaultWidget->clear();
6323 }
6324 }
6325
6327
6328 vlayout->addWidget( mDefaultWidget );
6329 setLayout( vlayout );
6330}
6331
6332QgsProcessingParameterDefinition *QgsProcessingExtentParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6333{
6334 const QString defaultVal = mDefaultWidget->isValid() ? u"%1,%2,%3,%4%5"_s.arg(
6335 QString::number( mDefaultWidget->outputExtent().xMinimum(), 'f', 9 ),
6336 QString::number( mDefaultWidget->outputExtent().xMaximum(), 'f', 9 ),
6337 QString::number( mDefaultWidget->outputExtent().yMinimum(), 'f', 9 ),
6338 QString::number( mDefaultWidget->outputExtent().yMaximum(), 'f', 9 ),
6339 mDefaultWidget->outputCrs().isValid() ? u" [%1]"_s.arg( mDefaultWidget->outputCrs().authid() ) : QString()
6340 )
6341 : QString();
6342 auto param = std::make_unique<QgsProcessingParameterExtent>( name, description, !defaultVal.isEmpty() ? QVariant( defaultVal ) : QVariant() );
6343 param->setFlags( flags );
6344 return param.release();
6345}
6346
6347
6348QgsProcessingExtentWidgetWrapper::QgsProcessingExtentWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6349 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
6350{}
6351
6352QWidget *QgsProcessingExtentWidgetWrapper::createWidget()
6353{
6354 const QgsProcessingParameterExtent *extentParam = dynamic_cast<const QgsProcessingParameterExtent *>( parameterDefinition() );
6355 if ( !extentParam )
6356 return nullptr;
6357
6358 switch ( type() )
6359 {
6363 {
6364 mExtentWidget = new QgsExtentWidget( nullptr );
6365 if ( widgetContext().mapCanvas() )
6366 mExtentWidget->setMapCanvas( widgetContext().mapCanvas() );
6367
6368 if ( extentParam->flags() & Qgis::ProcessingParameterFlag::Optional )
6369 mExtentWidget->setNullValueAllowed( true, tr( "Not set" ) );
6370
6371 mExtentWidget->setToolTip( parameterDefinition()->toolTip() );
6372
6373 connect( mExtentWidget, &QgsExtentWidget::extentChanged, this, [this] { emit widgetValueHasChanged( this ); } );
6374
6375 if ( mDialog && type() != Qgis::ProcessingMode::Modeler )
6376 setDialog( mDialog ); // setup connections to panel - dialog was previously set before the widget was created
6377
6378 return mExtentWidget;
6379 }
6380 }
6381 return nullptr;
6382}
6383
6384void QgsProcessingExtentWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
6385{
6387 if ( mExtentWidget && context.mapCanvas() && type() != Qgis::ProcessingMode::Modeler )
6388 mExtentWidget->setMapCanvas( context.mapCanvas() );
6389}
6390
6391void QgsProcessingExtentWidgetWrapper::setDialog( QWidget *dialog )
6392{
6393 mDialog = dialog;
6394 if ( mExtentWidget && mDialog && type() != Qgis::ProcessingMode::Modeler )
6395 {
6396 connect( mExtentWidget, &QgsExtentWidget::toggleDialogVisibility, mDialog, [this]( bool visible ) {
6397 if ( !visible )
6398 mDialog->showMinimized();
6399 else
6400 {
6401 mDialog->showNormal();
6402 mDialog->raise();
6403 mDialog->activateWindow();
6404 }
6405 } );
6406 }
6408}
6409
6410void QgsProcessingExtentWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
6411{
6412 if ( mExtentWidget )
6413 {
6414 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
6415 mExtentWidget->clear();
6416 else
6417 {
6418 QgsRectangle r = QgsProcessingParameters::parameterAsExtent( parameterDefinition(), value, context );
6419 QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsPointCrs( parameterDefinition(), value, context );
6420 mExtentWidget->setCurrentExtent( r, crs );
6421 mExtentWidget->setOutputExtentFromUser( r, crs );
6422 }
6423 }
6424}
6425
6426QVariant QgsProcessingExtentWidgetWrapper::widgetValue() const
6427{
6428 if ( mExtentWidget )
6429 {
6430 const QString val = mExtentWidget->isValid() ? u"%1,%2,%3,%4%5"_s.arg(
6431 QString::number( mExtentWidget->outputExtent().xMinimum(), 'f', 9 ),
6432 QString::number( mExtentWidget->outputExtent().xMaximum(), 'f', 9 ),
6433 QString::number( mExtentWidget->outputExtent().yMinimum(), 'f', 9 ),
6434 QString::number( mExtentWidget->outputExtent().yMaximum(), 'f', 9 ),
6435 mExtentWidget->outputCrs().isValid() ? u" [%1]"_s.arg( mExtentWidget->outputCrs().authid() ) : QString()
6436 )
6437 : QString();
6438
6439 return val.isEmpty() ? QVariant() : QVariant( val );
6440 }
6441 else
6442 return QVariant();
6443}
6444
6445QString QgsProcessingExtentWidgetWrapper::modelerExpressionFormatString() const
6446{
6447 return tr( "string of the format 'x min,x max,y min,y max' or a geometry value (bounding box is used)" );
6448}
6449
6450QString QgsProcessingExtentWidgetWrapper::parameterType() const
6451{
6453}
6454
6455QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExtentWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6456{
6457 return new QgsProcessingExtentWidgetWrapper( parameter, type );
6458}
6459
6460QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingExtentWidgetWrapper::createParameterDefinitionWidget(
6462)
6463{
6464 return new QgsProcessingExtentParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6465}
6466
6467
6468//
6469// QgsProcessingMapLayerWidgetWrapper
6470//
6471
6472QgsProcessingMapLayerParameterDefinitionWidget::QgsProcessingMapLayerParameterDefinitionWidget(
6473 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6474)
6475 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6476{
6477 QVBoxLayout *vlayout = new QVBoxLayout();
6478 vlayout->setContentsMargins( 0, 0, 0, 0 );
6479
6480 vlayout->addWidget( new QLabel( tr( "Layer type" ) ) );
6481 mLayerTypeComboBox = new QgsCheckableComboBox();
6482 mLayerTypeComboBox->addItem( tr( "Any Map Layer" ), static_cast<int>( Qgis::ProcessingSourceType::MapLayer ) );
6483 mLayerTypeComboBox->addItem( tr( "Vector (Point)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
6484 mLayerTypeComboBox->addItem( tr( "Vector (Line)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
6485 mLayerTypeComboBox->addItem( tr( "Vector (Polygon)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
6486 mLayerTypeComboBox->addItem( tr( "Vector (Any Geometry Type)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
6487 mLayerTypeComboBox->addItem( tr( "Vector (No Geometry Required)" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
6488 mLayerTypeComboBox->addItem( tr( "Raster" ), static_cast<int>( Qgis::ProcessingSourceType::Raster ) );
6489 mLayerTypeComboBox->addItem( tr( "Mesh" ), static_cast<int>( Qgis::ProcessingSourceType::Mesh ) );
6490 mLayerTypeComboBox->addItem( tr( "Plugin" ), static_cast<int>( Qgis::ProcessingSourceType::Plugin ) );
6491 mLayerTypeComboBox->addItem( tr( "Point Cloud" ), static_cast<int>( Qgis::ProcessingSourceType::PointCloud ) );
6492 mLayerTypeComboBox->addItem( tr( "Annotation" ), static_cast<int>( Qgis::ProcessingSourceType::Annotation ) );
6493
6494 if ( const QgsProcessingParameterMapLayer *layerParam = dynamic_cast<const QgsProcessingParameterMapLayer *>( definition ) )
6495 {
6496 for ( int i : layerParam->dataTypes() )
6497 {
6498 mLayerTypeComboBox->setItemCheckState( mLayerTypeComboBox->findData( i ), Qt::Checked );
6499 }
6500 }
6501
6502 vlayout->addWidget( mLayerTypeComboBox );
6503
6505
6506 setLayout( vlayout );
6507}
6508
6509QgsProcessingParameterDefinition *QgsProcessingMapLayerParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6510{
6511 QList<int> dataTypes;
6512 for ( const QVariant &v : mLayerTypeComboBox->checkedItemsData() )
6513 dataTypes << v.toInt();
6514
6515 auto param = std::make_unique<QgsProcessingParameterMapLayer>( name, description );
6516 param->setDataTypes( dataTypes );
6517 param->setFlags( flags );
6518 return param.release();
6519}
6520
6521QgsProcessingMapLayerWidgetWrapper::QgsProcessingMapLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6522 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
6523{}
6524
6525QWidget *QgsProcessingMapLayerWidgetWrapper::createWidget()
6526{
6527 mComboBox = new QgsProcessingMapLayerComboBox( parameterDefinition(), type() );
6528
6529 switch ( type() )
6530 {
6533 break;
6535 mComboBox->setEditable( true );
6536 break;
6537 }
6538
6539 mComboBox->setToolTip( parameterDefinition()->toolTip() );
6540
6541 connect( mComboBox, &QgsProcessingMapLayerComboBox::valueChanged, this, [this]() {
6542 if ( mBlockSignals )
6543 return;
6544
6545 emit widgetValueHasChanged( this );
6546 } );
6547
6548 setWidgetContext( widgetContext() );
6549 return mComboBox;
6550}
6551
6552void QgsProcessingMapLayerWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
6553{
6555 if ( mComboBox )
6556 {
6557 mComboBox->setWidgetContext( context );
6558
6559 if ( !( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional ) )
6560 {
6561 // non optional parameter -- if no default value set, default to active layer
6562 if ( !parameterDefinition()->defaultValueForGui().isValid() )
6563 mComboBox->setLayer( context.activeLayer() );
6564 }
6565 }
6566}
6567
6568void QgsProcessingMapLayerWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
6569{
6570 if ( mComboBox )
6571 mComboBox->setValue( value, context );
6572}
6573
6574QVariant QgsProcessingMapLayerWidgetWrapper::widgetValue() const
6575{
6576 return mComboBox ? mComboBox->value() : QVariant();
6577}
6578
6579QString QgsProcessingMapLayerWidgetWrapper::modelerExpressionFormatString() const
6580{
6581 return tr( "path to a map layer" );
6582}
6583
6584Qgis::ProcessingModelChildParameterSource QgsProcessingMapLayerWidgetWrapper::defaultModelSource( const QgsProcessingParameterDefinition *parameter ) const
6585{
6586 // non-optional layer sources default to a matching model input layer, but optional layer parameters
6587 // should default to static values. We don't want all optional layer parameters to have values set by default!
6588 if ( !( parameter->flags() & Qgis::ProcessingParameterFlag::Optional ) )
6589 {
6591 }
6592 else
6593 {
6595 }
6596}
6597
6598QString QgsProcessingMapLayerWidgetWrapper::parameterType() const
6599{
6601}
6602
6603QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMapLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6604{
6605 return new QgsProcessingMapLayerWidgetWrapper( parameter, type );
6606}
6607
6608QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMapLayerWidgetWrapper::createParameterDefinitionWidget(
6610)
6611{
6612 return new QgsProcessingMapLayerParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6613}
6614
6615
6616//
6617// QgsProcessingRasterLayerWidgetWrapper
6618//
6619
6620QgsProcessingRasterLayerWidgetWrapper::QgsProcessingRasterLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6621 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6622{}
6623
6624QString QgsProcessingRasterLayerWidgetWrapper::modelerExpressionFormatString() const
6625{
6626 return tr( "path to a raster layer" );
6627}
6628
6629QString QgsProcessingRasterLayerWidgetWrapper::parameterType() const
6630{
6632}
6633
6634QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingRasterLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6635{
6636 return new QgsProcessingRasterLayerWidgetWrapper( parameter, type );
6637}
6638
6639QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingRasterLayerWidgetWrapper::createParameterDefinitionWidget(
6641)
6642{
6643 Q_UNUSED( context );
6644 Q_UNUSED( widgetContext );
6645 Q_UNUSED( definition );
6646 Q_UNUSED( algorithm );
6647
6648 return nullptr;
6649}
6650
6651
6652//
6653// QgsProcessingVectorLayerWidgetWrapper
6654//
6655
6656QgsProcessingVectorLayerParameterDefinitionWidget::QgsProcessingVectorLayerParameterDefinitionWidget(
6657 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6658)
6659 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6660{
6661 QVBoxLayout *vlayout = new QVBoxLayout();
6662 vlayout->setContentsMargins( 0, 0, 0, 0 );
6663
6664 vlayout->addWidget( new QLabel( tr( "Geometry type" ) ) );
6665 mGeometryTypeComboBox = new QgsCheckableComboBox();
6666 mGeometryTypeComboBox->addItem( tr( "Geometry Not Required" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
6667 mGeometryTypeComboBox->addItem( tr( "Point" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
6668 mGeometryTypeComboBox->addItem( tr( "Line" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
6669 mGeometryTypeComboBox->addItem( tr( "Polygon" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
6670 mGeometryTypeComboBox->addItem( tr( "Any Geometry Type" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
6671
6672 if ( const QgsProcessingParameterVectorLayer *vectorParam = dynamic_cast<const QgsProcessingParameterVectorLayer *>( definition ) )
6673 {
6674 for ( int i : vectorParam->dataTypes() )
6675 {
6676 mGeometryTypeComboBox->setItemCheckState( mGeometryTypeComboBox->findData( i ), Qt::Checked );
6677 }
6678 }
6679
6680 vlayout->addWidget( mGeometryTypeComboBox );
6681
6683
6684 setLayout( vlayout );
6685}
6686
6687QgsProcessingParameterDefinition *QgsProcessingVectorLayerParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6688{
6689 QList<int> dataTypes;
6690 for ( const QVariant &v : mGeometryTypeComboBox->checkedItemsData() )
6691 dataTypes << v.toInt();
6692
6693 auto param = std::make_unique<QgsProcessingParameterVectorLayer>( name, description, dataTypes );
6694 param->setFlags( flags );
6695 return param.release();
6696}
6697
6698
6699QgsProcessingVectorLayerWidgetWrapper::QgsProcessingVectorLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6700 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6701{}
6702
6703QString QgsProcessingVectorLayerWidgetWrapper::modelerExpressionFormatString() const
6704{
6705 return tr( "path to a vector layer" );
6706}
6707
6708QString QgsProcessingVectorLayerWidgetWrapper::parameterType() const
6709{
6711}
6712
6713QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVectorLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6714{
6715 return new QgsProcessingVectorLayerWidgetWrapper( parameter, type );
6716}
6717
6718QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingVectorLayerWidgetWrapper::createParameterDefinitionWidget(
6720)
6721{
6722 return new QgsProcessingVectorLayerParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6723}
6724
6725
6726//
6727// QgsProcessingFeatureSourceLayerWidgetWrapper
6728//
6729
6730QgsProcessingFeatureSourceParameterDefinitionWidget::QgsProcessingFeatureSourceParameterDefinitionWidget(
6731 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6732)
6733 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6734{
6735 QVBoxLayout *vlayout = new QVBoxLayout();
6736 vlayout->setContentsMargins( 0, 0, 0, 0 );
6737
6738 vlayout->addWidget( new QLabel( tr( "Geometry type" ) ) );
6739 mGeometryTypeComboBox = new QgsCheckableComboBox();
6740 mGeometryTypeComboBox->addItem( tr( "Geometry Not Required" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
6741 mGeometryTypeComboBox->addItem( tr( "Point" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
6742 mGeometryTypeComboBox->addItem( tr( "Line" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
6743 mGeometryTypeComboBox->addItem( tr( "Polygon" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
6744 mGeometryTypeComboBox->addItem( tr( "Any Geometry Type" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
6745
6746 if ( const QgsProcessingParameterFeatureSource *sourceParam = dynamic_cast<const QgsProcessingParameterFeatureSource *>( definition ) )
6747 {
6748 for ( int i : sourceParam->dataTypes() )
6749 {
6750 mGeometryTypeComboBox->setItemCheckState( mGeometryTypeComboBox->findData( i ), Qt::Checked );
6751 }
6752 }
6753 else
6754 {
6755 mGeometryTypeComboBox->setItemCheckState( mGeometryTypeComboBox->findData( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ), Qt::Checked );
6756 }
6757
6758 vlayout->addWidget( mGeometryTypeComboBox );
6760
6761 setLayout( vlayout );
6762}
6763
6764QgsProcessingParameterDefinition *QgsProcessingFeatureSourceParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6765{
6766 QList<int> dataTypes;
6767 for ( const QVariant &v : mGeometryTypeComboBox->checkedItemsData() )
6768 dataTypes << v.toInt();
6769
6770 auto param = std::make_unique<QgsProcessingParameterFeatureSource>( name, description, dataTypes );
6771 param->setFlags( flags );
6772 return param.release();
6773}
6774
6775QgsProcessingFeatureSourceWidgetWrapper::QgsProcessingFeatureSourceWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6776 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6777{}
6778
6779QString QgsProcessingFeatureSourceWidgetWrapper::modelerExpressionFormatString() const
6780{
6781 return tr( "path to a vector layer" );
6782}
6783
6784QString QgsProcessingFeatureSourceWidgetWrapper::parameterType() const
6785{
6787}
6788
6789QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFeatureSourceWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6790{
6791 return new QgsProcessingFeatureSourceWidgetWrapper( parameter, type );
6792}
6793
6794QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFeatureSourceWidgetWrapper::createParameterDefinitionWidget(
6796)
6797{
6798 return new QgsProcessingFeatureSourceParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6799}
6800
6801//
6802// QgsProcessingMeshLayerWidgetWrapper
6803//
6804
6805QgsProcessingMeshLayerWidgetWrapper::QgsProcessingMeshLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6806 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6807{}
6808
6809QString QgsProcessingMeshLayerWidgetWrapper::modelerExpressionFormatString() const
6810{
6811 return tr( "path to a mesh layer" );
6812}
6813
6814QString QgsProcessingMeshLayerWidgetWrapper::parameterType() const
6815{
6817}
6818
6819QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMeshLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6820{
6821 return new QgsProcessingMeshLayerWidgetWrapper( parameter, type );
6822}
6823
6824QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMeshLayerWidgetWrapper::createParameterDefinitionWidget(
6826)
6827{
6828 Q_UNUSED( context );
6829 Q_UNUSED( widgetContext );
6830 Q_UNUSED( definition );
6831 Q_UNUSED( algorithm );
6832
6833 return nullptr;
6834}
6835
6836
6837//
6838// QgsProcessingRasterBandPanelWidget
6839//
6840
6841QgsProcessingRasterBandPanelWidget::QgsProcessingRasterBandPanelWidget( QWidget *parent, const QgsProcessingParameterBand *param )
6842 : QWidget( parent )
6843 , mParam( param )
6844{
6845 QHBoxLayout *hl = new QHBoxLayout();
6846 hl->setContentsMargins( 0, 0, 0, 0 );
6847
6848 mLineEdit = new QLineEdit();
6849 mLineEdit->setEnabled( false );
6850 hl->addWidget( mLineEdit, 1 );
6851
6852 mToolButton = new QToolButton();
6853 mToolButton->setText( QString( QChar( 0x2026 ) ) );
6854 hl->addWidget( mToolButton );
6855
6856 setLayout( hl );
6857
6858 if ( mParam )
6859 {
6860 mLineEdit->setText( tr( "%n band(s) selected", nullptr, 0 ) );
6861 }
6862
6863 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingRasterBandPanelWidget::showDialog );
6864}
6865
6866void QgsProcessingRasterBandPanelWidget::setBands( const QList<int> &bands )
6867{
6868 mBands = bands;
6869}
6870
6871void QgsProcessingRasterBandPanelWidget::setBandNames( const QHash<int, QString> &names )
6872{
6873 mBandNames = names;
6874}
6875
6876void QgsProcessingRasterBandPanelWidget::setValue( const QVariant &value )
6877{
6878 if ( value.isValid() )
6879 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
6880 else
6881 mValue.clear();
6882
6883 updateSummaryText();
6884 emit changed();
6885}
6886
6887void QgsProcessingRasterBandPanelWidget::showDialog()
6888{
6889 QVariantList availableOptions;
6890 availableOptions.reserve( mBands.size() );
6891 for ( int band : std::as_const( mBands ) )
6892 {
6893 availableOptions << band;
6894 }
6895
6897 if ( panel && panel->dockMode() )
6898 {
6899 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
6900 widget->setPanelTitle( mParam->description() );
6901
6902 widget->setValueFormatter( [this]( const QVariant &v ) -> QString {
6903 int band = v.toInt();
6904 return mBandNames.contains( band ) ? mBandNames.value( band ) : v.toString();
6905 } );
6906
6907 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
6908 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
6909 panel->openPanel( widget );
6910 }
6911 else
6912 {
6913 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
6914
6915 dlg.setValueFormatter( [this]( const QVariant &v ) -> QString {
6916 int band = v.toInt();
6917 return mBandNames.contains( band ) ? mBandNames.value( band ) : v.toString();
6918 } );
6919 if ( dlg.exec() )
6920 {
6921 setValue( dlg.selectedOptions() );
6922 }
6923 }
6924}
6925
6926void QgsProcessingRasterBandPanelWidget::updateSummaryText()
6927{
6928 if ( mParam )
6929 mLineEdit->setText( tr( "%n band(s) selected", nullptr, mValue.count() ) );
6930}
6931
6932
6933//
6934// QgsProcessingBandWidgetWrapper
6935//
6936
6937QgsProcessingBandParameterDefinitionWidget::QgsProcessingBandParameterDefinitionWidget(
6938 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6939)
6940 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6941{
6942 QVBoxLayout *vlayout = new QVBoxLayout();
6943 vlayout->setContentsMargins( 0, 0, 0, 0 );
6944
6945 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
6946
6947 mDefaultLineEdit = new QLineEdit();
6948 mDefaultLineEdit->setToolTip( tr( "Band number (separate bands with ; for multiple band parameters)" ) );
6949 if ( const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( definition ) )
6950 {
6951 const QList<int> bands = QgsProcessingParameters::parameterAsInts( bandParam, bandParam->defaultValueForGui(), context );
6952 QStringList defVal;
6953 for ( int b : bands )
6954 {
6955 defVal << QString::number( b );
6956 }
6957
6958 mDefaultLineEdit->setText( defVal.join( ';' ) );
6959 }
6960 vlayout->addWidget( mDefaultLineEdit );
6961 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6962
6963 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
6964 mParentLayerComboBox = new QComboBox();
6965
6966 QString initialParent;
6967 if ( const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( definition ) )
6968 initialParent = bandParam->parentLayerParameterName();
6969
6970 if ( auto *lModel = widgetContext.model() )
6971 {
6972 // populate combo box with other model input choices
6973 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
6974 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
6975 {
6976 if ( const QgsProcessingParameterRasterLayer *definition = dynamic_cast<const QgsProcessingParameterRasterLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
6977 {
6978 mParentLayerComboBox->addItem( definition->description(), definition->name() );
6979 if ( !initialParent.isEmpty() && initialParent == definition->name() )
6980 {
6981 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
6982 }
6983 }
6984 }
6985 }
6986
6987 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
6988 {
6989 // if no parent candidates found, we just add the existing one as a placeholder
6990 mParentLayerComboBox->addItem( initialParent, initialParent );
6991 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
6992 }
6993 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6994
6995 vlayout->addWidget( mParentLayerComboBox );
6996
6997 mAllowMultipleCheckBox = new QCheckBox( tr( "Allow multiple" ) );
6998 if ( const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( definition ) )
6999 mAllowMultipleCheckBox->setChecked( bandParam->allowMultiple() );
7000 connect( mAllowMultipleCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
7001
7002 vlayout->addWidget( mAllowMultipleCheckBox );
7003 setLayout( vlayout );
7004}
7005
7006QgsProcessingParameterDefinition *QgsProcessingBandParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
7007{
7008 auto param
7009 = std::make_unique<QgsProcessingParameterBand>( name, description, mDefaultLineEdit->text().split( ';' ), mParentLayerComboBox->currentData().toString(), false, mAllowMultipleCheckBox->isChecked() );
7010 param->setFlags( flags );
7011 return param.release();
7012}
7013
7014QgsProcessingBandWidgetWrapper::QgsProcessingBandWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7015 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
7016{}
7017
7018QWidget *QgsProcessingBandWidgetWrapper::createWidget()
7019{
7020 const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( parameterDefinition() );
7021 if ( !bandParam )
7022 return nullptr;
7023
7024 switch ( type() )
7025 {
7028 {
7029 if ( bandParam->allowMultiple() )
7030 {
7031 mPanel = new QgsProcessingRasterBandPanelWidget( nullptr, bandParam );
7032 mPanel->setToolTip( parameterDefinition()->toolTip() );
7033 connect( mPanel, &QgsProcessingRasterBandPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
7034 return mPanel;
7035 }
7036 else
7037 {
7038 mComboBox = new QgsRasterBandComboBox();
7039 mComboBox->setShowNotSetOption( bandParam->flags() & Qgis::ProcessingParameterFlag::Optional );
7040
7041 mComboBox->setToolTip( parameterDefinition()->toolTip() );
7042 connect( mComboBox, &QgsRasterBandComboBox::bandChanged, this, [this]( int ) { emit widgetValueHasChanged( this ); } );
7043 return mComboBox;
7044 }
7045 }
7046
7048 {
7049 mLineEdit = new QLineEdit();
7050 mLineEdit->setToolTip( QObject::tr( "Band number (separate bands with ; for multiple band parameters)" ) );
7051 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
7052 return mLineEdit;
7053 }
7054 }
7055 return nullptr;
7056}
7057
7058void QgsProcessingBandWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
7059{
7061 switch ( type() )
7062 {
7065 {
7066 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
7067 {
7068 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterBand *>( parameterDefinition() )->parentLayerParameterName() )
7069 {
7070 setParentLayerWrapperValue( wrapper );
7071 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
7072 break;
7073 }
7074 }
7075 break;
7076 }
7077
7079 break;
7080 }
7081}
7082
7083void QgsProcessingBandWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
7084{
7085 // evaluate value to layer
7086 QgsProcessingContext *context = nullptr;
7087 std::unique_ptr<QgsProcessingContext> tmpContext;
7088 if ( mProcessingContextGenerator )
7089 context = mProcessingContextGenerator->processingContext();
7090
7091 if ( !context )
7092 {
7093 tmpContext = std::make_unique<QgsProcessingContext>();
7094 context = tmpContext.get();
7095 }
7096
7097 QVariant value = parentWrapper->parameterValue();
7098
7099 QgsRasterLayer *layer = QgsProcessingParameters::parameterAsRasterLayer( parentWrapper->parameterDefinition(), value, *context );
7100 if ( layer && layer->isValid() )
7101 {
7102 // need to grab ownership of layer if required - otherwise layer may be deleted when context
7103 // goes out of scope
7104 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
7105 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Raster )
7106 {
7107 mParentLayer.reset( qobject_cast<QgsRasterLayer *>( ownedLayer.release() ) );
7108 layer = mParentLayer.get();
7109 }
7110 else
7111 {
7112 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
7113 }
7114
7115 if ( mComboBox )
7116 mComboBox->setLayer( layer );
7117 else if ( mPanel )
7118 {
7119 QgsRasterDataProvider *provider = layer->dataProvider();
7120 if ( provider && layer->isValid() )
7121 {
7122 //fill available bands
7123 int nBands = provider->bandCount();
7124 QList<int> bands;
7125 QHash<int, QString> bandNames;
7126 for ( int i = 1; i <= nBands; ++i )
7127 {
7128 bandNames.insert( i, QgsRasterBandComboBox::displayBandName( provider, i ) );
7129 bands << i;
7130 }
7131 mPanel->setBands( bands );
7132 mPanel->setBandNames( bandNames );
7133 }
7134 }
7135 }
7136 else
7137 {
7138 if ( mComboBox )
7139 mComboBox->setLayer( nullptr );
7140 else if ( mPanel )
7141 mPanel->setBands( QList<int>() );
7142
7143 if ( value.isValid() && widgetContext().messageBar() )
7144 {
7145 widgetContext().messageBar()->clearWidgets();
7146 widgetContext().messageBar()->pushMessage( QString(), QObject::tr( "Could not load selected layer/table. Dependent bands could not be populated" ), Qgis::MessageLevel::Info );
7147 }
7148 }
7149
7150 if ( parameterDefinition()->defaultValueForGui().isValid() )
7151 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
7152}
7153
7154void QgsProcessingBandWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
7155{
7156 if ( mComboBox )
7157 {
7158 if ( !value.isValid() )
7159 mComboBox->setBand( -1 );
7160 else
7161 {
7162 const int v = QgsProcessingParameters::parameterAsInt( parameterDefinition(), value, context );
7163 mComboBox->setBand( v );
7164 }
7165 }
7166 else if ( mPanel )
7167 {
7168 QVariantList opts;
7169 if ( value.isValid() )
7170 {
7171 const QList<int> v = QgsProcessingParameters::parameterAsInts( parameterDefinition(), value, context );
7172 opts.reserve( v.size() );
7173 for ( int i : v )
7174 opts << i;
7175 }
7176 if ( mPanel )
7177 mPanel->setValue( value.isValid() ? opts : QVariant() );
7178 }
7179 else if ( mLineEdit )
7180 {
7181 const QgsProcessingParameterBand *bandParam = static_cast<const QgsProcessingParameterBand *>( parameterDefinition() );
7182 if ( bandParam->allowMultiple() )
7183 {
7184 const QList<int> v = QgsProcessingParameters::parameterAsInts( parameterDefinition(), value, context );
7185 QStringList opts;
7186 opts.reserve( v.size() );
7187 for ( int i : v )
7188 opts << QString::number( i );
7189 mLineEdit->setText( value.isValid() && !opts.empty() ? opts.join( ';' ) : QString() );
7190 }
7191 else
7192 {
7193 if ( value.isValid() )
7194 mLineEdit->setText( QString::number( QgsProcessingParameters::parameterAsInt( parameterDefinition(), value, context ) ) );
7195 else
7196 mLineEdit->clear();
7197 }
7198 }
7199}
7200
7201QVariant QgsProcessingBandWidgetWrapper::widgetValue() const
7202{
7203 if ( mComboBox )
7204 return mComboBox->currentBand() == -1 ? QVariant() : mComboBox->currentBand();
7205 else if ( mPanel )
7206 return !mPanel->value().toList().isEmpty() ? mPanel->value() : QVariant();
7207 else if ( mLineEdit )
7208 {
7209 const QgsProcessingParameterBand *bandParam = static_cast<const QgsProcessingParameterBand *>( parameterDefinition() );
7210 if ( bandParam->allowMultiple() )
7211 {
7212 const QStringList parts = mLineEdit->text().split( ';', Qt::SkipEmptyParts );
7213 QVariantList res;
7214 res.reserve( parts.count() );
7215 for ( const QString &s : parts )
7216 {
7217 bool ok = false;
7218 int band = s.toInt( &ok );
7219 if ( ok )
7220 res << band;
7221 }
7222 return res.isEmpty() ? QVariant() : res;
7223 }
7224 else
7225 {
7226 return mLineEdit->text().isEmpty() ? QVariant() : mLineEdit->text();
7227 }
7228 }
7229 else
7230 return QVariant();
7231}
7232
7233QString QgsProcessingBandWidgetWrapper::modelerExpressionFormatString() const
7234{
7235 return tr( "selected band numbers as an array of numbers, or semicolon separated string of options (e.g. '1;3')" );
7236}
7237
7238QString QgsProcessingBandWidgetWrapper::parameterType() const
7239{
7241}
7242
7243QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingBandWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7244{
7245 return new QgsProcessingBandWidgetWrapper( parameter, type );
7246}
7247
7248QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingBandWidgetWrapper::createParameterDefinitionWidget(
7250)
7251{
7252 return new QgsProcessingBandParameterDefinitionWidget( context, widgetContext, definition, algorithm );
7253}
7254
7255//
7256// QgsProcessingMultipleLayerLineEdit
7257//
7258
7259QgsProcessingMultipleLayerLineEdit::QgsProcessingMultipleLayerLineEdit( QWidget *parent, const QgsProcessingParameterMultipleLayers *param )
7260 : QgsHighlightableLineEdit( parent )
7261 , mParam( param )
7262{
7263 setAcceptDrops( true );
7264}
7265
7266void QgsProcessingMultipleLayerLineEdit::dragEnterEvent( QDragEnterEvent *event )
7267{
7268 const QStringList uris = QgsProcessingMultipleInputPanelWidget::compatibleUrisFromMimeData( mParam, event->mimeData(), {} );
7269 if ( !uris.isEmpty() )
7270 {
7271 event->setDropAction( Qt::CopyAction );
7272 event->accept();
7273 setHighlighted( true );
7274 }
7275 else
7276 {
7277 event->ignore();
7278 }
7279}
7280
7281void QgsProcessingMultipleLayerLineEdit::dragLeaveEvent( QDragLeaveEvent *event )
7282{
7283 QgsHighlightableLineEdit::dragLeaveEvent( event );
7284 event->accept();
7285 setHighlighted( false );
7286}
7287
7288void QgsProcessingMultipleLayerLineEdit::dropEvent( QDropEvent *event )
7289{
7290 const QStringList uris = QgsProcessingMultipleInputPanelWidget::compatibleUrisFromMimeData( mParam, event->mimeData(), {} );
7291 if ( !uris.isEmpty() )
7292 {
7293 event->setDropAction( Qt::CopyAction );
7294 event->accept();
7295 QVariantList uriList;
7296 uriList.reserve( uris.size() );
7297 for ( const QString &uri : uris )
7298 uriList.append( QVariant( uri ) );
7299 emit layersDropped( uriList );
7300 }
7301
7302 setHighlighted( false );
7303}
7304
7305//
7306// QgsProcessingMultipleLayerPanelWidget
7307//
7308
7309QgsProcessingMultipleLayerPanelWidget::QgsProcessingMultipleLayerPanelWidget( QWidget *parent, const QgsProcessingParameterMultipleLayers *param )
7310 : QWidget( parent )
7311 , mParam( param )
7312{
7313 QHBoxLayout *hl = new QHBoxLayout();
7314 hl->setContentsMargins( 0, 0, 0, 0 );
7315
7316 mLineEdit = new QgsProcessingMultipleLayerLineEdit( nullptr, param );
7317 mLineEdit->setEnabled( true );
7318 mLineEdit->setReadOnly( true );
7319
7320 hl->addWidget( mLineEdit, 1 );
7321 connect( mLineEdit, &QgsProcessingMultipleLayerLineEdit::layersDropped, this, &QgsProcessingMultipleLayerPanelWidget::setValue );
7322
7323 mToolButton = new QToolButton();
7324 mToolButton->setText( QString( QChar( 0x2026 ) ) );
7325 hl->addWidget( mToolButton );
7326
7327 setLayout( hl );
7328
7329 if ( mParam )
7330 {
7331 mLineEdit->setText( tr( "%n input(s) selected", nullptr, 0 ) );
7332 }
7333
7334 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingMultipleLayerPanelWidget::showDialog );
7335}
7336
7337void QgsProcessingMultipleLayerPanelWidget::setValue( const QVariant &value )
7338{
7339 if ( value.isValid() )
7340 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
7341 else
7342 mValue.clear();
7343
7344 updateSummaryText();
7345 emit changed();
7346}
7347
7348void QgsProcessingMultipleLayerPanelWidget::setProject( QgsProject *project )
7349{
7350 mProject = project;
7351 if ( mProject )
7352 {
7353 connect( mProject, &QgsProject::layerRemoved, this, [&]( const QString &layerId ) {
7354 if ( mValue.removeAll( layerId ) )
7355 {
7356 updateSummaryText();
7357 emit changed();
7358 }
7359 } );
7360 }
7361}
7362
7363void QgsProcessingMultipleLayerPanelWidget::setModel( QgsProcessingModelAlgorithm *model, const QString &modelChildAlgorithmID )
7364{
7365 mModel = model;
7366 if ( !model )
7367 return;
7368
7369 switch ( mParam->layerType() )
7370 {
7372 mModelSources = model->availableSourcesForChild( modelChildAlgorithmID, QStringList() << QgsProcessingParameterFile::typeName(), QStringList() << QgsProcessingOutputFile::typeName() );
7373 break;
7374
7376 {
7377 mModelSources = model->availableSourcesForChild(
7378 modelChildAlgorithmID,
7381 );
7382 break;
7383 }
7384
7386 {
7387 mModelSources = model->availableSourcesForChild(
7388 modelChildAlgorithmID,
7391 );
7392 break;
7393 }
7394
7396 {
7397 mModelSources = model->availableSourcesForChild(
7398 modelChildAlgorithmID,
7401 );
7402 break;
7403 }
7404
7406 {
7407 mModelSources = model->availableSourcesForChild(
7408 modelChildAlgorithmID,
7411 );
7412 break;
7413 }
7414
7416 {
7417 mModelSources = model->availableSourcesForChild(
7418 modelChildAlgorithmID,
7421 );
7422 break;
7423 }
7424
7426 {
7427 mModelSources = model->availableSourcesForChild(
7428 modelChildAlgorithmID,
7431 );
7432 break;
7433 }
7434
7436 {
7437 mModelSources = model->availableSourcesForChild(
7438 modelChildAlgorithmID,
7441 );
7442 break;
7443 }
7444
7446 {
7447 mModelSources = model->availableSourcesForChild(
7448 modelChildAlgorithmID,
7449 QStringList()
7455 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector )
7456 );
7457 break;
7458 }
7459
7461 {
7462 mModelSources = model->availableSourcesForChild(
7463 modelChildAlgorithmID,
7464 QStringList()
7470 );
7471 break;
7472 }
7473
7475 {
7476 mModelSources = model->availableSourcesForChild(
7477 modelChildAlgorithmID,
7478 QStringList()
7484 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
7485 );
7486 break;
7487 }
7488
7490 {
7491 mModelSources = model->availableSourcesForChild(
7492 modelChildAlgorithmID,
7493 QStringList()
7499 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
7500 );
7501 break;
7502 }
7503
7505 {
7506 mModelSources = model->availableSourcesForChild(
7507 modelChildAlgorithmID,
7508 QStringList()
7514 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
7515 );
7516 break;
7517 }
7518
7520 {
7521 mModelSources = model->availableSourcesForChild(
7522 modelChildAlgorithmID,
7523 QStringList()
7530 QStringList()
7535 // << QgsProcessingOutputMeshLayer::typeName()
7537 );
7538 break;
7539 }
7540 }
7541}
7542
7543void QgsProcessingMultipleLayerPanelWidget::showDialog()
7544{
7546 if ( panel && panel->dockMode() )
7547 {
7548 QgsProcessingMultipleInputPanelWidget *widget = new QgsProcessingMultipleInputPanelWidget( mParam, mValue, mModelSources, mModel, nullptr );
7549 widget->setPanelTitle( mParam->description() );
7550 widget->setProject( mProject );
7551 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
7552 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
7553 panel->openPanel( widget );
7554 }
7555 else
7556 {
7557 QgsProcessingMultipleInputDialog dlg( mParam, mValue, mModelSources, mModel, this, Qt::WindowFlags() );
7558 dlg.setProject( mProject );
7559 if ( dlg.exec() )
7560 {
7561 setValue( dlg.selectedOptions() );
7562 }
7563 }
7564}
7565
7566void QgsProcessingMultipleLayerPanelWidget::updateSummaryText()
7567{
7568 if ( mParam )
7569 mLineEdit->setText( tr( "%n input(s) selected", nullptr, mValue.count() ) );
7570}
7571
7572//
7573// QgsProcessingMultipleLayerWidgetWrapper
7574//
7575
7576QgsProcessingMultipleLayerParameterDefinitionWidget::QgsProcessingMultipleLayerParameterDefinitionWidget(
7577 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
7578)
7579 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
7580{
7581 QVBoxLayout *vlayout = new QVBoxLayout();
7582 vlayout->setContentsMargins( 0, 0, 0, 0 );
7583
7584 vlayout->addWidget( new QLabel( tr( "Allowed layer type" ) ) );
7585 mLayerTypeComboBox = new QComboBox();
7586 mLayerTypeComboBox->addItem( tr( "Any Map Layer" ), static_cast<int>( Qgis::ProcessingSourceType::MapLayer ) );
7587 mLayerTypeComboBox->addItem( tr( "Vector (No Geometry Required)" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
7588 mLayerTypeComboBox->addItem( tr( "Vector (Point)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
7589 mLayerTypeComboBox->addItem( tr( "Vector (Line)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
7590 mLayerTypeComboBox->addItem( tr( "Vector (Polygon)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
7591 mLayerTypeComboBox->addItem( tr( "Any Geometry Type" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
7592 mLayerTypeComboBox->addItem( tr( "Raster" ), static_cast<int>( Qgis::ProcessingSourceType::Raster ) );
7593 mLayerTypeComboBox->addItem( tr( "File" ), static_cast<int>( Qgis::ProcessingSourceType::File ) );
7594 mLayerTypeComboBox->addItem( tr( "Mesh" ), static_cast<int>( Qgis::ProcessingSourceType::Mesh ) );
7595 mLayerTypeComboBox->addItem( tr( "Plugin" ), static_cast<int>( Qgis::ProcessingSourceType::Plugin ) );
7596 mLayerTypeComboBox->addItem( tr( "Point Cloud" ), static_cast<int>( Qgis::ProcessingSourceType::PointCloud ) );
7597 mLayerTypeComboBox->addItem( tr( "Annotation" ), static_cast<int>( Qgis::ProcessingSourceType::Annotation ) );
7598 if ( const QgsProcessingParameterMultipleLayers *layersParam = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( definition ) )
7599 mLayerTypeComboBox->setCurrentIndex( mLayerTypeComboBox->findData( static_cast<int>( layersParam->layerType() ) ) );
7600
7601 vlayout->addWidget( mLayerTypeComboBox );
7602 connect( mLayerTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
7603
7604 setLayout( vlayout );
7605}
7606
7607QgsProcessingParameterDefinition *QgsProcessingMultipleLayerParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
7608{
7609 auto param = std::make_unique<QgsProcessingParameterMultipleLayers>( name, description, static_cast<Qgis::ProcessingSourceType>( mLayerTypeComboBox->currentData().toInt() ) );
7610 param->setFlags( flags );
7611 return param.release();
7612}
7613
7614QgsProcessingMultipleLayerWidgetWrapper::QgsProcessingMultipleLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7615 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
7616{}
7617
7618QWidget *QgsProcessingMultipleLayerWidgetWrapper::createWidget()
7619{
7620 const QgsProcessingParameterMultipleLayers *layerParam = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( parameterDefinition() );
7621
7622 mPanel = new QgsProcessingMultipleLayerPanelWidget( nullptr, layerParam );
7623 mPanel->setToolTip( parameterDefinition()->toolTip() );
7624 mPanel->setProject( widgetContext().project() );
7625 if ( type() == Qgis::ProcessingMode::Modeler )
7626 mPanel->setModel( widgetContext().model(), widgetContext().modelChildAlgorithmId() );
7627 connect( mPanel, &QgsProcessingMultipleLayerPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
7628 return mPanel;
7629}
7630
7631void QgsProcessingMultipleLayerWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
7632{
7634 if ( mPanel )
7635 {
7636 mPanel->setProject( context.project() );
7637 if ( type() == Qgis::ProcessingMode::Modeler )
7638 mPanel->setModel( widgetContext().model(), widgetContext().modelChildAlgorithmId() );
7639 }
7640}
7641
7642void QgsProcessingMultipleLayerWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
7643{
7644 if ( mPanel )
7645 {
7646 QVariantList opts;
7647 if ( value.isValid() )
7648 {
7649 const QList<QgsMapLayer *> v = QgsProcessingParameters::parameterAsLayerList( parameterDefinition(), value, context );
7650 opts.reserve( v.size() );
7651 for ( const QgsMapLayer *l : v )
7652 opts << l->source();
7653 }
7654
7655 for ( const QVariant &v : value.toList() )
7656 {
7657 if ( v.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
7658 {
7659 const QgsProcessingModelChildParameterSource source = v.value<QgsProcessingModelChildParameterSource>();
7660 opts << QVariant::fromValue( source );
7661 }
7662 }
7663
7664 if ( mPanel )
7665 mPanel->setValue( value.isValid() ? opts : QVariant() );
7666 }
7667}
7668
7669QVariant QgsProcessingMultipleLayerWidgetWrapper::widgetValue() const
7670{
7671 if ( mPanel )
7672 return !mPanel->value().toList().isEmpty() ? mPanel->value() : QVariant();
7673 else
7674 return QVariant();
7675}
7676
7677QString QgsProcessingMultipleLayerWidgetWrapper::modelerExpressionFormatString() const
7678{
7679 return tr( "an array of layer paths, or semicolon separated string of layer paths" );
7680}
7681
7682QString QgsProcessingMultipleLayerWidgetWrapper::parameterType() const
7683{
7685}
7686
7687QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMultipleLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7688{
7689 return new QgsProcessingMultipleLayerWidgetWrapper( parameter, type );
7690}
7691
7692QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMultipleLayerWidgetWrapper::createParameterDefinitionWidget(
7694)
7695{
7696 return new QgsProcessingMultipleLayerParameterDefinitionWidget( context, widgetContext, definition, algorithm );
7697}
7698
7699
7700//
7701// QgsProcessingPointCloudLayerWidgetWrapper
7702//
7703
7704QgsProcessingPointCloudLayerWidgetWrapper::QgsProcessingPointCloudLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7705 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
7706{}
7707
7708QString QgsProcessingPointCloudLayerWidgetWrapper::modelerExpressionFormatString() const
7709{
7710 return tr( "path to a point cloud layer" );
7711}
7712
7713QString QgsProcessingPointCloudLayerWidgetWrapper::parameterType() const
7714{
7716}
7717
7718QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointCloudLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7719{
7720 return new QgsProcessingPointCloudLayerWidgetWrapper( parameter, type );
7721}
7722
7723QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingPointCloudLayerWidgetWrapper::createParameterDefinitionWidget(
7725)
7726{
7727 Q_UNUSED( context );
7728 Q_UNUSED( widgetContext );
7729 Q_UNUSED( definition );
7730 Q_UNUSED( algorithm );
7731
7732 return nullptr;
7733}
7734
7735
7736//
7737// QgsProcessingAnnotationLayerWidgetWrapper
7738//
7739
7740QgsProcessingAnnotationLayerWidgetWrapper::QgsProcessingAnnotationLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7741 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
7742{}
7743
7744QString QgsProcessingAnnotationLayerWidgetWrapper::modelerExpressionFormatString() const
7745{
7746 return tr( "name of an annotation layer, or \"main\" for the main annotation layer" );
7747}
7748
7749QString QgsProcessingAnnotationLayerWidgetWrapper::parameterType() const
7750{
7752}
7753
7754QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAnnotationLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7755{
7756 return new QgsProcessingAnnotationLayerWidgetWrapper( parameter, type );
7757}
7758
7759QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAnnotationLayerWidgetWrapper::createParameterDefinitionWidget(
7761)
7762{
7763 Q_UNUSED( context );
7764 Q_UNUSED( widgetContext );
7765 Q_UNUSED( definition );
7766 Q_UNUSED( algorithm );
7767
7768 return nullptr;
7769}
7770
7771void QgsProcessingAnnotationLayerWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
7772{
7774 if ( mComboBox )
7775 {
7776 if ( mWidgetContext.project() )
7777 mComboBox->setAdditionalLayers( { mWidgetContext.project()->mainAnnotationLayer() } );
7778 }
7779}
7780
7781QWidget *QgsProcessingAnnotationLayerWidgetWrapper::createWidget()
7782{
7783 mComboBox = new QgsMapLayerComboBox();
7784 mComboBox->setFilters( Qgis::LayerFilter::AnnotationLayer );
7785
7786 switch ( type() )
7787 {
7790 break;
7792 mComboBox->setEditable( true );
7793 break;
7794 }
7795
7796 mComboBox->setToolTip( parameterDefinition()->toolTip() );
7797
7798 if ( mWidgetContext.project() )
7799 mComboBox->setAdditionalLayers( { mWidgetContext.project()->mainAnnotationLayer() } );
7800
7801 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
7802 mComboBox->setAllowEmptyLayer( true );
7803
7804 connect( mComboBox, &QgsMapLayerComboBox::layerChanged, this, [this]() {
7805 if ( mBlockSignals )
7806 return;
7807
7808 emit widgetValueHasChanged( this );
7809 } );
7810
7811 setWidgetContext( widgetContext() );
7812 return mComboBox;
7813}
7814
7815void QgsProcessingAnnotationLayerWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
7816{
7817 if ( mComboBox )
7818 {
7819 if ( !value.isValid() && parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
7820 {
7821 mComboBox->setLayer( nullptr );
7822 return;
7823 }
7824
7825 QVariant val = value;
7826 if ( val.userType() == qMetaTypeId<QgsProperty>() )
7827 {
7828 if ( val.value<QgsProperty>().propertyType() == Qgis::PropertyType::Static )
7829 {
7830 val = val.value<QgsProperty>().staticValue();
7831 }
7832 else
7833 {
7834 val = val.value<QgsProperty>().valueAsString( context.expressionContext(), parameterDefinition()->defaultValueForGui().toString() );
7835 }
7836 }
7837
7838 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( val.value<QObject *>() );
7839 if ( !layer && val.userType() == QMetaType::Type::QString )
7840 {
7842 }
7843
7844 if ( layer )
7845 {
7846 mComboBox->setLayer( layer );
7847 }
7848 }
7849}
7850
7851QVariant QgsProcessingAnnotationLayerWidgetWrapper::widgetValue() const
7852{
7853 return mComboBox && mComboBox->currentLayer()
7854 ? ( mWidgetContext.project() ? ( mComboBox->currentLayer() == mWidgetContext.project()->mainAnnotationLayer() ? u"main"_s : mComboBox->currentLayer()->id() )
7855 : mComboBox->currentLayer()->id() )
7856 : QVariant();
7857}
7858
7859
7860//
7861// QgsProcessingPointCloudAttributePanelWidget
7862//
7863
7864QgsProcessingPointCloudAttributePanelWidget::QgsProcessingPointCloudAttributePanelWidget( QWidget *parent, const QgsProcessingParameterPointCloudAttribute *param )
7865 : QWidget( parent )
7866 , mParam( param )
7867{
7868 QHBoxLayout *hl = new QHBoxLayout();
7869 hl->setContentsMargins( 0, 0, 0, 0 );
7870
7871 mLineEdit = new QLineEdit();
7872 mLineEdit->setEnabled( false );
7873 hl->addWidget( mLineEdit, 1 );
7874
7875 mToolButton = new QToolButton();
7876 mToolButton->setText( QString( QChar( 0x2026 ) ) );
7877 hl->addWidget( mToolButton );
7878
7879 setLayout( hl );
7880
7881 if ( mParam )
7882 {
7883 mLineEdit->setText( tr( "%n attribute(s) selected", nullptr, 0 ) );
7884 }
7885
7886 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingPointCloudAttributePanelWidget::showDialog );
7887}
7888
7889void QgsProcessingPointCloudAttributePanelWidget::setAttributes( const QgsPointCloudAttributeCollection &attributes )
7890{
7891 mAttributes = attributes;
7892}
7893
7894void QgsProcessingPointCloudAttributePanelWidget::setValue( const QVariant &value )
7895{
7896 if ( value.isValid() )
7897 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
7898 else
7899 mValue.clear();
7900
7901 updateSummaryText();
7902 emit changed();
7903}
7904
7905void QgsProcessingPointCloudAttributePanelWidget::showDialog()
7906{
7907 QVariantList availableOptions;
7908 availableOptions.reserve( mAttributes.count() );
7909 const QVector<QgsPointCloudAttribute> attributes = mAttributes.attributes();
7910 for ( const QgsPointCloudAttribute &attr : attributes )
7911 {
7912 availableOptions << attr.name();
7913 }
7914
7916 if ( panel && panel->dockMode() )
7917 {
7918 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
7919 widget->setPanelTitle( mParam->description() );
7920
7921 widget->setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
7922
7923 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
7924 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
7925 panel->openPanel( widget );
7926 }
7927 else
7928 {
7929 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
7930
7931 dlg.setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
7932 if ( dlg.exec() )
7933 {
7934 setValue( dlg.selectedOptions() );
7935 }
7936 }
7937}
7938
7939void QgsProcessingPointCloudAttributePanelWidget::updateSummaryText()
7940{
7941 if ( !mParam )
7942 return;
7943
7944 if ( mValue.empty() )
7945 {
7946 mLineEdit->setText( tr( "%n attribute(s) selected", nullptr, 0 ) );
7947 }
7948 else
7949 {
7950 QStringList values;
7951 values.reserve( mValue.size() );
7952 for ( const QVariant &val : std::as_const( mValue ) )
7953 {
7954 values << val.toString();
7955 }
7956
7957 const QString concatenated = values.join( tr( "," ) );
7958 if ( concatenated.length() < 100 )
7959 mLineEdit->setText( concatenated );
7960 else
7961 mLineEdit->setText( tr( "%n attribute(s) selected", nullptr, mValue.count() ) );
7962 }
7963}
7964
7965
7966//
7967// QgsProcessingPointCloudAttributeWidgetWrapper
7968//
7969
7970QgsProcessingPointCloudAttributeParameterDefinitionWidget::QgsProcessingPointCloudAttributeParameterDefinitionWidget(
7971 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
7972)
7973 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
7974{
7975 QVBoxLayout *vlayout = new QVBoxLayout();
7976 vlayout->setContentsMargins( 0, 0, 0, 0 );
7977
7978 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
7979 mParentLayerComboBox = new QComboBox();
7980
7981 QString initialParent;
7982 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
7983 initialParent = attrParam->parentLayerParameterName();
7984
7985 if ( auto *lModel = widgetContext.model() )
7986 {
7987 // populate combo box with other model input choices
7988 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
7989 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
7990 {
7991 if ( const QgsProcessingParameterPointCloudLayer *definition = dynamic_cast<const QgsProcessingParameterPointCloudLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
7992 {
7993 mParentLayerComboBox->addItem( definition->description(), definition->name() );
7994 if ( !initialParent.isEmpty() && initialParent == definition->name() )
7995 {
7996 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
7997 }
7998 }
7999 }
8000 }
8001
8002 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
8003 {
8004 // if no parent candidates found, we just add the existing one as a placeholder
8005 mParentLayerComboBox->addItem( initialParent, initialParent );
8006 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
8007 }
8008
8009 vlayout->addWidget( mParentLayerComboBox );
8010
8011 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8012
8013 mAllowMultipleCheckBox = new QCheckBox( tr( "Accept multiple attributes" ) );
8014 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
8015 mAllowMultipleCheckBox->setChecked( attrParam->allowMultiple() );
8016
8017 vlayout->addWidget( mAllowMultipleCheckBox );
8018 connect( mAllowMultipleCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8019
8020 mDefaultToAllCheckBox = new QCheckBox( tr( "Select all attributes by default" ) );
8021 mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() );
8022 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
8023 mDefaultToAllCheckBox->setChecked( attrParam->defaultToAllAttributes() );
8024
8025 connect( mDefaultToAllCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8026
8027 vlayout->addWidget( mDefaultToAllCheckBox );
8028
8029 connect( mAllowMultipleCheckBox, &QCheckBox::stateChanged, this, [this] { mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() ); } );
8030
8031 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
8032
8033 mDefaultLineEdit = new QLineEdit();
8034 mDefaultLineEdit->setToolTip( tr( "Default attribute name, or ; separated list of attribute names for multiple attribute parameters" ) );
8035 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
8036 {
8037 const QStringList attributes = QgsProcessingParameters::parameterAsStrings( attrParam, attrParam->defaultValueForGui(), context );
8038 mDefaultLineEdit->setText( attributes.join( ';' ) );
8039 }
8040 vlayout->addWidget( mDefaultLineEdit );
8041
8042 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8043
8044 setLayout( vlayout );
8045}
8046
8047QgsProcessingParameterDefinition *QgsProcessingPointCloudAttributeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
8048{
8049 QVariant defaultValue;
8050 if ( !mDefaultLineEdit->text().trimmed().isEmpty() )
8051 {
8052 defaultValue = mDefaultLineEdit->text();
8053 }
8054 auto param = std::make_unique<
8055 QgsProcessingParameterPointCloudAttribute>( name, description, defaultValue, mParentLayerComboBox->currentData().toString(), mAllowMultipleCheckBox->isChecked(), false, mDefaultToAllCheckBox->isChecked() );
8056 param->setFlags( flags );
8057 return param.release();
8058}
8059
8060QgsProcessingPointCloudAttributeWidgetWrapper::QgsProcessingPointCloudAttributeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8061 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
8062{}
8063
8064QWidget *QgsProcessingPointCloudAttributeWidgetWrapper::createWidget()
8065{
8066 const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8067 if ( !attrParam )
8068 return nullptr;
8069
8070 switch ( type() )
8071 {
8074 {
8075 if ( attrParam->allowMultiple() )
8076 {
8077 mPanel = new QgsProcessingPointCloudAttributePanelWidget( nullptr, attrParam );
8078 mPanel->setToolTip( parameterDefinition()->toolTip() );
8079 connect( mPanel, &QgsProcessingPointCloudAttributePanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
8080 return mPanel;
8081 }
8082 else
8083 {
8084 mComboBox = new QgsPointCloudAttributeComboBox();
8085 mComboBox->setAllowEmptyAttributeName( attrParam->flags() & Qgis::ProcessingParameterFlag::Optional );
8086 mComboBox->setToolTip( parameterDefinition()->toolTip() );
8087 connect( mComboBox, &QgsPointCloudAttributeComboBox::attributeChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
8088 return mComboBox;
8089 }
8090 }
8091
8093 {
8094 mLineEdit = new QLineEdit();
8095 mLineEdit->setToolTip( QObject::tr( "Name of attribute (separate attribute names with ; for multiple attribute parameters)" ) );
8096 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
8097 return mLineEdit;
8098 }
8099 }
8100 return nullptr;
8101}
8102
8103void QgsProcessingPointCloudAttributeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
8104{
8106 switch ( type() )
8107 {
8110 {
8111 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
8112 {
8113 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() )->parentLayerParameterName() )
8114 {
8115 setParentLayerWrapperValue( wrapper );
8116 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
8117 break;
8118 }
8119 }
8120 break;
8121 }
8122
8124 break;
8125 }
8126}
8127
8128void QgsProcessingPointCloudAttributeWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
8129{
8130 // evaluate value to layer
8131 QgsProcessingContext *context = nullptr;
8132 std::unique_ptr<QgsProcessingContext> tmpContext;
8133 if ( mProcessingContextGenerator )
8134 context = mProcessingContextGenerator->processingContext();
8135
8136 if ( !context )
8137 {
8138 tmpContext = std::make_unique<QgsProcessingContext>();
8139 context = tmpContext.get();
8140 }
8141
8142 QVariant value = parentWrapper->parameterValue();
8143
8145 if ( layer && layer->isValid() )
8146 {
8147 // need to grab ownership of layer if required - otherwise layer may be deleted when context
8148 // goes out of scope
8149 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
8150 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::PointCloud )
8151 {
8152 mParentLayer.reset( qobject_cast<QgsPointCloudLayer *>( ownedLayer.release() ) );
8153 layer = mParentLayer.get();
8154 }
8155 else
8156 {
8157 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
8158 }
8159
8160 if ( mComboBox )
8161 mComboBox->setLayer( layer );
8162 else if ( mPanel )
8163 {
8164 mPanel->setAttributes( layer->attributes() );
8165 }
8166 }
8167 else
8168 {
8169 if ( mComboBox )
8170 {
8171 mComboBox->setLayer( nullptr );
8172 }
8173 else if ( mPanel )
8174 mPanel->setAttributes( QgsPointCloudAttributeCollection() );
8175
8176 if ( value.isValid() && widgetContext().messageBar() )
8177 {
8178 widgetContext().messageBar()->clearWidgets();
8179 widgetContext().messageBar()->pushMessage( QString(), QObject::tr( "Could not load selected layer/table. Dependent attributes could not be populated" ), Qgis::MessageLevel::Info );
8180 }
8181 }
8182
8183 const QgsProcessingParameterPointCloudAttribute *attrParam = static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8184 if ( mPanel && attrParam->defaultToAllAttributes() )
8185 {
8186 QVariantList val;
8187 val.reserve( mPanel->attributes().attributes().size() );
8188 for ( const QgsPointCloudAttribute &attr : mPanel->attributes().attributes() )
8189 val << attr.name();
8190 setWidgetValue( val, *context );
8191 }
8192 else if ( attrParam->defaultValueForGui().isValid() )
8193 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
8194}
8195
8196void QgsProcessingPointCloudAttributeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
8197{
8198 if ( mComboBox )
8199 {
8200 if ( !value.isValid() )
8201 mComboBox->setAttribute( QString() );
8202 else
8203 {
8204 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
8205 mComboBox->setAttribute( v );
8206 }
8207 }
8208 else if ( mPanel )
8209 {
8210 QVariantList opts;
8211 if ( value.isValid() )
8212 {
8213 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
8214 opts.reserve( v.size() );
8215 for ( const QString &i : v )
8216 opts << i;
8217 }
8218 if ( mPanel )
8219 mPanel->setValue( opts );
8220 }
8221 else if ( mLineEdit )
8222 {
8223 const QgsProcessingParameterPointCloudAttribute *attrParam = static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8224 if ( attrParam->allowMultiple() )
8225 {
8226 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
8227 mLineEdit->setText( v.join( ';' ) );
8228 }
8229 else
8230 {
8231 mLineEdit->setText( QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context ) );
8232 }
8233 }
8234}
8235
8236QVariant QgsProcessingPointCloudAttributeWidgetWrapper::widgetValue() const
8237{
8238 if ( mComboBox )
8239 return mComboBox->currentAttribute();
8240 else if ( mPanel )
8241 return mPanel->value();
8242 else if ( mLineEdit )
8243 {
8244 const QgsProcessingParameterPointCloudAttribute *attrParam = static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8245 if ( attrParam->allowMultiple() )
8246 {
8247 return mLineEdit->text().split( ';' );
8248 }
8249 else
8250 return mLineEdit->text();
8251 }
8252 else
8253 return QVariant();
8254}
8255
8256QString QgsProcessingPointCloudAttributeWidgetWrapper::modelerExpressionFormatString() const
8257{
8258 return tr( "selected attribute names as an array of names, or semicolon separated string of options (e.g. 'X;Intensity')" );
8259}
8260
8261QString QgsProcessingPointCloudAttributeWidgetWrapper::parameterType() const
8262{
8264}
8265
8266QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointCloudAttributeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8267{
8268 return new QgsProcessingPointCloudAttributeWidgetWrapper( parameter, type );
8269}
8270
8271QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingPointCloudAttributeWidgetWrapper::createParameterDefinitionWidget(
8273)
8274{
8275 return new QgsProcessingPointCloudAttributeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
8276}
8277
8278
8279//
8280// QgsProcessingOutputWidgetWrapper
8281//
8282
8283QgsProcessingOutputWidgetWrapper::QgsProcessingOutputWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8284 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
8285{}
8286
8287QWidget *QgsProcessingOutputWidgetWrapper::createWidget()
8288{
8289 const QgsProcessingDestinationParameter *destParam = dynamic_cast<const QgsProcessingDestinationParameter *>( parameterDefinition() );
8290 switch ( type() )
8291 {
8294 {
8295 mOutputWidget = new QgsProcessingLayerOutputDestinationWidget( destParam, false );
8296 if ( mProcessingContextGenerator )
8297 mOutputWidget->setContext( mProcessingContextGenerator->processingContext() );
8298 if ( mParametersGenerator )
8299 mOutputWidget->registerProcessingParametersGenerator( mParametersGenerator );
8300 mOutputWidget->setToolTip( parameterDefinition()->toolTip() );
8301
8302 connect( mOutputWidget, &QgsProcessingLayerOutputDestinationWidget::destinationChanged, this, [this]() {
8303 if ( mBlockSignals )
8304 return;
8305
8306 emit widgetValueHasChanged( this );
8307 } );
8308
8309 if ( type() == Qgis::ProcessingMode::Standard
8311 mOutputWidget->addOpenAfterRunningOption();
8312
8313 return mOutputWidget;
8314 }
8316 break;
8317 }
8318
8319 return nullptr;
8320}
8321
8322
8323void QgsProcessingOutputWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
8324{
8325 if ( mOutputWidget )
8326 mOutputWidget->setValue( value );
8327}
8328
8329QVariant QgsProcessingOutputWidgetWrapper::widgetValue() const
8330{
8331 if ( mOutputWidget )
8332 return mOutputWidget->value();
8333
8334 return QVariant();
8335}
8336
8337QVariantMap QgsProcessingOutputWidgetWrapper::customProperties() const
8338{
8339 QVariantMap res;
8340 if ( mOutputWidget )
8341 res.insert( u"OPEN_AFTER_RUNNING"_s, mOutputWidget->openAfterRunning() );
8342 return res;
8343}
8344
8345//
8346// QgsProcessingFeatureSinkWidgetWrapper
8347//
8348
8349QgsProcessingFeatureSinkWidgetWrapper::QgsProcessingFeatureSinkWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8350 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8351{}
8352
8353QString QgsProcessingFeatureSinkWidgetWrapper::parameterType() const
8354{
8356}
8357
8358QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFeatureSinkWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8359{
8360 return new QgsProcessingFeatureSinkWidgetWrapper( parameter, type );
8361}
8362
8363QString QgsProcessingFeatureSinkWidgetWrapper::modelerExpressionFormatString() const
8364{
8365 return tr( "path to layer destination" );
8366}
8367
8368//
8369// QgsProcessingFeatureSinkWidgetWrapper
8370//
8371
8372QgsProcessingVectorDestinationWidgetWrapper::QgsProcessingVectorDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8373 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8374{}
8375
8376QString QgsProcessingVectorDestinationWidgetWrapper::parameterType() const
8377{
8379}
8380
8381QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVectorDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8382{
8383 return new QgsProcessingVectorDestinationWidgetWrapper( parameter, type );
8384}
8385
8386QString QgsProcessingVectorDestinationWidgetWrapper::modelerExpressionFormatString() const
8387{
8388 return tr( "path to layer destination" );
8389}
8390
8391//
8392// QgsProcessingRasterDestinationWidgetWrapper
8393//
8394
8395QgsProcessingRasterDestinationWidgetWrapper::QgsProcessingRasterDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8396 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8397{}
8398
8399QString QgsProcessingRasterDestinationWidgetWrapper::parameterType() const
8400{
8402}
8403
8404QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingRasterDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8405{
8406 return new QgsProcessingRasterDestinationWidgetWrapper( parameter, type );
8407}
8408
8409QString QgsProcessingRasterDestinationWidgetWrapper::modelerExpressionFormatString() const
8410{
8411 return tr( "path to layer destination" );
8412}
8413
8414//
8415// QgsProcessingPointCloudDestinationWidgetWrapper
8416//
8417
8418QgsProcessingPointCloudDestinationWidgetWrapper::QgsProcessingPointCloudDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8419 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8420{}
8421
8422QString QgsProcessingPointCloudDestinationWidgetWrapper::parameterType() const
8423{
8425}
8426
8427QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointCloudDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8428{
8429 return new QgsProcessingPointCloudDestinationWidgetWrapper( parameter, type );
8430}
8431
8432QString QgsProcessingPointCloudDestinationWidgetWrapper::modelerExpressionFormatString() const
8433{
8434 return tr( "path to layer destination" );
8435}
8436
8437//
8438// QgsProcessingFileDestinationWidgetWrapper
8439//
8440
8441QgsProcessingFileDestinationWidgetWrapper::QgsProcessingFileDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8442 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8443{}
8444
8445QString QgsProcessingFileDestinationWidgetWrapper::parameterType() const
8446{
8448}
8449
8450QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFileDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8451{
8452 return new QgsProcessingFileDestinationWidgetWrapper( parameter, type );
8453}
8454
8455
8456QString QgsProcessingFileDestinationWidgetWrapper::modelerExpressionFormatString() const
8457{
8458 return tr( "path to file destination" );
8459}
8460
8461//
8462// QgsProcessingFolderDestinationWidgetWrapper
8463//
8464
8465QgsProcessingFolderDestinationWidgetWrapper::QgsProcessingFolderDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8466 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8467{}
8468
8469QString QgsProcessingFolderDestinationWidgetWrapper::parameterType() const
8470{
8472}
8473
8474QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFolderDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8475{
8476 return new QgsProcessingFolderDestinationWidgetWrapper( parameter, type );
8477}
8478
8479
8480QString QgsProcessingFolderDestinationWidgetWrapper::modelerExpressionFormatString() const
8481{
8482 return tr( "path to folder destination" );
8483}
8484
8485//
8486// QgsProcessingVectorTileDestinationWidgetWrapper
8487//
8488
8489QgsProcessingVectorTileDestinationWidgetWrapper::QgsProcessingVectorTileDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8490 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8491{}
8492
8493QString QgsProcessingVectorTileDestinationWidgetWrapper::parameterType() const
8494{
8496}
8497
8498QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVectorTileDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8499{
8500 return new QgsProcessingPointCloudDestinationWidgetWrapper( parameter, type );
8501}
8502
8503QString QgsProcessingVectorTileDestinationWidgetWrapper::modelerExpressionFormatString() const
8504{
8505 return tr( "path to layer destination" );
8506}
8507
8508
8509//
8510// QgsHeatmapPixelSizeWidget
8511//
8512
8513QgsHeatmapPixelSizeWidget::QgsHeatmapPixelSizeWidget( QWidget *parent )
8514 : QgsPanelWidget( parent )
8515{
8516 setupUi( this );
8517
8518 mCellXSpinBox->setShowClearButton( false );
8519 mCellYSpinBox->setShowClearButton( false );
8520 mRowsSpinBox->setShowClearButton( false );
8521 mColumnsSpinBox->setShowClearButton( false );
8522
8523 connect( mCellYSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), mCellXSpinBox, &QgsDoubleSpinBox::setValue );
8524 connect( mCellXSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsHeatmapPixelSizeWidget::pixelSizeChanged );
8525 connect( mRowsSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, &QgsHeatmapPixelSizeWidget::rowsChanged );
8526 connect( mColumnsSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, &QgsHeatmapPixelSizeWidget::columnsChanged );
8527}
8528
8529void QgsHeatmapPixelSizeWidget::setLayer( QgsVectorLayer *layer )
8530{
8531 mLayer = layer;
8532 if ( mLayer )
8533 mLayerBounds = mLayer->extent();
8534 else
8535 mLayerBounds = QgsRectangle();
8536
8537 recalculateBounds();
8538}
8539
8540QgsVectorLayer *QgsHeatmapPixelSizeWidget::layer()
8541{
8542 return mLayer;
8543}
8544
8545void QgsHeatmapPixelSizeWidget::setRadius( double radius )
8546{
8547 mRadius = radius;
8548 recalculateBounds();
8549}
8550
8551void QgsHeatmapPixelSizeWidget::setRadiusField( const QString &radiusField )
8552{
8553 mRadiusField = radiusField;
8554 recalculateBounds();
8555}
8556
8557void QgsHeatmapPixelSizeWidget::recalculateBounds()
8558{
8559 mRasterBounds = mLayerBounds;
8560 if ( !mLayer )
8561 return;
8562
8563 double maxRadius = mRadius;
8564 if ( !mRadiusField.isEmpty() )
8565 {
8566 const int idx = mLayer->fields().lookupField( mRadiusField );
8567 if ( idx >= 0 )
8568 maxRadius = mLayer->maximumValue( idx ).toDouble();
8569 }
8570
8571 mRasterBounds.setXMinimum( mRasterBounds.xMinimum() - maxRadius );
8572 mRasterBounds.setYMinimum( mRasterBounds.yMinimum() - maxRadius );
8573 mRasterBounds.setXMaximum( mRasterBounds.xMaximum() + maxRadius );
8574 mRasterBounds.setYMaximum( mRasterBounds.yMaximum() + maxRadius );
8575
8576 pixelSizeChanged();
8577}
8578
8579void QgsHeatmapPixelSizeWidget::pixelSizeChanged()
8580{
8581 const double prevValue = value();
8582 const double cellSize = mCellXSpinBox->value();
8583 if ( cellSize <= 0 || mRasterBounds.isNull() )
8584 {
8585 emit valueChanged();
8586 return;
8587 }
8588
8589 whileBlocking( mCellYSpinBox )->setValue( cellSize );
8590
8591 const int rows = std::max( static_cast<int>( std::round( mRasterBounds.height() / cellSize ) ) + 1, 1 );
8592 const int cols = std::max( static_cast<int>( std::round( mRasterBounds.width() / cellSize ) ) + 1, 1 );
8593
8594 whileBlocking( mRowsSpinBox )->setValue( rows );
8595 whileBlocking( mColumnsSpinBox )->setValue( cols );
8596
8597 if ( !qgsDoubleNear( prevValue, value() ) )
8598 {
8599 emit valueChanged();
8600 }
8601}
8602
8603void QgsHeatmapPixelSizeWidget::rowsChanged()
8604{
8605 const int rows = mRowsSpinBox->value();
8606 if ( rows <= 0 || mRasterBounds.isNull() )
8607 return;
8608
8609 const double cellSize = mRasterBounds.height() / rows;
8610 if ( cellSize == 0 )
8611 return;
8612
8613 const int cols = std::max( static_cast<int>( std::round( mRasterBounds.width() / cellSize ) ) + 1, 1 );
8614
8615 whileBlocking( mColumnsSpinBox )->setValue( cols );
8616 whileBlocking( mCellXSpinBox )->setValue( cellSize );
8617 whileBlocking( mCellYSpinBox )->setValue( cellSize );
8618
8619 emit valueChanged();
8620}
8621
8622void QgsHeatmapPixelSizeWidget::columnsChanged()
8623{
8624 const int cols = mColumnsSpinBox->value();
8625 if ( cols < 2 || mRasterBounds.isNull() )
8626 return;
8627
8628 const double cellSize = mRasterBounds.width() / ( cols - 1 );
8629 if ( cellSize == 0 )
8630 return;
8631
8632 const int rows = std::max( static_cast<int>( std::round( mRasterBounds.height() / cellSize ) ), 1 );
8633
8634 whileBlocking( mRowsSpinBox )->setValue( rows );
8635 whileBlocking( mCellXSpinBox )->setValue( cellSize );
8636 whileBlocking( mCellYSpinBox )->setValue( cellSize );
8637
8638 emit valueChanged();
8639}
8640
8641double QgsHeatmapPixelSizeWidget::value() const
8642{
8643 return mCellXSpinBox->value();
8644}
8645
8646void QgsHeatmapPixelSizeWidget::setValue( double value )
8647{
8648 mCellXSpinBox->setValue( value );
8649 mCellYSpinBox->setValue( value );
8650}
8651
8652//
8653// QgsProcessingHeatmapPixelSizeWidgetWrapper
8654//
8655
8656QgsProcessingHeatmapPixelSizeWidgetWrapper::QgsProcessingHeatmapPixelSizeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8657 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
8658{}
8659
8660QString QgsProcessingHeatmapPixelSizeWidgetWrapper::parameterType() const
8661{
8663}
8664
8665QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingHeatmapPixelSizeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8666{
8667 return new QgsProcessingHeatmapPixelSizeWidgetWrapper( parameter, type );
8668}
8669
8670QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingHeatmapPixelSizeWidgetWrapper::createParameterDefinitionWidget(
8672)
8673{
8674 return nullptr;
8675}
8676
8677QWidget *QgsProcessingHeatmapPixelSizeWidgetWrapper::createWidget()
8678{
8679 switch ( type() )
8680 {
8682 {
8683 mWidget = new QgsHeatmapPixelSizeWidget();
8684 connect( mWidget, &QgsHeatmapPixelSizeWidget::valueChanged, this, [this] { emit widgetValueHasChanged( this ); } );
8685 return mWidget;
8686 }
8687
8690 {
8691 mFallbackSpinBox = new QgsDoubleSpinBox();
8692 mFallbackSpinBox->setShowClearButton( false );
8693 mFallbackSpinBox->setMinimum( 0.0 );
8694 mFallbackSpinBox->setMaximum( 99999999999.0 );
8695 mFallbackSpinBox->setDecimals( 6 );
8696 mFallbackSpinBox->setToolTip( tr( "Resolution of each pixel in output raster, in layer units" ) );
8697 connect( mFallbackSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
8698 return mFallbackSpinBox;
8699 }
8700 }
8701 return nullptr;
8702}
8703
8704void QgsProcessingHeatmapPixelSizeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
8705{
8707
8708 switch ( type() )
8709 {
8711 {
8712 auto param = dynamic_cast<const QgsProcessingParameterHeatmapPixelSize *>( parameterDefinition() );
8713 if ( !param || !mWidget )
8714 return;
8715
8716 for ( QgsAbstractProcessingParameterWidgetWrapper *wrapper : std::as_const( wrappers ) )
8717 {
8718 if ( wrapper->parameterDefinition()->name() == param->parentLayerParameter() )
8719 {
8720 setParentLayerWrapperValue( wrapper );
8721 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
8722 }
8723 else if ( wrapper->parameterDefinition()->name() == param->radiusParameter() )
8724 {
8725 mWidget->setRadius( wrapper->parameterValue().toDouble() );
8726 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, &QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusChanged );
8727 }
8728 else if ( wrapper->parameterDefinition()->name() == param->radiusFieldParameter() )
8729 {
8730 mWidget->setRadiusField( wrapper->parameterValue().toString() );
8731 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, &QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusFieldChanged );
8732 }
8733 }
8734 break;
8735 }
8738 break;
8739 }
8740}
8741
8742void QgsProcessingHeatmapPixelSizeWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
8743{
8744 if ( !mWidget )
8745 return;
8746
8747 // evaluate value to layer
8748 QgsProcessingContext *context = nullptr;
8749 std::unique_ptr<QgsProcessingContext> tmpContext;
8750 if ( mProcessingContextGenerator )
8751 context = mProcessingContextGenerator->processingContext();
8752
8753 if ( !context )
8754 {
8755 tmpContext = std::make_unique<QgsProcessingContext>();
8756 context = tmpContext.get();
8757 }
8758
8759 QVariant value = parentWrapper->parameterValue();
8760 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
8761 {
8762 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
8763 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
8764 value = fromVar.source;
8765 }
8766
8767 QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), value, *context );
8768 if ( !layer )
8769 {
8770 mWidget->setLayer( nullptr );
8771 return;
8772 }
8773
8774 // need to grab ownership of layer if required - otherwise layer may be deleted when context
8775 // goes out of scope
8776 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
8777 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
8778 {
8779 mParentLayer = std::move( ownedLayer );
8780 layer = static_cast<QgsVectorLayer *>( mParentLayer.get() );
8781 }
8782 else
8783 {
8784 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
8785 }
8786
8787 mWidget->setLayer( layer );
8788}
8789
8790void QgsProcessingHeatmapPixelSizeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
8791{
8792 if ( mWidget )
8793 mWidget->setValue( value.toDouble() );
8794 else if ( mFallbackSpinBox )
8795 mFallbackSpinBox->setValue( value.toDouble() );
8796}
8797
8798QVariant QgsProcessingHeatmapPixelSizeWidgetWrapper::widgetValue() const
8799{
8800 if ( mWidget )
8801 return mWidget->value();
8802 else if ( mFallbackSpinBox )
8803 return mFallbackSpinBox->value();
8804 return QVariant();
8805}
8806
8807const QgsVectorLayer *QgsProcessingHeatmapPixelSizeWidgetWrapper::linkedVectorLayer() const
8808{
8809 if ( mWidget && mWidget->layer() )
8810 return mWidget->layer();
8811
8813}
8814
8815void QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
8816{
8817 if ( mWidget )
8818 mWidget->setRadius( wrapper->parameterValue().toDouble() );
8819}
8820
8821void QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusFieldChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
8822{
8823 if ( mWidget )
8824 mWidget->setRadiusField( wrapper->parameterValue().toString() );
8825}
8826
8827//
8828// QgsReliefColorsWidget
8829//
8830
8831QgsReliefColorsWidget::QgsReliefColorsWidget( QWidget *parent )
8832 : QgsPanelWidget( parent )
8833{
8834 setupUi( this );
8835
8836 connect( mButtonAdd, &QToolButton::clicked, this, &QgsReliefColorsWidget::addClicked );
8837 connect( mButtonRemove, &QToolButton::clicked, this, &QgsReliefColorsWidget::removeClicked );
8838 connect( mButtonUp, &QToolButton::clicked, this, &QgsReliefColorsWidget::upClicked );
8839 connect( mButtonDown, &QToolButton::clicked, this, &QgsReliefColorsWidget::downClicked );
8840 connect( mButtonLoad, &QToolButton::clicked, this, &QgsReliefColorsWidget::loadClicked );
8841 connect( mButtonSave, &QToolButton::clicked, this, &QgsReliefColorsWidget::saveClicked );
8842 connect( mButtonAuto, &QToolButton::clicked, this, &QgsReliefColorsWidget::autoCalculate );
8843 connect( mReliefColorsWidget, &QTreeWidget::itemDoubleClicked, this, &QgsReliefColorsWidget::itemDoubleClicked );
8844}
8845
8846void QgsReliefColorsWidget::setLayer( QgsRasterLayer *layer )
8847{
8848 mLayer = layer;
8849}
8850
8851QgsRasterLayer *QgsReliefColorsWidget::layer()
8852{
8853 return mLayer.data();
8854}
8855
8856void QgsReliefColorsWidget::addClicked()
8857{
8858 auto item = new QTreeWidgetItem();
8859 item->setText( 0, u"0.00"_s );
8860 item->setText( 1, u"0.00"_s );
8861 item->setBackground( 2, QBrush( QColor( 127, 127, 127 ) ) );
8862 mReliefColorsWidget->addTopLevelItem( item );
8863 emit valueChanged();
8864}
8865
8866void QgsReliefColorsWidget::removeClicked()
8867{
8868 const QList<QTreeWidgetItem *> selectedItems = mReliefColorsWidget->selectedItems();
8869 for ( QTreeWidgetItem *item : std::as_const( selectedItems ) )
8870 {
8871 delete item;
8872 }
8873 emit valueChanged();
8874}
8875
8876void QgsReliefColorsWidget::downClicked()
8877{
8878 const QList<QTreeWidgetItem *> selectedItems = mReliefColorsWidget->selectedItems();
8879 for ( QTreeWidgetItem *item : std::as_const( selectedItems ) )
8880 {
8881 const int currentIndex = mReliefColorsWidget->indexOfTopLevelItem( item );
8882 if ( currentIndex >= 0 && currentIndex < mReliefColorsWidget->topLevelItemCount() - 1 )
8883 {
8884 mReliefColorsWidget->takeTopLevelItem( currentIndex );
8885 mReliefColorsWidget->insertTopLevelItem( currentIndex + 1, item );
8886 mReliefColorsWidget->setCurrentItem( item );
8887 }
8888 }
8889 emit valueChanged();
8890}
8891
8892void QgsReliefColorsWidget::upClicked()
8893{
8894 const QList<QTreeWidgetItem *> selectedItems = mReliefColorsWidget->selectedItems();
8895 for ( QTreeWidgetItem *item : std::as_const( selectedItems ) )
8896 {
8897 const int currentIndex = mReliefColorsWidget->indexOfTopLevelItem( item );
8898 if ( currentIndex > 0 )
8899 {
8900 mReliefColorsWidget->takeTopLevelItem( currentIndex );
8901 mReliefColorsWidget->insertTopLevelItem( currentIndex - 1, item );
8902 mReliefColorsWidget->setCurrentItem( item );
8903 }
8904 }
8905 emit valueChanged();
8906}
8907
8908void QgsReliefColorsWidget::loadClicked()
8909{
8910 const QString fileName = QFileDialog::getOpenFileName( this, tr( "Import Colors from XML" ), QDir::homePath(), tr( "XML files (*.xml *.XML)" ) );
8911 if ( fileName.isEmpty() )
8912 return;
8913
8914 QFile file( fileName );
8915 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
8916 return;
8917
8918 QDomDocument doc;
8919 if ( !doc.setContent( &file ) )
8920 {
8921 QMessageBox::critical( this, tr( "Import Colors from XML" ), tr( "The XML file could not be loaded" ) );
8922 return;
8923 }
8924
8925 mReliefColorsWidget->clear();
8926 const QDomNodeList reliefColorList = doc.elementsByTagName( u"ReliefColor"_s );
8927 for ( int i = 0; i < reliefColorList.length(); ++i )
8928 {
8929 const QDomElement elem = reliefColorList.at( i ).toElement();
8930 auto item = new QTreeWidgetItem();
8931 item->setText( 0, elem.attribute( u"MinElevation"_s ) );
8932 item->setText( 1, elem.attribute( u"MaxElevation"_s ) );
8933 item->setBackground( 2, QBrush( QColor( elem.attribute( u"red"_s ).toInt(), elem.attribute( u"green"_s ).toInt(), elem.attribute( u"blue"_s ).toInt() ) ) );
8934 mReliefColorsWidget->addTopLevelItem( item );
8935 }
8936 emit valueChanged();
8937}
8938
8939void QgsReliefColorsWidget::saveClicked()
8940{
8941 QString fileName = QFileDialog::getSaveFileName( this, tr( "Export Colors as XML" ), QDir::homePath(), tr( "XML files (*.xml *.XML)" ) );
8942 if ( fileName.isEmpty() )
8943 return;
8944
8945 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { u"xml"_s } );
8946
8947 QDomDocument doc;
8948 QDomElement colorsElem = doc.createElement( u"ReliefColors"_s );
8949 doc.appendChild( colorsElem );
8950
8951 const QList<QgsRasterReliefColor> currentColors = colors();
8952 for ( const QgsRasterReliefColor &c : currentColors )
8953 {
8954 QDomElement elem = doc.createElement( u"ReliefColor"_s );
8955 elem.setAttribute( u"MinElevation"_s, qgsDoubleToString( c.minElevation ) );
8956 elem.setAttribute( u"MaxElevation"_s, qgsDoubleToString( c.maxElevation ) );
8957 elem.setAttribute( u"red"_s, QString::number( c.color.red() ) );
8958 elem.setAttribute( u"green"_s, QString::number( c.color.green() ) );
8959 elem.setAttribute( u"blue"_s, QString::number( c.color.blue() ) );
8960 colorsElem.appendChild( elem );
8961 }
8962
8963 QFile file( fileName );
8964 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
8965 {
8966 QTextStream out( &file );
8967 out << doc.toString( 2 );
8968 }
8969}
8970
8971void QgsReliefColorsWidget::autoCalculate()
8972{
8973 if ( !mLayer || !mLayer->isValid() )
8974 return;
8975
8976 const QList< QgsRasterReliefColor > autoColors = QgsRasterLayerUtils::calculateOptimizedReliefClasses( mLayer->dataProvider(), 1 );
8977 setColors( autoColors );
8978 emit valueChanged();
8979}
8980
8981void QgsReliefColorsWidget::itemDoubleClicked( QTreeWidgetItem *item, int column )
8982{
8983 if ( !item )
8984 return;
8985
8986 bool ok = false;
8987 if ( column == 0 )
8988 {
8989 const double d = QInputDialog::getDouble( this, tr( "Enter lower elevation class bound" ), tr( "Elevation" ), item->text( 0 ).toDouble(), -2147483647, 2147483647, 2, &ok );
8990 if ( ok )
8991 {
8992 item->setText( 0, QString::number( d ) );
8993 emit valueChanged();
8994 }
8995 }
8996 else if ( column == 1 )
8997 {
8998 const double d = QInputDialog::getDouble( this, tr( "Enter upper elevation class bound" ), tr( "Elevation" ), item->text( 1 ).toDouble(), -2147483647, 2147483647, 2, &ok );
8999 if ( ok )
9000 {
9001 item->setText( 1, QString::number( d ) );
9002 emit valueChanged();
9003 }
9004 }
9005 else if ( column == 2 )
9006 {
9007 const QColor c = QColorDialog::getColor( item->background( 2 ).color(), this, tr( "Select color for relief class" ) );
9008 if ( c.isValid() )
9009 {
9010 item->setBackground( 2, QBrush( c ) );
9011 emit valueChanged();
9012 }
9013 }
9014}
9015
9016QList<QgsRasterReliefColor> QgsReliefColorsWidget::colors() const
9017{
9018 QList<QgsRasterReliefColor> colors;
9019 for ( int i = 0; i < mReliefColorsWidget->topLevelItemCount(); ++i )
9020 {
9021 if ( QTreeWidgetItem *item = mReliefColorsWidget->topLevelItem( i ) )
9022 {
9023 colors.append( QgsRasterReliefColor( item->background( 2 ).color(), item->text( 0 ).toDouble(), item->text( 1 ).toDouble() ) );
9024 }
9025 }
9026 return colors;
9027}
9028
9029void QgsReliefColorsWidget::setColors( const QList<QgsRasterReliefColor> &colors )
9030{
9031 mReliefColorsWidget->clear();
9032 for ( const QgsRasterReliefColor &c : colors )
9033 {
9034 auto item = new QTreeWidgetItem();
9035 item->setText( 0, qgsDoubleToString( c.minElevation ) );
9036 item->setText( 1, qgsDoubleToString( c.maxElevation ) );
9037 item->setBackground( 2, QBrush( c.color ) );
9038 mReliefColorsWidget->addTopLevelItem( item );
9039 }
9040 emit valueChanged();
9041}
9042
9043//
9044// QgsProcessingReliefColorsWidgetWrapper
9045//
9046
9047QgsProcessingReliefColorsWidgetWrapper::QgsProcessingReliefColorsWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
9048 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
9049{}
9050
9051QString QgsProcessingReliefColorsWidgetWrapper::parameterType() const
9052{
9054}
9055
9056QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingReliefColorsWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
9057{
9058 return new QgsProcessingReliefColorsWidgetWrapper( parameter, type );
9059}
9060
9061QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingReliefColorsWidgetWrapper::createParameterDefinitionWidget(
9063)
9064{
9065 return nullptr;
9066}
9067
9068QWidget *QgsProcessingReliefColorsWidgetWrapper::createWidget()
9069{
9070 switch ( type() )
9071 {
9073 {
9074 mWidget = new QgsReliefColorsWidget();
9075 connect( mWidget, &QgsReliefColorsWidget::valueChanged, this, [this] { emit widgetValueHasChanged( this ); } );
9076 return mWidget;
9077 }
9078
9081 {
9082 mFallbackLineEdit = new QLineEdit();
9083 mFallbackLineEdit->setPlaceholderText( tr( "Relief colors definition" ) );
9084 connect( mFallbackLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
9085 return mFallbackLineEdit;
9086 }
9087 }
9088 return nullptr;
9089}
9090
9091void QgsProcessingReliefColorsWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
9092{
9094
9095 switch ( type() )
9096 {
9098 {
9099 auto param = dynamic_cast<const QgsProcessingParameterReliefColors *>( parameterDefinition() );
9100 if ( !param || !mWidget )
9101 return;
9102
9103 for ( QgsAbstractProcessingParameterWidgetWrapper *wrapper : std::as_const( wrappers ) )
9104 {
9105 if ( wrapper->parameterDefinition()->name() == param->parentLayerParameter() )
9106 {
9107 setParentLayerWrapperValue( wrapper );
9108 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
9109 }
9110 }
9111 break;
9112 }
9115 break;
9116 }
9117}
9118
9119void QgsProcessingReliefColorsWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
9120{
9121 if ( !mWidget )
9122 return;
9123
9124 // evaluate value to layer
9125 QgsProcessingContext *context = nullptr;
9126 std::unique_ptr<QgsProcessingContext> tmpContext;
9127 if ( mProcessingContextGenerator )
9128 context = mProcessingContextGenerator->processingContext();
9129
9130 if ( !context )
9131 {
9132 tmpContext = std::make_unique<QgsProcessingContext>();
9133 context = tmpContext.get();
9134 }
9135
9136 QVariant value = parentWrapper->parameterValue();
9137 QgsRasterLayer *layer = QgsProcessingParameters::parameterAsRasterLayer( parentWrapper->parameterDefinition(), value, *context );
9138 if ( layer && layer->isValid() )
9139 {
9140 // need to grab ownership of layer if required - otherwise layer may be deleted when context
9141 // goes out of scope
9142 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
9143 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Raster )
9144 {
9145 mParentLayer.reset( qobject_cast<QgsRasterLayer *>( ownedLayer.release() ) );
9146 layer = mParentLayer.get();
9147 }
9148 else
9149 {
9150 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
9151 }
9152
9153 if ( mWidget )
9154 mWidget->setLayer( layer );
9155 }
9156 else
9157 {
9158 if ( mWidget )
9159 mWidget->setLayer( nullptr );
9160 }
9161}
9162
9163void QgsProcessingReliefColorsWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
9164{
9165 auto param = dynamic_cast<const QgsProcessingParameterReliefColors *>( parameterDefinition() );
9166
9167 if ( mWidget && param )
9168 {
9169 mWidget->setColors( param->valueAsReliefColors( value, context ) );
9170 }
9171 else if ( mFallbackLineEdit )
9172 mFallbackLineEdit->setText( value.toString() );
9173}
9174
9175QVariant QgsProcessingReliefColorsWidgetWrapper::widgetValue() const
9176{
9177 if ( mWidget )
9178 return QgsProcessingParameterReliefColors::colorsAsVariant( mWidget->colors() );
9179 else if ( mFallbackLineEdit )
9180 return mFallbackLineEdit->text();
9181 return QVariant();
9182}
9183
9184QString QgsProcessingReliefColorsWidgetWrapper::modelerExpressionFormatString() const
9185{
9186 return tr( "string of the format 'min,max,red,green,blue' for each relief color, joined by a ; delimiter" );
9187}
9188
9189
9190//
9191// QgsExecuteSqlWidget
9192//
9193
9194QgsExecuteSqlWidget::QgsExecuteSqlWidget( QWidget *parent )
9195 : QWidget( parent )
9196{
9197 QVBoxLayout *mainLayout = new QVBoxLayout( this );
9198 mainLayout->setContentsMargins( 0, 0, 0, 0 );
9199
9200 mTextEdit = new QPlainTextEdit( this );
9201 mainLayout->addWidget( mTextEdit, 1 );
9202
9203 QHBoxLayout *bottomLayout = new QHBoxLayout();
9204 bottomLayout->setContentsMargins( 0, 0, 0, 0 );
9205
9206 mExpressionWidget = new QgsFieldExpressionWidget( this );
9207 bottomLayout->addWidget( mExpressionWidget, 1 );
9208
9209 mInsertButton = new QPushButton( tr( "Insert" ), this );
9210 bottomLayout->addWidget( mInsertButton );
9211
9212 mainLayout->addLayout( bottomLayout );
9213
9214 connect( mInsertButton, &QPushButton::clicked, this, &QgsExecuteSqlWidget::insertExpression );
9215 connect( mTextEdit, &QPlainTextEdit::textChanged, this, &QgsExecuteSqlWidget::changed );
9216}
9217
9218void QgsExecuteSqlWidget::insertExpression()
9219{
9220 if ( !mExpressionWidget->currentText().isEmpty() )
9221 {
9222 const QString formattedExpression = u"[% %1 %]"_s.arg( mExpressionWidget->currentText() );
9223 mTextEdit->insertPlainText( formattedExpression );
9224 }
9225}
9226
9227void QgsExecuteSqlWidget::setValue( const QString &text )
9228{
9229 if ( text == value() )
9230 return;
9231
9232 mTextEdit->setPlainText( text );
9233}
9234
9235QString QgsExecuteSqlWidget::value() const
9236{
9237 return mTextEdit->toPlainText();
9238}
9239
9240//
9241// QgsProcessingExecuteSqlWidgetWrapper
9242//
9243
9244QgsProcessingExecuteSqlWidgetWrapper::QgsProcessingExecuteSqlWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QObject *parent )
9245 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
9246{}
9247
9248QString QgsProcessingExecuteSqlWidgetWrapper::parameterType() const
9249{
9250 return u"executesql"_s;
9251}
9252
9253QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExecuteSqlWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
9254{
9255 return new QgsProcessingExecuteSqlWidgetWrapper( parameter, type );
9256}
9257
9258QWidget *QgsProcessingExecuteSqlWidgetWrapper::createWidget()
9259{
9260 mExecuteSqlWidget = new QgsExecuteSqlWidget();
9261 mExecuteSqlWidget->expressionWidget()->registerExpressionContextGenerator( this );
9262
9263 if ( parameterDefinition() )
9264 {
9265 mExecuteSqlWidget->setToolTip( parameterDefinition()->toolTip() );
9266 }
9267
9268 connect( mExecuteSqlWidget, &QgsExecuteSqlWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
9269
9270 return mExecuteSqlWidget;
9271}
9272
9273void QgsProcessingExecuteSqlWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
9274{
9275 if ( !mExecuteSqlWidget )
9276 {
9277 return;
9278 }
9279
9280 const QString sqlText = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
9281 mExecuteSqlWidget->setValue( sqlText );
9282}
9283
9284QVariant QgsProcessingExecuteSqlWidgetWrapper::widgetValue() const
9285{
9286 if ( !mExecuteSqlWidget )
9287 {
9288 return QVariant();
9289 }
9290
9291 return mExecuteSqlWidget->value();
9292}
9293
9294//
9295// QgsInterpolationSourceWidget
9296//
9297
9298QgsInterpolationSourceWidget::QgsInterpolationSourceWidget( QWidget *parent )
9299 : QWidget( parent )
9300{
9301 setupUi( this );
9302
9303 btnAdd->setIcon( QgsApplication::getThemeIcon( u"/symbologyAdd.svg"_s ) );
9304 btnRemove->setIcon( QgsApplication::getThemeIcon( u"/symbologyRemove.svg"_s ) );
9305
9306 connect( btnAdd, &QToolButton::clicked, this, &QgsInterpolationSourceWidget::addLayer );
9307 connect( btnRemove, &QToolButton::clicked, this, &QgsInterpolationSourceWidget::removeLayer );
9308
9309 cmbLayers->setFilters( Qgis::LayerFilter::VectorLayer );
9310 cmbFields->setFilters( QgsFieldProxyModel::Filter::Numeric );
9311
9312 connect( cmbLayers, &QgsMapLayerComboBox::layerChanged, this, [this]( QgsMapLayer *layer ) { layerChanged( qobject_cast<QgsVectorLayer *>( layer ) ); } );
9313
9314 layerChanged( qobject_cast< QgsVectorLayer * >( cmbLayers->currentLayer() ) );
9315}
9316
9317void QgsInterpolationSourceWidget::addLayer()
9318{
9319 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( cmbLayers->currentLayer() );
9320 if ( !layer )
9321 {
9322 return;
9323 }
9324
9325 const QString attribute = chkUseZCoordinate->isChecked() ? u"Z_COORD"_s : cmbFields->currentField();
9326 addLayerData( layer, attribute );
9327 emit changed();
9328}
9329
9330void QgsInterpolationSourceWidget::removeLayer()
9331{
9332 QTreeWidgetItem *item = layersTree->currentItem();
9333 if ( !item )
9334 {
9335 return;
9336 }
9337 delete item;
9338 emit changed();
9339}
9340
9341void QgsInterpolationSourceWidget::layerChanged( QgsVectorLayer *layer )
9342{
9343 chkUseZCoordinate->setEnabled( false );
9344 chkUseZCoordinate->setChecked( false );
9345
9346 if ( !layer || !layer->isValid() )
9347 {
9348 return;
9349 }
9350
9351 if ( QgsWkbTypes::hasZ( layer->wkbType() ) )
9352 {
9353 chkUseZCoordinate->setEnabled( true );
9354 }
9355
9356 cmbFields->setLayer( layer );
9357}
9358
9359void QgsInterpolationSourceWidget::addLayerData( QgsVectorLayer *layer, const QString &attribute )
9360{
9361 QTreeWidgetItem *item = new QTreeWidgetItem();
9362 item->setText( 0, layer->name() );
9363 item->setText( 1, attribute );
9364 item->setData( 0, Qt::UserRole, layer->source() );
9365 layersTree->addTopLevelItem( item );
9366
9367 QComboBox *comboBox = new QComboBox();
9368 comboBox->addItem( tr( "Points" ), QVariant::fromValue( Qgis::InterpolationSourceType::Points ) );
9369 comboBox->addItem( tr( "Structure Lines" ), QVariant::fromValue( Qgis::InterpolationSourceType::StructureLines ) );
9370 comboBox->addItem( tr( "Break Lines" ), QVariant::fromValue( Qgis::InterpolationSourceType::BreakLines ) );
9371 comboBox->setCurrentIndex( 0 );
9372 layersTree->setItemWidget( item, 2, comboBox );
9373}
9374
9375void QgsInterpolationSourceWidget::setValue( const QVariant &value, QgsProcessingContext &context )
9376{
9377 layersTree->clear();
9378 const QStringList rows = value.toString().split( "::|::"_L1, Qt::SkipEmptyParts );
9379 int itemIndex = 0;
9380 for ( const QString &row : rows )
9381 {
9382 const QStringList tokens = row.split( "::~::"_L1 );
9383 if ( tokens.size() < 4 )
9384 {
9385 continue;
9386 }
9387
9388 const QString layerSource = tokens.at( 0 );
9389 auto layer = qobject_cast< QgsVectorLayer * >( QgsProcessingUtils::mapLayerFromString( layerSource, context, true, QgsProcessingUtils::LayerHint::Vector ) );
9390 if ( !layer || !layer->isValid() )
9391 continue;
9392
9393 bool isInt = false;
9394 const int fieldIndexInt = tokens.at( 2 ).toInt( &isInt );
9395 if ( isInt && fieldIndexInt == -1 )
9396 {
9397 addLayerData( layer, u"Z_COORD"_s );
9398 }
9399 else if ( isInt )
9400 {
9401 addLayerData( layer, layer->fields().at( fieldIndexInt ).name() );
9402 }
9403 else
9404 {
9405 addLayerData( layer, tokens.at( 2 ) );
9406 }
9407
9408 const Qgis::InterpolationSourceType sourceType = static_cast< Qgis::InterpolationSourceType >( tokens.at( 3 ).toInt() );
9409 QComboBox *comboBox = qobject_cast<QComboBox *>( layersTree->itemWidget( layersTree->topLevelItem( itemIndex ), 2 ) );
9410 if ( comboBox )
9411 {
9412 comboBox->setCurrentIndex( comboBox->findData( QVariant::fromValue( sourceType ) ) );
9413 }
9414 itemIndex++;
9415 }
9416 emit changed();
9417}
9418
9419QVariant QgsInterpolationSourceWidget::value() const
9420{
9421 QStringList result;
9422 for ( int index = 0; index < layersTree->topLevelItemCount(); ++index )
9423 {
9424 QTreeWidgetItem *item = layersTree->topLevelItem( index );
9425 if ( !item )
9426 {
9427 continue;
9428 }
9429
9430 const QString layerSource = item->data( 0, Qt::UserRole ).toString();
9431 QString interpolationAttribute = item->text( 1 );
9432
9433 int valueSource = static_cast<int>( Qgis::InterpolationValueSource::Attribute );
9434 if ( interpolationAttribute == "Z_COORD"_L1 )
9435 {
9436 valueSource = static_cast<int>( Qgis::InterpolationValueSource::Z );
9437 interpolationAttribute = u"-1"_s;
9438 }
9439
9440 QComboBox *comboBox = qobject_cast<QComboBox *>( layersTree->itemWidget( item, 2 ) );
9441 const int inputType = comboBox ? static_cast< int >( comboBox->currentData().value< Qgis::InterpolationSourceType >() ) : 0;
9442
9443 result << u"%1::~::%2::~::%3::~::%4"_s.arg( layerSource ).arg( valueSource ).arg( interpolationAttribute ).arg( inputType );
9444 }
9445
9446 if ( result.isEmpty() )
9447 return QVariant();
9448
9449 return result.join( "::|::"_L1 );
9450}
9451
9452
9453//
9454// QgsProcessingInterpolationSourceWidgetWrapper
9455//
9456
9457QgsProcessingInterpolationSourceWidgetWrapper::QgsProcessingInterpolationSourceWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QObject *parent )
9458 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
9459{}
9460
9461QString QgsProcessingInterpolationSourceWidgetWrapper::parameterType() const
9462{
9464}
9465
9466QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingInterpolationSourceWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
9467{
9468 return new QgsProcessingInterpolationSourceWidgetWrapper( parameter, type );
9469}
9470
9471QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingInterpolationSourceWidgetWrapper::createParameterDefinitionWidget(
9473)
9474{
9475 return nullptr;
9476}
9477
9478QWidget *QgsProcessingInterpolationSourceWidgetWrapper::createWidget()
9479{
9480 mWidget = new QgsInterpolationSourceWidget();
9481 if ( parameterDefinition() )
9482 {
9483 mWidget->setToolTip( parameterDefinition()->toolTip() );
9484 }
9485
9486 connect( mWidget, &QgsInterpolationSourceWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
9487 return mWidget;
9488}
9489
9490void QgsProcessingInterpolationSourceWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
9491{
9492 if ( mWidget )
9493 {
9494 mWidget->setValue( value, context );
9495 }
9496}
9497
9498QVariant QgsProcessingInterpolationSourceWidgetWrapper::widgetValue() const
9499{
9500 return mWidget ? mWidget->value() : QVariant();
9501}
9502
9503
9504//
9505// QgsInterpolationPixelSizeWidget
9506//
9507
9508QgsInterpolationPixelSizeWidget::QgsInterpolationPixelSizeWidget( QWidget *parent )
9509 : QgsPanelWidget( parent )
9510{
9511 setupUi( this );
9512
9513 mCellXSpinBox->setShowClearButton( false );
9514 mCellYSpinBox->setShowClearButton( false );
9515 mRowsSpinBox->setShowClearButton( false );
9516 mColumnsSpinBox->setShowClearButton( false );
9517
9518 connect( mCellYSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), mCellXSpinBox, &QgsDoubleSpinBox::setValue );
9519 connect( mCellXSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsInterpolationPixelSizeWidget::pixelSizeChanged );
9520 connect( mRowsSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, &QgsInterpolationPixelSizeWidget::rowsChanged );
9521 connect( mColumnsSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, &QgsInterpolationPixelSizeWidget::columnsChanged );
9522}
9523
9524void QgsInterpolationPixelSizeWidget::setSourceData( const QString &sourceData, QgsProcessingContext &context )
9525{
9526 mExtent.setNull();
9527 const QStringList rows = sourceData.split( "::|::"_L1, Qt::SkipEmptyParts );
9528 for ( const QString &row : rows )
9529 {
9530 const QStringList tokens = row.split( "::~::"_L1 );
9531 if ( tokens.isEmpty() )
9532 {
9533 continue;
9534 }
9535
9536 std::unique_ptr<QgsFeatureSource> source( QgsProcessingUtils::variantToSource( tokens.at( 0 ), context ) );
9537 if ( source )
9538 {
9539 mExtent.combineExtentWith( source->sourceExtent() );
9540 }
9541 }
9542
9543 pixelSizeChanged();
9544}
9545
9546void QgsInterpolationPixelSizeWidget::setExtent( const QgsRectangle &extent )
9547{
9548 mExtent = extent;
9549 pixelSizeChanged();
9550}
9551
9552void QgsInterpolationPixelSizeWidget::pixelSizeChanged()
9553{
9554 const double prevValue = value();
9555 const double cellSize = mCellXSpinBox->value();
9556 if ( qgsDoubleLessThanOrNear( cellSize, 0 ) || mExtent.isNull() )
9557 {
9558 emit valueChanged();
9559 return;
9560 }
9561
9562 whileBlocking( mCellYSpinBox )->setValue( cellSize );
9563
9564 const int rows = std::max( static_cast<int>( std::round( mExtent.height() / cellSize ) ) + 1, 1 );
9565 const int cols = std::max( static_cast<int>( std::round( mExtent.width() / cellSize ) ) + 1, 1 );
9566
9567 whileBlocking( mRowsSpinBox )->setValue( rows );
9568 whileBlocking( mColumnsSpinBox )->setValue( cols );
9569
9570 if ( !qgsDoubleNear( prevValue, value() ) )
9571 {
9572 emit valueChanged();
9573 }
9574}
9575
9576void QgsInterpolationPixelSizeWidget::rowsChanged()
9577{
9578 const int rows = mRowsSpinBox->value();
9579 if ( rows <= 0 || mExtent.isNull() )
9580 return;
9581
9582 const double cellSize = mExtent.height() / rows;
9583 if ( qgsDoubleNear( cellSize, 0.0 ) )
9584 return;
9585
9586 const int cols = std::max( static_cast<int>( std::round( mExtent.width() / cellSize ) ) + 1, 1 );
9587
9588 whileBlocking( mColumnsSpinBox )->setValue( cols );
9589 whileBlocking( mCellXSpinBox )->setValue( cellSize );
9590 whileBlocking( mCellYSpinBox )->setValue( cellSize );
9591
9592 emit valueChanged();
9593}
9594
9595void QgsInterpolationPixelSizeWidget::columnsChanged()
9596{
9597 const int cols = mColumnsSpinBox->value();
9598 if ( cols < 2 || mExtent.isNull() )
9599 return;
9600
9601 const double cellSize = mExtent.width() / ( cols - 1 );
9602 if ( qgsDoubleNear( cellSize, 0.0 ) )
9603 return;
9604
9605 const int rows = std::max( static_cast<int>( std::round( mExtent.height() / cellSize ) ), 1 );
9606
9607 whileBlocking( mRowsSpinBox )->setValue( rows );
9608 whileBlocking( mCellXSpinBox )->setValue( cellSize );
9609 whileBlocking( mCellYSpinBox )->setValue( cellSize );
9610
9611 emit valueChanged();
9612}
9613
9614double QgsInterpolationPixelSizeWidget::value() const
9615{
9616 return mCellXSpinBox->value();
9617}
9618
9619void QgsInterpolationPixelSizeWidget::setValue( double value )
9620{
9621 mCellXSpinBox->setValue( value );
9622 mCellYSpinBox->setValue( value );
9623}
9624
9625
9626//
9627// QgsProcessingInterpolationPixelSizeWidgetWrapper
9628//
9629
9630QgsProcessingInterpolationPixelSizeWidgetWrapper::QgsProcessingInterpolationPixelSizeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
9631 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
9632{}
9633
9634QString QgsProcessingInterpolationPixelSizeWidgetWrapper::parameterType() const
9635{
9637}
9638
9639QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingInterpolationPixelSizeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
9640{
9641 return new QgsProcessingInterpolationPixelSizeWidgetWrapper( parameter, type );
9642}
9643
9644QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingInterpolationPixelSizeWidgetWrapper::createParameterDefinitionWidget(
9646)
9647{
9648 return nullptr;
9649}
9650
9651QWidget *QgsProcessingInterpolationPixelSizeWidgetWrapper::createWidget()
9652{
9653 switch ( type() )
9654 {
9656 {
9657 mWidget = new QgsInterpolationPixelSizeWidget();
9658 connect( mWidget, &QgsInterpolationPixelSizeWidget::valueChanged, this, [this] { emit widgetValueHasChanged( this ); } );
9659 return mWidget;
9660 }
9661
9664 {
9665 mFallbackSpinBox = new QgsDoubleSpinBox();
9666 mFallbackSpinBox->setShowClearButton( false );
9667 mFallbackSpinBox->setMinimum( 0.0 );
9668 mFallbackSpinBox->setMaximum( 99999999999.0 );
9669 mFallbackSpinBox->setDecimals( 6 );
9670 mFallbackSpinBox->setToolTip( tr( "Resolution of each pixel in output raster" ) );
9671 connect( mFallbackSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
9672 return mFallbackSpinBox;
9673 }
9674 }
9675 return nullptr;
9676}
9677
9678void QgsProcessingInterpolationPixelSizeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
9679{
9681
9682 switch ( type() )
9683 {
9685 {
9686 auto param = dynamic_cast<const QgsProcessingParameterInterpolationPixelSize *>( parameterDefinition() );
9687 if ( !param || !mWidget )
9688 return;
9689
9690 for ( QgsAbstractProcessingParameterWidgetWrapper *wrapper : std::as_const( wrappers ) )
9691 {
9692 if ( wrapper->parameterDefinition()->name() == param->interpolationSourceParameter() )
9693 {
9694 sourceChanged( wrapper );
9695 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { sourceChanged( wrapper ); } );
9696 }
9697 else if ( wrapper->parameterDefinition()->name() == param->extentParameter() )
9698 {
9699 extentChanged( wrapper );
9700 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { extentChanged( wrapper ); } );
9701 }
9702 }
9703 break;
9704 }
9707 break;
9708 }
9709}
9710
9711void QgsProcessingInterpolationPixelSizeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
9712{
9713 if ( mWidget )
9714 mWidget->setValue( value.toDouble() );
9715 else if ( mFallbackSpinBox )
9716 mFallbackSpinBox->setValue( value.toDouble() );
9717}
9718
9719QVariant QgsProcessingInterpolationPixelSizeWidgetWrapper::widgetValue() const
9720{
9721 if ( mWidget )
9722 return mWidget->value();
9723 else if ( mFallbackSpinBox )
9724 return mFallbackSpinBox->value();
9725 return QVariant();
9726}
9727
9728void QgsProcessingInterpolationPixelSizeWidgetWrapper::sourceChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
9729{
9730 if ( mWidget )
9731 {
9732 QgsProcessingContext *context = nullptr;
9733 std::unique_ptr<QgsProcessingContext> tmpContext;
9734 if ( mProcessingContextGenerator )
9735 context = mProcessingContextGenerator->processingContext();
9736
9737 if ( !context )
9738 {
9739 tmpContext = std::make_unique<QgsProcessingContext>();
9740 context = tmpContext.get();
9741 }
9742
9743 mWidget->setSourceData( wrapper->parameterValue().toString(), *context );
9744 }
9745}
9746
9747void QgsProcessingInterpolationPixelSizeWidgetWrapper::extentChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
9748{
9749 if ( mWidget )
9750 {
9751 QgsProcessingContext *context = nullptr;
9752 std::unique_ptr<QgsProcessingContext> tmpContext;
9753 if ( mProcessingContextGenerator )
9754 context = mProcessingContextGenerator->processingContext();
9755
9756 if ( !context )
9757 {
9758 tmpContext = std::make_unique<QgsProcessingContext>();
9759 context = tmpContext.get();
9760 }
9761
9762 const QgsRectangle extent = QgsProcessingParameters::parameterAsExtent( wrapper->parameterDefinition(), wrapper->parameterValue(), *context );
9763
9764 mWidget->setExtent( extent );
9765 }
9766}
9767
9768
@ Standard
Unit is a standard measurement unit.
Definition qgis.h:5548
ProcessingSourceType
Processing data source types.
Definition qgis.h:3747
@ File
Files (i.e. non map layer sources, such as text files).
Definition qgis.h:3754
@ Plugin
Plugin layers.
Definition qgis.h:3757
@ TiledScene
Tiled scene layers.
Definition qgis.h:3761
@ Annotation
Annotation layers.
Definition qgis.h:3759
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3755
@ VectorTile
Vector tile layers.
Definition qgis.h:3760
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer).
Definition qgis.h:3748
@ Mesh
Mesh layers.
Definition qgis.h:3756
@ Raster
Raster layers.
Definition qgis.h:3753
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3749
@ VectorPoint
Vector point layers.
Definition qgis.h:3750
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3752
@ VectorLine
Vector line layers.
Definition qgis.h:3751
@ PointCloud
Point cloud layers.
Definition qgis.h:3758
ProcessingFileParameterBehavior
Flags which dictate the behavior of QgsProcessingParameterFile.
Definition qgis.h:4007
@ File
Parameter is a single file.
Definition qgis.h:4008
@ Folder
Parameter is a folder.
Definition qgis.h:4009
ExpressionType
Expression types.
Definition qgis.h:6213
@ RasterCalculator
Raster calculator expression.
Definition qgis.h:6216
@ Qgis
Native QGIS expression.
Definition qgis.h:6214
@ PointCloud
Point cloud expression.
Definition qgis.h:6215
InterpolationSourceType
Interpolation source types.
Definition qgis.h:6639
@ StructureLines
Structure lines.
Definition qgis.h:6641
@ Points
Point source.
Definition qgis.h:6640
@ BreakLines
Break lines.
Definition qgis.h:6642
@ Z
Use feature's geometry Z values for interpolation.
Definition qgis.h:6654
@ Attribute
Take value from feature's attribute.
Definition qgis.h:6653
ProcessingMode
Types of modes which Processing widgets can be created for.
Definition qgis.h:3888
@ Batch
Batch processing mode.
Definition qgis.h:3890
@ Modeler
Modeler mode.
Definition qgis.h:3891
@ Standard
Standard (single-run) algorithm mode.
Definition qgis.h:3889
DistanceUnit
Units of distance.
Definition qgis.h:5485
@ Feet
Imperial feet.
Definition qgis.h:5488
@ Centimeters
Centimeters.
Definition qgis.h:5493
@ Millimeters
Millimeters.
Definition qgis.h:5494
@ Miles
Terrestrial miles.
Definition qgis.h:5491
@ Meters
Meters.
Definition qgis.h:5486
@ Unknown
Unknown distance unit.
Definition qgis.h:5535
@ Yards
Imperial yards.
Definition qgis.h:5490
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5492
@ Inches
Inches.
Definition qgis.h:5495
@ NauticalMiles
Nautical miles.
Definition qgis.h:5489
@ Kilometers
Kilometers.
Definition qgis.h:5487
ProcessingFieldParameterDataType
Processing field parameter data types.
Definition qgis.h:4035
@ String
Accepts string fields.
Definition qgis.h:4038
@ Boolean
Accepts boolean fields, since QGIS 3.34.
Definition qgis.h:4041
@ Binary
Accepts binary fields, since QGIS 3.34.
Definition qgis.h:4040
@ Numeric
Accepts numeric fields.
Definition qgis.h:4037
@ DateTime
Accepts datetime fields.
Definition qgis.h:4039
AreaUnit
Units of area.
Definition qgis.h:5562
@ Acres
Acres.
Definition qgis.h:5569
@ SquareFeet
Square feet.
Definition qgis.h:5565
@ SquareCentimeters
Square centimeters.
Definition qgis.h:5572
@ SquareInches
Square inches.
Definition qgis.h:5574
@ SquareNauticalMiles
Square nautical miles.
Definition qgis.h:5570
@ SquareMillimeters
Square millimeters.
Definition qgis.h:5573
@ SquareYards
Square yards.
Definition qgis.h:5566
@ Hectares
Hectares.
Definition qgis.h:5568
@ SquareKilometers
Square kilometers.
Definition qgis.h:5564
@ SquareMeters
Square meters.
Definition qgis.h:5563
@ Unknown
Unknown areal unit.
Definition qgis.h:5575
@ SquareDegrees
Square degrees, for planar geographic CRS area measurements.
Definition qgis.h:5571
@ SquareMiles
Square miles.
Definition qgis.h:5567
@ Info
Information message.
Definition qgis.h:161
@ Static
Static property.
Definition qgis.h:725
@ AnnotationLayer
QgsAnnotationLayer.
Definition qgis.h:238
TemporalUnit
Temporal units.
Definition qgis.h:5631
@ Milliseconds
Milliseconds.
Definition qgis.h:5632
@ Hours
Hours.
Definition qgis.h:5635
@ Centuries
Centuries.
Definition qgis.h:5641
@ Seconds
Seconds.
Definition qgis.h:5633
@ Weeks
Weeks.
Definition qgis.h:5637
@ Years
Years.
Definition qgis.h:5639
@ Decades
Decades.
Definition qgis.h:5640
@ Minutes
Minutes.
Definition qgis.h:5634
@ CrossX
A cross is used to highlight points (x).
Definition qgis.h:7003
@ Point
Points.
Definition qgis.h:380
@ Vector
Vector layer.
Definition qgis.h:207
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
QFlags< ProcessingParameterFlag > ProcessingParameterFlags
Flags which dictate the behavior of Processing parameters.
Definition qgis.h:3996
VolumeUnit
Units of volume.
Definition qgis.h:5587
@ CubicMeters
Cubic meters.
Definition qgis.h:5588
@ Barrel
Barrels.
Definition qgis.h:5591
@ CubicYards
Cubic yards.
Definition qgis.h:5590
@ CubicFeet
Cubic feet.
Definition qgis.h:5589
@ CubicDegrees
Cubic degrees, for planar geographic CRS volume measurements.
Definition qgis.h:5597
@ CubicDecimeter
Cubic decimeters.
Definition qgis.h:5592
@ Unknown
Unknown volume unit.
Definition qgis.h:5598
@ CubicInch
Cubic inches.
Definition qgis.h:5595
@ GallonUS
US Gallons.
Definition qgis.h:5594
@ Liters
Litres.
Definition qgis.h:5593
@ CubicCentimeter
Cubic Centimeters.
Definition qgis.h:5596
ProcessingModelChildParameterSource
Processing model child parameter sources.
Definition qgis.h:4066
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:4067
@ StaticValue
Parameter value is a static value.
Definition qgis.h:4069
@ Optional
Parameter is optional.
Definition qgis.h:3984
ProcessingDateTimeParameterDataType
Processing date time parameter data types.
Definition qgis.h:4053
ProcessingNumberParameterType
Processing numeric parameter data types.
Definition qgis.h:4021
@ Double
Double/float values.
Definition qgis.h:4023
A widget wrapper for Processing parameter value widgets.
QVariant parameterValue() const
Returns the current value of the parameter.
virtual void setDialog(QWidget *dialog)
Sets the parent dialog (or widget) in which the wrapper is shown.
virtual QLabel * createLabel()
Creates a new label to accompany widgets created by the wrapper.
virtual void registerProcessingContextGenerator(QgsProcessingContextGenerator *generator)
Registers a Processing context generator class that will be used to retrieve a Processing context for...
void widgetValueHasChanged(QgsAbstractProcessingParameterWidgetWrapper *wrapper)
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
virtual void postInitialize(const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers)
Called after all wrappers have been created within a particular dialog or context,...
virtual const QgsVectorLayer * linkedVectorLayer() const
Returns the optional vector layer associated with this widget wrapper, or nullptr if no vector layer ...
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
virtual void setWidgetContext(const QgsProcessingParameterWidgetContext &context)
Sets the context in which the Processing parameter widget is shown, e.g., the parent model algorithm,...
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ CapturePoint
Select and capture a point or a feature.
Selector widget for authentication configs.
void selectedConfigIdChanged(const QString &authcfg)
Emitted when authentication config is changed or missing.
QComboBox subclass which allows selecting multiple items.
void checkedItemsChanged(const QStringList &items)
Emitted whenever the checked items list changed.
A cross platform button subclass for selecting colors.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
A widget for selecting the coordinate operation to use when transforming between a source and destina...
void operationChanged()
Emitted when the operation selected in the dialog is changed.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
A combo box which displays the list of schemas for a specific database connection.
A combobox which displays the list of tables for a specific database connection.
A QDateEdit widget with the capability of setting/reading null dates.
void dateValueChanged(const QDate &date)
Signal emitted whenever the date changes.
A QDateTimeEdit with the capability of setting/reading null date/times.
void setAllowNull(bool allowNull)
Determines if the widget allows setting null date/time.
void setNullRepresentation(const QString &null)
Sets the widget's null representation, which defaults to QgsApplication::nullRepresentation().
void valueChanged(const QDateTime &date)
Signal emitted whenever the value changes.
A dialog for configuring transform operations between coordinate reference systems.
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
static double toDouble(const QString &input, bool *ok)
Converts input string to double value.
A reusable widget that can be used to build an expression string.
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
A widget which includes a line edit for entering expressions together with a button to open the expre...
void expressionChanged(const QString &expression)
Emitted when the expression is changed.
A widget for configuration of a map extent.
void toggleDialogVisibility(bool visible)
Emitted when the parent dialog visibility must be changed (e.g.
void extentChanged(const QgsRectangle &r)
Emitted when the widget's extent is changed.
A combobox which displays the list of fields of a given layer.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
A widget for selection of layer fields or expression creation.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
@ DateTime
Datetime fields.
@ Date
Date or datetime fields.
@ Binary
Binary fields, since QGIS 3.34.
@ String
String fields.
@ Boolean
Boolean fields, since QGIS 3.34.
@ Numeric
All numeric fields.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
Container of fields for a vector layer.
Definition qgsfields.h:45
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
int count
Definition qgsfields.h:49
bool isEmpty
Definition qgsfields.h:48
void remove(int fieldIdx)
Removes the field with the given index.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
A widget for selecting a file or a folder.
@ GetFile
Select a single file.
@ GetDirectory
Select a directory.
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
A widget for storing and interacting with a QgsGeometry object.
void geometryValueChanged(const QgsReferencedGeometry &value)
Emitted whenever the geometry value of the widget is changed.
A geometry is the spatial representation of a feature.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
A QgsFilterLineEdit subclass with the ability to "highlight" the edges of the widget.
A combobox which displays available layouts from a QgsLayoutManager.
void layoutChanged(QgsMasterLayoutInterface *layout)
Emitted whenever the currently selected layout changes.
A combo box which displays items of a matching type from a layout.
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
Base class for graphical items within a QgsLayout.
virtual QString uuid() const
Returns the item identification string.
@ FilterPrintLayouts
Includes print layouts.
QList< QgsPrintLayout * > printLayouts() const
Returns a list of all print layouts contained in the manager.
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
A combobox which displays a dynamic list of layers from a QGIS project.
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
QString source() const
Returns the source for the layer.
QString id
Definition qgsmaplayer.h:86
A mouse event which is the result of a user interaction with a QgsMapCanvas.
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Abstract base class for all map tools.
Definition qgsmaptool.h:74
virtual void deactivate()
called when map tool is being deactivated
Interface for master layout type objects, such as print layouts and reports.
virtual QString name() const =0
Returns the layout's name.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
bool clearWidgets()
Removes all items from the bar.
Base class for any widget that can be shown as an inline panel.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool dockMode() const
Returns the dock mode state.
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.
A collection of point cloud attributes.
A combobox which displays the list of attributes of a given point cloud layer.
void attributeChanged(const QString &name)
Emitted when the currently selected attribute changes.
Attribute for point cloud data pair of name and size in bytes.
Represents a map layer supporting display of point clouds.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes available from the layer.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Print layout, a QgsLayout subclass for static or atlas-based layouts.
Abstract base class for widgets which allow users to specify the properties of a Processing parameter...
void changed()
Emitted whenever the definition of the parameter is changed in the widget.
Abstract base class for processing algorithms.
An interface for objects which can create Processing contexts.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsMapLayer * takeResultLayer(const QString &id)
Takes the result map layer with matching id from the context and transfers ownership of it back to th...
Base class for all parameter definitions which represent file or layer destinations,...
Encapsulates settings relating to a feature source input to a processing algorithm.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the parameter class.
A double numeric parameter for area values.
Qgis::AreaUnit defaultUnit() const
Returns the default area unit for the parameter.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
A raster band parameter for Processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
bool allowMultiple() const
Returns whether multiple band selections are permitted.
A boolean parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A color parameter for processing algorithms.
bool opacityEnabled() const
Returns true if the parameter allows opacity control.
static QString typeName()
Returns the type name for the parameter class.
A coordinate operation parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
QVariant sourceCrs() const
Returns the static source CRS, or an invalid value if this is not set.
QVariant destinationCrs() const
Returns the static destination CRS, or an invalid value if this is not set.
A coordinate reference system parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A database schema parameter for processing algorithms, allowing users to select from existing schemas...
static QString typeName()
Returns the type name for the parameter class.
QString parentConnectionParameterName() const
Returns the name of the parent connection parameter, or an empty string if this is not set.
A database table name parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
QString parentConnectionParameterName() const
Returns the name of the parent connection parameter, or an empty string if this is not set.
QString parentSchemaParameterName() const
Returns the name of the parent schema parameter, or an empty string if this is not set.
bool allowNewTableNames() const
Returns true if the parameter allows users to enter names for a new (non-existing) tables.
A datetime (or pure date or time) parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
Qgis::ProcessingDateTimeParameterDataType dataType() const
Returns the acceptable data type for the parameter.
Base class for the definition of processing parameters.
void setFlags(Qgis::ProcessingParameterFlags flags)
Sets the flags associated with the parameter.
QVariantMap metadata() const
Returns the parameter's freeform metadata.
QString description() const
Returns the description for the parameter.
QVariant defaultValueForGui() const
Returns the default value to use for the parameter in a GUI.
virtual QString type() const =0
Unique parameter type name.
QString name() const
Returns the name of the parameter.
Qgis::ProcessingParameterFlags flags() const
Returns any flags associated with the parameter.
A double numeric parameter for distance values.
static QString typeName()
Returns the type name for the parameter class.
Qgis::DistanceUnit defaultUnit() const
Returns the default distance unit for the parameter.
A double numeric parameter for duration values.
Qgis::TemporalUnit defaultUnit() const
Returns the default duration unit for the parameter.
static QString typeName()
Returns the type name for the parameter class.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
bool allowMultiple() const
Returns true if the parameter allows multiple selected values.
QStringList options() const
Returns the list of acceptable options for the parameter.
bool usesStaticStrings() const
Returns true if the parameter uses static (non-translated) string values for its enumeration choice l...
static QString typeName()
Returns the type name for the parameter class.
An expression parameter for processing algorithms.
QString parentLayerParameterName() const
Returns the name of the parent layer parameter, or an empty string if this is not set.
static QString typeName()
Returns the type name for the parameter class.
Qgis::ExpressionType expressionType() const
Returns the parameter's expression type.
A rectangular map extent parameter 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.
An input feature source (such as vector layers) parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A vector layer or feature source field parameter for processing algorithms.
Qgis::ProcessingFieldParameterDataType dataType() const
Returns the acceptable data type for the field.
bool allowMultiple() const
Returns whether multiple field selections are permitted.
bool defaultToAllFields() const
Returns whether a parameter which allows multiple selections (see allowMultiple()) should automatical...
static QString typeName()
Returns the type name for the parameter class.
void setDataType(Qgis::ProcessingFieldParameterDataType type)
Sets the acceptable data type for the field.
static QString typeName()
Returns the type name for the parameter class.
An input file or folder parameter for processing algorithms.
QString extension() const
Returns any specified file extension for the parameter.
static QString typeName()
Returns the type name for the parameter class.
QString fileFilter() const
Returns the file filter string for file destinations compatible with this parameter.
Qgis::ProcessingFileParameterBehavior behavior() const
Returns the parameter behavior (e.g.
static QString typeName()
Returns the type name for the parameter class.
A geometry parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A parameter for the heatmap algorithm pixel size parameter.
static QString typeName()
Returns the type name for the parameter class.
A parameter for the interpolation algorithm pixel size parameter.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
A print layout item parameter, allowing users to select a particular item from a print layout.
static QString typeName()
Returns the type name for the parameter class.
int itemType() const
Returns the acceptable item type, or -1 if any item type is allowed.
A print layout parameter, allowing users to select a print layout.
static QString typeName()
Returns the type name for the parameter class.
A map layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A map theme parameter for processing algorithms, allowing users to select an existing map theme from ...
static QString typeName()
Returns the type name for the parameter class.
A table (matrix) parameter 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.
A parameter for processing algorithms which accepts multiple map layers.
static QString typeName()
Returns the type name for the parameter class.
A numeric parameter for processing algorithms.
double minimum() const
Returns the minimum value acceptable by the parameter.
double maximum() const
Returns the maximum value acceptable by the parameter.
Qgis::ProcessingNumberParameterType dataType() const
Returns the acceptable data type for the parameter.
static QString typeName()
Returns the type name for the parameter class.
A point cloud layer attribute parameter for Processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
bool allowMultiple() const
Returns whether multiple field selections are permitted.
bool defaultToAllAttributes() const
Returns whether a parameter which allows multiple selections (see allowMultiple()) should automatical...
static QString typeName()
Returns the type name for the parameter class.
A point cloud layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A point parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A data provider connection parameter for processing algorithms, allowing users to select from availab...
static QString typeName()
Returns the type name for the parameter class.
QString providerId() const
Returns the ID of the provider associated with the connections.
A numeric range parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
Qgis::ProcessingNumberParameterType dataType() const
Returns the acceptable data type for the range.
static QString typeName()
Returns the type name for the parameter class.
A raster layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
static QVariant colorsAsVariant(const QList< QgsRasterReliefColor > &colors)
Returns a list of raster relief colors encoded as a variant value.
static QString typeName()
Returns the type name for the parameter class.
A double numeric parameter for map scale values.
static QString typeName()
Returns the type name for the parameter class.
A string parameter 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.
A vector layer (with or without geometry) parameter 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.
A double numeric parameter for volume values.
static QString typeName()
Returns the type name for the parameter class.
Qgis::VolumeUnit defaultUnit() const
Returns the default volume unit for the parameter.
Contains settings which reflect the context in which a Processing parameter widget is shown.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QgsProject * project() const
Returns the project associated with the widget.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
QgsProcessingModelAlgorithm * model() const
Returns the model which the parameter widget is associated with.
QgsMapLayer * activeLayer() const
Returns the current active layer.
static int parameterAsEnum(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a enum value.
static double parameterAsDouble(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static double value.
static QgsPointXY parameterAsPoint(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Evaluates the parameter with matching definition to a point.
static QgsPrintLayout * parameterAsLayout(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a print layout.
static QList< QgsMapLayer * > parameterAsLayerList(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Evaluates the parameter with matching definition to a list of map layers.
static QTime parameterAsTime(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static time value.
static QgsRectangle parameterAsExtent(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Evaluates the parameter with matching definition to a rectangular extent.
static QString parameterAsEnumString(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static enum string.
static QList< double > parameterAsRange(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a range of values.
static QStringList parameterAsStrings(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a list of strings (e.g.
static QList< int > parameterAsInts(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a list of integer values.
static QString parameterAsConnectionName(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a connection name string.
static QgsProcessingFeatureSource * parameterAsSource(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a feature source.
static QgsPointCloudLayer * parameterAsPointCloudLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Evaluates the parameter with matching definition to a point cloud layer.
static QgsCoordinateReferenceSystem parameterAsPointCrs(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Returns the coordinate reference system associated with an point parameter value.
static QgsLayoutItem * parameterAsLayoutItem(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, QgsPrintLayout *layout)
Evaluates the parameter with matching definition to a print layout item, taken from the specified lay...
static bool parameterAsBool(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static boolean value.
static QColor parameterAsColor(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Returns the color associated with an point parameter value, or an invalid color if the parameter was ...
static QgsVectorLayer * parameterAsVectorLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a vector layer.
static int parameterAsInt(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static integer value.
static QString parameterAsDatabaseTableName(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a database table name.
static QString parameterAsSchema(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a database schema name.
static QgsGeometry parameterAsGeometry(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Evaluates the parameter with matching definition to a geometry.
static QString parameterAsExpression(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to an expression.
static QString parameterAsString(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static string value.
static QgsRasterLayer * parameterAsRasterLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a raster layer.
static QList< int > parameterAsEnums(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to list of enum values.
static QStringList parameterAsEnumStrings(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to list of static enum strings.
static QgsCoordinateReferenceSystem parameterAsExtentCrs(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Returns the coordinate reference system associated with an extent parameter value.
static QDateTime parameterAsDateTime(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static datetime value.
static QDate parameterAsDate(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static date value.
static QVariantList parameterAsMatrix(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a matrix/table of values.
static QgsCoordinateReferenceSystem parameterAsCrs(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a coordinate reference system.
Utility functions for use with processing classes.
@ Annotation
Annotation layer type, since QGIS 3.22.
static QgsProcessingFeatureSource * variantToSource(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a new feature source.
static QgsCoordinateReferenceSystem variantToCrs(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a coordinate reference system.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
@ SkipIndexGeneration
Do not generate index when creating a layer. Makes sense only for point cloud layers.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:123
const QgsLayoutManager * layoutManager() const
Returns the project's layout manager, which manages print layouts, atlases and reports within the pro...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
A widget for selecting a projection.
@ CrsNotSet
Not set (hidden by default).
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
A store for object properties.
Qgis::PropertyType propertyType() const
Returns the property type.
A combo box which displays the list of connections registered for a given provider.
A combobox widget which displays the bands present in a raster layer.
void bandChanged(int band)
Emitted when the currently selected band changes.
static QString displayBandName(QgsRasterDataProvider *provider, int band)
Returns a user-friendly band name for the specified band.
Base class for raster data providers.
virtual int bandCount() const =0
Gets number of bands.
static QList< QgsRasterReliefColor > calculateOptimizedReliefClasses(QgsRasterDataProvider *provider, int band)
Calculates optimized relief class breaks according with the method of Buenzli (2011) using an iterati...
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Defines elevation range and color for raster relief coloring.
A rectangle specified with double values.
A QgsGeometry with associated coordinate reference system.
A combobox which lets the user select map scale from predefined list and highlights nearest to curren...
void scaleChanged(double scale)
Emitted when user has finished editing/selecting a new scale.
Stores settings for use within QGIS.
Definition qgssettings.h:68
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A spin box with a clear button that will set the value to the defined clear value.
Definition qgsspinbox.h:45
A QTimeEdit widget with the capability of setting/reading null date/times.
void timeValueChanged(const QTime &time)
Signal emitted whenever the time changes.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE Qgis::DistanceUnitType unitType(Qgis::DistanceUnit unit)
Returns the type for a distance unit.
Represents a vector layer which manages a vector based dataset.
QgsRectangle extent() const final
Returns the extent of the layer.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7395
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7582
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7488
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7382
bool qgsDoubleLessThanOrNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles to see if one is less than the other or very near to the other.
Definition qgis.h:7501
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.