9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
12 from PyQt5.QtCore import pyqtSlot, Qt |
12 from PyQt5.QtCore import pyqtSlot, Qt |
13 from PyQt5.QtGui import QCursor |
13 from PyQt5.QtGui import QCursor |
14 from PyQt5.QtWidgets import QWidget, QToolButton, QApplication |
14 from PyQt5.QtWidgets import QWidget, QToolButton, QApplication, QHeaderView, \ |
|
15 QTreeWidgetItem |
|
16 |
|
17 from E5Gui.E5Application import e5App |
15 |
18 |
16 from .Ui_PipPackagesWidget import Ui_PipPackagesWidget |
19 from .Ui_PipPackagesWidget import Ui_PipPackagesWidget |
17 |
20 |
18 import UI.PixmapCache |
21 import UI.PixmapCache |
19 |
22 |
20 from .Pip import Pip |
23 from .Pip import Pip |
21 |
24 |
22 |
25 |
23 class PipPackagesWidget(QWidget, Ui_PipPackagesWidget): |
26 class PipPackagesWidget(QWidget, Ui_PipPackagesWidget): |
24 """ |
27 """ |
25 Class documentation goes here. |
28 Class implementing the pip packages management widget. |
26 """ |
29 """ |
|
30 ShowProcessGeneralMode = 0 |
|
31 ShowProcessClassifiersMode = 1 |
|
32 ShowProcessEntryPointsMode = 2 |
|
33 ShowProcessFilesListMode = 3 |
|
34 |
27 def __init__(self, parent=None): |
35 def __init__(self, parent=None): |
28 """ |
36 """ |
29 Constructor |
37 Constructor |
30 |
38 |
31 @param parent reference to the parent widget |
39 @param parent reference to the parent widget |
46 |
54 |
47 self.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find")) |
55 self.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find")) |
48 |
56 |
49 self.__pip = Pip(self) |
57 self.__pip = Pip(self) |
50 |
58 |
|
59 self.packagesList.header().setSortIndicator(0, Qt.AscendingOrder) |
|
60 |
|
61 self.__infoLabels = { |
|
62 "name": self.tr("Name:"), |
|
63 "version": self.tr("Version:"), |
|
64 "location": self.tr("Location:"), |
|
65 "requires": self.tr("Requires:"), |
|
66 "summary": self.tr("Summary:"), |
|
67 "home-page": self.tr("Homepage:"), |
|
68 "author": self.tr("Author:"), |
|
69 "author-email": self.tr("Author Email:"), |
|
70 "license": self.tr("License:"), |
|
71 "metadata-version": self.tr("Metadata Version:"), |
|
72 "installer": self.tr("Installer:"), |
|
73 "classifiers": self.tr("Classifiers:"), |
|
74 "entry-points": self.tr("Entry Points:"), |
|
75 "files": self.tr("Files:"), |
|
76 } |
|
77 self.infoWidget.setHeaderLabels(["Key", "Value"]) |
|
78 |
|
79 venvManager = e5App().getObject("VirtualEnvManager") |
|
80 venvManager.virtualEnvironmentAdded.connect( |
|
81 self.on_refreshButton_clicked) |
|
82 venvManager.virtualEnvironmentRemoved.connect( |
|
83 self.on_refreshButton_clicked) |
|
84 |
|
85 project = e5App().getObject("Project") |
|
86 project.projectOpened.connect( |
|
87 self.on_refreshButton_clicked) |
|
88 project.projectClosed.connect( |
|
89 self.on_refreshButton_clicked) |
|
90 |
51 self.__initPipMenu() |
91 self.__initPipMenu() |
52 self.__populateEnvironments() |
92 self.__populateEnvironments() |
53 self.__updateActionButtons() |
93 self.__updateActionButtons() |
54 |
94 |
55 self.statusLabel.hide() |
95 self.statusLabel.hide() |
56 self.searchWidget.hide() |
96 self.searchWidget.hide() |
57 |
|
58 def __initPipMenu(self): |
|
59 """ |
|
60 Private method to create the super menu and attach it to the super |
|
61 menu button. |
|
62 """ |
|
63 self.__pip.initActions() |
|
64 |
|
65 self.__pipMenu = self.__pip.initMenu() |
|
66 |
|
67 self.pipMenuButton.setMenu(self.__pipMenu) |
|
68 |
97 |
69 def __populateEnvironments(self): |
98 def __populateEnvironments(self): |
70 """ |
99 """ |
71 Private method to get a list of environments and populate the selector. |
100 Private method to get a list of environments and populate the selector. |
72 """ |
101 """ |
74 projectVenv = self.__pip.getProjectEnvironmentString() |
103 projectVenv = self.__pip.getProjectEnvironmentString() |
75 if projectVenv: |
104 if projectVenv: |
76 self.environmentsComboBox.addItem(projectVenv) |
105 self.environmentsComboBox.addItem(projectVenv) |
77 self.environmentsComboBox.addItems(self.__pip.getVirtualenvNames()) |
106 self.environmentsComboBox.addItems(self.__pip.getVirtualenvNames()) |
78 |
107 |
|
108 ####################################################################### |
|
109 ## Slots handling widget signals below |
|
110 ####################################################################### |
|
111 |
|
112 def __selectedUpdateableItems(self): |
|
113 """ |
|
114 Private method to get a list of selected items that can be updated. |
|
115 |
|
116 @return list of selected items that can be updated |
|
117 @rtype list of QTreeWidgetItem |
|
118 """ |
|
119 return [ |
|
120 itm for itm in self.packagesList.selectedItems() |
|
121 if bool(itm.text(2)) |
|
122 ] |
|
123 |
|
124 def __allUpdateableItems(self): |
|
125 """ |
|
126 Private method to get a list of all items that can be updated. |
|
127 |
|
128 @return list of all items that can be updated |
|
129 @rtype list of QTreeWidgetItem |
|
130 """ |
|
131 updateableItems = [] |
|
132 for index in range(self.packagesList.topLevelItemCount()): |
|
133 itm = self.packagesList.topLevelItem(index) |
|
134 if itm.text(2): |
|
135 updateableItems.append(itm) |
|
136 |
|
137 return updateableItems |
|
138 |
79 def __updateActionButtons(self): |
139 def __updateActionButtons(self): |
80 """ |
140 """ |
81 Private method to set the state of the action buttons. |
141 Private method to set the state of the action buttons. |
82 """ |
142 """ |
83 # TODO: not yet implemented |
143 self.upgradeButton.setEnabled( |
84 pass |
144 bool(self.__selectedUpdateableItems())) |
85 |
145 self.uninstallButton.setEnabled( |
86 ####################################################################### |
146 bool(self.packagesList.selectedItems())) |
87 ## Slots handling widget signals below |
147 self.upgradeAllButton.setEnabled( |
88 ####################################################################### |
148 bool(self.__allUpdateableItems())) |
89 |
149 |
90 @pyqtSlot(int) |
150 def __refreshPackagesList(self): |
91 def on_environmentsComboBox_currentIndexChanged(self, index): |
151 """ |
92 """ |
152 Private method to referesh the packages list. |
93 Private slot handling the selection of a conda environment. |
|
94 |
|
95 @param index index of the selected conda environment |
|
96 @type int |
|
97 """ |
153 """ |
98 self.packagesList.clear() |
154 self.packagesList.clear() |
99 venvName = self.environmentsComboBox.currentText() |
155 venvName = self.environmentsComboBox.currentText() |
100 if venvName: |
156 if venvName: |
101 interpreter = self.__pip.getVirtualenvInterpreter(venvName) |
157 interpreter = self.__pip.getVirtualenvInterpreter(venvName) |
105 self.statusLabel.setText( |
161 self.statusLabel.setText( |
106 self.tr("Getting installed packages...")) |
162 self.tr("Getting installed packages...")) |
107 QApplication.processEvents() |
163 QApplication.processEvents() |
108 |
164 |
109 # 1. populate with installed packages |
165 # 1. populate with installed packages |
110 pass # TODO: add code to list installed |
166 self.packagesList.setUpdatesEnabled(False) |
|
167 installedPackages = self.__pip.getInstalledPackages( |
|
168 venvName, |
|
169 localPackages=self.localCheckBox.isChecked(), |
|
170 notRequired=self.notRequiredCheckBox.isChecked(), |
|
171 usersite=self.userCheckBox.isChecked(), |
|
172 ) |
|
173 for package, version in installedPackages: |
|
174 QTreeWidgetItem(self.packagesList, [package, version]) |
|
175 self.packagesList.setUpdatesEnabled(True) |
|
176 self.statusLabel.setText( |
|
177 self.tr("Getting outdated packages...")) |
|
178 QApplication.processEvents() |
111 |
179 |
112 # 2. update with update information |
180 # 2. update with update information |
113 pass # TODO: add code to list outdated |
181 self.packagesList.setUpdatesEnabled(False) |
|
182 outdatedPackages = self.__pip.getOutdatedPackages( |
|
183 venvName, |
|
184 localPackages=self.localCheckBox.isChecked(), |
|
185 notRequired=self.notRequiredCheckBox.isChecked(), |
|
186 usersite=self.userCheckBox.isChecked(), |
|
187 ) |
|
188 for package, _version, latest in outdatedPackages: |
|
189 items = self.packagesList.findItems( |
|
190 package, Qt.MatchExactly | Qt.MatchCaseSensitive) |
|
191 if items: |
|
192 itm = items[0] |
|
193 itm.setText(2, latest) |
114 |
194 |
115 self.packagesList.sortItems(0, Qt.AscendingOrder) |
195 self.packagesList.sortItems(0, Qt.AscendingOrder) |
116 for col in range(self.packagesList.columnCount()): |
196 for col in range(self.packagesList.columnCount()): |
117 self.packagesList.resizeColumnToContents(col) |
197 self.packagesList.resizeColumnToContents(col) |
118 self.packagesList.setUpdatesEnabled(True) |
198 self.packagesList.setUpdatesEnabled(True) |
120 self.statusLabel.hide() |
200 self.statusLabel.hide() |
121 |
201 |
122 self.__updateActionButtons() |
202 self.__updateActionButtons() |
123 self.__updateSearchActionButtons() |
203 self.__updateSearchActionButtons() |
124 |
204 |
|
205 @pyqtSlot(int) |
|
206 def on_environmentsComboBox_currentIndexChanged(self, index): |
|
207 """ |
|
208 Private slot handling the selection of a conda environment. |
|
209 |
|
210 @param index index of the selected conda environment |
|
211 @type int |
|
212 """ |
|
213 self.__refreshPackagesList() |
|
214 |
|
215 @pyqtSlot(bool) |
|
216 def on_localCheckBox_clicked(self, checked): |
|
217 """ |
|
218 Private slot handling the switching of the local mode. |
|
219 |
|
220 @param checked state of the local check box |
|
221 @type bool |
|
222 """ |
|
223 self.__refreshPackagesList() |
|
224 |
|
225 @pyqtSlot(bool) |
|
226 def on_notRequiredCheckBox_clicked(self, checked): |
|
227 """ |
|
228 Private slot handling the switching of the 'not required' mode. |
|
229 |
|
230 @param checked state of the 'not required' check box |
|
231 @type bool |
|
232 """ |
|
233 self.__refreshPackagesList() |
|
234 |
|
235 @pyqtSlot(bool) |
|
236 def on_userCheckBox_clicked(self, checked): |
|
237 """ |
|
238 Private slot handling the switching of the 'user-site' mode. |
|
239 |
|
240 @param checked state of the 'user-site' check box |
|
241 @type bool |
|
242 """ |
|
243 self.__refreshPackagesList() |
|
244 |
|
245 @pyqtSlot() |
|
246 def on_packagesList_itemSelectionChanged(self): |
|
247 """ |
|
248 Private slot handling the selection of a package. |
|
249 """ |
|
250 self.infoWidget.clear() |
|
251 |
|
252 if len(self.packagesList.selectedItems()) == 1: |
|
253 itm = self.packagesList.selectedItems()[0] |
|
254 |
|
255 environment = self.environmentsComboBox.currentText() |
|
256 interpreter = self.__pip.getVirtualenvInterpreter(environment) |
|
257 if not interpreter: |
|
258 return |
|
259 |
|
260 QApplication.setOverrideCursor(Qt.WaitCursor) |
|
261 |
|
262 args = ["-m", "pip", "show"] |
|
263 if self.verboseCheckBox.isChecked(): |
|
264 args.append("--verbose") |
|
265 if self.installedFilesCheckBox.isChecked(): |
|
266 args.append("--files") |
|
267 args.append(itm.text(0)) |
|
268 success, output = self.__pip.runProcess(args, interpreter) |
|
269 |
|
270 if success and output: |
|
271 mode = self.ShowProcessGeneralMode |
|
272 for line in output.splitlines(): |
|
273 line = line.rstrip() |
|
274 if line != "---": |
|
275 if mode != self.ShowProcessGeneralMode: |
|
276 if line[0] == " ": |
|
277 QTreeWidgetItem( |
|
278 self.infoWidget, |
|
279 [" ", line.strip()]) |
|
280 else: |
|
281 mode = self.ShowProcessGeneralMode |
|
282 if mode == self.ShowProcessGeneralMode: |
|
283 try: |
|
284 label, info = line.split(": ", 1) |
|
285 except ValueError: |
|
286 label = line[:-1] |
|
287 info = "" |
|
288 label = label.lower() |
|
289 if label in self.__infoLabels: |
|
290 QTreeWidgetItem( |
|
291 self.infoWidget, |
|
292 [self.__infoLabels[label], info]) |
|
293 if label == "files": |
|
294 mode = self.ShowProcessFilesListMode |
|
295 elif label == "classifiers": |
|
296 mode = self.ShowProcessClassifiersMode |
|
297 elif label == "entry-points": |
|
298 mode = self.ShowProcessEntryPointsMode |
|
299 self.infoWidget.scrollToTop() |
|
300 |
|
301 header = self.infoWidget.header() |
|
302 header.setStretchLastSection(False) |
|
303 header.resizeSections(QHeaderView.ResizeToContents) |
|
304 if header.sectionSize(0) + header.sectionSize(1) < header.width(): |
|
305 header.setStretchLastSection(True) |
|
306 |
|
307 QApplication.restoreOverrideCursor() |
|
308 |
|
309 self.__updateActionButtons() |
|
310 |
|
311 @pyqtSlot(bool) |
|
312 def on_verboseCheckBox_clicked(self, checked): |
|
313 """ |
|
314 Private slot to handle a change of the verbose package information |
|
315 checkbox. |
|
316 |
|
317 @param checked state of the checkbox |
|
318 @type bool |
|
319 """ |
|
320 self.on_packagesList_itemSelectionChanged() |
|
321 |
|
322 @pyqtSlot(bool) |
|
323 def on_installedFilesCheckBox_clicked(self, checked): |
|
324 """ |
|
325 Private slot to handle a change of the installed files information |
|
326 checkbox. |
|
327 |
|
328 @param checked state of the checkbox |
|
329 @type bool |
|
330 """ |
|
331 self.on_packagesList_itemSelectionChanged() |
|
332 |
|
333 @pyqtSlot() |
|
334 def on_refreshButton_clicked(self): |
|
335 """ |
|
336 Private slot to refresh the display. |
|
337 """ |
|
338 currentEnvironment = self.environmentsComboBox.currentText() |
|
339 self.environmentsComboBox.clear() |
|
340 self.packagesList.clear() |
|
341 |
|
342 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
343 QApplication.processEvents() |
|
344 |
|
345 self.__populateEnvironments() |
|
346 |
|
347 index = self.environmentsComboBox.findText( |
|
348 currentEnvironment, Qt.MatchExactly | Qt.MatchCaseSensitive) |
|
349 if index != -1: |
|
350 self.environmentsComboBox.setCurrentIndex(index) |
|
351 |
|
352 QApplication.restoreOverrideCursor() |
|
353 self.__updateActionButtons() |
|
354 |
|
355 @pyqtSlot() |
|
356 def on_upgradeButton_clicked(self): |
|
357 """ |
|
358 Private slot to upgrade selected packages of the selected environment. |
|
359 """ |
|
360 packages = [itm.text(0) for itm in self.__selectedUpdateableItems()] |
|
361 if packages: |
|
362 ok = self.__executeUpgradePackages(packages) |
|
363 if ok: |
|
364 self.on_refreshButton_clicked() |
|
365 |
|
366 @pyqtSlot() |
|
367 def on_upgradeAllButton_clicked(self): |
|
368 """ |
|
369 Private slot to upgrade all packages of the selected environment. |
|
370 """ |
|
371 packages = [itm.text(0) for itm in self.__allUpdateableItems()] |
|
372 if packages: |
|
373 ok = self.__executeUpgradePackages(packages) |
|
374 if ok: |
|
375 self.on_refreshButton_clicked() |
|
376 |
|
377 @pyqtSlot() |
|
378 def on_uninstallButton_clicked(self): |
|
379 """ |
|
380 Private slot to remove selected packages of the selected environment. |
|
381 """ |
|
382 packages = [itm.text(0) for itm in self.packagesList.selectedItems()] |
|
383 if packages: |
|
384 ok = self.__pip.uninstallPackages( |
|
385 packages, |
|
386 venvName=self.environmentsComboBox.currentText()) |
|
387 if ok: |
|
388 self.on_refreshButton_clicked() |
|
389 |
|
390 def __executeUpgradePackages(self, packages): |
|
391 """ |
|
392 Private method to execute the pip upgrade command. |
|
393 |
|
394 @param packages list of package names to be upgraded |
|
395 @type list of str |
|
396 @return flag indicating success |
|
397 @rtype bool |
|
398 """ |
|
399 ok = self.__pip.upgradePackages( |
|
400 packages, venvName=self.environmentsComboBox.currentText(), |
|
401 userSite=self.userCheckBox.isChecked()) |
|
402 return ok |
|
403 |
125 ####################################################################### |
404 ####################################################################### |
126 ## Search widget related methods below |
405 ## Search widget related methods below |
127 ####################################################################### |
406 ####################################################################### |
128 |
407 |
129 def __updateSearchActionButtons(self): |
408 def __updateSearchActionButtons(self): |