17 #include "qgssettings.h"
28 #include <QToolButton>
29 #include <QDesktopServices>
31 #include <QApplication>
33 #include <QFileDialog>
39 QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback()
43 void QgsProcessingAlgorithmDialogFeedback::setProgressText(
const QString &text )
46 emit progressTextChanged( text );
49 void QgsProcessingAlgorithmDialogFeedback::reportError(
const QString &error,
bool fatalError )
52 emit errorReported( error, fatalError );
55 void QgsProcessingAlgorithmDialogFeedback::pushWarning(
const QString &warning )
58 emit warningPushed( warning );
61 void QgsProcessingAlgorithmDialogFeedback::pushInfo(
const QString &info )
64 emit infoPushed( info );
67 void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo(
const QString &info )
70 emit commandInfoPushed( info );
73 void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo(
const QString &info )
76 emit debugInfoPushed( info );
79 void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo(
const QString &info )
82 emit consoleInfoPushed( info );
89 QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
90 : QDialog( parent, flags )
95 splitter->setCollapsible( 0,
false );
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 );
111 QgsSettings settings;
112 splitter->restoreState( settings.value( QStringLiteral(
"/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
113 mSplitterState = splitter->saveState();
114 splitterChanged( 0, 0 );
117 mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
118 mButtonRun->setText( tr(
"Run" ) );
121 mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
122 mButtonChangeParameters->setText( tr(
"Change Parameters" ) );
124 buttonCancel->setEnabled(
false );
125 mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
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 );
134 connect( mButtonSaveLog, &QToolButton::clicked,
this, &QgsProcessingAlgorithmDialogBase::saveLog );
135 connect( mButtonCopyLog, &QToolButton::clicked,
this, &QgsProcessingAlgorithmDialogBase::copyLogToClipboard );
136 connect( mButtonClearLog, &QToolButton::clicked,
this, &QgsProcessingAlgorithmDialogBase::clearLog );
138 connect( mTabWidget, &QTabWidget::currentChanged,
this, &QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged );
141 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
142 verticalLayout->insertWidget( 0, mMessageBar );
147 QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() =
default;
159 title = mAlgorithm->displayName();
161 setWindowTitle( title );
163 QString algHelp = formatHelp(
algorithm );
164 if ( algHelp.isEmpty() )
165 textShortHelp->hide();
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 );
180 mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
184 if ( !warning.isEmpty() )
186 mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
192 return mAlgorithm.get();
195 void QgsProcessingAlgorithmDialogBase::setMainWidget(
QgsPanelWidget *widget )
199 mMainWidget->deleteLater();
202 mPanelStack->setMainPanel( widget );
205 mMainWidget = widget;
214 void QgsProcessingAlgorithmDialogBase::saveLogToFile(
const QString &path,
const LogFormat format )
216 QFile logFile( path );
217 if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
221 QTextStream fout( &logFile );
225 case FormatPlainText:
226 fout << txtLog->toPlainText();
230 fout << txtLog->toHtml();
237 auto feedback = std::make_unique< QgsProcessingAlgorithmDialogFeedback >();
239 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::commandInfoPushed,
this, &QgsProcessingAlgorithmDialogBase::pushCommandInfo );
240 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::consoleInfoPushed,
this, &QgsProcessingAlgorithmDialogBase::pushConsoleInfo );
241 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::debugInfoPushed,
this, &QgsProcessingAlgorithmDialogBase::pushDebugInfo );
242 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::errorReported,
this, &QgsProcessingAlgorithmDialogBase::reportError );
243 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::warningPushed,
this, &QgsProcessingAlgorithmDialogBase::pushWarning );
244 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::infoPushed,
this, &QgsProcessingAlgorithmDialogBase::pushInfo );
245 connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::progressTextChanged,
this, &QgsProcessingAlgorithmDialogBase::setProgressText );
247 return feedback.release();
250 QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
255 QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
260 void QgsProcessingAlgorithmDialogBase::showLog()
262 mTabWidget->setCurrentIndex( 1 );
265 void QgsProcessingAlgorithmDialogBase::showParameters()
267 mTabWidget->setCurrentIndex( 0 );
270 QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
275 QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
280 QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
282 return mButtonChangeParameters;
285 void QgsProcessingAlgorithmDialogBase::clearProgress()
287 progressBar->setMaximum( 0 );
290 void QgsProcessingAlgorithmDialogBase::setExecuted(
bool executed )
292 mExecuted = executed;
295 void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult(
bool executedAnyResult )
297 mExecutedAnyResult = executedAnyResult;
300 void QgsProcessingAlgorithmDialogBase::setResults(
const QVariantMap &results )
310 void QgsProcessingAlgorithmDialogBase::openHelp()
312 QUrl algHelp = mAlgorithm->helpUrl();
313 if ( algHelp.isEmpty() )
315 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() ) ) );
318 if ( !algHelp.isEmpty() )
319 QDesktopServices::openUrl( algHelp );
322 void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
324 if ( mHelpCollapsed )
326 splitter->restoreState( mSplitterState );
327 mButtonCollapse->setArrowType( Qt::RightArrow );
331 mSplitterState = splitter->saveState();
332 splitter->setSizes( QList<int>() << 1 << 0 );
333 mButtonCollapse->setArrowType( Qt::LeftArrow );
335 mHelpCollapsed = !mHelpCollapsed;
338 void QgsProcessingAlgorithmDialogBase::splitterChanged(
int,
int )
340 if ( splitter->sizes().at( 1 ) == 0 )
342 mHelpCollapsed =
true;
343 mButtonCollapse->setArrowType( Qt::LeftArrow );
347 mHelpCollapsed =
false;
348 mButtonCollapse->setArrowType( Qt::RightArrow );
352 void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged(
int )
354 updateRunButtonVisibility();
357 void QgsProcessingAlgorithmDialogBase::linkClicked(
const QUrl &url )
359 QDesktopServices::openUrl( url.toString() );
362 void QgsProcessingAlgorithmDialogBase::algExecuted(
bool successful,
const QVariantMap & )
364 mAlgorithmTask =
nullptr;
371 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
385 void QgsProcessingAlgorithmDialogBase::taskTriggered(
QgsTask *task )
387 if ( task == mAlgorithmTask )
391 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
397 void QgsProcessingAlgorithmDialogBase::closeClicked()
413 void QgsProcessingAlgorithmDialogBase::reportError(
const QString &error,
bool fatalError )
415 setInfo( error,
true );
422 void QgsProcessingAlgorithmDialogBase::pushWarning(
const QString &warning )
424 setInfo( warning,
false,
true,
true );
428 void QgsProcessingAlgorithmDialogBase::pushInfo(
const QString &info )
434 void QgsProcessingAlgorithmDialogBase::pushCommandInfo(
const QString &command )
436 txtLog->append( QStringLiteral(
"<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
437 scrollToBottomOfLog();
441 void QgsProcessingAlgorithmDialogBase::pushDebugInfo(
const QString &message )
443 txtLog->append( QStringLiteral(
"<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
444 scrollToBottomOfLog();
448 void QgsProcessingAlgorithmDialogBase::pushConsoleInfo(
const QString &info )
450 txtLog->append( QStringLiteral(
"<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
451 scrollToBottomOfLog();
455 QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
457 QgsProcessingAlgorithmProgressDialog *dialog =
new QgsProcessingAlgorithmProgressDialog(
this );
458 dialog->setWindowModality( Qt::ApplicationModal );
459 dialog->setWindowTitle( windowTitle() );
460 dialog->setGeometry( geometry() );
461 connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
462 connect( dialog->cancelButton(), &QPushButton::clicked, buttonCancel, &QPushButton::click );
463 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
464 connect( txtLog, &QTextEdit::textChanged, dialog, [
this, dialog]()
466 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
467 QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
468 sb->setValue( sb->maximum() );
473 void QgsProcessingAlgorithmDialogBase::clearLog()
478 void QgsProcessingAlgorithmDialogBase::saveLog()
480 QgsSettings settings;
481 QString lastUsedDir = settings.value( QStringLiteral(
"/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
484 const QString txtExt = tr(
"Text files" ) + QStringLiteral(
" (*.txt *.TXT)" );
485 const QString htmlExt = tr(
"HTML files" ) + QStringLiteral(
" (*.html *.HTML)" );
487 QString path = QFileDialog::getSaveFileName(
this, tr(
"Save Log to File" ), lastUsedDir, txtExt +
";;" + htmlExt, &filter );
488 if ( path.isEmpty() )
493 settings.setValue( QStringLiteral(
"/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
495 LogFormat format = FormatPlainText;
496 if ( filter == htmlExt )
500 saveLogToFile( path, format );
503 void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
505 QMimeData *m =
new QMimeData();
506 m->setText( txtLog->toPlainText() );
507 m->setHtml( txtLog->toHtml() );
508 QClipboard *cb = QApplication::clipboard();
511 cb->setMimeData( m, QClipboard::Selection );
513 cb->setMimeData( m, QClipboard::Clipboard );
516 void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
518 if ( !mHelpCollapsed )
520 QgsSettings settings;
521 settings.setValue( QStringLiteral(
"/Processing/dialogBaseSplitter" ), splitter->saveState() );
524 QDialog::closeEvent( e );
526 if ( !mAlgorithmTask )
535 void QgsProcessingAlgorithmDialogBase::runAlgorithm()
540 void QgsProcessingAlgorithmDialogBase::setPercentage(
double percent )
543 if ( progressBar->maximum() == 0 )
544 progressBar->setMaximum( 100 );
545 progressBar->setValue( percent );
549 void QgsProcessingAlgorithmDialogBase::setProgressText(
const QString &text )
551 lblProgress->setText( text );
552 setInfo( text,
false );
553 scrollToBottomOfLog();
560 if ( !text.isEmpty() )
562 QStringList paragraphs = text.split(
'\n' );
564 for (
const QString ¶graph : paragraphs )
566 help += QStringLiteral(
"<p>%1</p>" ).arg( paragraph );
578 void QgsProcessingAlgorithmDialogBase::processEvents()
580 if ( mAlgorithmTask )
596 while ( ++nIters < 100 )
599 QCoreApplication::processEvents();
603 void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
605 QScrollBar *sb = txtLog->verticalScrollBar();
606 sb->setValue( sb->maximum() );
609 void QgsProcessingAlgorithmDialogBase::resetGui()
611 lblProgress->clear();
612 progressBar->setMaximum( 100 );
613 progressBar->setValue( 0 );
614 mButtonRun->setEnabled(
true );
615 mButtonChangeParameters->setEnabled(
true );
616 mButtonClose->setEnabled(
true );
619 mMainWidget->setEnabled(
true );
621 updateRunButtonVisibility();
622 resetAdditionalGui();
625 void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
628 bool runButtonVisible = mTabWidget->currentIndex() == 0;
629 mButtonRun->setVisible( runButtonVisible );
630 mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
633 void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
638 void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
640 mButtonRun->setEnabled(
false );
641 mButtonChangeParameters->setEnabled(
false );
644 mMainWidget->setEnabled(
false );
646 blockAdditionalControlsWhileRunning();
649 void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
654 QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
659 void QgsProcessingAlgorithmDialogBase::hideShortHelp()
661 textShortHelp->setVisible(
false );
666 mAlgorithmTask = task;
671 QString QgsProcessingAlgorithmDialogBase::formatStringForLog(
const QString &
string )
674 s.replace(
'\n', QLatin1String(
"<br>" ) );
678 void QgsProcessingAlgorithmDialogBase::setInfo(
const QString &message,
bool isError,
bool escapeHtml,
bool isWarning )
682 if ( isError || isWarning )
683 txtLog->append( QStringLiteral(
"<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral(
"red" ) : QStringLiteral(
"#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
684 else if ( escapeHtml )
685 txtLog->append( QStringLiteral(
"<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
687 txtLog->append( QStringLiteral(
"<span>%1</span>" ).arg( formatStringForLog( message ) ) );
688 scrollToBottomOfLog();
692 void QgsProcessingAlgorithmDialogBase::reject()
694 if ( !mAlgorithmTask )
696 setAttribute( Qt::WA_DeleteOnClose );
705 QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
711 QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
716 QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
718 return mButtonBox->button( QDialogButtonBox::Cancel );
721 QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
726 void QgsProcessingAlgorithmProgressDialog::reject()
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 ...
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...
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
A bar for displaying non-blocking messages to the user.
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.
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