QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 <QToolButton>
29 #include <QDesktopServices>
30 #include <QScrollBar>
31 #include <QApplication>
32 #include <QClipboard>
33 #include <QFileDialog>
34 #include <QMimeData>
35 
36 
38 
39 QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback()
40  : QgsProcessingFeedback( false )
41 {}
42 
43 void QgsProcessingAlgorithmDialogFeedback::setProgressText( const QString &text )
44 {
46  emit progressTextChanged( text );
47 }
48 
49 void QgsProcessingAlgorithmDialogFeedback::reportError( const QString &error, bool fatalError )
50 {
51  QgsProcessingFeedback::reportError( error, fatalError );
52  emit errorReported( error, fatalError );
53 }
54 
55 void QgsProcessingAlgorithmDialogFeedback::pushWarning( const QString &warning )
56 {
58  emit warningPushed( warning );
59 }
60 
61 void QgsProcessingAlgorithmDialogFeedback::pushInfo( const QString &info )
62 {
64  emit infoPushed( info );
65 }
66 
67 void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( const QString &info )
68 {
70  emit commandInfoPushed( info );
71 }
72 
73 void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( const QString &info )
74 {
76  emit debugInfoPushed( info );
77 }
78 
79 void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( const QString &info )
80 {
82  emit consoleInfoPushed( info );
83 }
84 
85 //
86 // QgsProcessingAlgorithmDialogBase
87 //
88 
89 QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
90  : QDialog( parent, flags )
91 {
92  setupUi( this );
93 
94  //don't collapse parameters panel
95  splitter->setCollapsible( 0, false );
96 
97  // add collapse button to splitter
98  QSplitterHandle *splitterHandle = splitter->handle( 1 );
99  QVBoxLayout *handleLayout = new QVBoxLayout();
100  handleLayout->setContentsMargins( 0, 0, 0, 0 );
101  mButtonCollapse = new QToolButton( splitterHandle );
102  mButtonCollapse->setAutoRaise( true );
103  mButtonCollapse->setFixedSize( 12, 12 );
104  mButtonCollapse->setCursor( Qt::ArrowCursor );
105  handleLayout->addWidget( mButtonCollapse );
106  handleLayout->addStretch();
107  splitterHandle->setLayout( handleLayout );
108 
110 
111  const QgsSettings settings;
112  splitter->restoreState( settings.value( QStringLiteral( "/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
113  mSplitterState = splitter->saveState();
114  splitterChanged( 0, 0 );
115 
116  // Rename OK button to Run
117  mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
118  mButtonRun->setText( tr( "Run" ) );
119 
120  // Rename Yes button. Yes is used to ensure same position of Run and Change Parameters with respect to Close button.
121  mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
122  mButtonChangeParameters->setText( tr( "Change Parameters" ) );
123 
124  buttonCancel->setEnabled( false );
125  mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
126 
127  connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::runAlgorithm );
128  connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::showParameters );
129  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::closeClicked );
130  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmDialogBase::openHelp );
131  connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::toggleCollapsed );
132  connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmDialogBase::splitterChanged );
133 
134  connect( mButtonSaveLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::saveLog );
135  connect( mButtonCopyLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::copyLogToClipboard );
136  connect( mButtonClearLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::clearLog );
137 
138  connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged );
139 
140  mMessageBar = new QgsMessageBar();
141  mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
142  verticalLayout->insertWidget( 0, mMessageBar );
143 
144  connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingAlgorithmDialogBase::taskTriggered );
145 }
146 
147 QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() = default;
148 
149 void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
150 {
151  mAlgorithm.reset( algorithm );
152  QString title;
154  {
155  title = QgsStringUtils::capitalize( mAlgorithm->displayName(), QgsStringUtils::TitleCase );
156  }
157  else
158  {
159  title = mAlgorithm->displayName();
160  }
161  setWindowTitle( title );
162 
163  const QString algHelp = formatHelp( algorithm );
164  if ( algHelp.isEmpty() )
165  textShortHelp->hide();
166  else
167  {
168  textShortHelp->document()->setDefaultStyleSheet( QStringLiteral( ".summary { margin-left: 10px; margin-right: 10px; }\n"
169  "h2 { color: #555555; padding-bottom: 15px; }\n"
170  "a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
171  "p { color: #666666; }\n"
172  "b { color: #333333; }\n"
173  "dl dd { margin - bottom: 5px; }" ) );
174  textShortHelp->setHtml( algHelp );
175  connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmDialogBase::linkClicked );
176  textShortHelp->show();
177  }
178 
179  if ( algorithm->helpUrl().isEmpty() && algorithm->provider()->helpId().isEmpty() )
180  {
181  mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
182  }
183 
184  const QString warning = algorithm->provider()->warningMessage();
185  if ( !warning.isEmpty() )
186  {
187  mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
188  }
189 }
190 
192 {
193  return mAlgorithm.get();
194 }
195 
196 void QgsProcessingAlgorithmDialogBase::setMainWidget( QgsPanelWidget *widget )
197 {
198  if ( mMainWidget )
199  {
200  mMainWidget->deleteLater();
201  }
202 
203  mPanelStack->setMainPanel( widget );
204  widget->setDockMode( true );
205 
206  mMainWidget = widget;
207  connect( mMainWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
208 }
209 
210 QgsPanelWidget *QgsProcessingAlgorithmDialogBase::mainWidget()
211 {
212  return mMainWidget;
213 }
214 
215 void QgsProcessingAlgorithmDialogBase::saveLogToFile( const QString &path, const LogFormat format )
216 {
217  QFile logFile( path );
218  if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
219  {
220  return;
221  }
222  QTextStream fout( &logFile );
223 
224  switch ( format )
225  {
226  case FormatPlainText:
227  fout << txtLog->toPlainText();
228  break;
229 
230  case FormatHtml:
231  fout << txtLog->toHtml();
232  break;
233  }
234 }
235 
236 QgsProcessingFeedback *QgsProcessingAlgorithmDialogBase::createFeedback()
237 {
238  auto feedback = std::make_unique< QgsProcessingAlgorithmDialogFeedback >();
239  connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmDialogBase::setPercentage );
240  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::commandInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushCommandInfo );
241  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::consoleInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushConsoleInfo );
242  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::debugInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushDebugInfo );
243  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::errorReported, this, &QgsProcessingAlgorithmDialogBase::reportError );
244  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::warningPushed, this, &QgsProcessingAlgorithmDialogBase::pushWarning );
245  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::infoPushed, this, &QgsProcessingAlgorithmDialogBase::pushInfo );
246  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::progressTextChanged, this, &QgsProcessingAlgorithmDialogBase::setProgressText );
247  connect( buttonCancel, &QPushButton::clicked, feedback.get(), &QgsProcessingFeedback::cancel );
248  return feedback.release();
249 }
250 
251 QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
252 {
253  return mButtonBox;
254 }
255 
256 QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
257 {
258  return mTabWidget;
259 }
260 
261 void QgsProcessingAlgorithmDialogBase::showLog()
262 {
263  mTabWidget->setCurrentIndex( 1 );
264 }
265 
266 void QgsProcessingAlgorithmDialogBase::showParameters()
267 {
268  mTabWidget->setCurrentIndex( 0 );
269 }
270 
271 QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
272 {
273  return mButtonRun;
274 }
275 
276 QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
277 {
278  return buttonCancel;
279 }
280 
281 QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
282 {
283  return mButtonChangeParameters;
284 }
285 
286 void QgsProcessingAlgorithmDialogBase::clearProgress()
287 {
288  progressBar->setMaximum( 0 );
289 }
290 
291 void QgsProcessingAlgorithmDialogBase::setExecuted( bool executed )
292 {
293  mExecuted = executed;
294 }
295 
296 void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult( bool executedAnyResult )
297 {
298  mExecutedAnyResult = executedAnyResult;
299 }
300 
301 void QgsProcessingAlgorithmDialogBase::setResults( const QVariantMap &results )
302 {
303  mResults = results;
304 }
305 
306 void QgsProcessingAlgorithmDialogBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
307 {
308 
309 }
310 
311 void QgsProcessingAlgorithmDialogBase::openHelp()
312 {
313  QUrl algHelp = mAlgorithm->helpUrl();
314  if ( algHelp.isEmpty() )
315  {
316  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() ) ) );
317  }
318 
319  if ( !algHelp.isEmpty() )
320  QDesktopServices::openUrl( algHelp );
321 }
322 
323 void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
324 {
325  if ( mHelpCollapsed )
326  {
327  splitter->restoreState( mSplitterState );
328  mButtonCollapse->setArrowType( Qt::RightArrow );
329  }
330  else
331  {
332  mSplitterState = splitter->saveState();
333  splitter->setSizes( QList<int>() << 1 << 0 );
334  mButtonCollapse->setArrowType( Qt::LeftArrow );
335  }
336  mHelpCollapsed = !mHelpCollapsed;
337 }
338 
339 void QgsProcessingAlgorithmDialogBase::splitterChanged( int, int )
340 {
341  if ( splitter->sizes().at( 1 ) == 0 )
342  {
343  mHelpCollapsed = true;
344  mButtonCollapse->setArrowType( Qt::LeftArrow );
345  }
346  else
347  {
348  mHelpCollapsed = false;
349  mButtonCollapse->setArrowType( Qt::RightArrow );
350  }
351 }
352 
353 void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged( int )
354 {
355  updateRunButtonVisibility();
356 }
357 
358 void QgsProcessingAlgorithmDialogBase::linkClicked( const QUrl &url )
359 {
360  QDesktopServices::openUrl( url.toString() );
361 }
362 
363 void QgsProcessingAlgorithmDialogBase::algExecuted( bool successful, const QVariantMap & )
364 {
365  mAlgorithmTask = nullptr;
366 
367  if ( !successful )
368  {
369  // show dialog to display errors
370  show();
371  raise();
372  setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
373  activateWindow();
374  showLog();
375  }
376  else
377  {
378  // delete dialog if closed
379  if ( !isVisible() )
380  {
381  deleteLater();
382  }
383  }
384 }
385 
386 void QgsProcessingAlgorithmDialogBase::taskTriggered( QgsTask *task )
387 {
388  if ( task == mAlgorithmTask )
389  {
390  show();
391  raise();
392  setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
393  activateWindow();
394  showLog();
395  }
396 }
397 
398 void QgsProcessingAlgorithmDialogBase::closeClicked()
399 {
400  reject();
401  close();
402 }
403 
404 QgsProcessingContext::LogLevel QgsProcessingAlgorithmDialogBase::logLevel() const
405 {
406  return mLogLevel;
407 }
408 
409 void QgsProcessingAlgorithmDialogBase::setLogLevel( QgsProcessingContext::LogLevel level )
410 {
411  mLogLevel = level;
412 }
413 
414 void QgsProcessingAlgorithmDialogBase::reportError( const QString &error, bool fatalError )
415 {
416  setInfo( error, true );
417  if ( fatalError )
418  resetGui();
419  showLog();
420  processEvents();
421 }
422 
423 void QgsProcessingAlgorithmDialogBase::pushWarning( const QString &warning )
424 {
425  setInfo( warning, false, true, true );
426  processEvents();
427 }
428 
429 void QgsProcessingAlgorithmDialogBase::pushInfo( const QString &info )
430 {
431  setInfo( info );
432  processEvents();
433 }
434 
435 void QgsProcessingAlgorithmDialogBase::pushCommandInfo( const QString &command )
436 {
437  txtLog->append( QStringLiteral( "<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
438  scrollToBottomOfLog();
439  processEvents();
440 }
441 
442 void QgsProcessingAlgorithmDialogBase::pushDebugInfo( const QString &message )
443 {
444  txtLog->append( QStringLiteral( "<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
445  scrollToBottomOfLog();
446  processEvents();
447 }
448 
449 void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( const QString &info )
450 {
451  txtLog->append( QStringLiteral( "<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
452  scrollToBottomOfLog();
453  processEvents();
454 }
455 
456 QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
457 {
458  QgsProcessingAlgorithmProgressDialog *dialog = new QgsProcessingAlgorithmProgressDialog( this );
459  dialog->setWindowModality( Qt::ApplicationModal );
460  dialog->setWindowTitle( windowTitle() );
461  dialog->setGeometry( geometry() ); // match size/position to this dialog
462  connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
463  connect( dialog->cancelButton(), &QPushButton::clicked, buttonCancel, &QPushButton::click );
464  dialog->logTextEdit()->setHtml( txtLog->toHtml() );
465  connect( txtLog, &QTextEdit::textChanged, dialog, [this, dialog]()
466  {
467  dialog->logTextEdit()->setHtml( txtLog->toHtml() );
468  QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
469  sb->setValue( sb->maximum() );
470  } );
471  return dialog;
472 }
473 
474 void QgsProcessingAlgorithmDialogBase::clearLog()
475 {
476  txtLog->clear();
477 }
478 
479 void QgsProcessingAlgorithmDialogBase::saveLog()
480 {
481  QgsSettings settings;
482  const QString lastUsedDir = settings.value( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
483 
484  QString filter;
485  const QString txtExt = tr( "Text files" ) + QStringLiteral( " (*.txt *.TXT)" );
486  const QString htmlExt = tr( "HTML files" ) + QStringLiteral( " (*.html *.HTML)" );
487 
488  const QString path = QFileDialog::getSaveFileName( this, tr( "Save Log to File" ), lastUsedDir, txtExt + ";;" + htmlExt, &filter );
489  if ( path.isEmpty() )
490  {
491  return;
492  }
493 
494  settings.setValue( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
495 
496  LogFormat format = FormatPlainText;
497  if ( filter == htmlExt )
498  {
499  format = FormatHtml;
500  }
501  saveLogToFile( path, format );
502 }
503 
504 void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
505 {
506  QMimeData *m = new QMimeData();
507  m->setText( txtLog->toPlainText() );
508  m->setHtml( txtLog->toHtml() );
509  QClipboard *cb = QApplication::clipboard();
510 
511 #ifdef Q_OS_LINUX
512  cb->setMimeData( m, QClipboard::Selection );
513 #endif
514  cb->setMimeData( m, QClipboard::Clipboard );
515 }
516 
517 void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
518 {
519  if ( !mHelpCollapsed )
520  {
521  QgsSettings settings;
522  settings.setValue( QStringLiteral( "/Processing/dialogBaseSplitter" ), splitter->saveState() );
523  }
524 
525  QDialog::closeEvent( e );
526 
527  if ( !mAlgorithmTask )
528  {
529  // when running a background task, the dialog is kept around and deleted only when the task
530  // completes. But if not running a task, we auto cleanup (later - gotta give callers a chance
531  // to retrieve results and execution status).
532  deleteLater();
533  }
534 }
535 
536 void QgsProcessingAlgorithmDialogBase::runAlgorithm()
537 {
538 
539 }
540 
541 void QgsProcessingAlgorithmDialogBase::setPercentage( double percent )
542 {
543  // delay setting maximum progress value until we know algorithm reports progress
544  if ( progressBar->maximum() == 0 )
545  progressBar->setMaximum( 100 );
546  progressBar->setValue( percent );
547  processEvents();
548 }
549 
550 void QgsProcessingAlgorithmDialogBase::setProgressText( const QString &text )
551 {
552  lblProgress->setText( text );
553  setInfo( text, false );
554  scrollToBottomOfLog();
555  processEvents();
556 }
557 
558 QString QgsProcessingAlgorithmDialogBase::formatHelp( QgsProcessingAlgorithm *algorithm )
559 {
560  const QString text = algorithm->shortHelpString();
561  if ( !text.isEmpty() )
562  {
563  const QStringList paragraphs = text.split( '\n' );
564  QString help;
565  for ( const QString &paragraph : paragraphs )
566  {
567  help += QStringLiteral( "<p>%1</p>" ).arg( paragraph );
568  }
569  return QStringLiteral( "<h2>%1</h2>%2" ).arg( algorithm->displayName(), help );
570  }
571  else if ( !algorithm->shortDescription().isEmpty() )
572  {
573  return QStringLiteral( "<h2>%1</h2><p>%2</p>" ).arg( algorithm->displayName(), algorithm->shortDescription() );
574  }
575  else
576  return QString();
577 }
578 
579 void QgsProcessingAlgorithmDialogBase::processEvents()
580 {
581  if ( mAlgorithmTask )
582  {
583  // no need to call this - the algorithm is running in a thread.
584  // in fact, calling it causes a crash on Windows when the algorithm
585  // is running in a background thread... unfortunately we need something
586  // like this for non-threadable algorithms, otherwise there's no chance
587  // for users to hit cancel or see progress updates...
588  return;
589  }
590 
591  // So that we get a chance of hitting the Abort button
592 #ifdef Q_OS_LINUX
593  // One iteration is actually enough on Windows to get good interactivity
594  // whereas on Linux we must allow for far more iterations.
595  // For safety limit the number of iterations
596  int nIters = 0;
597  while ( ++nIters < 100 )
598 #endif
599  {
600  QCoreApplication::processEvents();
601  }
602 }
603 
604 void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
605 {
606  QScrollBar *sb = txtLog->verticalScrollBar();
607  sb->setValue( sb->maximum() );
608 }
609 
610 void QgsProcessingAlgorithmDialogBase::resetGui()
611 {
612  lblProgress->clear();
613  progressBar->setMaximum( 100 );
614  progressBar->setValue( 0 );
615  mButtonRun->setEnabled( true );
616  mButtonChangeParameters->setEnabled( true );
617  mButtonClose->setEnabled( true );
618  if ( mMainWidget )
619  {
620  mMainWidget->setEnabled( true );
621  }
622  updateRunButtonVisibility();
623  resetAdditionalGui();
624 }
625 
626 void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
627 {
628  // Activate run button if current tab is Parameters
629  const bool runButtonVisible = mTabWidget->currentIndex() == 0;
630  mButtonRun->setVisible( runButtonVisible );
631  mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
632 }
633 
634 void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
635 {
636 
637 }
638 
639 void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
640 {
641  mButtonRun->setEnabled( false );
642  mButtonChangeParameters->setEnabled( false );
643  if ( mMainWidget )
644  {
645  mMainWidget->setEnabled( false );
646  }
647  blockAdditionalControlsWhileRunning();
648 }
649 
650 void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
651 {
652 
653 }
654 
655 QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
656 {
657  return mMessageBar;
658 }
659 
660 void QgsProcessingAlgorithmDialogBase::hideShortHelp()
661 {
662  textShortHelp->setVisible( false );
663 }
664 
665 void QgsProcessingAlgorithmDialogBase::setCurrentTask( QgsProcessingAlgRunnerTask *task )
666 {
667  mAlgorithmTask = task;
668  connect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmDialogBase::algExecuted );
669  QgsApplication::taskManager()->addTask( mAlgorithmTask );
670 }
671 
672 QString QgsProcessingAlgorithmDialogBase::formatStringForLog( const QString &string )
673 {
674  QString s = string;
675  s.replace( '\n', QLatin1String( "<br>" ) );
676  return s;
677 }
678 
679 void QgsProcessingAlgorithmDialogBase::setInfo( const QString &message, bool isError, bool escapeHtml, bool isWarning )
680 {
681  constexpr int MESSAGE_COUNT_LIMIT = 10000;
682  // Avoid logging too many messages, which might blow memory.
683  if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
684  return;
685  ++mMessageLoggedCount;
686 
687  // note -- we have to wrap the message in a span block, or QTextEdit::append sometimes gets confused
688  // and varies between treating it as a HTML string or a plain text string! (see https://github.com/qgis/QGIS/issues/37934)
689  if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
690  txtLog->append( QStringLiteral( "<span style=\"color:red\">%1</span>" ).arg( tr( "Message log truncated" ) ) );
691  else if ( isError || isWarning )
692  txtLog->append( QStringLiteral( "<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral( "red" ) : QStringLiteral( "#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
693  else if ( escapeHtml )
694  txtLog->append( QStringLiteral( "<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
695  else
696  txtLog->append( QStringLiteral( "<span>%1</span>" ).arg( formatStringForLog( message ) ) );
697  scrollToBottomOfLog();
698  processEvents();
699 }
700 
701 void QgsProcessingAlgorithmDialogBase::reject()
702 {
703  if ( !mAlgorithmTask )
704  {
705  setAttribute( Qt::WA_DeleteOnClose );
706  }
707  QDialog::reject();
708 }
709 
710 //
711 // QgsProcessingAlgorithmProgressDialog
712 //
713 
714 QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
715  : QDialog( parent )
716 {
717  setupUi( this );
718 }
719 
720 QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
721 {
722  return mProgressBar;
723 }
724 
725 QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
726 {
727  return mButtonBox->button( QDialogButtonBox::Cancel );
728 }
729 
730 QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
731 {
732  return mTxtLog;
733 }
734 
735 void QgsProcessingAlgorithmProgressDialog::reject()
736 {
737 
738 }
739 
740 
741 
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 ...
Definition: qgsfeedback.h:85
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:168
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
Definition: qgsgui.h:220
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
Definition: qgsgui.cpp:187
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
Definition: qgshelp.cpp:41
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
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.
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.
@ 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.
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.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
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.
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