QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 <QMessageBox>
27 
28 QgsRunProcess::QgsRunProcess( const QString& action, bool capture )
29  : mProcess( NULL ), mOutput( NULL )
30 {
31  // Make up a string from the command and arguments that we'll use
32  // for display purposes
33  QgsDebugMsg( "Running command: " + action );
34 
35  mCommand = action;
36 
37  mProcess = new QProcess;
38 
39  if ( capture )
40  {
41  connect( mProcess, SIGNAL( error( QProcess::ProcessError ) ), this, SLOT( processError( QProcess::ProcessError ) ) );
42  connect( mProcess, SIGNAL( readyReadStandardOutput() ), this, SLOT( stdoutAvailable() ) );
43  connect( mProcess, SIGNAL( readyReadStandardError() ), this, SLOT( stderrAvailable() ) );
44  // We only care if the process has finished if we are capturing
45  // the output from the process, hence this connect() call is
46  // inside the capture if() statement.
47  connect( mProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( processExit( int, QProcess::ExitStatus ) ) );
48 
49  // Use QgsMessageOutput for displaying output to user
50  // It will delete itself when the dialog box is closed.
52  mOutput->setTitle( action );
53  mOutput->setMessage( tr( "<b>Starting %1...</b>" ).arg( action ), QgsMessageOutput::MessageHtml );
54  mOutput->showMessage( false ); // non-blocking
55 
56  // get notification of delete if it's derived from QObject
57  QObject* mOutputObj = dynamic_cast<QObject *>( mOutput );
58  if ( mOutputObj )
59  {
60  connect( mOutputObj, SIGNAL( destroyed() ), this, SLOT( dialogGone() ) );
61  }
62 
63  // start the process!
64  mProcess->start( action );
65  }
66  else
67  {
68  if ( ! mProcess->startDetached( action ) ) // let the program run by itself
69  {
70  QMessageBox::critical( 0, tr( "Action" ),
71  tr( "Unable to run command\n%1" ).arg( action ),
72  QMessageBox::Ok, Qt::NoButton );
73  }
74  // We're not capturing the output from the process, so we don't
75  // need to exist anymore.
76  die();
77  }
78 }
79 
80 QgsRunProcess::~QgsRunProcess()
81 {
82  delete mProcess;
83 }
84 
85 void QgsRunProcess::die()
86 {
87  // safe way to do "delete this" for QObjects
88  deleteLater();
89 }
90 
92 {
93  QString line( mProcess->readAllStandardOutput() );
94 
95  // Add the new output to the dialog box
96  mOutput->appendMessage( line );
97 }
98 
100 {
101  QString line( mProcess->readAllStandardError() );
102 
103  // Add the new output to the dialog box, but color it red
104  mOutput->appendMessage( "<font color=red>" + line + "</font>" );
105 }
106 
107 void QgsRunProcess::processExit( int, QProcess::ExitStatus )
108 {
109  // Because we catch the dialog box going (the dialogGone()
110  // function), and delete this instance, control will only pass to
111  // this function if the dialog box still exists when the process
112  // exits, so it's always safe to use the pointer to the dialog box
113  // (unless it was never created in the first case, which is what the
114  // test against 0 is for).
115 
116  if ( mOutput != 0 )
117  {
118  mOutput->appendMessage( "<b>" + tr( "Done" ) + "</b>" );
119  }
120 
121  // Since the dialog box takes care of deleting itself, and the
122  // process has gone, there's no need for this instance to stay
123  // around, so we disappear too.
124  die();
125 }
126 
128 {
129  // The dialog has gone, so the user is no longer interested in the
130  // output from the process. Since the process will run happily
131  // without the QProcess object, this instance and its data can then
132  // go too, but disconnect the signals to prevent further functions in this
133  // class being called after it has been deleted (Qt seems not to be
134  // disconnecting them itself)
135 
136  mOutput = 0;
137 
138  disconnect( mProcess, SIGNAL( error( QProcess::ProcessError ) ), this, SLOT( processError( QProcess::ProcessError ) ) );
139  disconnect( mProcess, SIGNAL( readyReadStandardOutput() ), this, SLOT( stdoutAvailable() ) );
140  disconnect( mProcess, SIGNAL( readyReadStandardError() ), this, SLOT( stderrAvailable() ) );
141  disconnect( mProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( processExit( int, QProcess::ExitStatus ) ) );
142 
143  die();
144 }
145 
146 void QgsRunProcess::processError( QProcess::ProcessError err )
147 {
148  if ( err == QProcess::FailedToStart )
149  {
150  QgsMessageOutput* output = mOutput ? mOutput : QgsMessageOutput::createMessageOutput();
151  output->setMessage( tr( "Unable to run command %1" ).arg( mCommand ), QgsMessageOutput::MessageText );
152  // Didn't work, so no need to hang around
153  die();
154  }
155  else
156  {
157  QgsDebugMsg( "Got error: " + QString( "%d" ).arg( err ) );
158  }
159 }