PipInterface/PipPackagesWidget.py

branch
pypi
changeset 6795
6e2ed2aac325
parent 6793
cca6a35f3ad2
child 6798
3985c1a67fa2
diff -r cca6a35f3ad2 -r 6e2ed2aac325 PipInterface/PipPackagesWidget.py
--- a/PipInterface/PipPackagesWidget.py	Wed Feb 20 19:44:13 2019 +0100
+++ b/PipInterface/PipPackagesWidget.py	Thu Feb 21 19:55:35 2019 +0100
@@ -10,11 +10,12 @@
 from __future__ import unicode_literals
 
 import textwrap
+import os
 
 from PyQt5.QtCore import pyqtSlot, Qt, QEventLoop, QRegExp
 from PyQt5.QtGui import QCursor
 from PyQt5.QtWidgets import QWidget, QToolButton, QApplication, QHeaderView, \
-    QTreeWidgetItem
+    QTreeWidgetItem, QInputDialog, QMenu, QDialog
 
 from E5Gui.E5Application import e5App
 from E5Gui import E5MessageBox
@@ -109,6 +110,11 @@
         
         self.statusLabel.hide()
         self.searchWidget.hide()
+        
+        self.__detailsData = {}
+        self.__query = []
+        
+        self.__packageDetailsDialog = None
     
     def __populateEnvironments(self):
         """
@@ -497,6 +503,13 @@
         self.__updateSearchButton()
     
     @pyqtSlot()
+    def on_searchEdit_returnPressed(self):
+        """
+        Private slot initiating a search via a press of the Return key.
+        """
+        self.__search()
+    
+    @pyqtSlot()
     def on_searchButton_clicked(self):
         """
         Private slot handling a press of the search button.
@@ -685,6 +698,165 @@
         
         return score
     
+    @pyqtSlot()
+    def on_installButton_clicked(self):
+        """
+        Private slot to handle pressing the Install button..
+        """
+        self.__install()
+    
+    @pyqtSlot()
+    def on_installUserSiteButton_clicked(self):
+        """
+        Private slot to handle pressing the Install to User-Site button..
+        """
+        self.__install(userSite=True)
+    
+    def __install(self, userSite=False):
+        """
+        Private slot to install the selected packages.
+        
+        @param userSite flag indicating to install to the user directory
+        @type bool
+        """
+        venvName = self.environmentsComboBox.currentText()
+        if venvName:
+            packages = []
+            for itm in self.searchResultList.selectedItems():
+                packages.append(itm.text(0).strip())
+            if packages:
+                self.__pip.installPackages(packages, venvName=venvName,
+                                           userSite=userSite)
+    
+    @pyqtSlot()
+    def on_showDetailsButton_clicked(self):
+        """
+        Private slot to handle pressing the Show Details button.
+        """
+        self.__showDetails()
+    
+    @pyqtSlot(QTreeWidgetItem, int)
+    def on_searchResultList_itemActivated(self, item, column):
+        """
+        Private slot reacting on an search result item activation.
+        
+        @param item reference to the activated item
+        @type QTreeWidgetItem
+        @param column activated column
+        @type int
+        """
+        self.__showDetails(item)
+    
+    def __showDetails(self, item=None):
+        """
+        Private slot to show details about the selected package.
+        
+        @param item reference to the search result item to show details for
+        @type QTreeWidgetItem
+        """
+        self.showDetailsButton.setEnabled(False)
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
+        
+        self.__detailsData = {}
+        
+        if not item:
+            item = self.searchResultList.selectedItems()[0]
+        
+        packageVersions = item.data(0, self.SearchVersionRole)
+        if len(packageVersions) == 1:
+            packageVersion = packageVersions[0]
+        elif len(packageVersions) == 0:
+            packageVersion = ""
+        else:
+            packageVersion, ok = QInputDialog.getItem(
+                self,
+                self.tr("Show Package Details"),
+                self.tr("Select the package version:"),
+                packageVersions,
+                0, False)
+            if not ok:
+                return
+        
+        packageName = item.text(0)
+        self.__client.call(
+            "release_data",
+            (packageName, packageVersion),
+            lambda d: self.__getPackageDownloadsData(packageName,
+                                                     packageVersion,
+                                                     d),
+            lambda c, s: self.__detailsError(packageName, c, s)
+        )
+    
+    def __getPackageDownloadsData(self, packageName, packageVersion, data):
+        """
+        Private method to store the details data and get downloads
+        information.
+        
+        @param packageName name of the package
+        @type str
+        @param packageVersion version info
+        @type str
+        @param data result data with package details in the first
+            element
+        @type tuple
+        """
+        if data and data[0]:
+            self.__detailsData = data[0]
+            self.__client.call(
+                "release_urls",
+                (packageName, packageVersion),
+                self.__displayPackageDetails,
+                lambda c, s: self.__detailsError(packageName, c, s)
+            )
+        else:
+            QApplication.restoreOverrideCursor()
+            E5MessageBox.warning(
+                self,
+                self.tr("Search PyPI"),
+                self.tr("""<p>No package details info for <b>{0}</b>"""
+                        """ available.</p>""").format(packageName))
+    
+    def __displayPackageDetails(self, data):
+        """
+        Private method to display the returned package details.
+        
+        @param data result data with downloads information in the first element
+        @type tuple
+        """
+        from .PipPackageDetailsDialog import PipPackageDetailsDialog
+        
+        QApplication.restoreOverrideCursor()
+        self.showDetailsButton.setEnabled(True)
+        
+        if self.__packageDetailsDialog is not None:
+            self.__packageDetailsDialog.close()
+        
+        self.__packageDetailsDialog = \
+            PipPackageDetailsDialog(self.__detailsData, data[0], self)
+        self.__packageDetailsDialog.show()
+    
+    def __detailsError(self, packageName, errorCode, errorString):
+        """
+        Private method handling a details error.
+        
+        @param packageName name of the package
+        @type str
+        @param errorCode code of the error
+        @type int
+        @param errorString error message
+        @type str
+        """
+        QApplication.restoreOverrideCursor()
+        self.showDetailsButton.setEnabled(True)
+        E5MessageBox.warning(
+            self,
+            self.tr("Search PyPI"),
+            self.tr("""<p>Package details info for <b>{0}</b> could not be"""
+                    """ retrieved.</p><p>Reason: {1}</p>""")
+            .format(packageName, errorString)
+        )
+    
     #######################################################################
     ## Menu related methods below
     #######################################################################
@@ -694,8 +866,229 @@
         Private method to create the super menu and attach it to the super
         menu button.
         """
-        self.__pip.initActions()
-        
-        self.__pipMenu = self.__pip.initMenu()
+        self.__pipMenu = QMenu()
+        self.__installPipAct = self.__pipMenu.addAction(
+            self.tr("Install Pip"),
+            self.__installPip)
+        self.__installPipUserAct = self.__pipMenu.addAction(
+            self.tr("Install Pip to User-Site"),
+            self.__installPipUser)
+        self.__repairPipAct = self.__pipMenu.addAction(
+            self.tr("Repair Pip"),
+            self.__repairPip)
+        self.__pipMenu.addSeparator()
+        self.__installPackagesAct = self.__pipMenu.addAction(
+            self.tr("Install Packages"),
+            self.__installPackages)
+        self.__installLocalPackageAct = self.__pipMenu.addAction(
+            self.tr("Install Local Package"),
+            self.__installLocalPackage)
+        self.__pipMenu.addSeparator()
+        self.__installRequirementsAct = self.__pipMenu.addAction(
+            self.tr("Install Requirements"),
+            self.__installRequirements)
+        self.__uninstallRequirementsAct = self.__pipMenu.addAction(
+            self.tr("Uninstall Requirements"),
+            self.__uninstallRequirements)
+        self.__generateRequirementsAct = self.__pipMenu.addAction(
+            self.tr("Generate Requirements..."),
+            self.__generateRequirements)
+        self.__pipMenu.addSeparator()
+        # editUserConfigAct
+        self.__pipMenu.addAction(
+            self.tr("Edit User Configuration..."),
+            self.__editUserConfiguration)
+        self.__editVirtualenvConfigAct = self.__pipMenu.addAction(
+            self.tr("Edit Current Virtualenv Configuration..."),
+            self.__editVirtualenvConfiguration)
+        self.__pipMenu.addSeparator()
+        # pipConfigAct
+        self.__pipMenu.addAction(
+            self.tr("Configure..."),
+            self.__pipConfigure)
+
+        self.__pipMenu.aboutToShow.connect(self.__aboutToShowPipMenu)
         
         self.pipMenuButton.setMenu(self.__pipMenu)
+    
+    def __aboutToShowPipMenu(self):
+        """
+        Private slot to set the action enabled status.
+        """
+        enable = bool(self.environmentsComboBox.currentText())
+        enablePip = self.__isPipAvailable()
+        
+        self.__installPipAct.setEnabled(not enablePip)
+        self.__installPipUserAct.setEnabled(not enablePip)
+        self.__repairPipAct.setEnabled(enablePip)
+        
+        self.__installPackagesAct.setEnabled(enablePip)
+        self.__installLocalPackageAct.setEnabled(enablePip)
+        
+        self.__installRequirementsAct.setEnabled(enablePip)
+        self.__uninstallRequirementsAct.setEnabled(enablePip)
+        self.__generateRequirementsAct.setEnabled(enablePip)
+        
+        self.__editVirtualenvConfigAct.setEnabled(enable)
+    
+    @pyqtSlot()
+    def __installPip(self):
+        """
+        Private slot to install pip into the selected environment.
+        """
+        venvName = self.environmentsComboBox.currentText()
+        if venvName:
+            self.__pip.installPip(venvName)
+    
+    @pyqtSlot()
+    def __installPipUser(self):
+        """
+        Private slot to install pip into the user site for the selected
+        environment.
+        """
+        venvName = self.environmentsComboBox.currentText()
+        if venvName:
+            self.__pip.installPip(venvName, userSite=True)
+    
+    @pyqtSlot()
+    def __repairPip(self):
+        """
+        Private slot to repair the pip installation of the selected
+        environment.
+        """
+        venvName = self.environmentsComboBox.currentText()
+        if venvName:
+            self.__pip.repairPip(venvName)
+    
+    @pyqtSlot()
+    def __installPackages(self):
+        """
+        Private slot to install packages to be given by the user.
+        """
+        venvName = self.environmentsComboBox.currentText()
+        if venvName:
+            from .PipPackagesInputDialog import PipPackagesInputDialog
+            dlg = PipPackagesInputDialog(self, self.tr("Install Packages"))
+            if dlg.exec_() == QDialog.Accepted:
+                packages, user = dlg.getData()
+                if packages:
+                    self.__pip.installPackages(packages, venvName=venvName,
+                                               userSite=user)
+    
+    @pyqtSlot()
+    def __installLocalPackage(self):
+        """
+        Private slot to install a package available on local storage.
+        """
+        venvName = self.environmentsComboBox.currentText()
+        if venvName:
+            from .PipFileSelectionDialog import PipFileSelectionDialog
+            dlg = PipFileSelectionDialog(self, "package")
+            if dlg.exec_() == QDialog.Accepted:
+                package, user = dlg.getData()
+                if package and os.path.exists(package):
+                    self.__pip.installPackages([package], venvName=venvName,
+                                               userSite=user)
+    
+    @pyqtSlot()
+    def __installRequirements(self):
+        """
+        
+        """
+        # TODO: call pip.installRequirements()
+    
+    @pyqtSlot()
+    def __uninstallRequirements(self):
+        """
+        
+        """
+        # TODO: call pip.uninstallRequirements()
+    
+    @pyqtSlot()
+    def __generateRequirements(self):
+        """
+        Private slot to generate the contents for a requirements file.
+        """
+        # TODO: modify to get selected environment
+        from .PipFreezeDialog import PipFreezeDialog
+        self.__freezeDialog = PipFreezeDialog(self)
+        self.__freezeDialog.show()
+        self.__freezeDialog.start()
+    
+    @pyqtSlot()
+    def __editUserConfiguration(self):
+        """
+        Private slot to edit the user configuration.
+        """
+        self.__editConfiguration()
+    
+    @pyqtSlot()
+    def __editVirtualenvConfiguration(self):
+        """
+        Private slot to edit the current virtualenv configuration.
+        """
+        # TODO: modify to get selected environment
+        self.__editConfiguration(virtualenv=True)
+    
+    def __editConfiguration(self, virtualenv=False):
+        """
+        Private method to edit a configuration.
+        
+        @param virtualenv flag indicating to edit the current virtualenv
+            configuration file
+        @type bool
+        """
+        # TODO: modify to use venvName
+        from QScintilla.MiniEditor import MiniEditor
+        if virtualenv:
+            cfgFile = self.__getVirtualenvConfig()
+            if not cfgFile:
+                return
+        else:
+            cfgFile = self.__getUserConfig()
+        cfgDir = os.path.dirname(cfgFile)
+        if not cfgDir:
+            E5MessageBox.critical(
+                None,
+                self.tr("Edit Configuration"),
+                self.tr("""No valid configuration path determined."""
+                        """ Is a virtual environment selected? Aborting"""))
+            return
+        
+        try:
+            if not os.path.isdir(cfgDir):
+                os.makedirs(cfgDir)
+        except OSError:
+            E5MessageBox.critical(
+                None,
+                self.tr("Edit Configuration"),
+                self.tr("""No valid configuration path determined."""
+                        """ Is a virtual environment selected? Aborting"""))
+            return
+        
+        if not os.path.exists(cfgFile):
+            try:
+                f = open(cfgFile, "w")
+                f.write("[global]\n")
+                f.close()
+            except (IOError, OSError):
+                # ignore these
+                pass
+        
+        # check, if the destination is writeable
+        if not os.access(cfgFile, os.W_OK):
+            E5MessageBox.critical(
+                None,
+                self.tr("Edit Configuration"),
+                self.tr("""No valid configuartion path determined."""
+                        """ Is a virtual environment selected? Aborting"""))
+            return
+        
+        self.__editor = MiniEditor(cfgFile, "Properties")
+        self.__editor.show()
+ 
+    def __pipConfigure(self):
+        """
+        Private slot to open the configuration page.
+        """
+        e5App().getObject("UserInterface").showPreferences("pipPage")

eric ide

mercurial