|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the Start Program dialog. |
|
8 """ |
|
9 |
|
10 import os |
|
11 |
|
12 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QComboBox, QInputDialog |
|
13 |
|
14 from EricWidgets.EricPathPicker import EricPathPickerModes |
|
15 from EricWidgets.EricApplication import ericApp |
|
16 |
|
17 import Preferences |
|
18 |
|
19 |
|
20 class StartDialog(QDialog): |
|
21 """ |
|
22 Class implementing the Start Program dialog. |
|
23 |
|
24 It implements a dialog that is used to start an |
|
25 application for debugging. It asks the user to enter |
|
26 the commandline parameters, the working directory and |
|
27 whether exception reporting should be disabled. |
|
28 """ |
|
29 |
|
30 def __init__( |
|
31 self, |
|
32 caption, |
|
33 lastUsedVenvName, |
|
34 argvList, |
|
35 wdList, |
|
36 envList, |
|
37 exceptions, |
|
38 parent=None, |
|
39 dialogType=0, |
|
40 modfuncList=None, |
|
41 tracePython=False, |
|
42 autoClearShell=True, |
|
43 autoContinue=True, |
|
44 enableMultiprocess=False, |
|
45 multiprocessNoDebugHistory=None, |
|
46 configOverride=None, |
|
47 forProject=False, |
|
48 scriptName="", |
|
49 scriptsList=None, |
|
50 ): |
|
51 """ |
|
52 Constructor |
|
53 |
|
54 @param caption the caption to be displayed |
|
55 @type str |
|
56 @param lastUsedVenvName name of the most recently used virtual |
|
57 environment |
|
58 @type str |
|
59 @param argvList history list of command line arguments |
|
60 @type list of str |
|
61 @param wdList history list of working directories |
|
62 @type list of str |
|
63 @param envList history list of environment parameter settings |
|
64 @type list of str |
|
65 @param exceptions exception reporting flag |
|
66 @type bool |
|
67 @param parent parent widget of this dialog |
|
68 @type QWidget |
|
69 @param dialogType type of the start dialog |
|
70 <ul> |
|
71 <li>0 = start debug dialog</li> |
|
72 <li>1 = start run dialog</li> |
|
73 <li>2 = start coverage dialog</li> |
|
74 <li>3 = start profile dialog</li> |
|
75 </ul> |
|
76 @type int (0 to 3) |
|
77 @param modfuncList history list of module functions |
|
78 @type list of str |
|
79 @param tracePython flag indicating if the Python library should |
|
80 be traced as well |
|
81 @type bool |
|
82 @param autoClearShell flag indicating, that the interpreter window |
|
83 should be cleared automatically |
|
84 @type bool |
|
85 @param autoContinue flag indicating, that the debugger should not |
|
86 stop at the first executable line |
|
87 @type bool |
|
88 @param enableMultiprocess flag indicating the support for multi process |
|
89 debugging |
|
90 @type bool |
|
91 @param multiprocessNoDebugHistory list of lists with programs not to be |
|
92 debugged |
|
93 @type list of str |
|
94 @param configOverride dictionary containing the global config override |
|
95 data |
|
96 @type dict |
|
97 @param forProject flag indicating to get the parameters for a |
|
98 run/debug/... action for a project |
|
99 @type bool |
|
100 @param scriptName name of the script |
|
101 @type str |
|
102 @param scriptsList history list of script names |
|
103 @type list of str |
|
104 """ |
|
105 super().__init__(parent) |
|
106 self.setModal(True) |
|
107 |
|
108 self.dialogType = dialogType |
|
109 if dialogType == 0: |
|
110 from .Ui_StartDebugDialog import Ui_StartDebugDialog |
|
111 |
|
112 self.ui = Ui_StartDebugDialog() |
|
113 elif dialogType == 1: |
|
114 from .Ui_StartRunDialog import Ui_StartRunDialog |
|
115 |
|
116 self.ui = Ui_StartRunDialog() |
|
117 elif dialogType == 2: |
|
118 from .Ui_StartCoverageDialog import Ui_StartCoverageDialog |
|
119 |
|
120 self.ui = Ui_StartCoverageDialog() |
|
121 elif dialogType == 3: |
|
122 from .Ui_StartProfileDialog import Ui_StartProfileDialog |
|
123 |
|
124 self.ui = Ui_StartProfileDialog() |
|
125 self.ui.setupUi(self) |
|
126 |
|
127 self.ui.venvComboBox.addItem("") |
|
128 self.ui.venvComboBox.addItems( |
|
129 sorted(ericApp().getObject("VirtualEnvManager").getVirtualenvNames()) |
|
130 ) |
|
131 |
|
132 self.ui.scriptnamePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
|
133 self.ui.scriptnamePicker.setDefaultDirectory( |
|
134 Preferences.getMultiProject("Workspace") |
|
135 ) |
|
136 self.ui.scriptnamePicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) |
|
137 self.ui.scriptnamePicker.setSizeAdjustPolicy( |
|
138 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon |
|
139 ) |
|
140 self.ui.scriptnamePicker.setFilters( |
|
141 self.tr( |
|
142 "Python Files (*.py *.py3);;" |
|
143 "Python GUI Files (*.pyw *.pyw3);;" |
|
144 "All Files (*)" |
|
145 ) |
|
146 ) |
|
147 self.ui.scriptnamePicker.setEnabled(not forProject) |
|
148 |
|
149 self.ui.workdirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) |
|
150 self.ui.workdirPicker.setDefaultDirectory( |
|
151 Preferences.getMultiProject("Workspace") |
|
152 ) |
|
153 self.ui.workdirPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) |
|
154 self.ui.workdirPicker.setSizeAdjustPolicy( |
|
155 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon |
|
156 ) |
|
157 |
|
158 self.clearButton = self.ui.buttonBox.addButton( |
|
159 self.tr("Clear Histories"), QDialogButtonBox.ButtonRole.ActionRole |
|
160 ) |
|
161 self.editButton = self.ui.buttonBox.addButton( |
|
162 self.tr("Edit History"), QDialogButtonBox.ButtonRole.ActionRole |
|
163 ) |
|
164 |
|
165 self.setWindowTitle(caption) |
|
166 self.ui.cmdlineCombo.clear() |
|
167 self.ui.cmdlineCombo.addItems(argvList) |
|
168 if len(argvList) > 0: |
|
169 self.ui.cmdlineCombo.setCurrentIndex(0) |
|
170 self.ui.workdirPicker.clear() |
|
171 self.ui.workdirPicker.addItems(wdList) |
|
172 if len(wdList) > 0: |
|
173 self.ui.workdirPicker.setCurrentIndex(0) |
|
174 self.ui.environmentCombo.clear() |
|
175 self.ui.environmentCombo.addItems(envList) |
|
176 self.ui.exceptionCheckBox.setChecked(exceptions) |
|
177 self.ui.clearShellCheckBox.setChecked(autoClearShell) |
|
178 self.ui.consoleCheckBox.setEnabled( |
|
179 Preferences.getDebugger("ConsoleDbgCommand") != "" |
|
180 ) |
|
181 self.ui.consoleCheckBox.setChecked(False) |
|
182 venvIndex = max(0, self.ui.venvComboBox.findText(lastUsedVenvName)) |
|
183 self.ui.venvComboBox.setCurrentIndex(venvIndex) |
|
184 self.ui.globalOverrideGroup.setChecked(configOverride["enable"]) |
|
185 self.ui.redirectCheckBox.setChecked(configOverride["redirect"]) |
|
186 |
|
187 self.ui.scriptnamePicker.addItems(scriptsList) |
|
188 self.ui.scriptnamePicker.setText(scriptName) |
|
189 |
|
190 if dialogType == 0: # start debug dialog |
|
191 enableMultiprocessGlobal = Preferences.getDebugger("MultiProcessEnabled") |
|
192 self.ui.tracePythonCheckBox.setChecked(tracePython) |
|
193 self.ui.tracePythonCheckBox.show() |
|
194 self.ui.autoContinueCheckBox.setChecked(autoContinue) |
|
195 self.ui.multiprocessGroup.setEnabled(enableMultiprocessGlobal) |
|
196 self.ui.multiprocessGroup.setChecked( |
|
197 enableMultiprocess & enableMultiprocessGlobal |
|
198 ) |
|
199 self.ui.multiprocessNoDebugCombo.clear() |
|
200 self.ui.multiprocessNoDebugCombo.setToolTip( |
|
201 self.tr( |
|
202 "Enter the list of programs or program patterns not to be" |
|
203 " debugged separated by '{0}'." |
|
204 ).format(os.pathsep) |
|
205 ) |
|
206 if multiprocessNoDebugHistory: |
|
207 self.ui.multiprocessNoDebugCombo.addItems(multiprocessNoDebugHistory) |
|
208 self.ui.multiprocessNoDebugCombo.setCurrentIndex(0) |
|
209 |
|
210 if dialogType == 3: # start coverage or profile dialog |
|
211 self.ui.eraseCheckBox.setChecked(True) |
|
212 |
|
213 self.__clearHistoryLists = False |
|
214 self.__historiesModified = False |
|
215 |
|
216 msh = self.minimumSizeHint() |
|
217 self.resize(max(self.width(), msh.width()), msh.height()) |
|
218 |
|
219 def on_modFuncCombo_editTextChanged(self): |
|
220 """ |
|
221 Private slot to enable/disable the OK button. |
|
222 """ |
|
223 self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setDisabled( |
|
224 self.ui.modFuncCombo.currentText() == "" |
|
225 ) |
|
226 |
|
227 def getData(self): |
|
228 """ |
|
229 Public method to retrieve the data entered into this dialog. |
|
230 |
|
231 @return a tuple of virtual environment, script name, argv, workdir, |
|
232 environment, exceptions flag, clear interpreter flag and run in |
|
233 console flag |
|
234 @rtype tuple of (str, str, str, str, str, bool, bool, bool) |
|
235 """ |
|
236 cmdLine = self.ui.cmdlineCombo.currentText() |
|
237 workdir = self.ui.workdirPicker.currentText(toNative=False) |
|
238 environment = self.ui.environmentCombo.currentText() |
|
239 venvName = self.ui.venvComboBox.currentText() |
|
240 scriptName = ( |
|
241 self.ui.scriptnamePicker.currentText() |
|
242 if self.ui.scriptnamePicker.isEnabled() |
|
243 else "" |
|
244 ) |
|
245 |
|
246 return ( |
|
247 venvName, |
|
248 scriptName, |
|
249 cmdLine, |
|
250 workdir, |
|
251 environment, |
|
252 self.ui.exceptionCheckBox.isChecked(), |
|
253 self.ui.clearShellCheckBox.isChecked(), |
|
254 self.ui.consoleCheckBox.isChecked(), |
|
255 ) |
|
256 |
|
257 def getGlobalOverrideData(self): |
|
258 """ |
|
259 Public method to retrieve the global configuration override data |
|
260 entered into this dialog. |
|
261 |
|
262 @return dictionary containing a flag indicating to activate the global |
|
263 override and a flag indicating a redirect of stdin/stdout/stderr |
|
264 @rtype dict |
|
265 """ |
|
266 return { |
|
267 "enable": self.ui.globalOverrideGroup.isChecked(), |
|
268 "redirect": self.ui.redirectCheckBox.isChecked(), |
|
269 } |
|
270 |
|
271 def getDebugData(self): |
|
272 """ |
|
273 Public method to retrieve the debug related data entered into this |
|
274 dialog. |
|
275 |
|
276 @return a tuple of a flag indicating, if the Python library should be |
|
277 traced as well, a flag indicating, that the debugger should not |
|
278 stop at the first executable line, a flag indicating to support |
|
279 multi process debugging and a space separated list of programs not |
|
280 to be debugged |
|
281 @rtype tuple of (bool, bool, bool, str) |
|
282 """ |
|
283 if self.dialogType == 0: |
|
284 return ( |
|
285 self.ui.tracePythonCheckBox.isChecked(), |
|
286 self.ui.autoContinueCheckBox.isChecked(), |
|
287 self.ui.multiprocessGroup.isChecked(), |
|
288 self.ui.multiprocessNoDebugCombo.currentText(), |
|
289 ) |
|
290 else: |
|
291 return (False, False, False, "") |
|
292 |
|
293 def getCoverageData(self): |
|
294 """ |
|
295 Public method to retrieve the coverage related data entered into this |
|
296 dialog. |
|
297 |
|
298 @return flag indicating erasure of coverage info |
|
299 @rtype bool |
|
300 """ |
|
301 if self.dialogType == 2: |
|
302 return self.ui.eraseCheckBox.isChecked() |
|
303 else: |
|
304 return False |
|
305 |
|
306 def getProfilingData(self): |
|
307 """ |
|
308 Public method to retrieve the profiling related data entered into this |
|
309 dialog. |
|
310 |
|
311 @return flag indicating erasure of profiling info |
|
312 @rtype bool |
|
313 """ |
|
314 if self.dialogType == 3: |
|
315 return self.ui.eraseCheckBox.isChecked() |
|
316 else: |
|
317 return False |
|
318 |
|
319 def __clearHistories(self): |
|
320 """ |
|
321 Private slot to clear the combo boxes lists and record a flag to |
|
322 clear the lists. |
|
323 """ |
|
324 self.__clearHistoryLists = True |
|
325 self.__historiesModified = False # clear catches it all |
|
326 |
|
327 cmdLine = self.ui.cmdlineCombo.currentText() |
|
328 workdir = self.ui.workdirPicker.currentText() |
|
329 environment = self.ui.environmentCombo.currentText() |
|
330 scriptName = self.ui.scriptnamePicker.currentText() |
|
331 |
|
332 self.ui.cmdlineCombo.clear() |
|
333 self.ui.workdirPicker.clear() |
|
334 self.ui.environmentCombo.clear() |
|
335 self.ui.scriptnamePicker.clear() |
|
336 |
|
337 self.ui.cmdlineCombo.addItem(cmdLine) |
|
338 self.ui.workdirPicker.addItem(workdir) |
|
339 self.ui.environmentCombo.addItem(environment) |
|
340 self.ui.scriptnamePicker.addItem("") |
|
341 self.ui.scriptnamePicker.setCurrentText(scriptName) |
|
342 |
|
343 if self.dialogType == 0: |
|
344 noDebugList = self.ui.multiprocessNoDebugCombo.currentText() |
|
345 self.ui.multiprocessNoDebugCombo.clear() |
|
346 self.ui.multiprocessNoDebugCombo.addItem(noDebugList) |
|
347 |
|
348 def __editHistory(self): |
|
349 """ |
|
350 Private slot to edit a history list. |
|
351 """ |
|
352 histories = [ |
|
353 "", |
|
354 self.tr("Script Name"), |
|
355 self.tr("Script Parameters"), |
|
356 self.tr("Working Directory"), |
|
357 self.tr("Environment"), |
|
358 ] |
|
359 widgets = [ |
|
360 None, |
|
361 self.ui.scriptnamePicker, |
|
362 self.ui.cmdlineCombo, |
|
363 self.ui.workdirPicker, |
|
364 self.ui.environmentCombo, |
|
365 ] |
|
366 if self.dialogType == 0: |
|
367 histories.append(self.tr("No Debug Programs")) |
|
368 widgets.append(self.ui.multiprocessNoDebugCombo) |
|
369 historyKind, ok = QInputDialog.getItem( |
|
370 self, |
|
371 self.tr("Edit History"), |
|
372 self.tr("Select the history list to be edited:"), |
|
373 histories, |
|
374 0, |
|
375 False, |
|
376 ) |
|
377 if ok and historyKind: |
|
378 history = [] |
|
379 historiesIndex = histories.index(historyKind) |
|
380 if historiesIndex in (1, 3): |
|
381 picker = widgets[historiesIndex] |
|
382 history = picker.getPathItems() |
|
383 else: |
|
384 combo = widgets[historiesIndex] |
|
385 if combo: |
|
386 history = [combo.itemText(idx) for idx in range(combo.count())] |
|
387 |
|
388 if history: |
|
389 from .StartHistoryEditDialog import StartHistoryEditDialog |
|
390 |
|
391 dlg = StartHistoryEditDialog(history, self) |
|
392 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
393 history = dlg.getHistory() |
|
394 combo = widgets[historiesIndex] |
|
395 if combo: |
|
396 combo.clear() |
|
397 combo.addItems(history) |
|
398 |
|
399 self.__historiesModified = True |
|
400 |
|
401 def historiesModified(self): |
|
402 """ |
|
403 Public method to test for modified histories. |
|
404 |
|
405 @return flag indicating modified histories |
|
406 @rtype bool |
|
407 """ |
|
408 return self.__historiesModified |
|
409 |
|
410 def clearHistories(self): |
|
411 """ |
|
412 Public method to test, if histories shall be cleared. |
|
413 |
|
414 @return flag indicating histories shall be cleared |
|
415 @rtype bool |
|
416 """ |
|
417 return self.__clearHistoryLists |
|
418 |
|
419 def getHistories(self): |
|
420 """ |
|
421 Public method to get the lists of histories. |
|
422 |
|
423 @return tuple containing the histories of script names, command line |
|
424 arguments, working directories, environment settings and no debug |
|
425 programs lists |
|
426 @rtype tuple of five list of str |
|
427 """ |
|
428 noDebugHistory = ( |
|
429 [ |
|
430 self.ui.multiprocessNoDebugCombo.itemText(index) |
|
431 for index in range(self.ui.multiprocessNoDebugCombo.count()) |
|
432 ] |
|
433 if self.dialogType == 0 |
|
434 else None |
|
435 ) |
|
436 return ( |
|
437 self.ui.scriptnamePicker.getPathItems(), |
|
438 [ |
|
439 self.ui.cmdlineCombo.itemText(index) |
|
440 for index in range(self.ui.cmdlineCombo.count()) |
|
441 ], |
|
442 self.ui.workdirPicker.getPathItems(), |
|
443 [ |
|
444 self.ui.environmentCombo.itemText(index) |
|
445 for index in range(self.ui.environmentCombo.count()) |
|
446 ], |
|
447 noDebugHistory, |
|
448 ) |
|
449 |
|
450 def on_buttonBox_clicked(self, button): |
|
451 """ |
|
452 Private slot called by a button of the button box clicked. |
|
453 |
|
454 @param button button that was clicked |
|
455 @type QAbstractButton |
|
456 """ |
|
457 if button == self.clearButton: |
|
458 self.__clearHistories() |
|
459 elif button == self.editButton: |
|
460 self.__editHistory() |