1 // -*- c++ -*-
2 // $Id: processwait.cpp,v 1.3 2009-08-28 17:08:55 robertl Exp $
3 //------------------------------------------------------------------------
4 //
5 // Copyright (C) 2009 S. Khai Mong <khai@mangrai.com>.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 // USA.
21 //
22 //------------------------------------------------------------------------
23 #include <QProcess>
24 #include <QStringList>
25 #include <QPlainTextEdit>
26 #include <QDialogButtonBox>
27 #include <QVBoxLayout>
28 #include <QDialog>
29 #include <QProgressBar>
30 #include <QPushButton>
31 #include <QTimer>
32 #include "processwait.h"
33 #include "appname.h"
34
35
36 //------------------------------------------------------------------------
37
processErrorString(QProcess::ProcessError err)38 QString ProcessWaitDialog::processErrorString(QProcess::ProcessError err)
39 {
40 switch (err) {
41 case QProcess::FailedToStart:
42 return QString(tr("Process failed to start"));
43 break;
44 case QProcess::Crashed:
45 return QString(tr("Process crashed"));
46 break;
47 case QProcess::Timedout:
48 return QString(tr("Process timedout"));
49 break;
50 case QProcess::WriteError:
51 return QString(tr("Error while trying to write to process"));
52 break;
53 case QProcess::ReadError:
54 return QString(tr("Error while trying to read from process"));
55 break;
56 case QProcess::UnknownError:
57 default:
58 return QString(tr("Unknown process error"));
59 }
60 return QString("");
61 }
62 //------------------------------------------------------------------------
ProcessWaitDialog(QWidget * parent,QProcess * process)63 ProcessWaitDialog::ProcessWaitDialog(QWidget* parent, QProcess* process):
64 QDialog(parent), process_(process)
65 {
66 this->resize(400, 220);
67 this->setWindowTitle(QString(appName) + tr(" ... Process GPSBabel"));
68 auto* layout = new QVBoxLayout(this);
69
70 textEdit_ = new QPlainTextEdit(this);
71 textEdit_->setReadOnly(true);
72 layout->addWidget(textEdit_);
73
74 progressBar_ = new QProgressBar(this);
75 progressBar_->setTextVisible(false);
76 layout->addWidget(progressBar_);
77
78 buttonBox_ = new QDialogButtonBox(this);
79 buttonBox_->setOrientation(Qt::Horizontal);
80 buttonBox_->setStandardButtons(QDialogButtonBox::Abort);
81 QPushButton* btn = buttonBox_->button(QDialogButtonBox::Abort);
82 btn->setText(tr("Stop Process"));
83 layout->addWidget(buttonBox_);
84
85 connect(process, SIGNAL(error(QProcess::ProcessError)),
86 this, SLOT(errorX(QProcess::ProcessError)));
87 connect(process, SIGNAL(finished(int,QProcess::ExitStatus)),
88 this, SLOT(finishedX(int,QProcess::ExitStatus)));
89 connect(process, SIGNAL(readyReadStandardError()),
90 this, SLOT(readyReadStandardErrorX()));
91 connect(process, SIGNAL(readyReadStandardOutput()),
92 this, SLOT(readyReadStandardOutputX()));
93 connect(btn, SIGNAL(clicked()),
94 this, SLOT(stopClickedX()));
95 exitStatus_ = QProcess::CrashExit; // Assume all errors are crashes for now.
96
97 bufferedOut_ = "";
98
99 //
100 for (int i=0; i<=100; i+=2) {
101 progressVals_.push_back(i);
102 }
103 for (int i=98; i>0; i-=2) {
104 progressVals_.push_back(i);
105 }
106 progressIndex_ = progressVals_.size()/2;
107
108 timer_ = new QTimer(this);
109 timer_->setInterval(100);
110 timer_->setSingleShot(false);
111 connect(timer_, SIGNAL(timeout()), this, SLOT(timeoutX()));
112 stopCount_ = -1;
113 ecode_ = 0;
114 timer_->start();
115 errorString_ = "";
116
117 }
118
119 //------------------------------------------------------------------------
120 ProcessWaitDialog::~ProcessWaitDialog()
121 = default;
122 //------------------------------------------------------------------------
getExitedNormally()123 bool ProcessWaitDialog::getExitedNormally()
124 {
125 return (errorString_.length() == 0);
126 }
127
128 //------------------------------------------------------------------------
getErrorString()129 QString ProcessWaitDialog::getErrorString()
130 {
131 return errorString_;
132 }
133
134 //------------------------------------------------------------------------
getExitCode()135 int ProcessWaitDialog::getExitCode()
136 {
137 return ecode_;
138 }
139
140 //------------------------------------------------------------------------
stopClickedX()141 void ProcessWaitDialog::stopClickedX()
142 {
143 process_->terminate();
144 }
145
146 //------------------------------------------------------------------------
timeoutX()147 void ProcessWaitDialog::timeoutX()
148 {
149 progressIndex_++;
150 int idx = progressIndex_ % progressVals_.size();
151 progressBar_->setValue(progressVals_[idx]);
152 if (stopCount_ >=0) {
153 stopCount_++;
154 }
155 if (stopCount_ > 150) {
156 process_->kill();
157 errorString_ = QString(tr("Process did not terminate successfully"));
158 timer_->stop();
159 accept();
160 }
161 }
162
163 //------------------------------------------------------------------------
errorX(QProcess::ProcessError err)164 void ProcessWaitDialog::errorX(QProcess::ProcessError err)
165 {
166 errorString_ = processErrorString(err);
167 timer_->stop();
168 accept();
169 }
170
171 //------------------------------------------------------------------------
finishedX(int exitCode,QProcess::ExitStatus es)172 void ProcessWaitDialog::finishedX(int exitCode, QProcess::ExitStatus es)
173 {
174 ecode_ = exitCode;
175 if (es == QProcess::CrashExit) {
176 errorString_ = QString(tr("Process crashed while running"));
177 }
178 timer_->stop();
179 accept();
180 }
181
182
183 //------------------------------------------------------------------------
184 // appendPlainText automatically puts in a new line with every call. That's
185 // why you have to buffer it, and only append when we get a real newline.
186 //
appendToText(const char * ptr)187 void ProcessWaitDialog::appendToText(const char* ptr)
188 {
189 outputString_ += QString(ptr);
190 for (const char* cptr = ptr; *cptr != 0; cptr++) {
191 if (*cptr == '\r') {
192 continue;
193 }
194 if (*cptr == '\n') {
195 textEdit_->appendPlainText(QString::fromStdString(bufferedOut_));
196 bufferedOut_ = "";
197 continue;
198 }
199 bufferedOut_ += *cptr;
200 }
201 }
202
203
204 //------------------------------------------------------------------------
readyReadStandardErrorX()205 void ProcessWaitDialog::readyReadStandardErrorX()
206 {
207 QByteArray d = process_->readAllStandardError();
208 appendToText(d.data());
209 }
210
211 //------------------------------------------------------------------------
readyReadStandardOutputX()212 void ProcessWaitDialog::readyReadStandardOutputX()
213 {
214 QByteArray d = process_->readAllStandardOutput();
215 appendToText(d.data());
216 }
217
closeEvent(QCloseEvent * event)218 void ProcessWaitDialog::closeEvent(QCloseEvent* event)
219 {
220 event->ignore();
221 }
222