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 );
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 const 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 );
176 textShortHelp->show();
181 mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
185 if ( !warning.isEmpty() )
187 mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
193 return mAlgorithm.get();
196 void QgsProcessingAlgorithmDialogBase::setMainWidget(
QgsPanelWidget *widget )
200 mMainWidget->deleteLater();
203 mPanelStack->setMainPanel( widget );
206 mMainWidget = widget;
215 void QgsProcessingAlgorithmDialogBase::saveLogToFile(
const QString &path,
const LogFormat format )
217 QFile logFile( path );
218 if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
222 QTextStream fout( &logFile );
226 case FormatPlainText:
227 fout << txtLog->toPlainText();
231 fout << txtLog->toHtml();
238 auto feedback = std::make_unique< QgsProcessingAlgorithmDialogFeedback >();
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 );
248 return feedback.release();
251 QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
256 QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
261 void QgsProcessingAlgorithmDialogBase::showLog()
263 mTabWidget->setCurrentIndex( 1 );
266 void QgsProcessingAlgorithmDialogBase::showParameters()
268 mTabWidget->setCurrentIndex( 0 );
271 QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
276 QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
281 QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
283 return mButtonChangeParameters;
286 void QgsProcessingAlgorithmDialogBase::clearProgress()
288 progressBar->setMaximum( 0 );
291 void QgsProcessingAlgorithmDialogBase::setExecuted(
bool executed )
293 mExecuted = executed;
296 void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult(
bool executedAnyResult )
298 mExecutedAnyResult = executedAnyResult;
301 void QgsProcessingAlgorithmDialogBase::setResults(
const QVariantMap &results )
311 void QgsProcessingAlgorithmDialogBase::openHelp()
313 QUrl algHelp = mAlgorithm->helpUrl();
314 if ( algHelp.isEmpty() )
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() ) ) );
319 if ( !algHelp.isEmpty() )
320 QDesktopServices::openUrl( algHelp );
323 void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
325 if ( mHelpCollapsed )
327 splitter->restoreState( mSplitterState );
328 mButtonCollapse->setArrowType( Qt::RightArrow );
332 mSplitterState = splitter->saveState();
333 splitter->setSizes( QList<int>() << 1 << 0 );
334 mButtonCollapse->setArrowType( Qt::LeftArrow );
336 mHelpCollapsed = !mHelpCollapsed;
339 void QgsProcessingAlgorithmDialogBase::splitterChanged(
int,
int )
341 if ( splitter->sizes().at( 1 ) == 0 )
343 mHelpCollapsed =
true;
344 mButtonCollapse->setArrowType( Qt::LeftArrow );
348 mHelpCollapsed =
false;
349 mButtonCollapse->setArrowType( Qt::RightArrow );
353 void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged(
int )
355 updateRunButtonVisibility();
358 void QgsProcessingAlgorithmDialogBase::linkClicked(
const QUrl &url )
360 QDesktopServices::openUrl( url.toString() );
363 void QgsProcessingAlgorithmDialogBase::algExecuted(
bool successful,
const QVariantMap & )
365 mAlgorithmTask =
nullptr;
372 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
386 void QgsProcessingAlgorithmDialogBase::taskTriggered(
QgsTask *task )
388 if ( task == mAlgorithmTask )
392 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
398 void QgsProcessingAlgorithmDialogBase::closeClicked()
414 void QgsProcessingAlgorithmDialogBase::reportError(
const QString &error,
bool fatalError )
416 setInfo( error,
true );
423 void QgsProcessingAlgorithmDialogBase::pushWarning(
const QString &warning )
425 setInfo( warning,
false,
true,
true );
429 void QgsProcessingAlgorithmDialogBase::pushInfo(
const QString &info )
435 void QgsProcessingAlgorithmDialogBase::pushCommandInfo(
const QString &command )
437 txtLog->append( QStringLiteral(
"<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
438 scrollToBottomOfLog();
442 void QgsProcessingAlgorithmDialogBase::pushDebugInfo(
const QString &message )
444 txtLog->append( QStringLiteral(
"<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
445 scrollToBottomOfLog();
449 void QgsProcessingAlgorithmDialogBase::pushConsoleInfo(
const QString &info )
451 txtLog->append( QStringLiteral(
"<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
452 scrollToBottomOfLog();
456 QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
458 QgsProcessingAlgorithmProgressDialog *dialog =
new QgsProcessingAlgorithmProgressDialog(
this );
459 dialog->setWindowModality( Qt::ApplicationModal );
460 dialog->setWindowTitle( windowTitle() );
461 dialog->setGeometry( geometry() );
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]()
467 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
468 QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
469 sb->setValue( sb->maximum() );
474 void QgsProcessingAlgorithmDialogBase::clearLog()
479 void QgsProcessingAlgorithmDialogBase::saveLog()
482 const QString lastUsedDir = settings.
value( QStringLiteral(
"/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
485 const QString txtExt = tr(
"Text files" ) + QStringLiteral(
" (*.txt *.TXT)" );
486 const QString htmlExt = tr(
"HTML files" ) + QStringLiteral(
" (*.html *.HTML)" );
488 const QString path = QFileDialog::getSaveFileName(
this, tr(
"Save Log to File" ), lastUsedDir, txtExt +
";;" + htmlExt, &filter );
489 if ( path.isEmpty() )
494 settings.
setValue( QStringLiteral(
"/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
496 LogFormat format = FormatPlainText;
497 if ( filter == htmlExt )
501 saveLogToFile( path, format );
504 void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
506 QMimeData *m =
new QMimeData();
507 m->setText( txtLog->toPlainText() );
508 m->setHtml( txtLog->toHtml() );
509 QClipboard *cb = QApplication::clipboard();
512 cb->setMimeData( m, QClipboard::Selection );
514 cb->setMimeData( m, QClipboard::Clipboard );
517 void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
519 if ( !mHelpCollapsed )
522 settings.
setValue( QStringLiteral(
"/Processing/dialogBaseSplitter" ), splitter->saveState() );
525 QDialog::closeEvent( e );
527 if ( !mAlgorithmTask )
536 void QgsProcessingAlgorithmDialogBase::runAlgorithm()
541 void QgsProcessingAlgorithmDialogBase::setPercentage(
double percent )
544 if ( progressBar->maximum() == 0 )
545 progressBar->setMaximum( 100 );
546 progressBar->setValue( percent );
550 void QgsProcessingAlgorithmDialogBase::setProgressText(
const QString &text )
552 lblProgress->setText( text );
553 setInfo( text,
false );
554 scrollToBottomOfLog();
561 if ( !text.isEmpty() )
563 const QStringList paragraphs = text.split(
'\n' );
565 for (
const QString ¶graph : paragraphs )
567 help += QStringLiteral(
"<p>%1</p>" ).arg( paragraph );
579 void QgsProcessingAlgorithmDialogBase::processEvents()
581 if ( mAlgorithmTask )
597 while ( ++nIters < 100 )
600 QCoreApplication::processEvents();
604 void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
606 QScrollBar *sb = txtLog->verticalScrollBar();
607 sb->setValue( sb->maximum() );
610 void QgsProcessingAlgorithmDialogBase::resetGui()
612 lblProgress->clear();
613 progressBar->setMaximum( 100 );
614 progressBar->setValue( 0 );
615 mButtonRun->setEnabled(
true );
616 mButtonChangeParameters->setEnabled(
true );
617 mButtonClose->setEnabled(
true );
620 mMainWidget->setEnabled(
true );
622 updateRunButtonVisibility();
623 resetAdditionalGui();
626 void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
629 const bool runButtonVisible = mTabWidget->currentIndex() == 0;
630 mButtonRun->setVisible( runButtonVisible );
631 mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
634 void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
639 void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
641 mButtonRun->setEnabled(
false );
642 mButtonChangeParameters->setEnabled(
false );
645 mMainWidget->setEnabled(
false );
647 blockAdditionalControlsWhileRunning();
650 void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
655 QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
660 void QgsProcessingAlgorithmDialogBase::hideShortHelp()
662 textShortHelp->setVisible(
false );
667 mAlgorithmTask = task;
672 QString QgsProcessingAlgorithmDialogBase::formatStringForLog(
const QString &
string )
675 s.replace(
'\n', QLatin1String(
"<br>" ) );
679 void QgsProcessingAlgorithmDialogBase::setInfo(
const QString &message,
bool isError,
bool escapeHtml,
bool isWarning )
681 constexpr
int MESSAGE_COUNT_LIMIT = 10000;
683 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
685 ++mMessageLoggedCount;
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() ) ) );
696 txtLog->append( QStringLiteral(
"<span>%1</span>" ).arg( formatStringForLog( message ) ) );
697 scrollToBottomOfLog();
701 void QgsProcessingAlgorithmDialogBase::reject()
703 if ( !mAlgorithmTask )
705 setAttribute( Qt::WA_DeleteOnClose );
714 QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
720 QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
725 QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
727 return mButtonBox->button( QDialogButtonBox::Cancel );
730 QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
735 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.
This class is a composition of two QSettings instances:
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