QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsrunprocess.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrunprocess.cpp
3 
4  A class that runs an external program
5 
6  -------------------
7  begin : Jan 2005
8  copyright : (C) 2005 by Gavin Macaulay
9  email : gavin at macaulay dot co dot nz
10  ***************************************************************************/
11 
12 /***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 
21 #include "qgsrunprocess.h"
22 
23 #include "qgslogger.h"
24 #include "qgsmessageoutput.h"
25 #include <QProcess>
26 #include <QTextCodec>
27 #include <QMessageBox>
28 
29 #if QT_CONFIG(process)
30 QgsRunProcess::QgsRunProcess( const QString &action, bool capture )
31 
32 {
33  // Make up a string from the command and arguments that we'll use
34  // for display purposes
35  QgsDebugMsg( "Running command: " + action );
36 
37  mCommand = action;
38 
39  mProcess = new QProcess;
40 
41  if ( capture )
42  {
43  connect( mProcess, static_cast < void ( QProcess::* )( QProcess::ProcessError ) >( &QProcess::error ), this, &QgsRunProcess::processError );
44  connect( mProcess, &QProcess::readyReadStandardOutput, this, &QgsRunProcess::stdoutAvailable );
45  connect( mProcess, &QProcess::readyReadStandardError, this, &QgsRunProcess::stderrAvailable );
46  // We only care if the process has finished if we are capturing
47  // the output from the process, hence this connect() call is
48  // inside the capture if() statement.
49  connect( mProcess, static_cast < void ( QProcess::* )( int, QProcess::ExitStatus ) >( &QProcess::finished ), this, &QgsRunProcess::processExit );
50 
51  // Use QgsMessageOutput for displaying output to user
52  // It will delete itself when the dialog box is closed.
54  mOutput->setTitle( action );
55  mOutput->setMessage( tr( "<b>Starting %1…</b>" ).arg( action ), QgsMessageOutput::MessageHtml );
56  mOutput->showMessage( false ); // non-blocking
57 
58  // get notification of delete if it's derived from QObject
59  QObject *mOutputObj = dynamic_cast<QObject *>( mOutput );
60  if ( mOutputObj )
61  {
62  connect( mOutputObj, &QObject::destroyed, this, &QgsRunProcess::dialogGone );
63  }
64 
65  // start the process!
66  mProcess->start( action );
67  }
68  else
69  {
70  if ( ! mProcess->startDetached( action ) ) // let the program run by itself
71  {
72  QMessageBox::critical( nullptr, tr( "Action" ),
73  tr( "Unable to run command\n%1" ).arg( action ),
74  QMessageBox::Ok, Qt::NoButton );
75  }
76  // We're not capturing the output from the process, so we don't
77  // need to exist anymore.
78  die();
79  }
80 }
81 
82 QgsRunProcess::~QgsRunProcess()
83 {
84  delete mProcess;
85 }
86 
87 void QgsRunProcess::die()
88 {
89  // safe way to do "delete this" for QObjects
90  deleteLater();
91 }
92 
93 void QgsRunProcess::stdoutAvailable()
94 {
95  QByteArray bytes( mProcess->readAllStandardOutput() );
96  QTextCodec *codec = QTextCodec::codecForLocale();
97  QString line( codec->toUnicode( bytes ) );
98 
99  // Add the new output to the dialog box
100  mOutput->appendMessage( line );
101 }
102 
103 void QgsRunProcess::stderrAvailable()
104 {
105  QByteArray bytes( mProcess->readAllStandardOutput() );
106  QTextCodec *codec = QTextCodec::codecForLocale();
107  QString line( codec->toUnicode( bytes ) );
108 
109  // Add the new output to the dialog box, but color it red
110  mOutput->appendMessage( "<font color=red>" + line + "</font>" );
111 }
112 
113 void QgsRunProcess::processExit( int, QProcess::ExitStatus )
114 {
115  // Because we catch the dialog box going (the dialogGone()
116  // function), and delete this instance, control will only pass to
117  // this function if the dialog box still exists when the process
118  // exits, so it's always safe to use the pointer to the dialog box
119  // (unless it was never created in the first case, which is what the
120  // test against 0 is for).
121 
122  if ( mOutput )
123  {
124  mOutput->appendMessage( "<b>" + tr( "Done" ) + "</b>" );
125  }
126 
127  // Since the dialog box takes care of deleting itself, and the
128  // process has gone, there's no need for this instance to stay
129  // around, so we disappear too.
130  die();
131 }
132 
133 void QgsRunProcess::dialogGone()
134 {
135  // The dialog has gone, so the user is no longer interested in the
136  // output from the process. Since the process will run happily
137  // without the QProcess object, this instance and its data can then
138  // go too, but disconnect the signals to prevent further functions in this
139  // class being called after it has been deleted (Qt seems not to be
140  // disconnecting them itself)
141 
142  mOutput = nullptr;
143 
144  disconnect( mProcess, static_cast < void ( QProcess::* )( QProcess::ProcessError ) >( &QProcess::error ), this, &QgsRunProcess::processError );
145  disconnect( mProcess, &QProcess::readyReadStandardOutput, this, &QgsRunProcess::stdoutAvailable );
146  disconnect( mProcess, &QProcess::readyReadStandardError, this, &QgsRunProcess::stderrAvailable );
147  disconnect( mProcess, static_cast < void ( QProcess::* )( int, QProcess::ExitStatus ) >( &QProcess::finished ), this, &QgsRunProcess::processExit );
148 
149  die();
150 }
151 
152 void QgsRunProcess::processError( QProcess::ProcessError err )
153 {
154  if ( err == QProcess::FailedToStart )
155  {
156  QgsMessageOutput *output = mOutput ? mOutput : QgsMessageOutput::createMessageOutput();
157  output->setMessage( tr( "Unable to run command %1" ).arg( mCommand ), QgsMessageOutput::MessageText );
158  // Didn't work, so no need to hang around
159  die();
160  }
161  else
162  {
163  QgsDebugMsg( "Got error: " + QString( "%d" ).arg( err ) );
164  }
165 }
166 #else
167 QgsRunProcess::QgsRunProcess( const QString &action, bool )
168 {
169  Q_UNUSED( action )
170  QgsDebugMsg( "Skipping command: " + action );
171 }
172 
173 QgsRunProcess::~QgsRunProcess()
174 {
175 }
176 #endif
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static QgsMessageOutput * createMessageOutput()
function that returns new class derived from QgsMessageOutput (don&#39;t forget to delete it then if show...
virtual void setMessage(const QString &message, MessageType msgType)=0
Sets message, it won&#39;t be displayed until.
Interface for showing messages from QGIS in GUI independent way.