src/eric7/VirtualEnv/VirtualenvConfigurationDialog.py

branch
eric7
changeset 11230
8a15b05eeee3
parent 11105
ec86fc991d28
equal deleted inserted replaced
11229:16a129d168f9 11230:8a15b05eeee3
12 import re 12 import re
13 13
14 from PyQt6.QtCore import QProcess, QTimer, pyqtSlot 14 from PyQt6.QtCore import QProcess, QTimer, pyqtSlot
15 from PyQt6.QtWidgets import QDialog, QDialogButtonBox 15 from PyQt6.QtWidgets import QDialog, QDialogButtonBox
16 16
17 from eric7 import CondaInterface, Preferences 17 from eric7 import Preferences
18 from eric7.EricWidgets.EricApplication import ericApp 18 from eric7.EricWidgets.EricApplication import ericApp
19 from eric7.EricWidgets.EricPathPicker import EricPathPickerModes 19 from eric7.EricWidgets.EricPathPicker import EricPathPickerModes
20 from eric7.SystemUtilities import PythonUtilities 20 from eric7.SystemUtilities import PythonUtilities
21 21
22 from .Ui_VirtualenvConfigurationDialog import Ui_VirtualenvConfigurationDialog 22 from .Ui_VirtualenvConfigurationDialog import Ui_VirtualenvConfigurationDialog
23 23
24 24
25 class VirtualenvConfigurationDialog(QDialog, Ui_VirtualenvConfigurationDialog): 25 class VirtualenvConfigurationDialog(QDialog, Ui_VirtualenvConfigurationDialog):
26 """ 26 """
27 Class implementing a dialog to enter the parameters for the 27 Class implementing a dialog to enter the parameters for the virtual environment.
28 virtual environment.
29 """ 28 """
30 29
31 def __init__(self, baseDir="", parent=None): 30 def __init__(self, baseDir="", parent=None):
32 """ 31 """
33 Constructor 32 Constructor
59 58
60 self.pythonExecPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) 59 self.pythonExecPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
61 self.pythonExecPicker.setWindowTitle(self.tr("Python Interpreter")) 60 self.pythonExecPicker.setWindowTitle(self.tr("Python Interpreter"))
62 self.pythonExecPicker.setDefaultDirectory(PythonUtilities.getPythonExecutable()) 61 self.pythonExecPicker.setDefaultDirectory(PythonUtilities.getPythonExecutable())
63 62
64 self.condaTargetDirectoryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) 63 self.versionComboBox.addItems(["", "3.13", "3.12", "3.11", "3.10", "3.9"])
65 self.condaTargetDirectoryPicker.setWindowTitle(
66 self.tr("Conda Environment Location")
67 )
68 self.condaTargetDirectoryPicker.setDefaultDirectory(os.path.expanduser("~"))
69
70 self.condaCloneDirectoryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE)
71 self.condaCloneDirectoryPicker.setWindowTitle(
72 self.tr("Conda Environment Location")
73 )
74 self.condaCloneDirectoryPicker.setDefaultDirectory(os.path.expanduser("~"))
75
76 self.condaRequirementsFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
77 self.condaRequirementsFilePicker.setWindowTitle(
78 self.tr("Conda Requirements File")
79 )
80 self.condaRequirementsFilePicker.setDefaultDirectory(os.path.expanduser("~"))
81 self.condaRequirementsFilePicker.setFilters(
82 self.tr("Text Files (*.txt);;All Files (*)")
83 )
84 64
85 self.__versionRe = re.compile(r""".*?(\d+\.\d+\.\d+).*""") 65 self.__versionRe = re.compile(r""".*?(\d+\.\d+\.\d+).*""")
86 66
87 self.__virtualenvFound = False 67 self.__virtualenvFound = False
88 self.__pyvenvFound = False 68 self.__pyvenvFound = False
89 self.__condaFound = False
90 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False) 69 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False)
91 70
92 self.__mandatoryStyleSheet = ( 71 self.__mandatoryStyleSheet = (
93 "QLineEdit {border: 2px solid; border-color: #dd8888}" 72 "QLineEdit {border: 2px solid; border-color: #dd8888}"
94 if ericApp().usesDarkPalette() 73 if ericApp().usesDarkPalette()
95 else "QLineEdit {border: 2px solid; border-color: #800000}" 74 else "QLineEdit {border: 2px solid; border-color: #800000}"
96 ) 75 )
97 self.targetDirectoryPicker.setStyleSheet(self.__mandatoryStyleSheet) 76 self.targetDirectoryPicker.setStyleSheet(self.__mandatoryStyleSheet)
98 self.nameEdit.setStyleSheet(self.__mandatoryStyleSheet) 77 self.nameEdit.setStyleSheet(self.__mandatoryStyleSheet)
99 self.condaTargetDirectoryPicker.setStyleSheet(self.__mandatoryStyleSheet)
100 self.condaNameEdit.setStyleSheet(self.__mandatoryStyleSheet)
101 78
102 self.__setVirtualenvVersion() 79 self.__setVirtualenvVersion()
103 self.__setPyvenvVersion() 80 self.__setPyvenvVersion()
104 self.__setCondaVersion()
105 if self.__pyvenvFound: 81 if self.__pyvenvFound:
106 self.pyvenvButton.setChecked(True) 82 self.pyvenvButton.setChecked(True)
107 elif self.__virtualenvFound: 83 elif self.__virtualenvFound:
108 self.virtualenvButton.setChecked(True) 84 self.virtualenvButton.setChecked(True)
109 elif self.__condaFound: 85
110 self.condaButton.setChecked(True) 86 self.nameEdit.textChanged.connect(self.__updateOK)
111 87 self.targetDirectoryPicker.textChanged.connect(self.__updateOK)
112 self.condaInsecureCheckBox.setEnabled( 88 self.virtualenvButton.toggled.connect(self.__updateUi)
113 CondaInterface.condaVersion() >= (4, 3, 18) 89 self.pyvenvButton.toggled.connect(self.__updateUi)
114 )
115 90
116 msh = self.minimumSizeHint() 91 msh = self.minimumSizeHint()
117 self.resize(max(self.width(), msh.width()), msh.height()) 92 self.resize(max(self.width(), msh.width()), msh.height())
118 93
94 @pyqtSlot()
119 def __updateOK(self): 95 def __updateOK(self):
120 """ 96 """
121 Private method to update the enabled status of the OK button. 97 Private slot to update the enabled status of the OK button.
122 """ 98 """
123 if self.virtualenvButton.isChecked() or self.pyvenvButton.isChecked(): 99 if self.virtualenvButton.isChecked() or self.pyvenvButton.isChecked():
124 enable = ( 100 enable = (
125 (self.__virtualenvFound or self.__pyvenvFound) 101 (self.__virtualenvFound or self.__pyvenvFound)
126 and bool(self.targetDirectoryPicker.text()) 102 and bool(self.targetDirectoryPicker.text())
127 and bool(self.nameEdit.text()) 103 and bool(self.nameEdit.text())
128 ) 104 )
129 enable &= self.targetDirectoryPicker.text() != self.__envBaseDir 105 enable &= self.targetDirectoryPicker.text() != self.__envBaseDir
130 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(enable) 106 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(enable)
131 elif self.condaButton.isChecked():
132 enable = bool(self.condaNameEdit.text()) or bool(
133 self.condaTargetDirectoryPicker.text()
134 )
135 if self.condaSpecialsGroup.isChecked():
136 if self.condaCloneButton.isChecked():
137 enable &= bool(self.condaCloneNameEdit.text()) or bool(
138 self.condaCloneDirectoryPicker.text()
139 )
140 elif self.condaRequirementsButton.isChecked():
141 enable &= bool(self.condaRequirementsFilePicker.text())
142 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(enable)
143 else: 107 else:
144 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False) 108 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False)
145 109
110 @pyqtSlot()
146 def __updateUi(self): 111 def __updateUi(self):
147 """ 112 """
148 Private method to update the UI depending on the selected 113 Private slot to update the UI depending on the selected
149 virtual environment creator (virtualenv or pyvenv). 114 virtual environment creator (virtualenv or pyvenv).
150 """ 115 """
151 # venv page
152 enable = self.virtualenvButton.isChecked() 116 enable = self.virtualenvButton.isChecked()
153 self.extraSearchPathLabel.setEnabled(enable) 117 self.extraSearchPathLabel.setEnabled(enable)
154 self.extraSearchPathPicker.setEnabled(enable) 118 self.extraSearchPathPicker.setEnabled(enable)
155 self.promptPrefixLabel.setEnabled(enable) 119 self.promptPrefixLabel.setEnabled(enable)
156 self.promptPrefixEdit.setEnabled(enable) 120 self.promptPrefixEdit.setEnabled(enable)
157 self.verbosityLabel.setEnabled(enable) 121 self.verbosityLabel.setEnabled(enable)
158 self.verbositySpinBox.setEnabled(enable) 122 self.verbositySpinBox.setEnabled(enable)
159 self.versionLabel.setEnabled(enable) 123 self.versionLabel.setEnabled(enable)
160 self.versionComboBox.setEnabled(enable) 124 self.versionComboBox.setEnabled(enable)
161 self.unzipCheckBox.setEnabled(enable) 125 self.noWheelCheckBox.setEnabled(enable)
162 self.noSetuptoolsCheckBox.setEnabled(enable) 126 self.noSetuptoolsCheckBox.setEnabled(enable)
163 self.symlinkCheckBox.setEnabled(not enable) 127 self.symlinkCheckBox.setEnabled(not enable)
164 self.upgradeCheckBox.setEnabled(not enable) 128 self.upgradeCheckBox.setEnabled(not enable)
165 129
166 # conda page
167 enable = not self.condaSpecialsGroup.isChecked()
168 self.condaPackagesEdit.setEnabled(enable)
169 self.condaPythonEdit.setEnabled(enable)
170 self.condaInsecureCheckBox.setEnabled(
171 enable and CondaInterface.condaVersion() >= (4, 3, 18)
172 )
173 self.condaDryrunCheckBox.setEnabled(enable)
174
175 # select page
176 if self.condaButton.isChecked():
177 self.venvStack.setCurrentWidget(self.condaPage)
178 else:
179 self.venvStack.setCurrentWidget(self.venvPage)
180
181 @pyqtSlot(str)
182 def on_nameEdit_textChanged(self, _txt):
183 """
184 Private slot handling a change of the virtual environment name.
185
186 @param _txt name of the virtual environment (unused)
187 @type str
188 """
189 self.__updateOK()
190
191 @pyqtSlot(str)
192 def on_targetDirectoryPicker_textChanged(self, _txt):
193 """
194 Private slot handling a change of the target directory.
195
196 @param _txt target directory (unused)
197 @type str
198 """
199 self.__updateOK()
200
201 @pyqtSlot(str) 130 @pyqtSlot(str)
202 def on_pythonExecPicker_textChanged(self, _txt): 131 def on_pythonExecPicker_textChanged(self, _txt):
203 """ 132 """
204 Private slot to react to a change of the Python executable. 133 Private slot to react to a change of the Python executable.
205 134
206 @param _txt contents of the picker's line edit (unused) 135 @param _txt contents of the picker's line edit (unused)
207 @type str 136 @type str
208 """ 137 """
209 self.__setVirtualenvVersion() 138 self.__setVirtualenvVersion()
210 self.__setPyvenvVersion() 139 self.__setPyvenvVersion()
211 self.__updateOK()
212
213 @pyqtSlot(bool)
214 def on_virtualenvButton_toggled(self, _checked):
215 """
216 Private slot to react to the selection of 'virtualenv'.
217
218 @param _checked state of the checkbox (unused)
219 @type bool
220 """
221 self.__updateUi()
222
223 @pyqtSlot(bool)
224 def on_pyvenvButton_toggled(self, _checked):
225 """
226 Private slot to react to the selection of 'pyvenv'.
227
228 @param _checked state of the checkbox (unused)
229 @type bool
230 """
231 self.__updateUi()
232
233 @pyqtSlot(bool)
234 def on_condaButton_toggled(self, _checked):
235 """
236 Private slot to react to the selection of 'conda'.
237
238 @param _checked state of the checkbox (unused)
239 @type bool
240 """
241 self.__updateUi()
242
243 @pyqtSlot(str)
244 def on_condaNameEdit_textChanged(self, _txt):
245 """
246 Private slot handling a change of the conda environment name.
247
248 @param _txt environment name (unused)
249 @type str
250 """
251 self.__updateOK()
252
253 @pyqtSlot(str)
254 def on_condaTargetDirectoryPicker_textChanged(self, _txt):
255 """
256 Private slot handling a change of the conda target directory.
257
258 @param _txt target directory (unused)
259 @type str
260 """
261 self.__updateOK()
262
263 @pyqtSlot()
264 def on_condaSpecialsGroup_clicked(self):
265 """
266 Private slot handling the selection of the specials group.
267 """
268 self.__updateOK()
269 self.__updateUi()
270
271 @pyqtSlot(str)
272 def on_condaCloneNameEdit_textChanged(self, _txt):
273 """
274 Private slot handling a change of the conda source environment name.
275
276 @param _txt name of the environment to be cloned (unused)
277 @type str
278 """
279 self.__updateOK()
280
281 @pyqtSlot(str)
282 def on_condaCloneDirectoryPicker_textChanged(self, _txt):
283 """
284 Private slot handling a change of the cloned from directory.
285
286 @param _txt target directory (unused)
287 @type str
288 """
289 self.__updateOK()
290
291 @pyqtSlot()
292 def on_condaCloneButton_clicked(self):
293 """
294 Private slot handling the selection of the clone button.
295 """
296 self.__updateOK()
297
298 @pyqtSlot()
299 def on_condaRequirementsButton_clicked(self):
300 """
301 Private slot handling the selection of the requirements button.
302 """
303 self.__updateOK()
304
305 @pyqtSlot(str)
306 def on_condaRequirementsFilePicker_textChanged(self, _txt):
307 """
308 Private slot handling a change of the requirements file entry.
309
310 @param _txt current text of the requirements file entry (unused)
311 @type str
312 """
313 self.__updateOK() 140 self.__updateOK()
314 141
315 def __setVirtualenvVersion(self): 142 def __setVirtualenvVersion(self):
316 """ 143 """
317 Private method to determine the virtualenv version and set the 144 Private method to determine the virtualenv version and set the
428 self.pyvenvButton.setText(self.tr("pyvenv Version: {0}".format(version))) 255 self.pyvenvButton.setText(self.tr("pyvenv Version: {0}".format(version)))
429 self.pyvenvButton.setEnabled(self.__pyvenvFound) 256 self.pyvenvButton.setEnabled(self.__pyvenvFound)
430 if not self.__pyvenvFound: 257 if not self.__pyvenvFound:
431 self.pyvenvButton.setChecked(False) 258 self.pyvenvButton.setChecked(False)
432 259
433 def __setCondaVersion(self):
434 """
435 Private method to determine the conda version and set the respective
436 label.
437 """
438 self.__condaFound = bool(CondaInterface.condaVersion())
439 self.condaButton.setText(
440 self.tr("conda Version: {0}".format(CondaInterface.condaVersionStr()))
441 )
442 self.condaButton.setEnabled(self.__condaFound)
443 if not self.__condaFound:
444 self.condaButton.setChecked(False)
445
446 def __generateTargetDir(self): 260 def __generateTargetDir(self):
447 """ 261 """
448 Private method to generate a valid target directory path. 262 Private method to generate a valid target directory path.
449 263
450 @return target directory path 264 @return target directory path
461 275
462 @return process arguments 276 @return process arguments
463 @rtype list of str 277 @rtype list of str
464 """ 278 """
465 args = [] 279 args = []
466 if self.condaButton.isChecked(): 280 if self.virtualenvButton.isChecked():
467 if bool(self.condaNameEdit.text()): 281 if self.extraSearchPathPicker.text():
468 args.extend(["--name", self.condaNameEdit.text()]) 282 args.append(
469 if bool(self.condaTargetDirectoryPicker.text()): 283 "--extra-search-dir={0}".format(self.extraSearchPathPicker.text())
470 args.extend(["--prefix", self.condaTargetDirectoryPicker.text()]) 284 )
471 if self.condaSpecialsGroup.isChecked(): 285 if self.promptPrefixEdit.text():
472 if self.condaCloneButton.isChecked(): 286 args.append(
473 if bool(self.condaCloneNameEdit.text()): 287 "--prompt={0}".format(
474 args.extend(["--clone", self.condaCloneNameEdit.text()]) 288 self.promptPrefixEdit.text().replace(" ", "_")
475 elif bool(self.condaCloneDirectoryPicker.text()):
476 args.extend(["--clone", self.condaCloneDirectoryPicker.text()])
477 elif self.condaRequirementsButton.isChecked():
478 args.extend(["--file", self.condaRequirementsFilePicker.text()])
479 if self.condaInsecureCheckBox.isChecked():
480 args.append("--insecure")
481 if self.condaDryrunCheckBox.isChecked():
482 args.append("--dry-run")
483 if not self.condaSpecialsGroup.isChecked():
484 if bool(self.condaPythonEdit.text()):
485 args.append("python={0}".format(self.condaPythonEdit.text()))
486 if bool(self.condaPackagesEdit.text()):
487 args.extend(self.condaPackagesEdit.text().split())
488 else:
489 if self.virtualenvButton.isChecked():
490 if self.extraSearchPathPicker.text():
491 args.append(
492 "--extra-search-dir={0}".format(
493 self.extraSearchPathPicker.text()
494 )
495 ) 289 )
496 if self.promptPrefixEdit.text(): 290 )
497 args.append( 291 if self.pythonExecPicker.text():
498 "--prompt={0}".format( 292 args.append("--python={0}".format(self.pythonExecPicker.text()))
499 self.promptPrefixEdit.text().replace(" ", "_") 293 elif self.versionComboBox.currentText():
500 ) 294 args.append(
501 ) 295 "--python=python{0}".format(self.versionComboBox.currentText())
502 if self.pythonExecPicker.text(): 296 )
503 args.append("--python={0}".format(self.pythonExecPicker.text())) 297 if self.verbositySpinBox.value() == 1:
504 elif self.versionComboBox.currentText(): 298 args.append("--verbose")
505 args.append( 299 elif self.verbositySpinBox.value() == -1:
506 "--python=python{0}".format(self.versionComboBox.currentText()) 300 args.append("--quiet")
507 ) 301 if self.clearCheckBox.isChecked():
508 if self.verbositySpinBox.value() == 1: 302 args.append("--clear")
509 args.append("--verbose") 303 if self.systemCheckBox.isChecked():
510 elif self.verbositySpinBox.value() == -1: 304 args.append("--system-site-packages")
511 args.append("--quiet") 305 if self.noWheelCheckBox.isChecked():
512 if self.clearCheckBox.isChecked(): 306 args.append("--no-wheel")
513 args.append("--clear") 307 if self.noSetuptoolsCheckBox.isChecked():
514 if self.systemCheckBox.isChecked(): 308 args.append("--no-setuptools")
515 args.append("--system-site-packages") 309 if self.noPipCcheckBox.isChecked():
516 if self.unzipCheckBox.isChecked(): 310 args.append("--no-pip")
517 args.append("--unzip-setuptools") 311 if self.copyCheckBox.isChecked():
518 if self.noSetuptoolsCheckBox.isChecked(): 312 args.append("--always-copy")
519 args.append("--no-setuptools") 313 elif self.pyvenvButton.isChecked():
520 if self.noPipCcheckBox.isChecked(): 314 if self.clearCheckBox.isChecked():
521 args.append("--no-pip") 315 args.append("--clear")
522 if self.copyCheckBox.isChecked(): 316 if self.systemCheckBox.isChecked():
523 args.append("--always-copy") 317 args.append("--system-site-packages")
524 elif self.pyvenvButton.isChecked(): 318 if self.noPipCcheckBox.isChecked():
525 if self.clearCheckBox.isChecked(): 319 args.append("--without-pip")
526 args.append("--clear") 320 if self.copyCheckBox.isChecked():
527 if self.systemCheckBox.isChecked(): 321 args.append("--copies")
528 args.append("--system-site-packages") 322 if self.symlinkCheckBox.isChecked():
529 if self.noPipCcheckBox.isChecked(): 323 args.append("--symlinks")
530 args.append("--without-pip") 324 if self.upgradeCheckBox.isChecked():
531 if self.copyCheckBox.isChecked(): 325 args.append("--upgrade")
532 args.append("--copies") 326 targetDirectory = self.__generateTargetDir()
533 if self.symlinkCheckBox.isChecked(): 327 args.append(targetDirectory)
534 args.append("--symlinks")
535 if self.upgradeCheckBox.isChecked():
536 args.append("--upgrade")
537 targetDirectory = self.__generateTargetDir()
538 args.append(targetDirectory)
539 328
540 return args 329 return args
541 330
542 def getData(self): 331 def getData(self):
543 """ 332 """
544 Public method to retrieve the dialog data. 333 Public method to retrieve the dialog data.
545 334
546 @return dictionary containing the data for the two environment 335 @return dictionary containing the data for the new environment. The keys
547 variants. The keys for both variants are 'arguments' containing the 336 are 'arguments' containing the command line arguments, 'logicalName'
548 command line arguments, 'logicalName' containing the environment 337 containing the environment name to be used with the virtual environment
549 name to be used with the virtual env manager and 'envType' 338 manager and 'envType' containing the environment type (virtualenv or
550 containing the environment type (virtualenv, pyvenv or conda). The 339 pyvenv). Additional keys are 'openTarget' containg a flag to open the
551 virtualenv/pyvenv specific keys are 'openTarget' containg a flag to 340 target directory after creation, 'createLog' containing a flag to write
552 open the target directory after creation, 'createLog' containing a 341 a log file, 'createScript' containing a flag to write a script,
553 flag to write a log file, 'createScript' containing a flag to write 342 'targetDirectory' containing the target directory and 'pythonExe'
554 a script, 'targetDirectory' containing the target directory and 343 containing the Python interpreter to be used.
555 'pythonExe' containing the Python interpreter to be used. The
556 conda specific key is 'command' giving the conda command to be
557 executed (always 'create').
558 @rtype dict 344 @rtype dict
559 """ 345 """
560 args = self.__generateArguments() 346 return {
561 resultDict = { 347 "arguments": self.__generateArguments(),
562 "arguments": args,
563 "logicalName": self.nameEdit.text(), 348 "logicalName": self.nameEdit.text(),
349 "envType": "pyvenv" if self.pyvenvButton.isChecked() else "virtualenv",
350 "openTarget": self.openCheckBox.isChecked(),
351 "createLog": self.logCheckBox.isChecked(),
352 "createScript": self.scriptCheckBox.isChecked(),
353 "targetDirectory": self.__generateTargetDir(),
354 "pythonExe": self.pythonExecPicker.text(),
564 } 355 }
565 if self.condaButton.isChecked():
566 resultDict.update(
567 {
568 "envType": "conda",
569 "command": "create",
570 }
571 )
572 else:
573 resultDict.update(
574 {
575 "envType": (
576 "pyvenv" if self.pyvenvButton.isChecked() else "virtualenv"
577 ),
578 "openTarget": self.openCheckBox.isChecked(),
579 "createLog": self.logCheckBox.isChecked(),
580 "createScript": self.scriptCheckBox.isChecked(),
581 "targetDirectory": self.__generateTargetDir(),
582 "pythonExe": self.pythonExecPicker.text(),
583 }
584 )
585
586 return resultDict

eric ide

mercurial