23 |
28 |
24 |
29 |
25 class GitDialog(QDialog, Ui_GitDialog): |
30 class GitDialog(QDialog, Ui_GitDialog): |
26 """ |
31 """ |
27 Class implementing a dialog starting a process and showing its output. |
32 Class implementing a dialog starting a process and showing its output. |
28 |
33 |
29 It starts a QProcess and displays a dialog that |
34 It starts a QProcess and displays a dialog that |
30 shows the output of the process. The dialog is modal, |
35 shows the output of the process. The dialog is modal, |
31 which causes a synchronized execution of the process. |
36 which causes a synchronized execution of the process. |
32 """ |
37 """ |
|
38 |
33 def __init__(self, text, git=None, parent=None): |
39 def __init__(self, text, git=None, parent=None): |
34 """ |
40 """ |
35 Constructor |
41 Constructor |
36 |
42 |
37 @param text text to be shown by the label (string) |
43 @param text text to be shown by the label (string) |
38 @param git reference to the Git interface object (Git) |
44 @param git reference to the Git interface object (Git) |
39 @param parent parent widget (QWidget) |
45 @param parent parent widget (QWidget) |
40 """ |
46 """ |
41 super().__init__(parent) |
47 super().__init__(parent) |
42 self.setupUi(self) |
48 self.setupUi(self) |
43 |
49 |
44 self.buttonBox.button( |
50 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
45 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
51 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
46 self.buttonBox.button( |
52 |
47 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
48 |
|
49 self.process = None |
53 self.process = None |
50 self.username = '' |
54 self.username = "" |
51 self.password = '' |
55 self.password = "" |
52 self.vcs = git |
56 self.vcs = git |
53 |
57 |
54 self.outputGroup.setTitle(text) |
58 self.outputGroup.setTitle(text) |
55 |
59 |
56 self.show() |
60 self.show() |
57 QCoreApplication.processEvents() |
61 QCoreApplication.processEvents() |
58 |
62 |
59 def __finish(self): |
63 def __finish(self): |
60 """ |
64 """ |
61 Private slot called when the process finished or the user pressed |
65 Private slot called when the process finished or the user pressed |
62 the button. |
66 the button. |
63 """ |
67 """ |
64 if ( |
68 if ( |
65 self.process is not None and |
69 self.process is not None |
66 self.process.state() != QProcess.ProcessState.NotRunning |
70 and self.process.state() != QProcess.ProcessState.NotRunning |
67 ): |
71 ): |
68 self.process.terminate() |
72 self.process.terminate() |
69 QTimer.singleShot(2000, self.process.kill) |
73 QTimer.singleShot(2000, self.process.kill) |
70 self.process.waitForFinished(3000) |
74 self.process.waitForFinished(3000) |
71 |
75 |
72 self.inputGroup.setEnabled(False) |
76 self.inputGroup.setEnabled(False) |
73 self.inputGroup.hide() |
77 self.inputGroup.hide() |
74 |
78 |
75 self.process = None |
79 self.process = None |
76 |
80 |
77 self.buttonBox.button( |
81 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
78 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
82 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
79 self.buttonBox.button( |
83 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
80 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
84 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setFocus( |
81 self.buttonBox.button( |
85 Qt.FocusReason.OtherFocusReason |
82 QDialogButtonBox.StandardButton.Close).setDefault(True) |
86 ) |
83 self.buttonBox.button( |
87 |
84 QDialogButtonBox.StandardButton.Close).setFocus( |
|
85 Qt.FocusReason.OtherFocusReason) |
|
86 |
|
87 if self.normal and self.errors.toPlainText(): |
88 if self.normal and self.errors.toPlainText(): |
88 self.errorGroup.setTitle(self.tr("Additional Output")) |
89 self.errorGroup.setTitle(self.tr("Additional Output")) |
89 |
90 |
90 if ( |
91 if ( |
91 Preferences.getVCS("AutoClose") and |
92 Preferences.getVCS("AutoClose") |
92 self.normal and |
93 and self.normal |
93 self.errors.toPlainText() == "" |
94 and self.errors.toPlainText() == "" |
94 ): |
95 ): |
95 self.accept() |
96 self.accept() |
96 |
97 |
97 def on_buttonBox_clicked(self, button): |
98 def on_buttonBox_clicked(self, button): |
98 """ |
99 """ |
99 Private slot called by a button of the button box clicked. |
100 Private slot called by a button of the button box clicked. |
100 |
101 |
101 @param button button that was clicked (QAbstractButton) |
102 @param button button that was clicked (QAbstractButton) |
102 """ |
103 """ |
103 if button == self.buttonBox.button( |
104 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
104 QDialogButtonBox.StandardButton.Close |
|
105 ): |
|
106 self.close() |
105 self.close() |
107 elif button == self.buttonBox.button( |
106 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): |
108 QDialogButtonBox.StandardButton.Cancel |
|
109 ): |
|
110 self.statusLabel.setText(self.tr("Process canceled.")) |
107 self.statusLabel.setText(self.tr("Process canceled.")) |
111 self.__finish() |
108 self.__finish() |
112 |
109 |
113 def __procFinished(self, exitCode, exitStatus): |
110 def __procFinished(self, exitCode, exitStatus): |
114 """ |
111 """ |
115 Private slot connected to the finished signal. |
112 Private slot connected to the finished signal. |
116 |
113 |
117 @param exitCode exit code of the process (integer) |
114 @param exitCode exit code of the process (integer) |
118 @param exitStatus exit status of the process (QProcess.ExitStatus) |
115 @param exitStatus exit status of the process (QProcess.ExitStatus) |
119 """ |
116 """ |
120 self.normal = ( |
117 self.normal = (exitStatus == QProcess.ExitStatus.NormalExit) and (exitCode == 0) |
121 (exitStatus == QProcess.ExitStatus.NormalExit) and |
|
122 (exitCode == 0) |
|
123 ) |
|
124 if self.normal: |
118 if self.normal: |
125 self.statusLabel.setText(self.tr("Process finished successfully.")) |
119 self.statusLabel.setText(self.tr("Process finished successfully.")) |
126 elif exitStatus == QProcess.ExitStatus.CrashExit: |
120 elif exitStatus == QProcess.ExitStatus.CrashExit: |
127 self.statusLabel.setText(self.tr("Process crashed.")) |
121 self.statusLabel.setText(self.tr("Process crashed.")) |
128 else: |
122 else: |
129 self.statusLabel.setText( |
123 self.statusLabel.setText( |
130 self.tr("Process finished with exit code {0}") |
124 self.tr("Process finished with exit code {0}").format(exitCode) |
131 .format(exitCode)) |
125 ) |
132 self.__finish() |
126 self.__finish() |
133 |
127 |
134 def startProcess(self, args, workingDir=None, showArgs=True, |
128 def startProcess(self, args, workingDir=None, showArgs=True, environment=None): |
135 environment=None): |
|
136 """ |
129 """ |
137 Public slot used to start the process. |
130 Public slot used to start the process. |
138 |
131 |
139 @param args list of arguments for the process (list of strings) |
132 @param args list of arguments for the process (list of strings) |
140 @param workingDir working directory for the process (string) |
133 @param workingDir working directory for the process (string) |
141 @param showArgs flag indicating to show the arguments (boolean) |
134 @param showArgs flag indicating to show the arguments (boolean) |
142 @param environment dictionary of environment settings to add |
135 @param environment dictionary of environment settings to add |
143 or change for the git process (dict of string and string) |
136 or change for the git process (dict of string and string) |
144 @return flag indicating a successful start of the process (boolean) |
137 @return flag indicating a successful start of the process (boolean) |
145 """ |
138 """ |
146 self.errorGroup.hide() |
139 self.errorGroup.hide() |
147 self.normal = False |
140 self.normal = False |
148 self.intercept = False |
141 self.intercept = False |
149 |
142 |
150 if environment is None: |
143 if environment is None: |
151 environment = {} |
144 environment = {} |
152 |
145 |
153 self.__hasAddOrDelete = False |
146 self.__hasAddOrDelete = False |
154 if args[0] in ["checkout", "fetch", "pull", "rebase", "reset", |
147 if args[0] in [ |
155 "merge", "cherry-pick", "stash"]: |
148 "checkout", |
|
149 "fetch", |
|
150 "pull", |
|
151 "rebase", |
|
152 "reset", |
|
153 "merge", |
|
154 "cherry-pick", |
|
155 "stash", |
|
156 ]: |
156 self.__updateCommand = True |
157 self.__updateCommand = True |
157 else: |
158 else: |
158 self.__updateCommand = False |
159 self.__updateCommand = False |
159 |
160 |
160 if showArgs: |
161 if showArgs: |
161 self.resultbox.append(' '.join(args)) |
162 self.resultbox.append(" ".join(args)) |
162 self.resultbox.append('') |
163 self.resultbox.append("") |
163 |
164 |
164 self.process = QProcess() |
165 self.process = QProcess() |
165 if environment: |
166 if environment: |
166 env = QProcessEnvironment.systemEnvironment() |
167 env = QProcessEnvironment.systemEnvironment() |
167 for key, value in environment.items(): |
168 for key, value in environment.items(): |
168 env.insert(key, value) |
169 env.insert(key, value) |
169 self.process.setProcessEnvironment(env) |
170 self.process.setProcessEnvironment(env) |
170 |
171 |
171 self.process.finished.connect(self.__procFinished) |
172 self.process.finished.connect(self.__procFinished) |
172 self.process.readyReadStandardOutput.connect(self.__readStdout) |
173 self.process.readyReadStandardOutput.connect(self.__readStdout) |
173 self.process.readyReadStandardError.connect(self.__readStderr) |
174 self.process.readyReadStandardError.connect(self.__readStderr) |
174 |
175 |
175 if workingDir: |
176 if workingDir: |
176 self.process.setWorkingDirectory(workingDir) |
177 self.process.setWorkingDirectory(workingDir) |
177 self.process.start('git', args) |
178 self.process.start("git", args) |
178 procStarted = self.process.waitForStarted(5000) |
179 procStarted = self.process.waitForStarted(5000) |
179 if not procStarted: |
180 if not procStarted: |
180 self.buttonBox.setFocus() |
181 self.buttonBox.setFocus() |
181 self.inputGroup.setEnabled(False) |
182 self.inputGroup.setEnabled(False) |
182 EricMessageBox.critical( |
183 EricMessageBox.critical( |
183 self, |
184 self, |
184 self.tr('Process Generation Error'), |
185 self.tr("Process Generation Error"), |
185 self.tr( |
186 self.tr( |
186 'The process {0} could not be started. ' |
187 "The process {0} could not be started. " |
187 'Ensure, that it is in the search path.' |
188 "Ensure, that it is in the search path." |
188 ).format('git')) |
189 ).format("git"), |
|
190 ) |
189 else: |
191 else: |
190 self.inputGroup.setEnabled(True) |
192 self.inputGroup.setEnabled(True) |
191 self.inputGroup.show() |
193 self.inputGroup.show() |
192 return procStarted |
194 return procStarted |
193 |
195 |
194 def normalExit(self): |
196 def normalExit(self): |
195 """ |
197 """ |
196 Public method to check for a normal process termination. |
198 Public method to check for a normal process termination. |
197 |
199 |
198 @return flag indicating normal process termination (boolean) |
200 @return flag indicating normal process termination (boolean) |
199 """ |
201 """ |
200 return self.normal |
202 return self.normal |
201 |
203 |
202 def normalExitWithoutErrors(self): |
204 def normalExitWithoutErrors(self): |
203 """ |
205 """ |
204 Public method to check for a normal process termination without |
206 Public method to check for a normal process termination without |
205 error messages. |
207 error messages. |
206 |
208 |
207 @return flag indicating normal process termination (boolean) |
209 @return flag indicating normal process termination (boolean) |
208 """ |
210 """ |
209 return self.normal and self.errors.toPlainText() == "" |
211 return self.normal and self.errors.toPlainText() == "" |
210 |
212 |
211 def __readStdout(self): |
213 def __readStdout(self): |
212 """ |
214 """ |
213 Private slot to handle the readyReadStandardOutput signal. |
215 Private slot to handle the readyReadStandardOutput signal. |
214 |
216 |
215 It reads the output of the process, formats it and inserts it into |
217 It reads the output of the process, formats it and inserts it into |
216 the contents pane. |
218 the contents pane. |
217 """ |
219 """ |
218 if self.process is not None: |
220 if self.process is not None: |
219 s = str(self.process.readAllStandardOutput(), |
221 s = str( |
220 Preferences.getSystem("IOEncoding"), |
222 self.process.readAllStandardOutput(), |
221 'replace') |
223 Preferences.getSystem("IOEncoding"), |
|
224 "replace", |
|
225 ) |
222 self.__showOutput(s) |
226 self.__showOutput(s) |
223 |
227 |
224 def __showOutput(self, out): |
228 def __showOutput(self, out): |
225 """ |
229 """ |
226 Private slot to show some output. |
230 Private slot to show some output. |
227 |
231 |
228 @param out output to be shown (string) |
232 @param out output to be shown (string) |
229 """ |
233 """ |
230 self.resultbox.insertPlainText(out) |
234 self.resultbox.insertPlainText(out) |
231 self.resultbox.ensureCursorVisible() |
235 self.resultbox.ensureCursorVisible() |
232 |
236 |
233 # check for a changed project file |
237 # check for a changed project file |
234 if self.__updateCommand: |
238 if self.__updateCommand: |
235 for line in out.splitlines(): |
239 for line in out.splitlines(): |
236 if ( |
240 if ".epj" in line or ".e4p" in line: |
237 '.epj' in line or |
|
238 '.e4p' in line |
|
239 ): |
|
240 self.__hasAddOrDelete = True |
241 self.__hasAddOrDelete = True |
241 break |
242 break |
242 |
243 |
243 QCoreApplication.processEvents() |
244 QCoreApplication.processEvents() |
244 |
245 |
245 def __readStderr(self): |
246 def __readStderr(self): |
246 """ |
247 """ |
247 Private slot to handle the readyReadStandardError signal. |
248 Private slot to handle the readyReadStandardError signal. |
248 |
249 |
249 It reads the error output of the process and inserts it into the |
250 It reads the error output of the process and inserts it into the |
250 error pane. |
251 error pane. |
251 """ |
252 """ |
252 if self.process is not None: |
253 if self.process is not None: |
253 s = str(self.process.readAllStandardError(), |
254 s = str( |
254 Preferences.getSystem("IOEncoding"), |
255 self.process.readAllStandardError(), |
255 'replace') |
256 Preferences.getSystem("IOEncoding"), |
|
257 "replace", |
|
258 ) |
256 self.__showError(s) |
259 self.__showError(s) |
257 |
260 |
258 def __showError(self, out): |
261 def __showError(self, out): |
259 """ |
262 """ |
260 Private slot to show some error. |
263 Private slot to show some error. |
261 |
264 |
262 @param out error to be shown (string) |
265 @param out error to be shown (string) |
263 """ |
266 """ |
264 self.errorGroup.show() |
267 self.errorGroup.show() |
265 self.errors.insertPlainText(out) |
268 self.errors.insertPlainText(out) |
266 self.errors.ensureCursorVisible() |
269 self.errors.ensureCursorVisible() |
267 |
270 |
268 QCoreApplication.processEvents() |
271 QCoreApplication.processEvents() |
269 |
272 |
270 def on_passwordCheckBox_toggled(self, isOn): |
273 def on_passwordCheckBox_toggled(self, isOn): |
271 """ |
274 """ |
272 Private slot to handle the password checkbox toggled. |
275 Private slot to handle the password checkbox toggled. |
273 |
276 |
274 @param isOn flag indicating the status of the check box (boolean) |
277 @param isOn flag indicating the status of the check box (boolean) |
275 """ |
278 """ |
276 if isOn: |
279 if isOn: |
277 self.input.setEchoMode(QLineEdit.EchoMode.Password) |
280 self.input.setEchoMode(QLineEdit.EchoMode.Password) |
278 else: |
281 else: |
279 self.input.setEchoMode(QLineEdit.EchoMode.Normal) |
282 self.input.setEchoMode(QLineEdit.EchoMode.Normal) |
280 |
283 |
281 @pyqtSlot() |
284 @pyqtSlot() |
282 def on_sendButton_clicked(self): |
285 def on_sendButton_clicked(self): |
283 """ |
286 """ |
284 Private slot to send the input to the git process. |
287 Private slot to send the input to the git process. |
285 """ |
288 """ |
286 inputTxt = self.input.text() |
289 inputTxt = self.input.text() |
287 inputTxt += os.linesep |
290 inputTxt += os.linesep |
288 |
291 |
289 if self.passwordCheckBox.isChecked(): |
292 if self.passwordCheckBox.isChecked(): |
290 self.errors.insertPlainText(os.linesep) |
293 self.errors.insertPlainText(os.linesep) |
291 self.errors.ensureCursorVisible() |
294 self.errors.ensureCursorVisible() |
292 else: |
295 else: |
293 self.errors.insertPlainText(inputTxt) |
296 self.errors.insertPlainText(inputTxt) |
294 self.errors.ensureCursorVisible() |
297 self.errors.ensureCursorVisible() |
295 |
298 |
296 self.process.write(strToQByteArray(inputTxt)) |
299 self.process.write(strToQByteArray(inputTxt)) |
297 |
300 |
298 self.passwordCheckBox.setChecked(False) |
301 self.passwordCheckBox.setChecked(False) |
299 self.input.clear() |
302 self.input.clear() |
300 |
303 |
301 def on_input_returnPressed(self): |
304 def on_input_returnPressed(self): |
302 """ |
305 """ |
303 Private slot to handle the press of the return key in the input field. |
306 Private slot to handle the press of the return key in the input field. |
304 """ |
307 """ |
305 self.intercept = True |
308 self.intercept = True |
306 self.on_sendButton_clicked() |
309 self.on_sendButton_clicked() |
307 |
310 |
308 def keyPressEvent(self, evt): |
311 def keyPressEvent(self, evt): |
309 """ |
312 """ |
310 Protected slot to handle a key press event. |
313 Protected slot to handle a key press event. |
311 |
314 |
312 @param evt the key press event (QKeyEvent) |
315 @param evt the key press event (QKeyEvent) |
313 """ |
316 """ |
314 if self.intercept: |
317 if self.intercept: |
315 self.intercept = False |
318 self.intercept = False |
316 evt.accept() |
319 evt.accept() |
317 return |
320 return |
318 super().keyPressEvent(evt) |
321 super().keyPressEvent(evt) |
319 |
322 |
320 def hasAddOrDelete(self): |
323 def hasAddOrDelete(self): |
321 """ |
324 """ |
322 Public method to check, if the last action contained an add or delete. |
325 Public method to check, if the last action contained an add or delete. |
323 |
326 |
324 @return flag indicating the presence of an add or delete (boolean) |
327 @return flag indicating the presence of an add or delete (boolean) |
325 """ |
328 """ |
326 return self.__hasAddOrDelete |
329 return self.__hasAddOrDelete |