7 Module implementing a dialog starting a process and showing its output. |
7 Module implementing a dialog starting a process and showing its output. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 |
11 |
12 from PyQt5.QtCore import pyqtSlot, QProcess, QTimer, QFileInfo |
12 from PyQt6.QtCore import pyqtSlot, QProcess, QTimer, QFileInfo |
13 from PyQt5.QtWidgets import ( |
13 from PyQt6.QtWidgets import ( |
14 QDialog, QDialogButtonBox, QAbstractButton, QTextEdit, QLineEdit |
14 QDialog, QDialogButtonBox, QAbstractButton, QTextEdit, QLineEdit |
15 ) |
15 ) |
16 |
16 |
17 from E5Gui import E5MessageBox, E5FileDialog |
17 from EricWidgets import EricMessageBox, EricFileDialog |
18 |
18 |
19 from .Ui_DjangoDialog import Ui_DjangoDialog |
19 from .Ui_DjangoDialog import Ui_DjangoDialog |
20 |
20 |
21 import Preferences |
21 import Preferences |
22 |
22 |
36 saveFilters=None, showInput=False, |
36 saveFilters=None, showInput=False, |
37 parent=None): |
37 parent=None): |
38 """ |
38 """ |
39 Constructor |
39 Constructor |
40 |
40 |
41 @param text text to be shown by the label (string) |
41 @param text text to be shown by the label |
42 @keyparam fixed flag indicating a fixed font should be used (boolean) |
42 @type str |
43 @keyparam linewrap flag indicating to wrap long lines (boolean) |
43 @param fixed flag indicating a fixed font should be used |
44 @keyparam msgSuccess optional string to show upon successful execution |
44 @type bool |
45 (string) |
45 @param linewrap flag indicating to wrap long lines |
46 @keyparam msgError optional string to show upon unsuccessful execution |
46 @type bool |
47 (string) |
47 @param msgSuccess optional string to show upon successful execution |
48 @keyparam saveFilters filename filter string (string) |
48 @type str |
49 @keyparam showInput flag indicating to show the input widgets (bool) |
49 @param msgError optional string to show upon unsuccessful execution |
50 @keyparam parent parent widget (QWidget) |
50 @type str |
|
51 @param saveFilters filename filter string |
|
52 @type str |
|
53 @param showInput flag indicating to show the input widgets |
|
54 @type bool |
|
55 @param parent parent widget |
|
56 @type QWidget |
51 """ |
57 """ |
52 super().__init__(parent) |
58 super().__init__(parent) |
53 self.setupUi(self) |
59 self.setupUi(self) |
54 |
60 |
55 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
61 self.buttonBox.button( |
56 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
62 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
57 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False) |
63 self.buttonBox.button( |
|
64 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
65 self.buttonBox.button( |
|
66 QDialogButtonBox.StandardButton.Save).setEnabled(False) |
58 if saveFilters is None: |
67 if saveFilters is None: |
59 self.buttonBox.button(QDialogButtonBox.Save).setHidden(True) |
68 self.buttonBox.button( |
|
69 QDialogButtonBox.StandardButton.Save).setHidden(True) |
60 |
70 |
61 self.ioEncoding = Preferences.getSystem("IOEncoding") |
71 self.ioEncoding = Preferences.getSystem("IOEncoding") |
62 |
72 |
63 self.proc = None |
73 self.proc = None |
64 self.argsLists = [] |
74 self.argsLists = [] |
77 self.resultbox.setFontFamily("Lucida Console") |
87 self.resultbox.setFontFamily("Lucida Console") |
78 else: |
88 else: |
79 self.resultbox.setFontFamily("Monospace") |
89 self.resultbox.setFontFamily("Monospace") |
80 |
90 |
81 if not linewrap: |
91 if not linewrap: |
82 self.resultbox.setLineWrapMode(QTextEdit.NoWrap) |
92 self.resultbox.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap) |
83 |
93 |
84 @pyqtSlot(QAbstractButton) |
94 @pyqtSlot(QAbstractButton) |
85 def on_buttonBox_clicked(self, button): |
95 def on_buttonBox_clicked(self, button): |
86 """ |
96 """ |
87 Private slot called by a button of the button box clicked. |
97 Private slot called by a button of the button box clicked. |
88 |
98 |
89 @param button button that was clicked (QAbstractButton) |
99 @param button button that was clicked |
90 """ |
100 @type QAbstractButton |
91 if button == self.buttonBox.button(QDialogButtonBox.Close): |
101 """ |
|
102 if button == self.buttonBox.button( |
|
103 QDialogButtonBox.StandardButton.Close |
|
104 ): |
92 self.close() |
105 self.close() |
93 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): |
106 elif button == self.buttonBox.button( |
|
107 QDialogButtonBox.StandardButton.Cancel |
|
108 ): |
94 self.__finish() |
109 self.__finish() |
95 elif button == self.buttonBox.button(QDialogButtonBox.Save): |
110 elif button == self.buttonBox.button( |
|
111 QDialogButtonBox.StandardButton.Save |
|
112 ): |
96 self.__saveData() |
113 self.__saveData() |
97 |
114 |
98 def __finish(self): |
115 def __finish(self): |
99 """ |
116 """ |
100 Private slot called when the process finished or the user pressed the |
117 Private slot called when the process finished or the user pressed the |
101 button. |
118 button. |
102 """ |
119 """ |
103 if ( |
120 if ( |
104 self.proc is not None and |
121 self.proc is not None and |
105 self.proc.state() != QProcess.NotRunning |
122 self.proc.state() != QProcess.ProcessState.NotRunning |
106 ): |
123 ): |
107 self.proc.terminate() |
124 self.proc.terminate() |
108 QTimer.singleShot(2000, self.proc.kill) |
125 QTimer.singleShot(2000, self.proc.kill) |
109 self.proc.waitForFinished(3000) |
126 self.proc.waitForFinished(3000) |
110 |
127 |
111 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
128 self.buttonBox.button( |
112 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
129 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
113 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
130 self.buttonBox.button( |
114 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(True) |
131 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
|
132 self.buttonBox.button( |
|
133 QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
134 self.buttonBox.button( |
|
135 QDialogButtonBox.StandardButton.Save).setEnabled(True) |
115 |
136 |
116 self.inputGroup.setEnabled(False) |
137 self.inputGroup.setEnabled(False) |
117 self.inputGroup.hide() |
138 self.inputGroup.hide() |
118 |
139 |
119 self.proc = None |
140 self.proc = None |
126 |
147 |
127 def __procFinished(self, exitCode, exitStatus): |
148 def __procFinished(self, exitCode, exitStatus): |
128 """ |
149 """ |
129 Private slot connected to the finished signal. |
150 Private slot connected to the finished signal. |
130 |
151 |
131 @param exitCode exit code of the process (integer) |
152 @param exitCode exit code of the process |
132 @param exitStatus exit status of the process (QProcess.ExitStatus) |
153 @type int |
133 """ |
154 @param exitStatus exit status of the process |
134 self.normal = (exitStatus == QProcess.NormalExit) and (exitCode == 0) |
155 @type QProcess.ExitStatus |
|
156 """ |
|
157 self.normal = ( |
|
158 exitStatus == QProcess.ExitStatus.NormalExit and |
|
159 exitCode == 0 |
|
160 ) |
135 self.__finish() |
161 self.__finish() |
136 |
162 |
137 if self.normal and self.msgSuccess: |
163 if self.normal and self.msgSuccess: |
138 self.resultbox.insertPlainText(self.msgSuccess) |
164 self.resultbox.insertPlainText(self.msgSuccess) |
139 elif not self.normal and self.msgError: |
165 elif not self.normal and self.msgError: |
144 def startProcess(self, args, workingDir=None, showCommand=True, |
170 def startProcess(self, args, workingDir=None, showCommand=True, |
145 mergedOutput=False): |
171 mergedOutput=False): |
146 """ |
172 """ |
147 Public slot used to start the process. |
173 Public slot used to start the process. |
148 |
174 |
149 @param args list of arguments for the process (list of strings) |
175 @param args list of arguments for the process |
150 @param workingDir working directory for the process (string) |
176 @type list of str |
|
177 @param workingDir working directory for the process |
|
178 @type str |
151 @param showCommand flag indicating to show the command executed |
179 @param showCommand flag indicating to show the command executed |
152 (boolean) |
180 @type bool |
153 @param mergedOutput flag indicating to merge the output of the process |
181 @param mergedOutput flag indicating to merge the output of the process |
154 (boolean) |
182 @type bool |
155 @return flag indicating a successful start of the process (boolean) |
183 @return flag indicating a successful start of the process |
|
184 @rtype bool |
156 """ |
185 """ |
157 self.errorGroup.hide() |
186 self.errorGroup.hide() |
158 |
187 |
159 self.normal = False |
188 self.normal = False |
160 |
189 |
161 self.proc = QProcess() |
190 self.proc = QProcess() |
162 if mergedOutput: |
191 if mergedOutput: |
163 self.proc.setProcessChannelMode(QProcess.MergedChannels) |
192 self.proc.setProcessChannelMode( |
|
193 QProcess.ProcessChannelMode.MergedChannels) |
164 |
194 |
165 if showCommand: |
195 if showCommand: |
166 self.resultbox.append(' '.join(args)) |
196 self.resultbox.append(' '.join(args)) |
167 self.resultbox.append('') |
197 self.resultbox.append('') |
168 |
198 |
181 self.proc.start(prog, args) |
211 self.proc.start(prog, args) |
182 procStarted = self.proc.waitForStarted() |
212 procStarted = self.proc.waitForStarted() |
183 if not procStarted: |
213 if not procStarted: |
184 self.buttonBox.setFocus() |
214 self.buttonBox.setFocus() |
185 self.inputGroup.setEnabled(False) |
215 self.inputGroup.setEnabled(False) |
186 E5MessageBox.critical( |
216 EricMessageBox.critical( |
187 self, |
217 self, |
188 self.tr('Process Generation Error'), |
218 self.tr('Process Generation Error'), |
189 self.tr( |
219 self.tr( |
190 'The process {0} could not be started. ' |
220 'The process {0} could not be started. ' |
191 'Ensure, that it is in the search path.' |
221 'Ensure, that it is in the search path.' |
204 mergedOutput=False): |
234 mergedOutput=False): |
205 """ |
235 """ |
206 Public slot used to start a batch of processes. |
236 Public slot used to start a batch of processes. |
207 |
237 |
208 @param argsLists list of lists of arguments for the processes |
238 @param argsLists list of lists of arguments for the processes |
209 (list of lists of strings) |
239 @type list of list of str |
210 @param workingDir working directory for the process (string) |
240 @param workingDir working directory for the process |
|
241 @type str |
211 @param mergedOutput flag indicating to merge the output of the process |
242 @param mergedOutput flag indicating to merge the output of the process |
212 (boolean) |
243 @type bool |
213 @return flag indicating a successful start of the first process |
244 @return flag indicating a successful start of the first process |
214 (boolean) |
245 @rtype bool |
215 """ |
246 """ |
216 self.argsLists = argsLists[:] |
247 self.argsLists = argsLists[:] |
217 self.workingDir = workingDir |
248 self.workingDir = workingDir |
218 self.mergedOutput = mergedOutput |
249 self.mergedOutput = mergedOutput |
219 |
250 |
229 |
260 |
230 def normalExit(self): |
261 def normalExit(self): |
231 """ |
262 """ |
232 Public method to check for a normal process termination. |
263 Public method to check for a normal process termination. |
233 |
264 |
234 @return flag indicating normal process termination (boolean) |
265 @return flag indicating normal process termination |
|
266 @rtype bool |
235 """ |
267 """ |
236 return self.normal |
268 return self.normal |
237 |
269 |
238 def normalExitWithoutErrors(self): |
270 def normalExitWithoutErrors(self): |
239 """ |
271 """ |
240 Public method to check for a normal process termination without |
272 Public method to check for a normal process termination without |
241 error messages. |
273 error messages. |
242 |
274 |
243 @return flag indicating normal process termination (boolean) |
275 @return flag indicating normal process termination |
|
276 @rtype bool |
244 """ |
277 """ |
245 return self.normal and self.errors.toPlainText() == "" |
278 return self.normal and self.errors.toPlainText() == "" |
246 |
279 |
247 def __readStdout(self): |
280 def __readStdout(self): |
248 """ |
281 """ |
293 |
326 |
294 try: |
327 try: |
295 with open(fname, "w", encoding="utf-8") as f: |
328 with open(fname, "w", encoding="utf-8") as f: |
296 f.write(txt) |
329 f.write(txt) |
297 except OSError as err: |
330 except OSError as err: |
298 E5MessageBox.critical( |
331 EricMessageBox.critical( |
299 self, |
332 self, |
300 self.tr("Error saving data"), |
333 self.tr("Error saving data"), |
301 self.tr("""<p>The data could not be written""" |
334 self.tr("""<p>The data could not be written""" |
302 """ to <b>{0}</b></p><p>Reason: {1}</p>""") |
335 """ to <b>{0}</b></p><p>Reason: {1}</p>""") |
303 .format(fname, str(err))) |
336 .format(fname, str(err))) |
304 |
337 |
305 def on_passwordCheckBox_toggled(self, isOn): |
338 def on_passwordCheckBox_toggled(self, isOn): |
306 """ |
339 """ |
307 Private slot to handle the password checkbox toggled. |
340 Private slot to handle the password checkbox toggled. |
308 |
341 |
309 @param isOn flag indicating the status of the check box (boolean) |
342 @param isOn flag indicating the status of the check box |
|
343 @type bool |
310 """ |
344 """ |
311 if isOn: |
345 if isOn: |
312 self.input.setEchoMode(QLineEdit.Password) |
346 self.input.setEchoMode(QLineEdit.EchoMode.Password) |
313 else: |
347 else: |
314 self.input.setEchoMode(QLineEdit.Normal) |
348 self.input.setEchoMode(QLineEdit.EchoMode.Normal) |
315 |
349 |
316 @pyqtSlot() |
350 @pyqtSlot() |
317 def on_sendButton_clicked(self): |
351 def on_sendButton_clicked(self): |
318 """ |
352 """ |
319 Private slot to send the input to the manage.py process. |
353 Private slot to send the input to the manage.py process. |