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