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