|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2018 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to manage the list of defined virtual |
|
8 environments. |
|
9 """ |
|
10 |
|
11 import os |
|
12 |
|
13 from PyQt6.QtCore import pyqtSlot, Qt |
|
14 from PyQt6.QtWidgets import ( |
|
15 QWidget, QDialog, QDialogButtonBox, QTreeWidgetItem, QHeaderView, |
|
16 QVBoxLayout |
|
17 ) |
|
18 |
|
19 from EricWidgets.EricPathPicker import EricPathPickerModes |
|
20 from EricWidgets.EricMainWindow import EricMainWindow |
|
21 |
|
22 from .Ui_VirtualenvManagerWidget import Ui_VirtualenvManagerWidget |
|
23 |
|
24 import Utilities |
|
25 import UI.PixmapCache |
|
26 |
|
27 |
|
28 class VirtualenvManagerWidget(QWidget, Ui_VirtualenvManagerWidget): |
|
29 """ |
|
30 Class implementing a widget to manage the list of defined virtual |
|
31 environments. |
|
32 """ |
|
33 IsGlobalRole = Qt.ItemDataRole.UserRole + 1 |
|
34 IsCondaRole = Qt.ItemDataRole.UserRole + 2 |
|
35 IsRemoteRole = Qt.ItemDataRole.UserRole + 3 |
|
36 ExecPathRole = Qt.ItemDataRole.UserRole + 4 |
|
37 |
|
38 def __init__(self, manager, parent=None): |
|
39 """ |
|
40 Constructor |
|
41 |
|
42 @param manager reference to the virtual environment manager |
|
43 @type VirtualenvManager |
|
44 @param parent reference to the parent widget |
|
45 @type QWidget |
|
46 """ |
|
47 super().__init__(parent) |
|
48 self.setupUi(self) |
|
49 |
|
50 self.__manager = manager |
|
51 |
|
52 self.refreshButton.setIcon(UI.PixmapCache.getIcon("reload")) |
|
53 self.addButton.setIcon(UI.PixmapCache.getIcon("plus")) |
|
54 self.newButton.setIcon(UI.PixmapCache.getIcon("new")) |
|
55 self.editButton.setIcon(UI.PixmapCache.getIcon("edit")) |
|
56 self.upgradeButton.setIcon(UI.PixmapCache.getIcon("upgrade")) |
|
57 self.removeButton.setIcon(UI.PixmapCache.getIcon("minus")) |
|
58 self.removeAllButton.setIcon(UI.PixmapCache.getIcon("minus_3")) |
|
59 self.deleteButton.setIcon(UI.PixmapCache.getIcon("fileDelete")) |
|
60 self.deleteAllButton.setIcon(UI.PixmapCache.getIcon("fileDeleteList")) |
|
61 self.saveButton.setIcon(UI.PixmapCache.getIcon("fileSave")) |
|
62 |
|
63 baseDir = self.__manager.getVirtualEnvironmentsBaseDir() |
|
64 if not baseDir: |
|
65 baseDir = Utilities.getHomeDir() |
|
66 |
|
67 self.envBaseDirectoryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) |
|
68 self.envBaseDirectoryPicker.setWindowTitle( |
|
69 self.tr("Virtualenv Base Directory")) |
|
70 self.envBaseDirectoryPicker.setText(baseDir) |
|
71 |
|
72 self.__populateVenvList() |
|
73 self.__updateButtons() |
|
74 |
|
75 self.venvList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) |
|
76 |
|
77 self.__manager.virtualEnvironmentsListChanged.connect(self.__refresh) |
|
78 |
|
79 def __updateButtons(self): |
|
80 """ |
|
81 Private method to update the enabled state of the various buttons. |
|
82 """ |
|
83 selectedItemsCount = len(self.venvList.selectedItems()) |
|
84 topLevelItemCount = self.venvList.topLevelItemCount() |
|
85 |
|
86 deletableSelectedItemCount = 0 |
|
87 for itm in self.venvList.selectedItems(): |
|
88 if ( |
|
89 itm.text(0) != "<default>" and |
|
90 bool(itm.text(1)) and |
|
91 not itm.data(0, VirtualenvManagerWidget.IsGlobalRole) and |
|
92 not itm.data(0, VirtualenvManagerWidget.IsRemoteRole) |
|
93 ): |
|
94 deletableSelectedItemCount += 1 |
|
95 |
|
96 deletableItemCount = 0 |
|
97 for index in range(topLevelItemCount): |
|
98 itm = self.venvList.topLevelItem(index) |
|
99 if ( |
|
100 itm.text(0) != "<default>" and |
|
101 bool(itm.text(1)) and |
|
102 not itm.data(0, VirtualenvManagerWidget.IsRemoteRole) |
|
103 ): |
|
104 deletableItemCount += 1 |
|
105 |
|
106 canBeRemoved = ( |
|
107 selectedItemsCount == 1 and |
|
108 self.venvList.selectedItems()[0].text(0) != "<default>" |
|
109 ) |
|
110 canAllBeRemoved = ( |
|
111 topLevelItemCount == 1 and |
|
112 self.venvList.topLevelItem(0).text(0) != "<default>" |
|
113 ) |
|
114 |
|
115 self.editButton.setEnabled(selectedItemsCount == 1) |
|
116 |
|
117 self.removeButton.setEnabled(selectedItemsCount > 1 or canBeRemoved) |
|
118 self.removeAllButton.setEnabled( |
|
119 topLevelItemCount > 1 or canAllBeRemoved) |
|
120 |
|
121 self.deleteButton.setEnabled(deletableSelectedItemCount) |
|
122 self.deleteAllButton.setEnabled(deletableItemCount) |
|
123 |
|
124 if selectedItemsCount == 1: |
|
125 venvName = self.venvList.selectedItems()[0].text(0) |
|
126 venvDirectory = self.__manager.getVirtualenvDirectory(venvName) |
|
127 self.upgradeButton.setEnabled(os.path.exists(os.path.join( |
|
128 venvDirectory, "pyvenv.cfg"))) |
|
129 else: |
|
130 self.upgradeButton.setEnabled(False) |
|
131 |
|
132 @pyqtSlot() |
|
133 def on_refreshButton_clicked(self): |
|
134 """ |
|
135 Private slot to refresh the list of virtual environments. |
|
136 """ |
|
137 self.__manager.reloadSettings() |
|
138 self.__refresh() |
|
139 |
|
140 @pyqtSlot() |
|
141 def on_addButton_clicked(self): |
|
142 """ |
|
143 Private slot to add a new entry. |
|
144 """ |
|
145 from .VirtualenvAddEditDialog import VirtualenvAddEditDialog |
|
146 dlg = VirtualenvAddEditDialog( |
|
147 self.__manager, |
|
148 baseDir=self.envBaseDirectoryPicker.text() |
|
149 ) |
|
150 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
151 (venvName, venvDirectory, venvInterpreter, isGlobal, isConda, |
|
152 isRemote, execPath) = dlg.getData() |
|
153 |
|
154 self.__manager.addVirtualEnv( |
|
155 venvName, venvDirectory, venvInterpreter, isGlobal, isConda, |
|
156 isRemote, execPath) |
|
157 |
|
158 @pyqtSlot() |
|
159 def on_newButton_clicked(self): |
|
160 """ |
|
161 Private slot to create a new virtual environment. |
|
162 """ |
|
163 self.__manager.createVirtualEnv( |
|
164 baseDir=self.envBaseDirectoryPicker.text()) |
|
165 |
|
166 @pyqtSlot() |
|
167 def on_editButton_clicked(self): |
|
168 """ |
|
169 Private slot to edit the selected entry. |
|
170 """ |
|
171 selectedItem = self.venvList.selectedItems()[0] |
|
172 oldVenvName = selectedItem.text(0) |
|
173 |
|
174 from .VirtualenvAddEditDialog import VirtualenvAddEditDialog |
|
175 dlg = VirtualenvAddEditDialog( |
|
176 self.__manager, selectedItem.text(0), |
|
177 selectedItem.text(1), selectedItem.text(2), |
|
178 selectedItem.data(0, VirtualenvManagerWidget.IsGlobalRole), |
|
179 selectedItem.data(0, VirtualenvManagerWidget.IsCondaRole), |
|
180 selectedItem.data(0, VirtualenvManagerWidget.IsRemoteRole), |
|
181 selectedItem.data(0, VirtualenvManagerWidget.ExecPathRole), |
|
182 baseDir=self.envBaseDirectoryPicker.text() |
|
183 ) |
|
184 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
185 (venvName, venvDirectory, venvInterpreter, isGlobal, isConda, |
|
186 isRemote, execPath) = dlg.getData() |
|
187 if venvName != oldVenvName: |
|
188 self.__manager.renameVirtualEnv( |
|
189 oldVenvName, venvName, venvDirectory, venvInterpreter, |
|
190 isGlobal, isConda, isRemote, execPath) |
|
191 else: |
|
192 self.__manager.setVirtualEnv( |
|
193 venvName, venvDirectory, venvInterpreter, isGlobal, |
|
194 isConda, isRemote, execPath) |
|
195 |
|
196 @pyqtSlot() |
|
197 def on_upgradeButton_clicked(self): |
|
198 """ |
|
199 Private slot to upgrade a virtual environment. |
|
200 """ |
|
201 self.__manager.upgradeVirtualEnv( |
|
202 self.venvList.selectedItems()[0].text(0)) |
|
203 |
|
204 @pyqtSlot() |
|
205 def on_removeButton_clicked(self): |
|
206 """ |
|
207 Private slot to remove all selected entries from the list but keep |
|
208 their directories. |
|
209 """ |
|
210 selectedVenvs = [] |
|
211 for itm in self.venvList.selectedItems(): |
|
212 selectedVenvs.append(itm.text(0)) |
|
213 |
|
214 if selectedVenvs: |
|
215 self.__manager.removeVirtualEnvs(selectedVenvs) |
|
216 |
|
217 @pyqtSlot() |
|
218 def on_removeAllButton_clicked(self): |
|
219 """ |
|
220 Private slot to remove all entries from the list but keep their |
|
221 directories. |
|
222 """ |
|
223 venvNames = [] |
|
224 for index in range(self.venvList.topLevelItemCount()): |
|
225 itm = self.venvList.topLevelItem(index) |
|
226 venvNames.append(itm.text(0)) |
|
227 |
|
228 if venvNames: |
|
229 self.__manager.removeVirtualEnvs(venvNames) |
|
230 |
|
231 @pyqtSlot() |
|
232 def on_deleteButton_clicked(self): |
|
233 """ |
|
234 Private slot to delete all selected entries from the list and disk. |
|
235 """ |
|
236 selectedVenvs = [] |
|
237 for itm in self.venvList.selectedItems(): |
|
238 selectedVenvs.append(itm.text(0)) |
|
239 |
|
240 if selectedVenvs: |
|
241 self.__manager.deleteVirtualEnvs(selectedVenvs) |
|
242 |
|
243 @pyqtSlot() |
|
244 def on_deleteAllButton_clicked(self): |
|
245 """ |
|
246 Private slot to delete all entries from the list and disk. |
|
247 """ |
|
248 venvNames = [] |
|
249 for index in range(self.venvList.topLevelItemCount()): |
|
250 itm = self.venvList.topLevelItem(index) |
|
251 venvNames.append(itm.text(0)) |
|
252 |
|
253 if venvNames: |
|
254 self.__manager.deleteVirtualEnvs(venvNames) |
|
255 |
|
256 @pyqtSlot() |
|
257 def on_venvList_itemSelectionChanged(self): |
|
258 """ |
|
259 Private slot handling a change of the selected items. |
|
260 """ |
|
261 self.__updateButtons() |
|
262 |
|
263 @pyqtSlot() |
|
264 def __refresh(self): |
|
265 """ |
|
266 Private slot to refresh the list of shown items. |
|
267 """ |
|
268 # 1. remember selected entries |
|
269 selectedVenvs = [] |
|
270 for itm in self.venvList.selectedItems(): |
|
271 selectedVenvs.append(itm.text(0)) |
|
272 |
|
273 # 2. clear the list |
|
274 self.venvList.clear() |
|
275 |
|
276 # 3. re-populate the list |
|
277 self.__populateVenvList() |
|
278 |
|
279 # 4. re-establish selection |
|
280 for venvName in selectedVenvs: |
|
281 itms = self.venvList.findItems( |
|
282 venvName, Qt.MatchFlag.MatchExactly, 0) |
|
283 if itms: |
|
284 itms[0].setSelected(True) |
|
285 |
|
286 def __populateVenvList(self): |
|
287 """ |
|
288 Private method to populate the list of virtual environments. |
|
289 """ |
|
290 environments = self.__manager.getEnvironmentEntries() |
|
291 for venvName in environments: |
|
292 itm = QTreeWidgetItem(self.venvList, [ |
|
293 venvName, |
|
294 environments[venvName]["path"], |
|
295 environments[venvName]["interpreter"], |
|
296 ]) |
|
297 itm.setData(0, VirtualenvManagerWidget.IsGlobalRole, |
|
298 environments[venvName]["is_global"]) |
|
299 itm.setData(0, VirtualenvManagerWidget.IsCondaRole, |
|
300 environments[venvName]["is_conda"]) |
|
301 itm.setData(0, VirtualenvManagerWidget.IsRemoteRole, |
|
302 environments[venvName]["is_remote"]) |
|
303 itm.setData(0, VirtualenvManagerWidget.ExecPathRole, |
|
304 environments[venvName]["exec_path"]) |
|
305 |
|
306 # show remote environments with underlined font |
|
307 if environments[venvName]["is_remote"]: |
|
308 font = itm.font(0) |
|
309 font.setUnderline(True) |
|
310 for column in range(itm.columnCount()): |
|
311 itm.setFont(column, font) |
|
312 else: |
|
313 # local environments |
|
314 |
|
315 # show global environments with bold font |
|
316 if environments[venvName]["is_global"]: |
|
317 font = itm.font(0) |
|
318 font.setBold(True) |
|
319 for column in range(itm.columnCount()): |
|
320 itm.setFont(column, font) |
|
321 |
|
322 # show Anaconda environments with italic font |
|
323 if environments[venvName]["is_conda"]: |
|
324 font = itm.font(0) |
|
325 font.setItalic(True) |
|
326 for column in range(itm.columnCount()): |
|
327 itm.setFont(column, font) |
|
328 |
|
329 self.__resizeSections() |
|
330 |
|
331 def __resizeSections(self): |
|
332 """ |
|
333 Private method to resize the sections of the environment list to their |
|
334 contents. |
|
335 """ |
|
336 self.venvList.header().resizeSections( |
|
337 QHeaderView.ResizeMode.ResizeToContents) |
|
338 self.venvList.header().setStretchLastSection(True) |
|
339 |
|
340 @pyqtSlot() |
|
341 def on_saveButton_clicked(self): |
|
342 """ |
|
343 Private slot to save the base directory name. |
|
344 """ |
|
345 baseDir = self.envBaseDirectoryPicker.text() |
|
346 self.__manager.setVirtualEnvironmentsBaseDir(baseDir) |
|
347 |
|
348 |
|
349 class VirtualenvManagerDialog(QDialog): |
|
350 """ |
|
351 Class implementing the virtual environments manager dialog variant. |
|
352 """ |
|
353 def __init__(self, manager, parent=None): |
|
354 """ |
|
355 Constructor |
|
356 |
|
357 @param manager reference to the virtual environment manager |
|
358 @type VirtualenvManager |
|
359 @param parent reference to the parent widget |
|
360 @type QWidget |
|
361 """ |
|
362 super().__init__(parent) |
|
363 self.setSizeGripEnabled(True) |
|
364 |
|
365 self.__layout = QVBoxLayout(self) |
|
366 self.setLayout(self.__layout) |
|
367 |
|
368 self.cw = VirtualenvManagerWidget(manager, self) |
|
369 self.__layout.addWidget(self.cw) |
|
370 |
|
371 self.buttonBox = QDialogButtonBox( |
|
372 QDialogButtonBox.StandardButton.Close, |
|
373 Qt.Orientation.Horizontal, |
|
374 self) |
|
375 self.__layout.addWidget(self.buttonBox) |
|
376 |
|
377 self.resize(700, 500) |
|
378 self.setWindowTitle(self.tr("Manage Virtual Environments")) |
|
379 |
|
380 self.buttonBox.accepted.connect(self.accept) |
|
381 self.buttonBox.rejected.connect(self.reject) |
|
382 |
|
383 |
|
384 class VirtualenvManagerWindow(EricMainWindow): |
|
385 """ |
|
386 Main window class for the standalone virtual environments manager. |
|
387 """ |
|
388 def __init__(self, parent=None): |
|
389 """ |
|
390 Constructor |
|
391 |
|
392 @param parent reference to the parent widget (QWidget) |
|
393 """ |
|
394 super().__init__(parent) |
|
395 |
|
396 from VirtualEnv.VirtualenvManager import VirtualenvManager |
|
397 self.__virtualenvManager = VirtualenvManager(self) |
|
398 |
|
399 self.__centralWidget = QWidget(self) |
|
400 self.__layout = QVBoxLayout(self.__centralWidget) |
|
401 self.__centralWidget.setLayout(self.__layout) |
|
402 |
|
403 self.__virtualenvManagerWidget = VirtualenvManagerWidget( |
|
404 self.__virtualenvManager, self.__centralWidget) |
|
405 self.__layout.addWidget(self.__virtualenvManagerWidget) |
|
406 |
|
407 self.__buttonBox = QDialogButtonBox( |
|
408 QDialogButtonBox.StandardButton.Close, |
|
409 Qt.Orientation.Horizontal, |
|
410 self) |
|
411 self.__layout.addWidget(self.__buttonBox) |
|
412 |
|
413 self.setCentralWidget(self.__centralWidget) |
|
414 self.resize(700, 500) |
|
415 self.setWindowTitle(self.tr("Manage Virtual Environments")) |
|
416 |
|
417 self.__buttonBox.accepted.connect(self.close) |
|
418 self.__buttonBox.rejected.connect(self.close) |