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