22 |
22 |
23 class CondaExecDialog(QDialog, Ui_CondaExecDialog): |
23 class CondaExecDialog(QDialog, Ui_CondaExecDialog): |
24 """ |
24 """ |
25 Class implementing a dialog to show the output of a conda execution. |
25 Class implementing a dialog to show the output of a conda execution. |
26 """ |
26 """ |
|
27 |
27 def __init__(self, command, parent=None): |
28 def __init__(self, command, parent=None): |
28 """ |
29 """ |
29 Constructor |
30 Constructor |
30 |
31 |
31 @param command conda command executed |
32 @param command conda command executed |
32 @type str |
33 @type str |
33 @param parent reference to the parent widget |
34 @param parent reference to the parent widget |
34 @type QWidget |
35 @type QWidget |
35 """ |
36 """ |
36 super().__init__(parent) |
37 super().__init__(parent) |
37 self.setupUi(self) |
38 self.setupUi(self) |
38 |
39 |
39 self.buttonBox.button( |
40 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
40 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
41 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
41 self.buttonBox.button( |
42 |
42 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
43 |
|
44 self.__condaCommand = command |
43 self.__condaCommand = command |
45 |
44 |
46 self.__process = None |
45 self.__process = None |
47 self.__condaExe = Preferences.getConda("CondaExecutable") |
46 self.__condaExe = Preferences.getConda("CondaExecutable") |
48 if not self.__condaExe: |
47 if not self.__condaExe: |
49 self.__condaExe = "conda" |
48 self.__condaExe = "conda" |
50 |
49 |
51 @pyqtSlot(QAbstractButton) |
50 @pyqtSlot(QAbstractButton) |
52 def on_buttonBox_clicked(self, button): |
51 def on_buttonBox_clicked(self, button): |
53 """ |
52 """ |
54 Private slot called by a button of the button box clicked. |
53 Private slot called by a button of the button box clicked. |
55 |
54 |
56 @param button button that was clicked |
55 @param button button that was clicked |
57 @type QAbstractButton |
56 @type QAbstractButton |
58 """ |
57 """ |
59 if button == self.buttonBox.button( |
58 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
60 QDialogButtonBox.StandardButton.Close |
|
61 ): |
|
62 self.accept() |
59 self.accept() |
63 elif button == self.buttonBox.button( |
60 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): |
64 QDialogButtonBox.StandardButton.Cancel |
|
65 ): |
|
66 self.__finish(1, 0) |
61 self.__finish(1, 0) |
67 |
62 |
68 def start(self, arguments): |
63 def start(self, arguments): |
69 """ |
64 """ |
70 Public slot to start the conda command. |
65 Public slot to start the conda command. |
71 |
66 |
72 @param arguments commandline arguments for conda program |
67 @param arguments commandline arguments for conda program |
73 @type list of str |
68 @type list of str |
74 """ |
69 """ |
75 self.errorGroup.hide() |
70 self.errorGroup.hide() |
76 self.progressLabel.hide() |
71 self.progressLabel.hide() |
77 self.progressBar.hide() |
72 self.progressBar.hide() |
78 |
73 |
79 self.contents.clear() |
74 self.contents.clear() |
80 self.errors.clear() |
75 self.errors.clear() |
81 self.progressLabel.clear() |
76 self.progressLabel.clear() |
82 self.progressBar.setValue(0) |
77 self.progressBar.setValue(0) |
83 |
78 |
84 self.__bufferedStdout = None |
79 self.__bufferedStdout = None |
85 self.__json = "--json" in arguments |
80 self.__json = "--json" in arguments |
86 self.__firstProgress = True |
81 self.__firstProgress = True |
87 self.__lastFetchFile = "" |
82 self.__lastFetchFile = "" |
88 |
83 |
89 self.__statusOk = False |
84 self.__statusOk = False |
90 self.__result = None |
85 self.__result = None |
91 |
86 |
92 self.__logOutput(self.__condaExe + " " + " ".join(arguments) + "\n\n") |
87 self.__logOutput(self.__condaExe + " " + " ".join(arguments) + "\n\n") |
93 |
88 |
94 self.__process = QProcess() |
89 self.__process = QProcess() |
95 self.__process.readyReadStandardOutput.connect(self.__readStdout) |
90 self.__process.readyReadStandardOutput.connect(self.__readStdout) |
96 self.__process.readyReadStandardError.connect(self.__readStderr) |
91 self.__process.readyReadStandardError.connect(self.__readStderr) |
97 self.__process.finished.connect(self.__finish) |
92 self.__process.finished.connect(self.__finish) |
98 |
93 |
99 self.__process.start(self.__condaExe, arguments) |
94 self.__process.start(self.__condaExe, arguments) |
100 procStarted = self.__process.waitForStarted(5000) |
95 procStarted = self.__process.waitForStarted(5000) |
101 if not procStarted: |
96 if not procStarted: |
102 EricMessageBox.critical( |
97 EricMessageBox.critical( |
103 self, |
98 self, |
104 self.tr("Conda Execution"), |
99 self.tr("Conda Execution"), |
105 self.tr("""The conda executable could not be started. Is it""" |
100 self.tr( |
106 """ configured correctly?""")) |
101 """The conda executable could not be started. Is it""" |
|
102 """ configured correctly?""" |
|
103 ), |
|
104 ) |
107 self.__finish(1, 0) |
105 self.__finish(1, 0) |
108 else: |
106 else: |
109 self.__logOutput(self.tr("Operation started.\n")) |
107 self.__logOutput(self.tr("Operation started.\n")) |
110 |
108 |
111 def __finish(self, exitCode, exitStatus, giveUp=False): |
109 def __finish(self, exitCode, exitStatus, giveUp=False): |
112 """ |
110 """ |
113 Private slot called when the process finished. |
111 Private slot called when the process finished. |
114 |
112 |
115 It is called when the process finished or |
113 It is called when the process finished or |
116 the user pressed the button. |
114 the user pressed the button. |
117 |
115 |
118 @param exitCode exit code of the process |
116 @param exitCode exit code of the process |
119 @type int |
117 @type int |
120 @param exitStatus exit status of the process |
118 @param exitStatus exit status of the process |
121 @type QProcess.ExitStatus |
119 @type QProcess.ExitStatus |
122 @param giveUp flag indicating to not start another attempt |
120 @param giveUp flag indicating to not start another attempt |
123 @type bool |
121 @type bool |
124 """ |
122 """ |
125 if (self.__process is not None and |
123 if ( |
126 self.__process.state() != QProcess.ProcessState.NotRunning): |
124 self.__process is not None |
|
125 and self.__process.state() != QProcess.ProcessState.NotRunning |
|
126 ): |
127 self.__process.terminate() |
127 self.__process.terminate() |
128 QTimer.singleShot(2000, self.__process.kill) |
128 QTimer.singleShot(2000, self.__process.kill) |
129 self.__process.waitForFinished(3000) |
129 self.__process.waitForFinished(3000) |
130 |
130 |
131 self.buttonBox.button( |
131 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
132 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
132 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
133 self.buttonBox.button( |
133 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
134 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
134 |
135 self.buttonBox.button( |
|
136 QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
137 |
|
138 self.progressLabel.hide() |
135 self.progressLabel.hide() |
139 self.progressBar.hide() |
136 self.progressBar.hide() |
140 |
137 |
141 self.__statusOk = exitCode == 0 |
138 self.__statusOk = exitCode == 0 |
142 |
139 |
143 self.__logOutput(self.tr("Operation finished.\n")) |
140 self.__logOutput(self.tr("Operation finished.\n")) |
144 if not self.__json and self.__bufferedStdout: |
141 if not self.__json and self.__bufferedStdout: |
145 self.__logOutput(self.__bufferedStdout) |
142 self.__logOutput(self.__bufferedStdout) |
146 |
143 |
147 if self.__json and self.__bufferedStdout: |
144 if self.__json and self.__bufferedStdout: |
148 index = self.__bufferedStdout.find("{") |
145 index = self.__bufferedStdout.find("{") |
149 rindex = self.__bufferedStdout.rfind("}") |
146 rindex = self.__bufferedStdout.rfind("}") |
150 self.__bufferedStdout = self.__bufferedStdout[index:rindex + 1] |
147 self.__bufferedStdout = self.__bufferedStdout[index : rindex + 1] |
151 try: |
148 try: |
152 self.__result = json.loads(self.__bufferedStdout) |
149 self.__result = json.loads(self.__bufferedStdout) |
153 except Exception as error: |
150 except Exception as error: |
154 self.__result = {} |
151 self.__result = {} |
155 self.__logError(str(error)) |
152 self.__logError(str(error)) |
156 return |
153 return |
157 |
154 |
158 if "error" in self.__result: |
155 if "error" in self.__result: |
159 self.__logError(self.__result["error"]) |
156 self.__logError(self.__result["error"]) |
160 self.__statusOk = False |
157 self.__statusOk = False |
161 elif ("success" in self.__result and |
158 elif "success" in self.__result and not self.__result["success"]: |
162 not self.__result["success"]): |
|
163 self.__logError( |
159 self.__logError( |
164 self.tr("Conda command '{0}' did not return success.") |
160 self.tr("Conda command '{0}' did not return success.").format( |
165 .format(self.__condaCommand)) |
161 self.__condaCommand |
|
162 ) |
|
163 ) |
166 if "message" in self.__result: |
164 if "message" in self.__result: |
167 self.__logError("\n") |
165 self.__logError("\n") |
168 self.__logError( |
166 self.__logError( |
169 self.tr("\nConda Message: {0}").format( |
167 self.tr("\nConda Message: {0}").format(self.__result["message"]) |
170 self.__result["message"])) |
168 ) |
171 self.__statusOk = False |
169 self.__statusOk = False |
172 elif "message" in self.__result: |
170 elif "message" in self.__result: |
173 self.__logOutput( |
171 self.__logOutput( |
174 self.tr("\nConda Message: {0}").format( |
172 self.tr("\nConda Message: {0}").format(self.__result["message"]) |
175 self.__result["message"])) |
173 ) |
176 |
174 |
177 def getResult(self): |
175 def getResult(self): |
178 """ |
176 """ |
179 Public method to the result of the command execution. |
177 Public method to the result of the command execution. |
180 |
178 |
181 @return tuple containing a flag indicating success and the result data. |
179 @return tuple containing a flag indicating success and the result data. |
182 @rtype tuple of (bool, dict) |
180 @rtype tuple of (bool, dict) |
183 """ |
181 """ |
184 return self.__statusOk, self.__result |
182 return self.__statusOk, self.__result |
185 |
183 |
186 def __setProgressValues(self, jsonDict, progressType): |
184 def __setProgressValues(self, jsonDict, progressType): |
187 """ |
185 """ |
188 Private method to set the value of the progress bar. |
186 Private method to set the value of the progress bar. |
189 |
187 |
190 @param jsonDict dictionary containing the progress info |
188 @param jsonDict dictionary containing the progress info |
191 @type dict |
189 @type dict |
192 @param progressType action type to check for |
190 @param progressType action type to check for |
193 @type str |
191 @type str |
194 @return flag indicating success |
192 @return flag indicating success |
196 """ |
194 """ |
197 if progressType in jsonDict and "progress" in jsonDict: |
195 if progressType in jsonDict and "progress" in jsonDict: |
198 if jsonDict["maxval"] == 1: |
196 if jsonDict["maxval"] == 1: |
199 self.progressBar.setMaximum(100) |
197 self.progressBar.setMaximum(100) |
200 # percent values |
198 # percent values |
201 self.progressBar.setValue( |
199 self.progressBar.setValue(int(jsonDict["progress"] * 100)) |
202 int(jsonDict["progress"] * 100)) |
|
203 parts = jsonDict["fetch"].split("|") |
200 parts = jsonDict["fetch"].split("|") |
204 filename = parts[0].strip() |
201 filename = parts[0].strip() |
205 filesize = parts[1].strip() |
202 filesize = parts[1].strip() |
206 else: |
203 else: |
207 self.progressBar.setMaximum(jsonDict["maxval"]) |
204 self.progressBar.setMaximum(jsonDict["maxval"]) |
208 self.progressBar.setValue(jsonDict["progress"]) |
205 self.progressBar.setValue(jsonDict["progress"]) |
209 filename = jsonDict["fetch"].strip() |
206 filename = jsonDict["fetch"].strip() |
210 filesize = Globals.dataString(int(jsonDict["maxval"])) |
207 filesize = Globals.dataString(int(jsonDict["maxval"])) |
211 |
208 |
212 self.progressLabel.setText( |
209 self.progressLabel.setText( |
213 self.tr("{0} (Size: {1})").format(filename, filesize)) |
210 self.tr("{0} (Size: {1})").format(filename, filesize) |
214 |
211 ) |
|
212 |
215 if progressType == "fetch": |
213 if progressType == "fetch": |
216 if filename != self.__lastFetchFile: |
214 if filename != self.__lastFetchFile: |
217 self.__logOutput( |
215 self.__logOutput(self.tr("Fetching {0} ...").format(filename)) |
218 self.tr("Fetching {0} ...").format(filename)) |
|
219 self.__lastFetchFile = filename |
216 self.__lastFetchFile = filename |
220 elif jsonDict["finished"]: |
217 elif jsonDict["finished"]: |
221 self.__logOutput(self.tr(" Done.\n")) |
218 self.__logOutput(self.tr(" Done.\n")) |
222 |
219 |
223 if self.__firstProgress: |
220 if self.__firstProgress: |
224 self.progressLabel.show() |
221 self.progressLabel.show() |
225 self.progressBar.show() |
222 self.progressBar.show() |
226 self.__firstProgress = False |
223 self.__firstProgress = False |
227 |
224 |
228 return True |
225 return True |
229 |
226 |
230 return False |
227 return False |
231 |
228 |
232 def __readStdout(self): |
229 def __readStdout(self): |
233 """ |
230 """ |
234 Private slot to handle the readyReadStandardOutput signal. |
231 Private slot to handle the readyReadStandardOutput signal. |
235 |
232 |
236 It reads the output of the process, formats it and inserts it into |
233 It reads the output of the process, formats it and inserts it into |
237 the contents pane. |
234 the contents pane. |
238 """ |
235 """ |
239 all_stdout = str(self.__process.readAllStandardOutput(), |
236 all_stdout = str( |
240 Preferences.getSystem("IOEncoding"), |
237 self.__process.readAllStandardOutput(), |
241 'replace') |
238 Preferences.getSystem("IOEncoding"), |
|
239 "replace", |
|
240 ) |
242 all_stdout = all_stdout.replace("\x00", "") |
241 all_stdout = all_stdout.replace("\x00", "") |
243 if self.__json: |
242 if self.__json: |
244 for stdout in all_stdout.splitlines(): |
243 for stdout in all_stdout.splitlines(): |
245 try: |
244 try: |
246 jsonDict = json.loads(stdout.replace("\x00", "").strip()) |
245 jsonDict = json.loads(stdout.replace("\x00", "").strip()) |
257 self.__bufferedStdout = stdout |
256 self.__bufferedStdout = stdout |
258 else: |
257 else: |
259 self.__bufferedStdout += stdout |
258 self.__bufferedStdout += stdout |
260 else: |
259 else: |
261 self.__logOutput(all_stdout) |
260 self.__logOutput(all_stdout) |
262 |
261 |
263 def __readStderr(self): |
262 def __readStderr(self): |
264 """ |
263 """ |
265 Private slot to handle the readyReadStandardError signal. |
264 Private slot to handle the readyReadStandardError signal. |
266 |
265 |
267 It reads the error output of the process and inserts it into the |
266 It reads the error output of the process and inserts it into the |
268 error pane. |
267 error pane. |
269 """ |
268 """ |
270 self.__process.setReadChannel(QProcess.ProcessChannel.StandardError) |
269 self.__process.setReadChannel(QProcess.ProcessChannel.StandardError) |
271 |
270 |
272 while self.__process.canReadLine(): |
271 while self.__process.canReadLine(): |
273 stderr = str(self.__process.readLine(), |
272 stderr = str( |
274 Preferences.getSystem("IOEncoding"), |
273 self.__process.readLine(), |
275 'replace') |
274 Preferences.getSystem("IOEncoding"), |
|
275 "replace", |
|
276 ) |
276 self.__logError(stderr) |
277 self.__logError(stderr) |
277 |
278 |
278 def __logOutput(self, stdout): |
279 def __logOutput(self, stdout): |
279 """ |
280 """ |
280 Private method to log some output. |
281 Private method to log some output. |
281 |
282 |
282 @param stdout output string to log |
283 @param stdout output string to log |
283 @type str |
284 @type str |
284 """ |
285 """ |
285 self.contents.insertPlainText(stdout) |
286 self.contents.insertPlainText(stdout) |
286 self.contents.ensureCursorVisible() |
287 self.contents.ensureCursorVisible() |
287 |
288 |
288 def __logError(self, stderr): |
289 def __logError(self, stderr): |
289 """ |
290 """ |
290 Private method to log an error. |
291 Private method to log an error. |
291 |
292 |
292 @param stderr error string to log |
293 @param stderr error string to log |
293 @type str |
294 @type str |
294 """ |
295 """ |
295 self.errorGroup.show() |
296 self.errorGroup.show() |
296 self.errors.insertPlainText(stderr) |
297 self.errors.insertPlainText(stderr) |