QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsprocessingalgorithmdialogbase.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingalgorithmdialogbase.cpp
3 ------------------------------------
4 Date : November 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgssettings.h"
18#include "qgshelp.h"
19#include "qgsmessagebar.h"
20#include "qgsgui.h"
23#include "qgstaskmanager.h"
25#include "qgsstringutils.h"
26#include "qgsapplication.h"
27#include "qgspanelwidget.h"
28#include "qgsjsonutils.h"
29#include "qgsunittypes.h"
30#include <QToolButton>
31#include <QDesktopServices>
32#include <QScrollBar>
33#include <QApplication>
34#include <QClipboard>
35#include <QFileDialog>
36#include <QMimeData>
37#include <QMenu>
38#include <nlohmann/json.hpp>
39
40
42
43QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback()
44 : QgsProcessingFeedback( false )
45{}
46
47void QgsProcessingAlgorithmDialogFeedback::setProgressText( const QString &text )
48{
50 emit progressTextChanged( text );
51}
52
53void QgsProcessingAlgorithmDialogFeedback::reportError( const QString &error, bool fatalError )
54{
55 QgsProcessingFeedback::reportError( error, fatalError );
56 emit errorReported( error, fatalError );
57}
58
59void QgsProcessingAlgorithmDialogFeedback::pushWarning( const QString &warning )
60{
62 emit warningPushed( warning );
63}
64
65void QgsProcessingAlgorithmDialogFeedback::pushInfo( const QString &info )
66{
68 emit infoPushed( info );
69}
70
71void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( const QString &info )
72{
74 emit commandInfoPushed( info );
75}
76
77void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( const QString &info )
78{
80 emit debugInfoPushed( info );
81}
82
83void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( const QString &info )
84{
86 emit consoleInfoPushed( info );
87}
88
89//
90// QgsProcessingAlgorithmDialogBase
91//
92
93QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags, DialogMode mode )
94 : QDialog( parent, flags )
95 , mMode( mode )
96{
97 setupUi( this );
98
99 //don't collapse parameters panel
100 splitter->setCollapsible( 0, false );
101
102 // add collapse button to splitter
103 QSplitterHandle *splitterHandle = splitter->handle( 1 );
104 QVBoxLayout *handleLayout = new QVBoxLayout();
105 handleLayout->setContentsMargins( 0, 0, 0, 0 );
106 mButtonCollapse = new QToolButton( splitterHandle );
107 mButtonCollapse->setAutoRaise( true );
108 mButtonCollapse->setFixedSize( 12, 12 );
109 mButtonCollapse->setCursor( Qt::ArrowCursor );
110 handleLayout->addWidget( mButtonCollapse );
111 handleLayout->addStretch();
112 splitterHandle->setLayout( handleLayout );
113
115
116 const QgsSettings settings;
117 splitter->restoreState( settings.value( QStringLiteral( "/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
118 mSplitterState = splitter->saveState();
119 splitterChanged( 0, 0 );
120
121 // Rename OK button to Run
122 mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
123 mButtonRun->setText( tr( "Run" ) );
124
125 // Rename Yes button. Yes is used to ensure same position of Run and Change Parameters with respect to Close button.
126 mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
127 mButtonChangeParameters->setText( tr( "Change Parameters" ) );
128
129 buttonCancel->setEnabled( false );
130 mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
131
132 switch ( mMode )
133 {
134 case DialogMode::Single:
135 {
136 mAdvancedButton = new QPushButton( tr( "Advanced" ) );
137 mAdvancedMenu = new QMenu( this );
138 mAdvancedButton->setMenu( mAdvancedMenu );
139
140 mContextSettingsAction = new QAction( tr( "Algorithm Settingsā€¦" ), mAdvancedMenu );
141 mContextSettingsAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/settings.svg" ) ) );
142 mAdvancedMenu->addAction( mContextSettingsAction );
143
144 connect( mContextSettingsAction, &QAction::triggered, this, [this]
145 {
146 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( mMainWidget ) )
147 {
148 mTabWidget->setCurrentIndex( 0 );
149
150 if ( !mContextOptionsWidget )
151 {
152 mContextOptionsWidget = new QgsProcessingContextOptionsWidget();
153 mContextOptionsWidget->setFromContext( processingContext() );
154 mContextOptionsWidget->setLogLevel( mLogLevel );
155 panel->openPanel( mContextOptionsWidget );
156
157 connect( mContextOptionsWidget, &QgsPanelWidget::widgetChanged, this, [ = ]
158 {
159 mOverrideDefaultContextSettings = true;
160 mGeometryCheck = mContextOptionsWidget->invalidGeometryCheck();
161 mDistanceUnits = mContextOptionsWidget->distanceUnit();
162 mAreaUnits = mContextOptionsWidget->areaUnit();
163 mTemporaryFolderOverride = mContextOptionsWidget->temporaryFolder();
164 mMaximumThreads = mContextOptionsWidget->maximumThreads();
165 mLogLevel = mContextOptionsWidget->logLevel();
166 } );
167 }
168 }
169 } );
170 mAdvancedMenu->addSeparator();
171
172 QAction *copyAsPythonCommand = new QAction( tr( "Copy as Python Command" ), mAdvancedMenu );
173 copyAsPythonCommand->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconPythonFile.svg" ) ) );
174
175 mAdvancedMenu->addAction( copyAsPythonCommand );
176 connect( copyAsPythonCommand, &QAction::triggered, this, [this]
177 {
178 if ( const QgsProcessingAlgorithm *alg = algorithm() )
179 {
180 QgsProcessingContext *context = processingContext();
181 if ( !context )
182 return;
183
184 const QString command = alg->asPythonCommand( createProcessingParameters(), *context );
185 QMimeData *m = new QMimeData();
186 m->setText( command );
187 QClipboard *cb = QApplication::clipboard();
188
189#ifdef Q_OS_LINUX
190 cb->setMimeData( m, QClipboard::Selection );
191#endif
192 cb->setMimeData( m, QClipboard::Clipboard );
193 }
194 } );
195
196 mCopyAsQgisProcessCommand = new QAction( tr( "Copy as qgis_process Command" ), mAdvancedMenu );
197 mCopyAsQgisProcessCommand->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionTerminal.svg" ) ) );
198 mAdvancedMenu->addAction( mCopyAsQgisProcessCommand );
199
200 connect( mCopyAsQgisProcessCommand, &QAction::triggered, this, [this]
201 {
202 if ( const QgsProcessingAlgorithm *alg = algorithm() )
203 {
204 QgsProcessingContext *context = processingContext();
205 if ( !context )
206 return;
207
208 bool ok = false;
209 const QString command = alg->asQgisProcessCommand( createProcessingParameters(), *context, ok );
210 if ( ! ok )
211 {
212 mMessageBar->pushMessage( tr( "Current settings cannot be specified as arguments to qgis_process (Pipe parameters as JSON to qgis_process instead)" ), Qgis::MessageLevel::Warning );
213 }
214 else
215 {
216 QMimeData *m = new QMimeData();
217 m->setText( command );
218 QClipboard *cb = QApplication::clipboard();
219
220#ifdef Q_OS_LINUX
221 cb->setMimeData( m, QClipboard::Selection );
222#endif
223 cb->setMimeData( m, QClipboard::Clipboard );
224 }
225 }
226 } );
227
228 mAdvancedMenu->addSeparator();
229
230 QAction *copyAsJson = new QAction( tr( "Copy as JSON" ), mAdvancedMenu );
231 copyAsJson->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionEditCopy.svg" ) ) );
232
233 mAdvancedMenu->addAction( copyAsJson );
234 connect( copyAsJson, &QAction::triggered, this, [this]
235 {
236 if ( const QgsProcessingAlgorithm *alg = algorithm() )
237 {
238 QgsProcessingContext *context = processingContext();
239 if ( !context )
240 return;
241
242 const QVariantMap properties = alg->asMap( createProcessingParameters(), *context );
243 const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( properties ).dump( 2 ) );
244
245 QMimeData *m = new QMimeData();
246 m->setText( json );
247 QClipboard *cb = QApplication::clipboard();
248
249#ifdef Q_OS_LINUX
250 cb->setMimeData( m, QClipboard::Selection );
251#endif
252 cb->setMimeData( m, QClipboard::Clipboard );
253 }
254 } );
255
256 mPasteJsonAction = new QAction( tr( "Paste Settings" ), mAdvancedMenu );
257 mPasteJsonAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionEditPaste.svg" ) ) );
258
259 mAdvancedMenu->addAction( mPasteJsonAction );
260 connect( mPasteJsonAction, &QAction::triggered, this, [this]
261 {
262 const QString text = QApplication::clipboard()->text();
263 if ( text.isEmpty() )
264 return;
265
266 const QVariantMap parameterValues = QgsJsonUtils::parseJson( text ).toMap().value( QStringLiteral( "inputs" ) ).toMap();
267 if ( parameterValues.isEmpty() )
268 return;
269
270 bool ok = false;
271 QString error;
272 const QVariantMap preparedValues = QgsProcessingUtils::preprocessQgisProcessParameters( parameterValues, ok, error );
273
274 setParameters( preparedValues );
275 } );
276
277 mButtonBox->addButton( mAdvancedButton, QDialogButtonBox::ResetRole );
278 break;
279 }
280
281 case DialogMode::Batch:
282 break;
283 }
284
285 if ( mAdvancedMenu )
286 {
287 connect( mAdvancedMenu, &QMenu::aboutToShow, this, [ = ]
288 {
289 mCopyAsQgisProcessCommand->setEnabled( algorithm()
291 mPasteJsonAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );
292 } );
293 }
294
295 connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::runAlgorithm );
296 connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::showParameters );
297 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::closeClicked );
298 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmDialogBase::openHelp );
299 connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::toggleCollapsed );
300 connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmDialogBase::splitterChanged );
301
302 connect( mButtonSaveLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::saveLog );
303 connect( mButtonCopyLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::copyLogToClipboard );
304 connect( mButtonClearLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::clearLog );
305
306 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged );
307
308 mMessageBar = new QgsMessageBar();
309 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
310 verticalLayout->insertWidget( 0, mMessageBar );
311
312 connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingAlgorithmDialogBase::taskTriggered );
313}
314
315QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() = default;
316
317void QgsProcessingAlgorithmDialogBase::setParameters( const QVariantMap & )
318{}
319
320void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
321{
322 mAlgorithm.reset( algorithm );
323 QString title;
325 {
326 title = QgsStringUtils::capitalize( mAlgorithm->displayName(), Qgis::Capitalization::TitleCase );
327 }
328 else
329 {
330 title = mAlgorithm->displayName();
331 }
332 setWindowTitle( title );
333
334 const QString algHelp = formatHelp( algorithm );
335 if ( algHelp.isEmpty() )
336 textShortHelp->hide();
337 else
338 {
339 textShortHelp->document()->setDefaultStyleSheet( QStringLiteral( ".summary { margin-left: 10px; margin-right: 10px; }\n"
340 "h2 { color: #555555; padding-bottom: 15px; }\n"
341 "a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
342 "p { color: #666666; }\n"
343 "b { color: #333333; }\n"
344 "dl dd { margin - bottom: 5px; }" ) );
345 textShortHelp->setHtml( algHelp );
346 connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmDialogBase::linkClicked );
347 textShortHelp->show();
348 }
349
350 if ( algorithm->helpUrl().isEmpty() && ( !algorithm->provider() || algorithm->provider()->helpId().isEmpty() ) )
351 {
352 mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
353 }
354
355 const QString warning = algorithm->provider() ? algorithm->provider()->warningMessage() : QString();
356 if ( !warning.isEmpty() )
357 {
358 mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
359 }
360}
361
362QgsProcessingAlgorithm *QgsProcessingAlgorithmDialogBase::algorithm()
363{
364 return mAlgorithm.get();
365}
366
367void QgsProcessingAlgorithmDialogBase::setMainWidget( QgsPanelWidget *widget )
368{
369 if ( mMainWidget )
370 {
371 mMainWidget->deleteLater();
372 }
373
374 mPanelStack->setMainPanel( widget );
375 widget->setDockMode( true );
376
377 mMainWidget = widget;
378 connect( mMainWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
379}
380
381QgsPanelWidget *QgsProcessingAlgorithmDialogBase::mainWidget()
382{
383 return mMainWidget;
384}
385
386void QgsProcessingAlgorithmDialogBase::saveLogToFile( const QString &path, const LogFormat format )
387{
388 QFile logFile( path );
389 if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
390 {
391 return;
392 }
393 QTextStream fout( &logFile );
394
395 switch ( format )
396 {
397 case FormatPlainText:
398 fout << txtLog->toPlainText();
399 break;
400
401 case FormatHtml:
402 fout << txtLog->toHtml();
403 break;
404 }
405}
406
407QgsProcessingFeedback *QgsProcessingAlgorithmDialogBase::createFeedback()
408{
409 auto feedback = std::make_unique< QgsProcessingAlgorithmDialogFeedback >();
410 connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmDialogBase::setPercentage );
411 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::commandInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushCommandInfo );
412 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::consoleInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushConsoleInfo );
413 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::debugInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushDebugInfo );
414 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::errorReported, this, &QgsProcessingAlgorithmDialogBase::reportError );
415 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::warningPushed, this, &QgsProcessingAlgorithmDialogBase::pushWarning );
416 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::infoPushed, this, &QgsProcessingAlgorithmDialogBase::pushInfo );
417 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::progressTextChanged, this, &QgsProcessingAlgorithmDialogBase::setProgressText );
418 connect( buttonCancel, &QPushButton::clicked, feedback.get(), &QgsProcessingFeedback::cancel );
419 return feedback.release();
420}
421
422QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
423{
424 return mButtonBox;
425}
426
427QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
428{
429 return mTabWidget;
430}
431
432void QgsProcessingAlgorithmDialogBase::showLog()
433{
434 mTabWidget->setCurrentIndex( 1 );
435}
436
437void QgsProcessingAlgorithmDialogBase::showParameters()
438{
439 mTabWidget->setCurrentIndex( 0 );
440}
441
442QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
443{
444 return mButtonRun;
445}
446
447QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
448{
449 return buttonCancel;
450}
451
452QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
453{
454 return mButtonChangeParameters;
455}
456
457void QgsProcessingAlgorithmDialogBase::clearProgress()
458{
459 progressBar->setMaximum( 0 );
460}
461
462void QgsProcessingAlgorithmDialogBase::setExecuted( bool executed )
463{
464 mExecuted = executed;
465}
466
467void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult( bool executedAnyResult )
468{
469 mExecutedAnyResult = executedAnyResult;
470}
471
472void QgsProcessingAlgorithmDialogBase::setResults( const QVariantMap &results )
473{
474 mResults = results;
475}
476
477void QgsProcessingAlgorithmDialogBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
478{
479
480}
481
482void QgsProcessingAlgorithmDialogBase::openHelp()
483{
484 QUrl algHelp = mAlgorithm->helpUrl();
485 if ( algHelp.isEmpty() && mAlgorithm->provider() )
486 {
487 algHelp = QgsHelp::helpUrl( QStringLiteral( "processing_algs/%1/%2.html#%3" ).arg( mAlgorithm->provider()->helpId(), mAlgorithm->groupId(), QStringLiteral( "%1%2" ).arg( mAlgorithm->provider()->helpId() ).arg( mAlgorithm->name() ) ) );
488 }
489
490 if ( !algHelp.isEmpty() )
491 QDesktopServices::openUrl( algHelp );
492}
493
494void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
495{
496 if ( mHelpCollapsed )
497 {
498 splitter->restoreState( mSplitterState );
499 mButtonCollapse->setArrowType( Qt::RightArrow );
500 }
501 else
502 {
503 mSplitterState = splitter->saveState();
504 splitter->setSizes( QList<int>() << 1 << 0 );
505 mButtonCollapse->setArrowType( Qt::LeftArrow );
506 }
507 mHelpCollapsed = !mHelpCollapsed;
508}
509
510void QgsProcessingAlgorithmDialogBase::splitterChanged( int, int )
511{
512 if ( splitter->sizes().at( 1 ) == 0 )
513 {
514 mHelpCollapsed = true;
515 mButtonCollapse->setArrowType( Qt::LeftArrow );
516 }
517 else
518 {
519 mHelpCollapsed = false;
520 mButtonCollapse->setArrowType( Qt::RightArrow );
521 }
522}
523
524void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged( int )
525{
526 updateRunButtonVisibility();
527}
528
529void QgsProcessingAlgorithmDialogBase::linkClicked( const QUrl &url )
530{
531 QDesktopServices::openUrl( url.toString() );
532}
533
534void QgsProcessingAlgorithmDialogBase::algExecuted( bool successful, const QVariantMap & )
535{
536 mAlgorithmTask = nullptr;
537
538 if ( !successful )
539 {
540 // show dialog to display errors
541 show();
542 raise();
543 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
544 activateWindow();
545 showLog();
546 }
547 else
548 {
549 if ( isFinalized() && successful )
550 {
551 progressBar->setFormat( tr( "Complete" ) );
552 }
553
554 // delete dialog if closed
555 if ( isFinalized() && !isVisible() )
556 {
557 deleteLater();
558 }
559 }
560}
561
562void QgsProcessingAlgorithmDialogBase::taskTriggered( QgsTask *task )
563{
564 if ( task == mAlgorithmTask )
565 {
566 show();
567 raise();
568 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
569 activateWindow();
570 showLog();
571 }
572}
573
574void QgsProcessingAlgorithmDialogBase::closeClicked()
575{
576 reject();
577 close();
578}
579
580QgsProcessingContext::LogLevel QgsProcessingAlgorithmDialogBase::logLevel() const
581{
582 return mLogLevel;
583}
584
585void QgsProcessingAlgorithmDialogBase::setLogLevel( QgsProcessingContext::LogLevel level )
586{
587 mLogLevel = level;
588}
589
590void QgsProcessingAlgorithmDialogBase::reportError( const QString &error, bool fatalError )
591{
592 setInfo( error, true );
593 if ( fatalError )
594 resetGui();
595 showLog();
596 processEvents();
597}
598
599void QgsProcessingAlgorithmDialogBase::pushWarning( const QString &warning )
600{
601 setInfo( warning, false, true, true );
602 processEvents();
603}
604
605void QgsProcessingAlgorithmDialogBase::pushInfo( const QString &info )
606{
607 setInfo( info );
608 processEvents();
609}
610
611void QgsProcessingAlgorithmDialogBase::pushCommandInfo( const QString &command )
612{
613 txtLog->append( QStringLiteral( "<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
614 scrollToBottomOfLog();
615 processEvents();
616}
617
618void QgsProcessingAlgorithmDialogBase::pushDebugInfo( const QString &message )
619{
620 txtLog->append( QStringLiteral( "<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
621 scrollToBottomOfLog();
622 processEvents();
623}
624
625void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( const QString &info )
626{
627 txtLog->append( QStringLiteral( "<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
628 scrollToBottomOfLog();
629 processEvents();
630}
631
632QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
633{
634 QgsProcessingAlgorithmProgressDialog *dialog = new QgsProcessingAlgorithmProgressDialog( this );
635 dialog->setWindowModality( Qt::ApplicationModal );
636 dialog->setWindowTitle( windowTitle() );
637 dialog->setGeometry( geometry() ); // match size/position to this dialog
638 connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
639 connect( dialog->cancelButton(), &QPushButton::clicked, buttonCancel, &QPushButton::click );
640 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
641 connect( txtLog, &QTextEdit::textChanged, dialog, [this, dialog]()
642 {
643 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
644 QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
645 sb->setValue( sb->maximum() );
646 } );
647 return dialog;
648}
649
650void QgsProcessingAlgorithmDialogBase::clearLog()
651{
652 txtLog->clear();
653}
654
655void QgsProcessingAlgorithmDialogBase::saveLog()
656{
657 QgsSettings settings;
658 const QString lastUsedDir = settings.value( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
659
660 QString filter;
661 const QString txtExt = tr( "Text files" ) + QStringLiteral( " (*.txt *.TXT)" );
662 const QString htmlExt = tr( "HTML files" ) + QStringLiteral( " (*.html *.HTML)" );
663
664 const QString path = QFileDialog::getSaveFileName( this, tr( "Save Log to File" ), lastUsedDir, txtExt + ";;" + htmlExt, &filter );
665 // return dialog focus on Mac
666 activateWindow();
667 raise();
668 if ( path.isEmpty() )
669 {
670 return;
671 }
672
673 settings.setValue( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
674
675 LogFormat format = FormatPlainText;
676 if ( filter == htmlExt )
677 {
678 format = FormatHtml;
679 }
680 saveLogToFile( path, format );
681}
682
683void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
684{
685 QMimeData *m = new QMimeData();
686 m->setText( txtLog->toPlainText() );
687 m->setHtml( txtLog->toHtml() );
688 QClipboard *cb = QApplication::clipboard();
689
690#ifdef Q_OS_LINUX
691 cb->setMimeData( m, QClipboard::Selection );
692#endif
693 cb->setMimeData( m, QClipboard::Clipboard );
694}
695
696void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
697{
698 if ( !mHelpCollapsed )
699 {
700 QgsSettings settings;
701 settings.setValue( QStringLiteral( "/Processing/dialogBaseSplitter" ), splitter->saveState() );
702 }
703
704 QDialog::closeEvent( e );
705
706 if ( !mAlgorithmTask && isFinalized() )
707 {
708 // when running a background task, the dialog is kept around and deleted only when the task
709 // completes. But if not running a task, we auto cleanup (later - gotta give callers a chance
710 // to retrieve results and execution status).
711 deleteLater();
712 }
713}
714
715void QgsProcessingAlgorithmDialogBase::runAlgorithm()
716{
717
718}
719
720void QgsProcessingAlgorithmDialogBase::setPercentage( double percent )
721{
722 // delay setting maximum progress value until we know algorithm reports progress
723 if ( progressBar->maximum() == 0 )
724 progressBar->setMaximum( 100 );
725 progressBar->setValue( percent );
726 processEvents();
727}
728
729void QgsProcessingAlgorithmDialogBase::setProgressText( const QString &text )
730{
731 lblProgress->setText( text );
732 setInfo( text, false );
733 scrollToBottomOfLog();
734 processEvents();
735}
736
737QString QgsProcessingAlgorithmDialogBase::formatHelp( QgsProcessingAlgorithm *algorithm )
738{
739 const QString text = algorithm->shortHelpString();
740 if ( !text.isEmpty() )
741 {
742 const QStringList paragraphs = text.split( '\n' );
743 QString help;
744 for ( const QString &paragraph : paragraphs )
745 {
746 help += QStringLiteral( "<p>%1</p>" ).arg( paragraph );
747 }
748 return QStringLiteral( "<h2>%1</h2>%2" ).arg( algorithm->displayName(), help );
749 }
750 else if ( !algorithm->shortDescription().isEmpty() )
751 {
752 return QStringLiteral( "<h2>%1</h2><p>%2</p>" ).arg( algorithm->displayName(), algorithm->shortDescription() );
753 }
754 else
755 return QString();
756}
757
758void QgsProcessingAlgorithmDialogBase::processEvents()
759{
760 if ( mAlgorithmTask )
761 {
762 // no need to call this - the algorithm is running in a thread.
763 // in fact, calling it causes a crash on Windows when the algorithm
764 // is running in a background thread... unfortunately we need something
765 // like this for non-threadable algorithms, otherwise there's no chance
766 // for users to hit cancel or see progress updates...
767 return;
768 }
769
770 // So that we get a chance of hitting the Abort button
771#ifdef Q_OS_LINUX
772 // One iteration is actually enough on Windows to get good interactivity
773 // whereas on Linux we must allow for far more iterations.
774 // For safety limit the number of iterations
775 int nIters = 0;
776 while ( ++nIters < 100 )
777#endif
778 {
779 QCoreApplication::processEvents();
780 }
781}
782
783void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
784{
785 QScrollBar *sb = txtLog->verticalScrollBar();
786 sb->setValue( sb->maximum() );
787}
788
789void QgsProcessingAlgorithmDialogBase::resetGui()
790{
791 lblProgress->clear();
792 progressBar->setMaximum( 100 );
793 progressBar->setValue( 0 );
794 mButtonRun->setEnabled( true );
795 mButtonChangeParameters->setEnabled( true );
796 mButtonClose->setEnabled( true );
797 if ( mMainWidget )
798 {
799 mMainWidget->setEnabled( true );
800 }
801 updateRunButtonVisibility();
802 resetAdditionalGui();
803}
804
805void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
806{
807 // Activate run button if current tab is Parameters
808 const bool runButtonVisible = mTabWidget->currentIndex() == 0;
809 mButtonRun->setVisible( runButtonVisible );
810 if ( runButtonVisible )
811 progressBar->resetFormat();
812 mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
813}
814
815void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
816{
817
818}
819
820void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
821{
822 mButtonRun->setEnabled( false );
823 mButtonChangeParameters->setEnabled( false );
824 if ( mMainWidget )
825 {
826 mMainWidget->setEnabled( false );
827 }
828 blockAdditionalControlsWhileRunning();
829}
830
831void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
832{
833
834}
835
836QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
837{
838 return mMessageBar;
839}
840
841void QgsProcessingAlgorithmDialogBase::hideShortHelp()
842{
843 textShortHelp->setVisible( false );
844}
845
846void QgsProcessingAlgorithmDialogBase::setCurrentTask( QgsProcessingAlgRunnerTask *task )
847{
848 mAlgorithmTask = task;
849 connect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmDialogBase::algExecuted );
850 QgsApplication::taskManager()->addTask( mAlgorithmTask );
851}
852
853QString QgsProcessingAlgorithmDialogBase::formatStringForLog( const QString &string )
854{
855 QString s = string;
856 s.replace( '\n', QLatin1String( "<br>" ) );
857 return s;
858}
859
860bool QgsProcessingAlgorithmDialogBase::isFinalized()
861{
862 return true;
863}
864
865void QgsProcessingAlgorithmDialogBase::applyContextOverrides( QgsProcessingContext *context )
866{
867 if ( !context )
868 return;
869
870 context->setLogLevel( logLevel() );
871
872 if ( mOverrideDefaultContextSettings )
873 {
874 context->setInvalidGeometryCheck( mGeometryCheck );
875 context->setDistanceUnit( mDistanceUnits );
876 context->setAreaUnit( mAreaUnits );
877 context->setTemporaryFolder( mTemporaryFolderOverride );
878 context->setMaximumThreads( mMaximumThreads );
879 }
880}
881
882void QgsProcessingAlgorithmDialogBase::setInfo( const QString &message, bool isError, bool escapeHtml, bool isWarning )
883{
884 constexpr int MESSAGE_COUNT_LIMIT = 10000;
885 // Avoid logging too many messages, which might blow memory.
886 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
887 return;
888 ++mMessageLoggedCount;
889
890 // note -- we have to wrap the message in a span block, or QTextEdit::append sometimes gets confused
891 // and varies between treating it as a HTML string or a plain text string! (see https://github.com/qgis/QGIS/issues/37934)
892 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
893 txtLog->append( QStringLiteral( "<span style=\"color:red\">%1</span>" ).arg( tr( "Message log truncated" ) ) );
894 else if ( isError || isWarning )
895 txtLog->append( QStringLiteral( "<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral( "red" ) : QStringLiteral( "#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
896 else if ( escapeHtml )
897 txtLog->append( QStringLiteral( "<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
898 else
899 txtLog->append( QStringLiteral( "<span>%1</span>" ).arg( formatStringForLog( message ) ) );
900 scrollToBottomOfLog();
901 processEvents();
902}
903
904void QgsProcessingAlgorithmDialogBase::reject()
905{
906 if ( !mAlgorithmTask && isFinalized() )
907 {
908 setAttribute( Qt::WA_DeleteOnClose );
909 }
910 QDialog::reject();
911}
912
913//
914// QgsProcessingAlgorithmProgressDialog
915//
916
917QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
918 : QDialog( parent )
919{
920 setupUi( this );
921}
922
923QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
924{
925 return mProgressBar;
926}
927
928QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
929{
930 return mButtonBox->button( QDialogButtonBox::Cancel );
931}
932
933QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
934{
935 return mTxtLog;
936}
937
938void QgsProcessingAlgorithmProgressDialog::reject()
939{
940
941}
942
943
944//
945// QgsProcessingContextOptionsWidget
946//
947
948QgsProcessingContextOptionsWidget::QgsProcessingContextOptionsWidget( QWidget *parent )
949 : QgsPanelWidget( parent )
950{
951 setupUi( this );
952 setPanelTitle( tr( "Algorithm Settings" ) );
953
954 mComboInvalidFeatureFiltering->addItem( tr( "Do not Filter (Better Performance)" ), QgsFeatureRequest::GeometryNoCheck );
955 mComboInvalidFeatureFiltering->addItem( tr( "Skip (Ignore) Features with Invalid Geometries" ), QgsFeatureRequest::GeometrySkipInvalid );
956 mComboInvalidFeatureFiltering->addItem( tr( "Stop Algorithm Execution When a Geometry is Invalid" ), QgsFeatureRequest::GeometryAbortOnInvalid );
957
958 mTemporaryFolderWidget->setDialogTitle( tr( "Select Temporary Directory" ) );
959 mTemporaryFolderWidget->setStorageMode( QgsFileWidget::GetDirectory );
960 mTemporaryFolderWidget->lineEdit()->setPlaceholderText( tr( "Default" ) );
961
962 mLogLevelComboBox->addItem( tr( "Default" ), QgsProcessingContext::LogLevel::DefaultLevel );
963 mLogLevelComboBox->addItem( tr( "Verbose" ), QgsProcessingContext::LogLevel::Verbose );
964 mLogLevelComboBox->addItem( tr( "Verbose (Model Debugging)" ), QgsProcessingContext::LogLevel::ModelDebug );
965
966 mDistanceUnitsCombo->addItem( tr( "Default" ), QVariant::fromValue( Qgis::DistanceUnit::Unknown ) );
967 for ( Qgis::DistanceUnit unit :
968 {
979 } )
980 {
981 QString title;
983 {
985 }
986 else
987 {
988 title = QgsUnitTypes::toString( unit );
989 }
990
991 mDistanceUnitsCombo->addItem( title, QVariant::fromValue( unit ) );
992 }
993
994 mAreaUnitsCombo->addItem( tr( "Default" ), QVariant::fromValue( Qgis::AreaUnit::Unknown ) );
995 for ( Qgis::AreaUnit unit :
996 {
1009 } )
1010 {
1011 QString title;
1013 {
1015 }
1016 else
1017 {
1018 title = QgsUnitTypes::toString( unit );
1019 }
1020
1021 mAreaUnitsCombo->addItem( title, QVariant::fromValue( unit ) );
1022 }
1023
1024 mThreadsSpinBox->setRange( 1, QThread::idealThreadCount() );
1025
1026 connect( mLogLevelComboBox, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
1027 connect( mComboInvalidFeatureFiltering, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
1028 connect( mDistanceUnitsCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
1029 connect( mAreaUnitsCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
1030 connect( mTemporaryFolderWidget, &QgsFileWidget::fileChanged, this, &QgsPanelWidget::widgetChanged );
1031 connect( mThreadsSpinBox, qOverload< int >( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
1032}
1033
1034void QgsProcessingContextOptionsWidget::setFromContext( const QgsProcessingContext *context )
1035{
1036 whileBlocking( mComboInvalidFeatureFiltering )->setCurrentIndex( mComboInvalidFeatureFiltering->findData( static_cast< int >( context->invalidGeometryCheck() ) ) );
1037 whileBlocking( mDistanceUnitsCombo )->setCurrentIndex( mDistanceUnitsCombo->findData( QVariant::fromValue( context->distanceUnit() ) ) );
1038 whileBlocking( mAreaUnitsCombo )->setCurrentIndex( mAreaUnitsCombo->findData( QVariant::fromValue( context->areaUnit() ) ) );
1039 whileBlocking( mTemporaryFolderWidget )->setFilePath( context->temporaryFolder() );
1040 whileBlocking( mThreadsSpinBox )->setValue( context->maximumThreads() );
1041 whileBlocking( mLogLevelComboBox )->setCurrentIndex( mLogLevelComboBox->findData( static_cast< int >( context->logLevel() ) ) );
1042}
1043
1044QgsFeatureRequest::InvalidGeometryCheck QgsProcessingContextOptionsWidget::invalidGeometryCheck() const
1045{
1046 return static_cast< QgsFeatureRequest::InvalidGeometryCheck >( mComboInvalidFeatureFiltering->currentData().toInt() );
1047}
1048
1049Qgis::DistanceUnit QgsProcessingContextOptionsWidget::distanceUnit() const
1050{
1051 return mDistanceUnitsCombo->currentData().value< Qgis::DistanceUnit >();
1052}
1053
1054Qgis::AreaUnit QgsProcessingContextOptionsWidget::areaUnit() const
1055{
1056 return mAreaUnitsCombo->currentData().value< Qgis::AreaUnit >();
1057}
1058
1059QString QgsProcessingContextOptionsWidget::temporaryFolder()
1060{
1061 return mTemporaryFolderWidget->filePath();
1062}
1063
1064int QgsProcessingContextOptionsWidget::maximumThreads() const
1065{
1066 return mThreadsSpinBox->value();
1067}
1068
1069void QgsProcessingContextOptionsWidget::setLogLevel( QgsProcessingContext::LogLevel level )
1070{
1071 whileBlocking( mLogLevelComboBox )->setCurrentIndex( mLogLevelComboBox->findData( static_cast< int >( level ) ) );
1072}
1073
1074QgsProcessingContext::LogLevel QgsProcessingContextOptionsWidget::logLevel() const
1075{
1076 return static_cast< QgsProcessingContext::LogLevel >( mLogLevelComboBox->currentData().toInt() );
1077}
1078
DistanceUnit
Units of distance.
Definition qgis.h:3496
@ Feet
Imperial feet.
@ Centimeters
Centimeters.
@ Millimeters
Millimeters.
@ Miles
Terrestrial miles.
@ Unknown
Unknown distance unit.
@ Yards
Imperial yards.
@ Degrees
Degrees, for planar geographic CRS distance measurements.
@ Inches
Inches (since QGIS 3.32)
@ NauticalMiles
Nautical miles.
@ Kilometers
Kilometers.
AreaUnit
Units of area.
Definition qgis.h:3534
@ SquareFeet
Square feet.
@ SquareCentimeters
Square centimeters.
@ SquareInches
Square inches (since QGIS 3.32)
@ SquareNauticalMiles
Square nautical miles.
@ SquareMillimeters
Square millimeters.
@ SquareYards
Square yards.
@ Hectares
Hectares.
@ SquareKilometers
Square kilometers.
@ SquareMeters
Square meters.
@ Unknown
Unknown areal unit.
@ SquareDegrees
Square degrees, for planar geographic CRS area measurements.
@ SquareMiles
Square miles.
@ Warning
Warning message.
Definition qgis.h:101
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
InvalidGeometryCheck
Handling of features with invalid geometries.
@ GeometryNoCheck
No invalid geometry checking.
@ GeometryAbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
@ GeometrySkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
@ GetDirectory
Select a directory.
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:194
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
Definition qgsgui.h:256
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
Definition qgsgui.cpp:218
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
Definition qgshelp.cpp:43
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
A bar for displaying non-blocking messages to the user.
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void widgetChanged()
Emitted when the widget state changes.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsTask task which runs a QgsProcessingAlgorithm in a background task.
void executed(bool successful, const QVariantMap &results)
Emitted when the algorithm has finished execution.
Abstract base class for processing algorithms.
virtual QString helpUrl() const
Returns a url pointing to the algorithm's help page.
virtual QString shortHelpString() const
Returns a localised short helper string for the algorithm.
virtual QString shortDescription() const
Returns an optional translated short description of the algorithm.
@ FlagNotAvailableInStandaloneTool
Algorithm should not be available from the standalone "qgis_process" tool. Used to flag algorithms wh...
@ FlagDisplayNameIsLiteral
Algorithm's display name is a static literal string, and should not be translated or automatically fo...
virtual QString displayName() const =0
Returns the translated algorithm name, which should be used for any user-visible display of the algor...
QgsProcessingProvider * provider() const
Returns the provider to which this algorithm belongs.
Contains information about the context in which a processing algorithm is executed.
LogLevel
Logging level for algorithms to use when pushing feedback messages.
@ Verbose
Verbose logging.
@ DefaultLevel
Default logging level.
@ ModelDebug
Model debug level logging. Includes verbose logging and other outputs useful for debugging models (si...
Qgis::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
void setMaximumThreads(int threads)
Sets the (optional) number of threads to use when running algorithms.
void setDistanceUnit(Qgis::DistanceUnit unit)
Sets the unit to use for distance calculations.
void setAreaUnit(Qgis::AreaUnit areaUnit)
Sets the unit to use for area calculations.
Qgis::DistanceUnit distanceUnit() const
Returns the distance unit to use for distance calculations.
void setLogLevel(LogLevel level)
Sets the logging level for algorithms to use when pushing feedback messages to users.
void setInvalidGeometryCheck(QgsFeatureRequest::InvalidGeometryCheck check)
Sets the behavior used for checking invalid geometries in input layers.
LogLevel logLevel() const
Returns the logging level for algorithms to use when pushing feedback messages to users.
QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const
Returns the behavior used for checking invalid geometries in input layers.
void setTemporaryFolder(const QString &folder)
Sets the (optional) temporary folder to use when running algorithms.
QString temporaryFolder() const
Returns the (optional) temporary folder to use when running algorithms.
int maximumThreads() const
Returns the (optional) number of threads to use when running algorithms.
QgsProcessingAlgorithm::Flags flags() const override
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Base class for providing feedback from a processing algorithm.
virtual void pushCommandInfo(const QString &info)
Pushes an informational message containing a command from the algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
virtual void pushConsoleInfo(const QString &info)
Pushes a console feedback message from the algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
virtual QString helpId() const
Returns the provider help id string, used for creating QgsHelp urls for algorithms belong to this pro...
virtual QString warningMessage() const
Returns an optional warning message to show users when running algorithms from this provider.
static QVariantMap preprocessQgisProcessParameters(const QVariantMap &parameters, bool &ok, QString &error)
Pre-processes a set of parameter values for the qgis_process command.
This class is a composition of two QSettings instances:
Definition qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
Abstract base class for long running background tasks.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
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
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:4258