eric6/E5Gui/E5ProcessDialog.py

branch
micropython
changeset 7108
4f6133a01c6a
child 7192
a22eee00b052
equal deleted inserted replaced
7103:aea236dc8002 7108:4f6133a01c6a
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog starting a process and showing its output.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 import os
17
18 from PyQt5.QtCore import QProcess, QTimer, pyqtSlot, Qt, QCoreApplication, \
19 QProcessEnvironment
20 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QLineEdit
21
22 from E5Gui import E5MessageBox
23
24 from .Ui_E5ProcessDialog import Ui_E5ProcessDialog
25
26 from Globals import strToQByteArray
27 import Preferences
28
29
30 class E5ProcessDialog(QDialog, Ui_E5ProcessDialog):
31 """
32 Class implementing a dialog starting a process and showing its output.
33
34 It starts a QProcess and displays a dialog that
35 shows the output of the process. The dialog is modal,
36 which causes a synchronized execution of the process.
37 """
38 def __init__(self, outputTitle="", windowTitle="", parent=None):
39 """
40 Constructor
41
42 @param outputTitle title for the output group
43 @type str
44 @param windowTitle title of the dialog
45 @type str
46 @param parent reference to the parent widget
47 @type QWidget
48 """
49 super(E5ProcessDialog, self).__init__(parent)
50 self.setupUi(self)
51
52 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
53 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
54
55 if windowTitle:
56 self.setWindowTitle(windowTitle)
57 if outputTitle:
58 self.outputGroup.setTitle(outputTitle)
59
60 self.__process = None
61
62 self.show()
63 QCoreApplication.processEvents()
64
65 def __finish(self):
66 """
67 Private slot called when the process finished or the user pressed
68 the button.
69 """
70 if self.__process is not None and \
71 self.__process.state() != QProcess.NotRunning:
72 self.__process.terminate()
73 QTimer.singleShot(2000, self.__process.kill)
74 self.__process.waitForFinished(3000)
75
76 self.inputGroup.setEnabled(False)
77 self.inputGroup.hide()
78
79 self.__process = None
80
81 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
82 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
83 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
84 self.buttonBox.button(QDialogButtonBox.Close).setFocus(
85 Qt.OtherFocusReason)
86
87 def on_buttonBox_clicked(self, button):
88 """
89 Private slot called by a button of the button box clicked.
90
91 @param button button that was clicked
92 @type QAbstractButton
93 """
94 if button == self.buttonBox.button(QDialogButtonBox.Close):
95 self.close()
96 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
97 self.statusLabel.setText(self.tr("Process canceled."))
98 self.__finish()
99
100 def __procFinished(self, exitCode, exitStatus):
101 """
102 Private slot connected to the finished signal.
103
104 @param exitCode exit code of the process
105 @type int
106 @param exitStatus exit status of the process
107 @type QProcess.ExitStatus
108 """
109 self.__normal = (exitStatus == QProcess.NormalExit) and (exitCode == 0)
110 if self.__normal:
111 self.statusLabel.setText(self.tr("Process finished successfully."))
112 elif exitStatus == QProcess.CrashExit:
113 self.statusLabel.setText(self.tr("Process crashed."))
114 else:
115 self.statusLabel.setText(
116 self.tr("Process finished with exit code {0}")
117 .format(exitCode))
118 self.__finish()
119
120 def startProcess(self, program, args, workingDir=None, showArgs=True,
121 environment=None):
122 """
123 Public slot used to start the process.
124
125 @param program path of the program to be executed
126 @type str
127 @param args list of arguments for the process
128 @type list of str
129 @param workingDir working directory for the process
130 @type str
131 @param showArgs flag indicating to show the arguments
132 @type bool
133 @param environment dictionary of environment settings to add
134 or change for the process
135 @type dict
136 @return flag indicating a successful start of the process
137 @rtype bool
138 """
139 self.errorGroup.hide()
140 self.__normal = False
141 self.__intercept = False
142
143 if environment is None:
144 environment = {}
145
146 if showArgs:
147 self.resultbox.append(program + ' ' + ' '.join(args))
148 self.resultbox.append('')
149
150 self.__process = QProcess()
151 if environment:
152 env = QProcessEnvironment.systemEnvironment()
153 for key, value in environment.items():
154 env.insert(key, value)
155 self.__process.setProcessEnvironment(env)
156
157 self.__process.finished.connect(self.__procFinished)
158 self.__process.readyReadStandardOutput.connect(self.__readStdout)
159 self.__process.readyReadStandardError.connect(self.__readStderr)
160
161 if workingDir:
162 self.__process.setWorkingDirectory(workingDir)
163
164 self.__process.start(program, args)
165 procStarted = self.__process.waitForStarted(10000)
166 if not procStarted:
167 self.buttonBox.setFocus()
168 self.inputGroup.setEnabled(False)
169 E5MessageBox.critical(
170 self,
171 self.tr('Process Generation Error'),
172 self.tr(
173 '<p>The process <b>{0}</b> could not be started.</p>'
174 ).format(program))
175 else:
176 self.inputGroup.setEnabled(True)
177 self.inputGroup.show()
178
179 return procStarted
180
181 def normalExit(self):
182 """
183 Public method to check for a normal process termination.
184
185 @return flag indicating normal process termination
186 @rtype bool
187 """
188 return self.__normal
189
190 def normalExitWithoutErrors(self):
191 """
192 Public method to check for a normal process termination without
193 error messages.
194
195 @return flag indicating normal process termination
196 @rtype bool
197 """
198 return self.__normal and self.errors.toPlainText() == ""
199
200 def __readStdout(self):
201 """
202 Private slot to handle the readyReadStandardOutput signal.
203
204 It reads the output of the process and inserts it into the
205 output pane.
206 """
207 if self.__process is not None:
208 s = str(self.__process.readAllStandardOutput(),
209 Preferences.getSystem("IOEncoding"),
210 'replace')
211 self.resultbox.insertPlainText(s)
212 self.resultbox.ensureCursorVisible()
213
214 QCoreApplication.processEvents()
215
216 def __readStderr(self):
217 """
218 Private slot to handle the readyReadStandardError signal.
219
220 It reads the error output of the process and inserts it into the
221 error pane.
222 """
223 if self.__process is not None:
224 s = str(self.__process.readAllStandardError(),
225 Preferences.getSystem("IOEncoding"),
226 'replace')
227
228 self.errorGroup.show()
229 self.errors.insertPlainText(s)
230 self.errors.ensureCursorVisible()
231
232 QCoreApplication.processEvents()
233
234 def on_passwordCheckBox_toggled(self, isOn):
235 """
236 Private slot to handle the password checkbox toggled.
237
238 @param isOn flag indicating the status of the check box
239 @type bool
240 """
241 if isOn:
242 self.input.setEchoMode(QLineEdit.Password)
243 else:
244 self.input.setEchoMode(QLineEdit.Normal)
245
246 @pyqtSlot()
247 def on_sendButton_clicked(self):
248 """
249 Private slot to send the input to the git process.
250 """
251 inputTxt = self.input.text()
252 inputTxt += os.linesep
253
254 if self.passwordCheckBox.isChecked():
255 self.errors.insertPlainText(os.linesep)
256 self.errors.ensureCursorVisible()
257 else:
258 self.errors.insertPlainText(inputTxt)
259 self.errors.ensureCursorVisible()
260
261 self.__process.write(strToQByteArray(inputTxt))
262
263 self.passwordCheckBox.setChecked(False)
264 self.input.clear()
265
266 def on_input_returnPressed(self):
267 """
268 Private slot to handle the press of the return key in the input field.
269 """
270 self.__intercept = True
271 self.on_sendButton_clicked()
272
273 def keyPressEvent(self, evt):
274 """
275 Protected slot to handle a key press event.
276
277 @param evt the key press event (QKeyEvent)
278 """
279 if self.__intercept:
280 self.__intercept = False
281 evt.accept()
282 return
283
284 super(E5ProcessDialog, self).keyPressEvent(evt)

eric ide

mercurial