QGIS API Documentation 4.3.0-Master (52aa6503bed)
Loading...
Searching...
No Matches
qgsprocessingalgorithmwidgetbase.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
18#include <nlohmann/json.hpp>
19
24#include "qgsapplication.h"
26#include "qgsgui.h"
27#include "qgshelp.h"
28#include "qgsjsonutils.h"
29#include "qgsmessagebar.h"
30#include "qgsnative.h"
31#include "qgspanelwidget.h"
34#include "qgssettings.h"
35#include "qgsstringutils.h"
36#include "qgstaskmanager.h"
37#include "qgsunittypes.h"
38
39#include <QApplication>
40#include <QClipboard>
41#include <QDesktopServices>
42#include <QFileDialog>
43#include <QMainWindow>
44#include <QMenu>
45#include <QMimeData>
46#include <QScrollBar>
47#include <QString>
48#include <QToolButton>
49
50#include "moc_qgsprocessingalgorithmwidgetbase.cpp"
51
52using namespace Qt::StringLiterals;
53
55
56//
57// QgsProcessingFeedbackGenerator
58//
59
60QgsProcessingFeedbackGenerator::~QgsProcessingFeedbackGenerator()
61{}
62
63//
64// QgsProcessingAlgorithmWidgetBase
65//
66
67QgsProcessingAlgorithmWidgetBase::QgsProcessingAlgorithmWidgetBase(
68 QMainWindow *parentWindow, WidgetMode mode, QgsProcessingAlgorithmWidgetBase::WidgetFlags flags, Qgis::DockableWidgetInitialState initialState
69)
70 : QWidget()
71 , mMode( mode )
72{
73 setupUi( this );
74
75 const QSize defaultDialogSize = size();
76
77 //don't collapse parameters panel
78 splitter->setCollapsible( 0, false );
79
80 // add collapse button to splitter
81 QSplitterHandle *splitterHandle = splitter->handle( 1 );
82 QVBoxLayout *handleLayout = new QVBoxLayout();
83 handleLayout->setContentsMargins( 0, 0, 0, 0 );
84 mButtonCollapse = new QToolButton( splitterHandle );
85 mButtonCollapse->setAutoRaise( true );
86 mButtonCollapse->setFixedSize( 12, 12 );
87 mButtonCollapse->setCursor( Qt::ArrowCursor );
88 handleLayout->addWidget( mButtonCollapse );
89 handleLayout->addStretch();
90 splitterHandle->setLayout( handleLayout );
91
93
94 txtLog->setOpenLinks( false );
95 connect( txtLog, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmWidgetBase::urlClicked );
96
97 const QgsSettings settings;
98 splitter->restoreState( settings.value( u"/Processing/dialogBaseSplitter"_s, QByteArray() ).toByteArray() );
99 mSplitterState = splitter->saveState();
100 splitterChanged( 0, 0 );
101
102 // Rename OK button to Run
103 mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
104 mButtonRun->setText( tr( "Run" ) );
105
106 // Rename Yes button. Yes is used to ensure same position of Run and Change Parameters with respect to Close button.
107 mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
108 mButtonChangeParameters->setText( tr( "Change Parameters" ) );
109
110 connect( buttonCancel, &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::cancel );
111 buttonCancel->setEnabled( false );
112 mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
113
114 if ( !parentWindow )
115 parentWindow = qobject_cast< QMainWindow * >( QApplication::activeWindow() );
116
117 bool defaultIsDocked = false;
118 QString dockId = u"ProcessingAlgorithm"_s;
119 if ( flags.testFlags( QgsProcessingAlgorithmWidgetBase::WidgetFlag::NoDocking ) )
120 {
122 dockId = u"ProcessingAlgorithmNonDockable"_s;
123 }
124 else if ( initialState == Qgis::DockableWidgetInitialState::ForceDocked )
125 {
126 dockId = u"ProcessingAlgorithmForceDocked"_s;
127 }
128
129 mDockableWidgetHelper
130 = new QgsDockableWidgetHelper( tr( "Processing" ), this, parentWindow, dockId, QStringList(), initialState, defaultIsDocked, Qt::DockWidgetArea::RightDockWidgetArea, QgsDockableWidgetHelper::Option::RaiseTab );
131 connect( mDockableWidgetHelper, &QgsDockableWidgetHelper::closed, this, &QgsProcessingAlgorithmWidgetBase::closeClicked );
132 mDockableWidgetHelper->setDefaultDialogSize( defaultDialogSize );
133
134 switch ( mMode )
135 {
136 case QgsProcessingAlgorithmWidgetBase::WidgetMode::Single:
137 {
138 mAdvancedButton = new QPushButton( tr( "Advanced" ) );
139 mAdvancedMenu = new QMenu( this );
140 mAdvancedButton->setMenu( mAdvancedMenu );
141
142 mContextSettingsAction = new QAction( tr( "Algorithm Settings…" ), mAdvancedMenu );
143 mContextSettingsAction->setIcon( QgsApplication::getThemeIcon( u"/propertyicons/settings.svg"_s ) );
144 mAdvancedMenu->addAction( mContextSettingsAction );
145
146 connect( mContextSettingsAction, &QAction::triggered, this, [this] {
147 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( mMainWidget ) )
148 {
149 mTabWidget->setCurrentIndex( 0 );
150
151 if ( !mContextOptionsWidget )
152 {
153 mContextOptionsWidget = new QgsProcessingContextOptionsWidget();
154 mContextOptionsWidget->setFromContext( processingContext() );
155 mContextOptionsWidget->setLogLevel( mLogLevel );
156 panel->openPanel( mContextOptionsWidget );
157
158 connect( mContextOptionsWidget, &QgsPanelWidget::changed, this, [this] {
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( u"mIconPythonFile.svg"_s ) );
174
175 mAdvancedMenu->addAction( copyAsPythonCommand );
176 connect( copyAsPythonCommand, &QAction::triggered, this, [this] {
177 if ( const QgsProcessingAlgorithm *alg = algorithm() )
178 {
179 QgsProcessingContext *context = processingContext();
180 if ( !context )
181 return;
182
183 const QString command = alg->asPythonCommand( createProcessingParameters(), *context );
184 QMimeData *m = new QMimeData();
185 m->setText( command );
186 QClipboard *cb = QApplication::clipboard();
187
188 if ( cb->supportsSelection() )
189 {
190 cb->setMimeData( m, QClipboard::Selection );
191 }
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( u"mActionTerminal.svg"_s ) );
198 mAdvancedMenu->addAction( mCopyAsQgisProcessCommand );
199
200 connect( mCopyAsQgisProcessCommand, &QAction::triggered, this, [this] {
201 if ( const QgsProcessingAlgorithm *alg = algorithm() )
202 {
203 QgsProcessingContext *context = processingContext();
204 if ( !context )
205 return;
206
207 bool ok = false;
208 const QString command = alg->asQgisProcessCommand( createProcessingParameters(), *context, ok );
209 if ( !ok )
210 {
211 mMessageBar->pushMessage( tr( "Current settings cannot be specified as arguments to qgis_process (Pipe parameters as JSON to qgis_process instead)" ), Qgis::MessageLevel::Warning );
212 }
213 else
214 {
215 QMimeData *m = new QMimeData();
216 m->setText( command );
217 QClipboard *cb = QApplication::clipboard();
218
219 if ( cb->supportsSelection() )
220 {
221 cb->setMimeData( m, QClipboard::Selection );
222 }
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( u"mActionEditCopy.svg"_s ) );
232
233 mAdvancedMenu->addAction( copyAsJson );
234 connect( copyAsJson, &QAction::triggered, this, [this] {
235 if ( const QgsProcessingAlgorithm *alg = algorithm() )
236 {
237 QgsProcessingContext *context = processingContext();
238 if ( !context )
239 return;
240
241 const QVariantMap properties = alg->asMap( createProcessingParameters(), *context );
242 const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( properties ).dump( 2 ) );
243
244 QMimeData *m = new QMimeData();
245 m->setText( json );
246 QClipboard *cb = QApplication::clipboard();
247
248 if ( cb->supportsSelection() )
249 {
250 cb->setMimeData( m, QClipboard::Selection );
251 }
252 cb->setMimeData( m, QClipboard::Clipboard );
253 }
254 } );
255
256 mPasteJsonAction = new QAction( tr( "Paste Settings" ), mAdvancedMenu );
257 mPasteJsonAction->setIcon( QgsApplication::getThemeIcon( u"mActionEditPaste.svg"_s ) );
258
259 mAdvancedMenu->addAction( mPasteJsonAction );
260 connect( mPasteJsonAction, &QAction::triggered, this, [this] {
261 const QString text = QApplication::clipboard()->text();
262 if ( text.isEmpty() )
263 return;
264
265 const QVariantMap parameterValues = QgsJsonUtils::parseJson( text ).toMap().value( u"inputs"_s ).toMap();
266 if ( parameterValues.isEmpty() )
267 return;
268
269 bool ok = false;
270 QString error;
271 const QVariantMap preparedValues = QgsProcessingUtils::preprocessQgisProcessParameters( parameterValues, ok, error );
272
273 setParameters( preparedValues );
274 } );
275
276 mButtonBox->addButton( mAdvancedButton, QDialogButtonBox::ResetRole );
277 break;
278 }
279
280 case QgsProcessingAlgorithmWidgetBase::WidgetMode::Batch:
281 break;
282 }
283
284 if ( mAdvancedMenu )
285 {
286 connect( mAdvancedMenu, &QMenu::aboutToShow, this, [this] {
287 mCopyAsQgisProcessCommand->setEnabled( algorithm() && !( algorithm()->flags() & Qgis::ProcessingAlgorithmFlag::NotAvailableInStandaloneTool ) );
288 mPasteJsonAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );
289 } );
290 }
291
292 connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::runAlgorithm );
293 connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::showParameters );
294 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmWidgetBase::closeClicked );
295 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmWidgetBase::openHelp );
296 connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::toggleCollapsed );
297 connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmWidgetBase::splitterChanged );
298
299 connect( mButtonSaveLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::saveLog );
300 connect( mButtonCopyLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::copyLogToClipboard );
301 connect( mButtonClearLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::clearLog );
302
303 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsProcessingAlgorithmWidgetBase::mTabWidget_currentChanged );
304
305 mMessageBar = new QgsMessageBar();
306 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
307 verticalLayout->insertWidget( 0, mMessageBar );
308
309 connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingAlgorithmWidgetBase::taskTriggered );
310}
311
312QgsProcessingAlgorithmWidgetBase::~QgsProcessingAlgorithmWidgetBase()
313{
314 delete mDockableWidgetHelper;
315}
316
317void QgsProcessingAlgorithmWidgetBase::setParameters( const QVariantMap & )
318{}
319
320void QgsProcessingAlgorithmWidgetBase::setTitle( const QString &title )
321{
322 mDockableWidgetHelper->setWindowTitle( title );
323}
324
325void QgsProcessingAlgorithmWidgetBase::exec()
326{
327 // when forcing the widget to show as a dialog, we use a distinct setting key
328 // to prevent the setting for freely dockable algorithm widgets from getting
329 // overridden, which would otherwise reset that setting so that the widgets
330 // are ALWAYS opened as dialogs
331 mDockableWidgetHelper->setSettingKeyDockId( u"ProcessingAlgorithmNonDockable"_s );
332 mDockableWidgetHelper->toggleDockMode( false );
333 mDockableWidgetHelper->dialog()->exec();
334}
335
336void QgsProcessingAlgorithmWidgetBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
337{
338 mAlgorithm.reset( algorithm );
339 QString title;
341 {
342 title = mAlgorithm->group().isEmpty()
343 ? QgsStringUtils::capitalize( mAlgorithm->displayName(), Qgis::Capitalization::TitleCase )
344 : u"%1 - %2"_s.arg( QgsStringUtils::capitalize( mAlgorithm->group(), Qgis::Capitalization::TitleCase ), QgsStringUtils::capitalize( mAlgorithm->displayName(), Qgis::Capitalization::TitleCase ) );
345 }
346 else
347 {
348 title = mAlgorithm->group().isEmpty() ? mAlgorithm->displayName() : u"%1 - %2"_s.arg( mAlgorithm->group(), mAlgorithm->displayName() );
349 }
350 mDockableWidgetHelper->setWindowTitle( title );
351
352 const QString algHelp = formatHelp( algorithm );
353 if ( algHelp.isEmpty() )
354 textShortHelp->hide();
355 else
356 {
357 textShortHelp->document()->setDefaultStyleSheet( QStringLiteral(
358 ".summary { margin-left: 10px; margin-right: 10px; }\n"
359 "h2 { color: #555555; padding-bottom: 15px; }\n"
360 "a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
361 "p, ul, li { color: #666666; }\n"
362 "b { color: #333333; }\n"
363 "dl dd { margin - bottom: 5px; }"
364 ) );
365 textShortHelp->setHtml( algHelp );
366 connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmWidgetBase::linkClicked );
367 textShortHelp->show();
368 }
369
370 if ( algorithm->helpUrl().isEmpty() && ( !algorithm->provider() || algorithm->provider()->helpId().isEmpty() ) )
371 {
372 mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
373 }
374
375 const QString warning = algorithm->provider() ? algorithm->provider()->warningMessage() : QString();
376 if ( !warning.isEmpty() )
377 {
378 mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
379 }
380}
381
382QgsProcessingAlgorithm *QgsProcessingAlgorithmWidgetBase::algorithm()
383{
384 return mAlgorithm.get();
385}
386
387void QgsProcessingAlgorithmWidgetBase::setMainWidget( QgsPanelWidget *widget )
388{
389 if ( mMainWidget )
390 {
391 mMainWidget->deleteLater();
392 }
393
394 mPanelStack->setMainPanel( widget );
395 widget->setDockMode( true );
396
397 mMainWidget = widget;
398 connect( mMainWidget, &QgsPanelWidget::panelAccepted, mDockableWidgetHelper, &QgsDockableWidgetHelper::reject );
399}
400
401QgsPanelWidget *QgsProcessingAlgorithmWidgetBase::mainWidget()
402{
403 return mMainWidget;
404}
405
406void QgsProcessingAlgorithmWidgetBase::saveLogToFile( const QString &path, const LogFormat format )
407{
408 QFile logFile( path );
409 if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
410 {
411 return;
412 }
413 QTextStream fout( &logFile );
414
415 switch ( format )
416 {
417 case QgsProcessingAlgorithmWidgetBase::LogFormat::FormatPlainText:
418 fout << txtLog->toPlainText();
419 break;
420
421 case QgsProcessingAlgorithmWidgetBase::LogFormat::FormatHtml:
422 fout << txtLog->toHtml();
423 break;
424 }
425}
426
427void QgsProcessingAlgorithmWidgetBase::registerProcessingFeedbackGenerator( QgsProcessingFeedbackGenerator *factory )
428{
429 mFeedbackFactory = factory;
430}
431
432QgsProcessingFeedback *QgsProcessingAlgorithmWidgetBase::createFeedback()
433{
434 std::unique_ptr< QgsProcessingFeedback > feedback;
435 if ( mFeedbackFactory )
436 {
437 feedback.reset( mFeedbackFactory->createFeedback() );
438 }
439 if ( !feedback )
440 {
441 feedback = std::make_unique< QgsProcessingFeedback >();
442 }
443 connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmWidgetBase::setPercentage );
444 connect( feedback.get(), &QgsProcessingFeedback::commandInfoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushCommandInfo );
445 connect( feedback.get(), &QgsProcessingFeedback::consoleInfoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushConsoleInfo );
446 connect( feedback.get(), &QgsProcessingFeedback::debugInfoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushDebugInfo );
447 connect( feedback.get(), &QgsProcessingFeedback::errorReported, this, &QgsProcessingAlgorithmWidgetBase::reportError );
448 connect( feedback.get(), &QgsProcessingFeedback::warningPushed, this, &QgsProcessingAlgorithmWidgetBase::pushWarning );
449 connect( feedback.get(), &QgsProcessingFeedback::infoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushInfo );
450 connect( feedback.get(), &QgsProcessingFeedback::formattedMessagePushed, this, &QgsProcessingAlgorithmWidgetBase::pushFormattedMessage );
451 connect( feedback.get(), &QgsProcessingFeedback::progressTextChanged, this, &QgsProcessingAlgorithmWidgetBase::setProgressText );
452 connect( this, &QgsProcessingAlgorithmWidgetBase::cancelRequested, feedback.get(), &QgsProcessingFeedback::cancel );
453 return feedback.release();
454}
455
456QDialogButtonBox *QgsProcessingAlgorithmWidgetBase::buttonBox()
457{
458 return mButtonBox;
459}
460
461QTabWidget *QgsProcessingAlgorithmWidgetBase::tabWidget()
462{
463 return mTabWidget;
464}
465
466void QgsProcessingAlgorithmWidgetBase::showLog()
467{
468 mTabWidget->setCurrentIndex( 1 );
469}
470
471void QgsProcessingAlgorithmWidgetBase::showParameters()
472{
473 mTabWidget->setCurrentIndex( 0 );
474}
475
476QPushButton *QgsProcessingAlgorithmWidgetBase::runButton()
477{
478 return mButtonRun;
479}
480
481QPushButton *QgsProcessingAlgorithmWidgetBase::cancelButton()
482{
483 return buttonCancel;
484}
485
486QPushButton *QgsProcessingAlgorithmWidgetBase::changeParametersButton()
487{
488 return mButtonChangeParameters;
489}
490
491void QgsProcessingAlgorithmWidgetBase::clearProgress()
492{
493 progressBar->setMaximum( 0 );
494}
495
496void QgsProcessingAlgorithmWidgetBase::setExecuted( bool executed )
497{
498 mExecuted = executed;
499}
500
501void QgsProcessingAlgorithmWidgetBase::setExecutedAnyResult( bool executedAnyResult )
502{
503 mExecutedAnyResult = executedAnyResult;
504}
505
506void QgsProcessingAlgorithmWidgetBase::setResults( const QVariantMap &results )
507{
508 mResults = results;
509}
510
511void QgsProcessingAlgorithmWidgetBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
512{}
513
514void QgsProcessingAlgorithmWidgetBase::openHelp()
515{
516 QUrl algHelp = mAlgorithm->helpUrl();
517 if ( algHelp.isEmpty() && mAlgorithm->provider() && !mAlgorithm->provider()->helpId().isEmpty() )
518 {
519 algHelp = QgsHelp::helpUrl(
520 u"processing_algs/%1/%2.html#%3"_s.arg( mAlgorithm->provider()->helpId(), mAlgorithm->groupId(), u"%1%2"_s.arg( mAlgorithm->provider()->helpId() ).arg( mAlgorithm->name().replace( "_", "-" ) ) )
521 );
522 }
523
524 if ( !algHelp.isEmpty() )
525 QDesktopServices::openUrl( algHelp );
526}
527
528void QgsProcessingAlgorithmWidgetBase::toggleCollapsed()
529{
530 if ( mHelpCollapsed )
531 {
532 splitter->restoreState( mSplitterState );
533 mButtonCollapse->setArrowType( Qt::RightArrow );
534 }
535 else
536 {
537 mSplitterState = splitter->saveState();
538 splitter->setSizes( QList<int>() << 1 << 0 );
539 mButtonCollapse->setArrowType( Qt::LeftArrow );
540 }
541 mHelpCollapsed = !mHelpCollapsed;
542}
543
544void QgsProcessingAlgorithmWidgetBase::splitterChanged( int, int )
545{
546 if ( splitter->sizes().at( 1 ) == 0 )
547 {
548 mHelpCollapsed = true;
549 mButtonCollapse->setArrowType( Qt::LeftArrow );
550 }
551 else
552 {
553 mHelpCollapsed = false;
554 mButtonCollapse->setArrowType( Qt::RightArrow );
555 }
556}
557
558void QgsProcessingAlgorithmWidgetBase::mTabWidget_currentChanged( int )
559{
560 updateRunButtonVisibility();
561}
562
563void QgsProcessingAlgorithmWidgetBase::linkClicked( const QUrl &url )
564{
565 if ( url.toString() == "#help"_L1 )
566 {
567 openHelp();
568 return;
569 }
570
571 QDesktopServices::openUrl( url.toString() );
572}
573
574void QgsProcessingAlgorithmWidgetBase::algExecuted( bool successful, const QVariantMap & )
575{
576 mAlgorithmTask = nullptr;
577
578 if ( !successful )
579 {
580 // show widget to display errors
581 showWidget();
582 showLog();
583 }
584 else
585 {
586 if ( isFinalized() && successful )
587 {
588 progressBar->setFormat( tr( "Complete" ) );
589 }
590
591 // delete widget if closed
592 if ( isFinalized() && !isVisible() )
593 {
594 deleteLater();
595 }
596 }
597}
598
599void QgsProcessingAlgorithmWidgetBase::taskTriggered( QgsTask *task )
600{
601 if ( task == mAlgorithmTask )
602 {
603 showWidget();
604 showLog();
605 }
606}
607
608void QgsProcessingAlgorithmWidgetBase::showWidget()
609{
610 mDockableWidgetHelper->setUserVisible( true );
611}
612
613void QgsProcessingAlgorithmWidgetBase::closeClicked()
614{
615 disconnect( mDockableWidgetHelper, &QgsDockableWidgetHelper::closed, this, &QgsProcessingAlgorithmWidgetBase::closeClicked );
616
617 if ( isRunning() )
618 {
619 mDockableWidgetHelper->setUserVisible( false );
620 }
621 else
622 {
623 reject();
624 close();
625 }
626}
627
628void QgsProcessingAlgorithmWidgetBase::urlClicked( const QUrl &url )
629{
630 const QFileInfo file( url.toLocalFile() );
631 if ( file.exists() && !file.isDir() )
632 {
633 if ( file.suffix().compare( "html"_L1, Qt::CaseInsensitive ) == 0 )
634 {
635 QDesktopServices::openUrl( url );
636 }
637 else
638 {
639 QgsGui::nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
640 }
641 }
642 else
643 QDesktopServices::openUrl( url );
644}
645
646Qgis::ProcessingLogLevel QgsProcessingAlgorithmWidgetBase::logLevel() const
647{
648 return mLogLevel;
649}
650
651void QgsProcessingAlgorithmWidgetBase::setLogLevel( Qgis::ProcessingLogLevel level )
652{
653 mLogLevel = level;
654}
655
656void QgsProcessingAlgorithmWidgetBase::reportError( const QString &error, bool fatalError )
657{
658 setInfo( error, true );
659 if ( fatalError )
660 resetGui();
661 showLog();
662 processEvents();
663}
664
665void QgsProcessingAlgorithmWidgetBase::pushWarning( const QString &warning )
666{
667 setInfo( warning, false, true, true );
668 processEvents();
669}
670
671void QgsProcessingAlgorithmWidgetBase::pushInfo( const QString &info )
672{
673 setInfo( info );
674 processEvents();
675}
676
677void QgsProcessingAlgorithmWidgetBase::pushFormattedMessage( const QString &html )
678{
679 setInfo( html, false, false );
680 processEvents();
681}
682
683void QgsProcessingAlgorithmWidgetBase::pushCommandInfo( const QString &command )
684{
685 txtLog->append( u"<code>%1<code>"_s.arg( formatStringForLog( command.toHtmlEscaped() ) ) );
686 scrollToBottomOfLog();
687 processEvents();
688}
689
690void QgsProcessingAlgorithmWidgetBase::pushDebugInfo( const QString &message )
691{
692 txtLog->append( u"<span style=\"color:#777\">%1</span>"_s.arg( formatStringForLog( message.toHtmlEscaped() ) ) );
693 scrollToBottomOfLog();
694 processEvents();
695}
696
697void QgsProcessingAlgorithmWidgetBase::pushConsoleInfo( const QString &info )
698{
699 txtLog->append( u"<code style=\"color:#777\">%1</code>"_s.arg( formatStringForLog( info.toHtmlEscaped() ) ) );
700 scrollToBottomOfLog();
701 processEvents();
702}
703
704QDialog *QgsProcessingAlgorithmWidgetBase::createProgressDialog()
705{
706 QgsProcessingAlgorithmProgressDialog *dialog = new QgsProcessingAlgorithmProgressDialog( this );
707 dialog->setWindowModality( Qt::ApplicationModal );
708 dialog->setWindowTitle( windowTitle() );
709 dialog->setGeometry( geometry() ); // match size/position to this dialog
710 connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
711 connect( dialog->cancelButton(), &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::cancel );
712 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
713 connect( txtLog, &QTextEdit::textChanged, dialog, [this, dialog]() {
714 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
715 QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
716 sb->setValue( sb->maximum() );
717 } );
718 return dialog;
719}
720
721void QgsProcessingAlgorithmWidgetBase::clearLog()
722{
723 txtLog->clear();
724}
725
726void QgsProcessingAlgorithmWidgetBase::saveLog()
727{
728 QgsSettings settings;
729 const QString lastUsedDir = settings.value( u"/Processing/lastUsedLogDirectory"_s, QDir::homePath() ).toString();
730
731 QString filter;
732 const QString txtExt = tr( "Text files" ) + u" (*.txt *.TXT)"_s;
733 const QString htmlExt = tr( "HTML files" ) + u" (*.html *.HTML)"_s;
734
735 const QString path = QFileDialog::getSaveFileName( this, tr( "Save Log to File" ), lastUsedDir, txtExt + ";;" + htmlExt, &filter );
736 // return dialog focus on Mac
737 activateWindow();
738 raise();
739 if ( path.isEmpty() )
740 {
741 return;
742 }
743
744 settings.setValue( u"/Processing/lastUsedLogDirectory"_s, QFileInfo( path ).path() );
745
746 LogFormat format = QgsProcessingAlgorithmWidgetBase::LogFormat::FormatPlainText;
747 if ( filter == htmlExt )
748 {
749 format = QgsProcessingAlgorithmWidgetBase::LogFormat::FormatHtml;
750 }
751 saveLogToFile( path, format );
752}
753
754void QgsProcessingAlgorithmWidgetBase::copyLogToClipboard()
755{
756 QMimeData *m = new QMimeData();
757 m->setText( txtLog->toPlainText() );
758 m->setHtml( txtLog->toHtml() );
759 QClipboard *cb = QApplication::clipboard();
760
761 if ( cb->supportsSelection() )
762 {
763 cb->setMimeData( m, QClipboard::Selection );
764 }
765 cb->setMimeData( m, QClipboard::Clipboard );
766}
767
768void QgsProcessingAlgorithmWidgetBase::closeEvent( QCloseEvent *e )
769{
770 if ( !mHelpCollapsed )
771 {
772 QgsSettings settings;
773 settings.setValue( u"/Processing/dialogBaseSplitter"_s, splitter->saveState() );
774 }
775
776 QWidget::closeEvent( e );
777
778 if ( !mAlgorithmTask && isFinalized() )
779 {
780 // when running a background task, the dialog is kept around and deleted only when the task
781 // completes. But if not running a task, we auto cleanup (later - gotta give callers a chance
782 // to retrieve results and execution status).
783 deleteLater();
784 }
785}
786
787void QgsProcessingAlgorithmWidgetBase::runAlgorithm()
788{}
789
790void QgsProcessingAlgorithmWidgetBase::setPercentage( double percent )
791{
792 // delay setting maximum progress value until we know algorithm reports progress
793 if ( progressBar->maximum() == 0 )
794 progressBar->setMaximum( 100 );
795 progressBar->setValue( percent );
796 processEvents();
797}
798
799void QgsProcessingAlgorithmWidgetBase::setProgressText( const QString &text )
800{
801 lblProgress->setText( text );
802 setInfo( text, false );
803 scrollToBottomOfLog();
804 processEvents();
805}
806
807QString QgsProcessingAlgorithmWidgetBase::formatHelp( QgsProcessingAlgorithm *algorithm )
808{
809 QString result;
810 const QString text = algorithm->shortHelpString();
811 if ( !text.isEmpty() )
812 {
813 const QStringList paragraphs = text.split( '\n' );
814 QString help;
815 for ( const QString &paragraph : paragraphs )
816 {
817 help += u"<p>%1</p>"_s.arg( paragraph );
818 }
819 result = u"<h2>%1</h2>%2"_s.arg( algorithm->displayName(), help );
820 }
821 else if ( !algorithm->shortDescription().isEmpty() )
822 {
823 result = u"<h2>%1</h2><p>%2</p>"_s.arg( algorithm->displayName(), algorithm->shortDescription() );
824 }
825
826 const QList< QgsAcademicReference > references = algorithm->academicReferences();
827 if ( !references.empty() )
828 {
829 QStringList referenceStrings;
830 for ( const QgsAcademicReference &reference : references )
831 {
832 referenceStrings << reference.asHtml();
833 }
834 result += u"<h4>%1</h4>"_s.arg( tr( "References" ) );
835 result += u"<ul><li>%1</li></ul>"_s.arg( referenceStrings.join( "</li><li>"_L1 ) );
836 }
837
838 if ( algorithm->documentationFlags() != Qgis::ProcessingAlgorithmDocumentationFlags() )
839 {
840 QStringList flags;
842 {
843 if ( algorithm->documentationFlags() & flag )
844 {
846 }
847 }
848 result += u"<ul><li><i>%1</i></li></ul>"_s.arg( flags.join( "</i></li><li><i>"_L1 ) );
849 }
851 {
852 result += u"<p><b>%1</b></p>"_s.arg( tr( "Warning: This algorithm is a potential security risk if executed with unchecked inputs, and may result in system damage or data leaks." ) );
853 }
855 {
856 result += u"<p><b>%1</b></p>"_s.arg( tr( "Warning: This algorithm has known issues. The results must be carefully validated by the user." ) );
857 }
858
859 QStringList links;
860 if ( !algorithm->helpUrl().isEmpty() || ( algorithm->provider() && !algorithm->provider()->helpId().isEmpty() ) )
861 {
862 // DO NOT resolve the help url here using QgsHelp::helpUrl -- that is VERY slow as it triggers a network
863 // request. Defer this until the link is actually clicked.
864 const QString linkHtml = QStringLiteral( R"(<a href="#help">%1</a>)" ).arg( tr( "Algorithm documentation" ) );
865 links << linkHtml;
866 }
867
868 const QString implementationSourceUri = algorithm->implementationSourceUri();
869 if ( !implementationSourceUri.isEmpty() )
870 {
871 const QString linkHtml = QStringLiteral( R"(<a href="%1">%2</a>)" ).arg( implementationSourceUri, tr( "Algorithm source code" ) );
872 links << linkHtml;
873 }
874
875 const QList< QgsProcessingAlgorithm::ExternalLink > externalLinks = algorithm->externalLinks();
876 for ( const QgsProcessingAlgorithm::ExternalLink &link : externalLinks )
877 {
878 const QString linkHtml = QStringLiteral( R"(<a href="%1">%2</a>)" ).arg( link.url, link.description );
879 links << linkHtml;
880 }
881
882 if ( !links.empty() )
883 {
884 result += u"<h4>%1</h4>"_s.arg( tr( "Links" ) );
885 result += u"<ul><li>%1</li></ul>"_s.arg( links.join( "</li><li>"_L1 ) );
886 }
887
888 return result;
889}
890
891void QgsProcessingAlgorithmWidgetBase::processEvents()
892{
893 if ( mAlgorithmTask )
894 {
895 // no need to call this - the algorithm is running in a thread.
896 // in fact, calling it causes a crash on Windows when the algorithm
897 // is running in a background thread... unfortunately we need something
898 // like this for non-threadable algorithms, otherwise there's no chance
899 // for users to hit cancel or see progress updates...
900 return;
901 }
902
903 // So that we get a chance of hitting the Abort button
904#ifdef Q_OS_LINUX
905 // One iteration is actually enough on Windows to get good interactivity
906 // whereas on Linux we must allow for far more iterations.
907 // For safety limit the number of iterations
908 int nIters = 0;
909 while ( ++nIters < 100 )
910#endif
911 {
912 QCoreApplication::processEvents();
913 }
914}
915
916void QgsProcessingAlgorithmWidgetBase::scrollToBottomOfLog()
917{
918 QScrollBar *sb = txtLog->verticalScrollBar();
919 sb->setValue( sb->maximum() );
920}
921
922void QgsProcessingAlgorithmWidgetBase::resetGui()
923{
924 lblProgress->clear();
925 progressBar->setMaximum( 100 );
926 progressBar->setValue( 0 );
927 mButtonRun->setEnabled( true );
928 mButtonChangeParameters->setEnabled( true );
929 mButtonClose->setEnabled( true );
930 if ( mMainWidget )
931 {
932 mMainWidget->setEnabled( true );
933 }
934 updateRunButtonVisibility();
935 resetAdditionalGui();
936}
937
938void QgsProcessingAlgorithmWidgetBase::updateRunButtonVisibility()
939{
940 // Activate run button if current tab is Parameters
941 const bool runButtonVisible = mTabWidget->currentIndex() == 0;
942 mButtonRun->setVisible( runButtonVisible );
943 if ( runButtonVisible )
944 progressBar->resetFormat();
945 mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
946}
947
948void QgsProcessingAlgorithmWidgetBase::resetAdditionalGui()
949{}
950
951void QgsProcessingAlgorithmWidgetBase::blockControlsWhileRunning()
952{
953 mButtonRun->setEnabled( false );
954 mButtonChangeParameters->setEnabled( false );
955 if ( mMainWidget )
956 {
957 mMainWidget->setEnabled( false );
958 }
959 blockAdditionalControlsWhileRunning();
960}
961
962void QgsProcessingAlgorithmWidgetBase::blockAdditionalControlsWhileRunning()
963{}
964
965QgsMessageBar *QgsProcessingAlgorithmWidgetBase::messageBar()
966{
967 return mMessageBar;
968}
969
970void QgsProcessingAlgorithmWidgetBase::hideShortHelp()
971{
972 textShortHelp->setVisible( false );
973}
974
975void QgsProcessingAlgorithmWidgetBase::setCurrentTask( QgsProcessingAlgRunnerTask *task )
976{
977 mAlgorithmTask = task;
978 connect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmWidgetBase::algExecuted );
979 QgsApplication::taskManager()->addTask( mAlgorithmTask );
980}
981
982void QgsProcessingAlgorithmWidgetBase::disconnectCurrentTask()
983{
984 if ( mAlgorithmTask )
985 {
986 disconnect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmWidgetBase::algExecuted );
987 mAlgorithmTask = nullptr;
988 }
989}
990
991QString QgsProcessingAlgorithmWidgetBase::formatStringForLog( const QString &string )
992{
993 QString s = string;
994 s.replace( '\n', "<br>"_L1 );
995 return s;
996}
997
998bool QgsProcessingAlgorithmWidgetBase::isFinalized()
999{
1000 return true;
1001}
1002
1003bool QgsProcessingAlgorithmWidgetBase::isRunning()
1004{
1005 return false;
1006}
1007
1008void QgsProcessingAlgorithmWidgetBase::cancel()
1009{
1010 emit cancelRequested();
1011}
1012
1013void QgsProcessingAlgorithmWidgetBase::applyContextOverrides( QgsProcessingContext *context )
1014{
1015 if ( !context )
1016 return;
1017
1018 context->setLogLevel( logLevel() );
1019
1020 if ( mOverrideDefaultContextSettings )
1021 {
1022 context->setInvalidGeometryCheck( mGeometryCheck );
1023 context->setDistanceUnit( mDistanceUnits );
1024 context->setAreaUnit( mAreaUnits );
1025 context->setTemporaryFolder( mTemporaryFolderOverride );
1026 context->setMaximumThreads( mMaximumThreads );
1027 }
1028}
1029
1030void QgsProcessingAlgorithmWidgetBase::setInfo( const QString &message, bool isError, bool escapeHtml, bool isWarning )
1031{
1032 constexpr int MESSAGE_COUNT_LIMIT = 10000;
1033 // Avoid logging too many messages, which might blow memory.
1034 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
1035 return;
1036 ++mMessageLoggedCount;
1037
1038 // note -- we have to wrap the message in a span block, or QTextEdit::append sometimes gets confused
1039 // and varies between treating it as a HTML string or a plain text string! (see https://github.com/qgis/QGIS/issues/37934)
1040 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
1041 txtLog->append( u"<span style=\"color:red\">%1</span>"_s.arg( tr( "Message log truncated" ) ) );
1042 else if ( isError || isWarning )
1043 txtLog->append( u"<span style=\"color:%1\">%2</span>"_s.arg( isError ? u"red"_s : u"#b85a20"_s, escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
1044 else if ( escapeHtml )
1045 txtLog->append( u"<span>%1</span"_s.arg( formatStringForLog( message.toHtmlEscaped() ) ) );
1046 else
1047 txtLog->append( u"<span>%1</span>"_s.arg( formatStringForLog( message ) ) );
1048 scrollToBottomOfLog();
1049 processEvents();
1050}
1051
1052void QgsProcessingAlgorithmWidgetBase::reject()
1053{
1054 if ( !mAlgorithmTask && isFinalized() )
1055 {
1056 setAttribute( Qt::WA_DeleteOnClose );
1057 }
1058
1059 mDockableWidgetHelper->reject();
1060}
1061
1062void QgsProcessingAlgorithmWidgetBase::forceClose()
1063{
1064 disconnectCurrentTask();
1065 reject();
1066}
1067
1068//
1069// QgsProcessingAlgorithmProgressDialog
1070//
1071
1072QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
1073 : QDialog( parent )
1074{
1075 setupUi( this );
1076}
1077
1078QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
1079{
1080 return mProgressBar;
1081}
1082
1083QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
1084{
1085 return mButtonBox->button( QDialogButtonBox::Cancel );
1086}
1087
1088QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
1089{
1090 return mTxtLog;
1091}
1092
1093void QgsProcessingAlgorithmProgressDialog::reject()
1094{}
1095
1096
1097//
1098// QgsProcessingContextOptionsWidget
1099//
1100
1101QgsProcessingContextOptionsWidget::QgsProcessingContextOptionsWidget( QWidget *parent )
1102 : QgsPanelWidget( parent )
1103{
1104 setupUi( this );
1105 setPanelTitle( tr( "Algorithm Settings" ) );
1106
1107 mComboInvalidFeatureFiltering->addItem( tr( "Do not Filter (Better Performance)" ), QVariant::fromValue( Qgis::InvalidGeometryCheck::NoCheck ) );
1108 mComboInvalidFeatureFiltering->addItem( tr( "Skip (Ignore) Features with Invalid Geometries" ), QVariant::fromValue( Qgis::InvalidGeometryCheck::SkipInvalid ) );
1109 mComboInvalidFeatureFiltering->addItem( tr( "Stop Algorithm Execution When a Geometry is Invalid" ), QVariant::fromValue( Qgis::InvalidGeometryCheck::AbortOnInvalid ) );
1110
1111 mTemporaryFolderWidget->setDialogTitle( tr( "Select Temporary Directory" ) );
1112 mTemporaryFolderWidget->setStorageMode( QgsFileWidget::GetDirectory );
1113 mTemporaryFolderWidget->lineEdit()->setPlaceholderText( tr( "Default" ) );
1114
1115 mLogLevelComboBox->addItem( tr( "Default" ), static_cast<int>( Qgis::ProcessingLogLevel::DefaultLevel ) );
1116 mLogLevelComboBox->addItem( tr( "Verbose" ), static_cast<int>( Qgis::ProcessingLogLevel::Verbose ) );
1117 mLogLevelComboBox->addItem( tr( "Verbose (Model Debugging)" ), static_cast<int>( Qgis::ProcessingLogLevel::ModelDebug ) );
1118
1119 mDistanceUnitsCombo->addItem( tr( "Default" ), QVariant::fromValue( Qgis::DistanceUnit::Unknown ) );
1120 for ( Qgis::DistanceUnit unit : {
1131 } )
1132 {
1133 QString title;
1135 {
1137 }
1138 else
1139 {
1140 title = QgsUnitTypes::toString( unit );
1141 }
1142
1143 mDistanceUnitsCombo->addItem( title, QVariant::fromValue( unit ) );
1144 }
1145
1146 mAreaUnitsCombo->addItem( tr( "Default" ), QVariant::fromValue( Qgis::AreaUnit::Unknown ) );
1147 for ( Qgis::AreaUnit unit : {
1160 } )
1161 {
1162 QString title;
1164 {
1166 }
1167 else
1168 {
1169 title = QgsUnitTypes::toString( unit );
1170 }
1171
1172 mAreaUnitsCombo->addItem( title, QVariant::fromValue( unit ) );
1173 }
1174
1175 mThreadsSpinBox->setRange( 1, QThread::idealThreadCount() );
1176
1177 connect( mLogLevelComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1178 connect( mComboInvalidFeatureFiltering, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1179 connect( mDistanceUnitsCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1180 connect( mAreaUnitsCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1181 connect( mTemporaryFolderWidget, &QgsFileWidget::fileChanged, this, &QgsPanelWidget::changed );
1182 connect( mThreadsSpinBox, qOverload<int>( &QSpinBox::valueChanged ), this, &QgsPanelWidget::changed );
1183}
1184
1185void QgsProcessingContextOptionsWidget::setFromContext( const QgsProcessingContext *context )
1186{
1187 whileBlocking( mComboInvalidFeatureFiltering )->setCurrentIndex( mComboInvalidFeatureFiltering->findData( QVariant::fromValue( context->invalidGeometryCheck() ) ) );
1188 whileBlocking( mDistanceUnitsCombo )->setCurrentIndex( mDistanceUnitsCombo->findData( QVariant::fromValue( context->distanceUnit() ) ) );
1189 whileBlocking( mAreaUnitsCombo )->setCurrentIndex( mAreaUnitsCombo->findData( QVariant::fromValue( context->areaUnit() ) ) );
1190 whileBlocking( mTemporaryFolderWidget )->setFilePath( context->temporaryFolder() );
1191 whileBlocking( mThreadsSpinBox )->setValue( context->maximumThreads() );
1192 whileBlocking( mLogLevelComboBox )->setCurrentIndex( mLogLevelComboBox->findData( static_cast<int>( context->logLevel() ) ) );
1193}
1194
1195Qgis::InvalidGeometryCheck QgsProcessingContextOptionsWidget::invalidGeometryCheck() const
1196{
1197 return mComboInvalidFeatureFiltering->currentData().value<Qgis::InvalidGeometryCheck>();
1198}
1199
1200Qgis::DistanceUnit QgsProcessingContextOptionsWidget::distanceUnit() const
1201{
1202 return mDistanceUnitsCombo->currentData().value<Qgis::DistanceUnit>();
1203}
1204
1205Qgis::AreaUnit QgsProcessingContextOptionsWidget::areaUnit() const
1206{
1207 return mAreaUnitsCombo->currentData().value<Qgis::AreaUnit>();
1208}
1209
1210QString QgsProcessingContextOptionsWidget::temporaryFolder()
1211{
1212 return mTemporaryFolderWidget->filePath();
1213}
1214
1215int QgsProcessingContextOptionsWidget::maximumThreads() const
1216{
1217 return mThreadsSpinBox->value();
1218}
1219
1220void QgsProcessingContextOptionsWidget::setLogLevel( Qgis::ProcessingLogLevel level )
1221{
1222 whileBlocking( mLogLevelComboBox )->setCurrentIndex( mLogLevelComboBox->findData( static_cast<int>( level ) ) );
1223}
1224
1225Qgis::ProcessingLogLevel QgsProcessingContextOptionsWidget::logLevel() const
1226{
1227 return static_cast<Qgis::ProcessingLogLevel>( mLogLevelComboBox->currentData().toInt() );
1228}
1229
1230
1231//
1232// QgsProcessingAlgorithmWidget
1233//
1234
1235QgsProcessingAlgorithmWidget::QgsProcessingAlgorithmWidget( QgsProcessingAlgorithm *algorithm, bool inPlace, QMainWindow *parentWindow, WidgetFlags flags, Qgis::DockableWidgetInitialState initialState )
1236 : QgsProcessingAlgorithmWidgetBase( parentWindow, WidgetMode::Single, flags, initialState )
1237 , mInPlace( inPlace )
1238{
1239 setAlgorithm( algorithm );
1240
1241 connect( buttonBox(), &QDialogButtonBox::accepted, this, [this] {
1242 for ( QWidget *widget : std::as_const( mWidgetsToResetPalette ) )
1243 {
1244 widget->setPalette( QPalette() );
1245 }
1246 mWidgetsToResetPalette.clear();
1247 } );
1248}
1249
1250QgsProcessingParametersWidget *QgsProcessingAlgorithmWidget::createParametersPanel( bool inPlaceMode, QgsVectorLayer *activeLayer, QgsMessageBar *messageBar )
1251{
1252 auto widget = new QgsProcessingParametersWidget( algorithm(), inPlaceMode, activeLayer, messageBar );
1253 widget->registerProcessingContextGenerator( this );
1254 widget->initWidgets();
1255 return widget;
1256}
1257
1258QgsProcessingParametersWidget *QgsProcessingAlgorithmWidget::parametersWidget()
1259{
1260 return qobject_cast< QgsProcessingParametersWidget * >( mainWidget() );
1261}
1262
1263void QgsProcessingAlgorithmWidget::flagInvalidParameterValue( const QString &message, QWidget *widget )
1264{
1265 if ( !widget )
1266 return;
1267
1268 QPalette palette = widget->palette();
1269 palette.setColor( QPalette::ColorRole::Base, QColor( 255, 255, 0 ) );
1270 widget->setPalette( palette );
1271 mWidgetsToResetPalette.append( widget );
1272
1273 messageBar()->clearWidgets();
1274 messageBar()->pushMessage( QString(), tr( "Wrong or missing parameter value: %1" ).arg( message ), Qgis::MessageLevel::Warning, 5 );
1275}
1276
1277void QgsProcessingAlgorithmWidget::flagInvalidOutputExtension( const QString &message, QWidget *widget )
1278{
1279 if ( !widget )
1280 return;
1281
1282 QPalette palette = widget->palette();
1283 palette.setColor( QPalette::ColorRole::Base, QColor( 255, 255, 0 ) );
1284 widget->setPalette( palette );
1285 mWidgetsToResetPalette.append( widget );
1286
1287 messageBar()->clearWidgets();
1288 messageBar()->pushMessage( QString(), message, Qgis::MessageLevel::Warning, 5 );
1289}
1290
1291bool QgsProcessingAlgorithmWidget::handleValidationResults( const QList<ParameterValidationResult> &results )
1292{
1293 bool allValid = true;
1294 QgsProcessingParametersWidget *widget = parametersWidget();
1295 for ( const ParameterValidationResult &result : results )
1296 {
1297 QgsAbstractProcessingParameterWidgetWrapper *wrapper = widget ? widget->wrapper( result.parameterName ) : nullptr;
1298 QWidget *widget = wrapper ? wrapper->wrappedWidget() : nullptr;
1299
1300 const QgsProcessingParameterDefinition *parameter = algorithm()->parameterDefinition( result.parameterName );
1301 switch ( result.result )
1302 {
1304 break;
1306 flagInvalidOutputExtension( result.message, widget );
1307 allValid = false;
1308 break;
1310 flagInvalidParameterValue( parameter ? parameter->description() : QString(), widget );
1311 allValid = false;
1312 break;
1313 }
1314 }
1315
1316 return allValid;
1317}
1318
1319bool QgsProcessingAlgorithmWidget::inPlace() const
1320{
1321 return mInPlace;
1322}
1323
1324void QgsProcessingAlgorithmWidget::setParameters( const QVariantMap &values )
1325{
1326 if ( QgsProcessingParametersWidget *widget = parametersWidget() )
1327 {
1328 widget->setParameters( values );
1329 }
1330}
1331
1332QVariantMap QgsProcessingAlgorithmWidget::createProcessingParameters( Flags flags )
1333{
1334 QgsProcessingParametersWidget *widget = parametersWidget();
1335 if ( !widget )
1336 return {};
1337
1338 QList<ParameterValidationResult> validationResults;
1339 const QVariantMap parameters = widget->createAndValidateParameters( flags, validationResults );
1340
1341 const bool allValid = handleValidationResults( validationResults );
1342
1343 return allValid ? parameters : QVariantMap();
1344}
1345
1346QgsProcessingContext *QgsProcessingAlgorithmWidget::processingContext() const
1347{
1348 if ( !mContext )
1349 {
1350 mContext.reset( QgsGui::processingGuiRegistry()->contextFactory() ? QgsGui::processingGuiRegistry()->contextFactory()->createContext() : new QgsProcessingContext() );
1351 }
1352
1353 if ( !mFeedback )
1354 {
1355 mFeedback.reset( const_cast< QgsProcessingAlgorithmWidget * >( this )->createFeedback() );
1356 }
1357 mContext->setFeedback( mFeedback.get() );
1358
1359 const_cast< QgsProcessingAlgorithmWidget * >( this )->applyContextOverrides( mContext.get() );
1360 return mContext.get();
1361}
1362
1363
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
DistanceUnit
Units of distance.
Definition qgis.h:5568
@ Feet
Imperial feet.
Definition qgis.h:5571
@ Centimeters
Centimeters.
Definition qgis.h:5576
@ Millimeters
Millimeters.
Definition qgis.h:5577
@ Miles
Terrestrial miles.
Definition qgis.h:5574
@ Meters
Meters.
Definition qgis.h:5569
@ Unknown
Unknown distance unit.
Definition qgis.h:5618
@ Yards
Imperial yards.
Definition qgis.h:5573
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5575
@ Inches
Inches.
Definition qgis.h:5578
@ NauticalMiles
Nautical miles.
Definition qgis.h:5572
@ Kilometers
Kilometers.
Definition qgis.h:5570
AreaUnit
Units of area.
Definition qgis.h:5645
@ Acres
Acres.
Definition qgis.h:5652
@ SquareFeet
Square feet.
Definition qgis.h:5648
@ SquareCentimeters
Square centimeters.
Definition qgis.h:5655
@ SquareInches
Square inches.
Definition qgis.h:5657
@ SquareNauticalMiles
Square nautical miles.
Definition qgis.h:5653
@ SquareMillimeters
Square millimeters.
Definition qgis.h:5656
@ SquareYards
Square yards.
Definition qgis.h:5649
@ Hectares
Hectares.
Definition qgis.h:5651
@ SquareKilometers
Square kilometers.
Definition qgis.h:5647
@ SquareMeters
Square meters.
Definition qgis.h:5646
@ Unknown
Unknown areal unit.
Definition qgis.h:5658
@ SquareDegrees
Square degrees, for planar geographic CRS area measurements.
Definition qgis.h:5654
@ SquareMiles
Square miles.
Definition qgis.h:5650
@ Warning
Warning message.
Definition qgis.h:162
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
Definition qgis.h:3633
ProcessingAlgorithmDocumentationFlag
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3857
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3869
InvalidGeometryCheck
Methods for handling of features with invalid geometries.
Definition qgis.h:2423
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2424
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2426
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2425
@ NotAvailableInStandaloneTool
Algorithm should not be available from the standalone "qgis_process" tool. Used to flag algorithms wh...
Definition qgis.h:3834
@ SecurityRisk
The algorithm represents a potential security risk if executed with untrusted inputs.
Definition qgis.h:3836
@ DisplayNameIsLiteral
Algorithm's display name is a static literal string, and should not be translated or automatically fo...
Definition qgis.h:3828
@ KnownIssues
Algorithm has known issues.
Definition qgis.h:3830
DockableWidgetInitialState
Dockable widget initial states.
Definition qgis.h:7103
@ ForceDocked
Force the widget to be docked.
Definition qgis.h:7105
@ ForceDialog
Force the widget to be shown in a dialog.
Definition qgis.h:7106
ProcessingLogLevel
Logging level for algorithms to use when pushing feedback messages.
Definition qgis.h:3895
@ DefaultLevel
Default logging level.
Definition qgis.h:3896
@ Verbose
Verbose logging.
Definition qgis.h:3897
@ ModelDebug
Model debug level logging. Includes verbose logging and other outputs useful for debugging models.
Definition qgis.h:3898
A widget wrapper for Processing parameter value widgets.
QWidget * wrappedWidget()
Returns the current wrapped widget, if any.
Encapsulates an academic reference and formats it according to style guidelines.
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.
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 QgsProcessingGuiRegistry * processingGuiRegistry()
Returns the global processing gui registry, used for registering the GUI behavior of processing algor...
Definition qgsgui.cpp:170
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:225
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
Definition qgsgui.h:284
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition qgsgui.cpp:100
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
Definition qgsgui.cpp:254
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
Definition qgshelp.cpp:46
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.
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 changed()
Emitted when the widget state changes.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
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.
const QgsProcessingParameterDefinition * parameterDefinition(const QString &name) const
Returns a matching parameter by name.
Contains information about the context in which a processing algorithm is executed.
Qgis::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
void setLogLevel(Qgis::ProcessingLogLevel level)
Sets the logging level for algorithms to use when pushing feedback messages to users.
void setFeedback(QgsProcessingFeedback *feedback)
Sets an associated feedback object.
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 setInvalidGeometryCheck(Qgis::InvalidGeometryCheck check)
Sets the behavior used for checking invalid geometries in input layers.
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.
Qgis::ProcessingLogLevel logLevel() const
Returns the logging level for algorithms to use when pushing feedback messages to users.
Qgis::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.
Base class for providing feedback from a processing algorithm.
void warningPushed(const QString &text)
Emitted when an warning is pushed.
void infoPushed(const QString &text)
Emitted when information text is pushed.
void errorReported(const QString &text, bool fatalError)
Emitted when an error is reported.
void progressTextChanged(const QString &text)
Emitted when the progress text is changed.
void debugInfoPushed(const QString &text)
Emitted when debug information text is pushed.
void formattedMessagePushed(const QString &html)
Emitted when a formatted html message is pushed.
void commandInfoPushed(const QString &text)
Emitted when command information text is pushed.
void consoleInfoPushed(const QString &text)
Emitted when console information text is pushed.
Base class for the definition of processing parameters.
QString description() const
Returns the description for the parameter.
@ InvalidValue
Generic result for invalid parameter values.
@ InvalidOutputExtension
Output parameter extension is invalid.
static QVariantMap preprocessQgisProcessParameters(const QVariantMap &parameters, bool &ok, QString &error)
Pre-processes a set of parameter values for the qgis_process command.
static QString documentationFlagToString(Qgis::ProcessingAlgorithmDocumentationFlag flag)
Converts a documentation flag to a translated string.
Stores settings for use within QGIS.
Definition qgssettings.h:68
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.
Utility functions for working with strings.
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.
Represents a vector layer which manages a vector based dataset.
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
const QList< T > qgsEnumList()
Returns a list all enum entries.
Definition qgis.h:8011
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7665