30 #include <QMessageBox>
31 #include <QApplication>
33 #if QT_CONFIG(process)
34 QgsRunProcess::QgsRunProcess(
const QString &action,
bool capture )
43 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
44 QStringList arguments = QProcess::splitCommand( action );
45 const QString command = arguments.value( 0 );
46 if ( !arguments.isEmpty() )
47 arguments.removeFirst();
50 mProcess =
new QProcess;
54 connect( mProcess, &QProcess::errorOccurred,
this, &QgsRunProcess::processError );
55 connect( mProcess, &QProcess::readyReadStandardOutput,
this, &QgsRunProcess::stdoutAvailable );
56 connect( mProcess, &QProcess::readyReadStandardError,
this, &QgsRunProcess::stderrAvailable );
60 connect( mProcess,
static_cast < void ( QProcess::* )(
int, QProcess::ExitStatus )
>( &QProcess::finished ),
this, &QgsRunProcess::processExit );
65 mOutput->setTitle( action );
67 mOutput->showMessage(
false );
70 QObject *mOutputObj =
dynamic_cast<QObject *
>( mOutput );
73 connect( mOutputObj, &QObject::destroyed,
this, &QgsRunProcess::dialogGone );
77 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
78 mProcess->start( action );
80 mProcess->start( command, arguments );
85 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
86 if ( ! mProcess->startDetached( action ) )
88 if ( ! mProcess->startDetached( command, arguments ) )
91 QMessageBox::critical(
nullptr, tr(
"Action" ),
92 tr(
"Unable to run command\n%1" ).arg( action ),
93 QMessageBox::Ok, Qt::NoButton );
101 QgsRunProcess::~QgsRunProcess()
106 void QgsRunProcess::die()
112 void QgsRunProcess::stdoutAvailable()
114 QByteArray bytes( mProcess->readAllStandardOutput() );
115 QTextCodec *codec = QTextCodec::codecForLocale();
116 QString line( codec->toUnicode( bytes ) );
119 mOutput->appendMessage( line );
122 void QgsRunProcess::stderrAvailable()
124 QByteArray bytes( mProcess->readAllStandardOutput() );
125 QTextCodec *codec = QTextCodec::codecForLocale();
126 QString line( codec->toUnicode( bytes ) );
129 mOutput->appendMessage(
"<font color=red>" + line +
"</font>" );
132 void QgsRunProcess::processExit(
int, QProcess::ExitStatus )
143 mOutput->appendMessage(
"<b>" + tr(
"Done" ) +
"</b>" );
152 void QgsRunProcess::dialogGone()
163 disconnect( mProcess, &QProcess::errorOccurred,
this, &QgsRunProcess::processError );
164 disconnect( mProcess, &QProcess::readyReadStandardOutput,
this, &QgsRunProcess::stdoutAvailable );
165 disconnect( mProcess, &QProcess::readyReadStandardError,
this, &QgsRunProcess::stderrAvailable );
166 disconnect( mProcess,
static_cast < void ( QProcess::* )(
int, QProcess::ExitStatus )
>( &QProcess::finished ),
this, &QgsRunProcess::processExit );
171 void QgsRunProcess::processError( QProcess::ProcessError err )
173 if ( err == QProcess::FailedToStart )
182 QgsDebugMsg(
"Got error: " + QString(
"%d" ).arg( err ) );
188 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
189 return QProcess::splitCommand( command );
195 bool inQuote =
false;
200 for (
int i = 0; i < command.size(); ++i )
202 if ( command.at( i ) == QLatin1Char(
'"' ) )
205 if ( quoteCount == 3 )
209 tmp += command.at( i );
215 if ( quoteCount == 1 )
219 if ( !inQuote && command.at( i ).isSpace() )
221 if ( !tmp.isEmpty() )
229 tmp += command.at( i );
232 if ( !tmp.isEmpty() )
239 QgsRunProcess::QgsRunProcess(
const QString &action,
bool )
245 QgsRunProcess::~QgsRunProcess()
251 return QStringList();
260 #if QT_CONFIG(process)
261 QgsBlockingProcess::QgsBlockingProcess(
const QString &process,
const QStringList &arguments )
263 , mProcess( process )
264 , mArguments( arguments )
269 int QgsBlockingProcess::run(
QgsFeedback *feedback )
271 const bool requestMadeFromMainThread = QThread::currentThread() == QCoreApplication::instance()->thread();
274 QProcess::ExitStatus exitStatus = QProcess::NormalExit;
275 QProcess::ProcessError error = QProcess::UnknownError;
277 std::function<void()> runFunction = [
this, &result, &exitStatus, &error, feedback]()
284 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
285 p.setProcessEnvironment( env );
291 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
305 connect( &p, qOverload< int, QProcess::ExitStatus >( &QProcess::finished ),
this, [&loop, &result, &exitStatus](
int res, QProcess::ExitStatus st )
310 }, Qt::DirectConnection );
312 connect( &p, &QProcess::readyReadStandardOutput, &p, [&p,
this]
314 QByteArray ba = p.readAllStandardOutput();
315 mStdoutHandler( ba );
317 connect( &p, &QProcess::readyReadStandardError, &p, [&p,
this]
319 QByteArray ba = p.readAllStandardError();
320 mStderrHandler( ba );
322 p.start( mProcess, mArguments, QProcess::Unbuffered | QProcess::ReadWrite );
323 if ( !p.waitForStarted() )
326 exitStatus = QProcess::NormalExit;
334 mStdoutHandler( p.readAllStandardOutput() );
335 mStderrHandler( p.readAllStandardError() );
338 if ( requestMadeFromMainThread )
340 std::unique_ptr<ProcessThread> processThread = std::make_unique<ProcessThread>( runFunction );
341 processThread->start();
343 processThread->wait();
350 mExitStatus = exitStatus;
351 mProcessError = error;
355 QProcess::ExitStatus QgsBlockingProcess::exitStatus()
const
360 QProcess::ProcessError QgsBlockingProcess::processError()
const
362 return mProcessError;
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
void canceled()
Internal routines can connect to this signal if they use event loop.
Interface for showing messages from QGIS in GUI independent way.
static QgsMessageOutput * createMessageOutput()
function that returns new class derived from QgsMessageOutput (don't forget to delete it then if show...
virtual void setMessage(const QString &message, MessageType msgType)=0
Sets message, it won't be displayed until.
static QStringList splitCommand(const QString &command)
Splits the string command into a list of tokens, and returns the list.