def on_configButton_clicked(self):
     """
     Private slot to edit the Subversion config file.
     """
     cfgFile = self.__plugin.getConfigPath()
     editor = MiniEditor(cfgFile, "Properties", self)
     editor.show()
Beispiel #2
0
 def on_configButton_clicked(self):
     """
     Private slot to edit the (per user) Git configuration file.
     """
     from QScintilla.MiniEditor import MiniEditor
     cfgFile = self.__plugin.getConfigPath()
     if not os.path.exists(cfgFile):
         from ..GitUserConfigDataDialog import GitUserConfigDataDialog
         dlg = GitUserConfigDataDialog()
         if dlg.exec_() == QDialog.Accepted:
             firstName, lastName, email = dlg.getData()
         else:
             firstName, lastName, email = ("Firstname", "Lastname",
                                           "email_address")
         try:
             f = open(cfgFile, "w")
             f.write("[user]\n")
             f.write("    name = {0} {1}\n".format(firstName, lastName))
             f.write("    email = {0}\n".format(email))
             f.close()
         except (IOError, OSError):
             # ignore these
             pass
     editor = MiniEditor(cfgFile, "Properties", self)
     editor.show()
 def on_showScriptSourceButton_clicked(self):
     """
     Private slot to show an editor window with the script source code.
     """
     from QScintilla.MiniEditor import MiniEditor
     editor = MiniEditor(self.__scriptFileName, "JavaScript", self)
     editor.show()
 def on_serversButton_clicked(self):
     """
     Private slot to edit the Subversion servers file.
     """
     serversFile = self.__plugin.getServersPath()
     editor = MiniEditor(serversFile, "Properties", self)
     editor.show()
 def on_showScriptSourceButton_clicked(self):
     """
     Private slot to show an editor window with the script source code.
     """
     from QScintilla.MiniEditor import MiniEditor
     editor = MiniEditor(self.__scriptFileName, "JavaScript", self)
     editor.show()
Beispiel #6
0
 def on_configButton_clicked(self):
     """
     Private slot to edit the Subversion config file.
     """
     from QScintilla.MiniEditor import MiniEditor
     cfgFile = self.__plugin.getConfigPath()
     editor = MiniEditor(cfgFile, "Properties", self)
     editor.show()
Beispiel #7
0
 def on_serversButton_clicked(self):
     """
     Private slot to edit the Subversion servers file.
     """
     from QScintilla.MiniEditor import MiniEditor
     serversFile = self.__plugin.getServersPath()
     editor = MiniEditor(serversFile, "Properties", self)
     editor.show()
    def on_showScriptSourceButton_clicked(self):
        """
        Private slot to show an editor window with the source code.
        """
        from Helpviewer import HelpUtilities

        tmpFileName = HelpUtilities.ensureUniqueFilename(
            os.path.join(QDir.tempPath(), "tmp-userscript.js"))
        if QFile.copy(self.__script.fileName(), tmpFileName):
            from QScintilla.MiniEditor import MiniEditor
            editor = MiniEditor(tmpFileName, "JavaScript", self)
            editor.show()
Beispiel #9
0
def createMainWidget(argv):
    """
    Function to create the main widget.
    
    @param argv list of commandline parameters (list of strings)
    @return reference to the main widget (QWidget)
    """
    from QScintilla.MiniEditor import MiniEditor
    if len(argv) > 1:
        return MiniEditor(argv[1])
    else:
        return MiniEditor()
 def on_showScriptSourceButton_clicked(self):
     """
     Private slot to show an editor window with the source code.
     """
     from Helpviewer import HelpUtilities
     
     tmpFileName = HelpUtilities.ensureUniqueFilename(
         os.path.join(QDir.tempPath(), "tmp-userscript.js"))
     if QFile.copy(self.__script.fileName(), tmpFileName):
         from QScintilla.MiniEditor import MiniEditor
         editor = MiniEditor(tmpFileName, "JavaScript", self)
         editor.show()
Beispiel #11
0
    def __editConfiguration(self, venvName=""):
        """
        Private method to edit a configuration.
        
        @param venvName name of the environment to act upon
        @type str
        """
        from QScintilla.MiniEditor import MiniEditor
        if venvName:
            cfgFile = self.__pip.getVirtualenvConfig(venvName)
            if not cfgFile:
                return
        else:
            cfgFile = self.__pip.getUserConfig()
        cfgDir = os.path.dirname(cfgFile)
        if not cfgDir:
            E5MessageBox.critical(
                None, self.tr("Edit Configuration"),
                self.tr("""No valid configuration path determined."""
                        """ 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."""
                        """ 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 configuration path determined."""
                        """ Aborting"""))
            return

        self.__editor = MiniEditor(cfgFile, "Properties")
        self.__editor.show()
Beispiel #12
0
    def __editConfiguration(self, virtualenv=False):
        """
        Private method to edit a configuration.
        
        @param virtualenv flag indicating to edit the current virtualenv
            configuration file (boolean)
        """
        from QScintilla.MiniEditor import MiniEditor
        if virtualenv:
            cfgFile = self.__getVirtualenvConfig()
        else:
            cfgFile = self.__getUserConfig()
        cfgDir = os.path.dirname(cfgFile)
        if not cfgDir:
            E5MessageBox.critical(
                None, self.tr("Edit Configuration"),
                self.tr("""No valid configuartion 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 configuartion 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()
Beispiel #13
0
    def on_editorButton_clicked(self):
        """
        Private slot to open the user configuration file in a text editor.
        """
        from QScintilla.MiniEditor import MiniEditor
        cfgFile = getConfigPath()

        yes = E5MessageBox.yesNo(
            self,
            self.tr("Edit User Configuration"),
            self.tr("""You will loose all changes made in this dialog."""
                    """ Shall the data be saved first?"""),
            icon=E5MessageBox.Warning,
            yesDefault=True)
        if yes:
            self.writeUserConfig()

        self.__editor = MiniEditor(cfgFile, "Properties", self)
        self.__editor.setWindowModality(Qt.WindowModal)
        self.__editor.installEventFilter(self)
        self.__editor.show()
Beispiel #14
0
 def on_configButton_clicked(self):
     """
     Private slot to edit the (per user) Mercurial configuration file.
     """
     from QScintilla.MiniEditor import MiniEditor
     cfgFile = self.__plugin.getConfigPath()
     if not os.path.exists(cfgFile):
         from ..HgUserConfigDataDialog import HgUserConfigDataDialog
         dlg = HgUserConfigDataDialog()
         if dlg.exec_() == QDialog.Accepted:
             firstName, lastName, email, extensions, extensionsData = \
                 dlg.getData()
         else:
             firstName, lastName, email, extensions, extensionsData = (
                 "Firstname", "Lastname", "email_address", [], {})
         try:
             f = open(cfgFile, "w")
             f.write("[ui]\n")
             f.write("username = {0} {1} <{2}>\n".format(
                 firstName, lastName, email))
             if extensions:
                 f.write("\n[extensions]\n")
                 f.write(" =\n".join(extensions))
                 f.write(" =\n")  # complete the last line
             if "largefiles" in extensionsData:
                 dataDict = extensionsData["largefiles"]
                 f.write("\n[largefiles]\n")
                 if "minsize" in dataDict:
                     f.write("minsize = {0}\n".format(dataDict["minsize"]))
                 if "patterns" in dataDict:
                     f.write("patterns =\n")
                     f.write("  {0}\n".format("\n  ".join(
                         dataDict["patterns"])))
             f.close()
         except (IOError, OSError):
             # ignore these
             pass
     editor = MiniEditor(cfgFile, "Properties", self)
     editor.show()
Beispiel #15
0
 def on_configButton_clicked(self):
     """
     Private slot to edit the (per user) Mercurial configuration file.
     """
     from QScintilla.MiniEditor import MiniEditor
     cfgFile = self.__plugin.getConfigPath()
     if not os.path.exists(cfgFile):
         from ..HgUserConfigDataDialog import HgUserConfigDataDialog
         dlg = HgUserConfigDataDialog()
         if dlg.exec_() == QDialog.Accepted:
             firstName, lastName, email, extensions, extensionsData = \
                 dlg.getData()
         else:
             firstName, lastName, email, extensions, extensionsData = (
                 "Firstname", "Lastname", "email_address", [], {})
         try:
             f = open(cfgFile, "w")
             f.write("[ui]\n")
             f.write("username = {0} {1} <{2}>\n".format(
                 firstName, lastName, email))
             if extensions:
                 f.write("\n[extensions]\n")
                 f.write(" =\n".join(extensions))
                 f.write(" =\n")     # complete the last line
             if "largefiles" in extensionsData:
                 dataDict = extensionsData["largefiles"]
                 f.write("\n[largefiles]\n")
                 if "minsize" in dataDict:
                     f.write("minsize = {0}\n".format(dataDict["minsize"]))
                 if "patterns" in dataDict:
                     f.write("patterns =\n")
                     f.write("  {0}\n".format(
                         "\n  ".join(dataDict["patterns"])))
             f.close()
         except (IOError, OSError):
             # ignore these
             pass
     editor = MiniEditor(cfgFile, "Properties", self)
     editor.show()
 def __editUserConfiguration(self):
     """
     Private slot to edit the user configuration.
     """
     from QScintilla.MiniEditor import MiniEditor
     
     cfgFile = CondaInterface.userConfiguration()
     if not cfgFile:
         return
     
     if not os.path.exists(cfgFile):
         self.__conda.writeDefaultConfiguration()
     
     # check, if the destination is writeable
     if not os.access(cfgFile, os.W_OK):
         E5MessageBox.critical(
             None,
             self.tr("Edit Configuration"),
             self.tr("""The configuration file "{0}" does not exist"""
                     """ or is not writable."""))
         return
     
     self.__editor = MiniEditor(cfgFile, "YAML")
     self.__editor.show()
Beispiel #17
0
class HgUserConfigDialog(QDialog, Ui_HgUserConfigDialog):
    """
    Class implementing a dialog to enter some user data.
    """
    def __init__(self, version=(0, 0, 0), parent=None):
        """
        Constructor
        
        @param version Mercurial version info
        @type tuple of three integers
        @param parent reference to the parent widget
        @type QWidget
        """
        super(HgUserConfigDialog, self).__init__(parent)
        self.setupUi(self)

        self.__version = version

        self.__minimumProtocols = {
            "tls1.0": self.tr("TLS 1.0"),
            "tls1.1": self.tr("TLS 1.1"),
            "tls1.2": self.tr("TLS 1.2"),
        }

        self.lfUserCachePicker.setMode(E5PathPickerModes.DirectoryMode)
        if Globals.isLinuxPlatform():
            self.lfUserCachePicker.setDefaultDirectory(
                os.path.expanduser("~/.cache/largefiles"))
        elif Globals.isMacPlatform():
            self.lfUserCachePicker.setDefaultDirectory(
                os.path.expanduser("~/Library/Caches/largefiles"))
        else:
            self.lfUserCachePicker.setDefaultDirectory(
                os.path.expanduser("~\\AppData\\Local\\largefiles"))

        self.fpAddButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
        self.fpDeleteButton.setIcon(UI.PixmapCache.getIcon("minus.png"))
        self.fpEditButton.setIcon(UI.PixmapCache.getIcon("edit.png"))

        self.protocolAddButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
        self.protocolDeleteButton.setIcon(UI.PixmapCache.getIcon("minus.png"))
        self.protocolEditButton.setIcon(UI.PixmapCache.getIcon("edit.png"))

        self.minimumProtocolComboBox.addItem(self.tr("Default"), "")
        for protocol in sorted(self.__minimumProtocols.keys()):
            self.minimumProtocolComboBox.addItem(
                self.__minimumProtocols[protocol], protocol)

        self.fingerprintsList.headerItem().setText(
            self.fingerprintsList.columnCount(), "")
        self.protocolsList.headerItem().setText(
            self.protocolsList.columnCount(), "")

        if self.__version < (3, 9, 0):
            self.disableTls10WarningCheckBox.setEnabled(False)
            self.minimumProtocolComboBox.setEnabled(False)
            self.minimumProtcolGroupBox.setEnabled(False)

        self.tabWidget.setCurrentIndex(0)

        self.__editor = None

        self.__config = None
        self.readUserConfig()

        self.__updateFingerprintsButtons()
        self.__updateProtocolsButtons()

    def writeUserConfig(self):
        """
        Public method to write the user configuration file.
        """
        if self.__config is None:
            self.__config = ConfigParser()

        ###################################################################
        ## ui section
        ###################################################################
        if "ui" not in self.__config:
            self.__config["ui"] = {}
        self.__config["ui"]["username"] = "******".format(
            self.userNameEdit.text(),
            self.emailEdit.text(),
        )
        ###################################################################
        ## extensions section
        ###################################################################
        if "extensions" not in self.__config:
            self.__config["extensions"] = {}
        if self.fetchCheckBox.isChecked():
            self.__config["extensions"]["fetch"] = ""
        else:
            if "fetch" in self.__config["extensions"]:
                del self.__config["extensions"]["fetch"]
            self.__config["extensions"]["#fetch"] = ""

        if self.gpgCheckBox.isChecked():
            self.__config["extensions"]["gpg"] = ""
        else:
            if "gpg" in self.__config["extensions"]:
                del self.__config["extensions"]["gpg"]
            self.__config["extensions"]["#gpg"] = ""

        if self.purgeCheckBox.isChecked():
            self.__config["extensions"]["purge"] = ""
        else:
            if "purge" in self.__config["extensions"]:
                del self.__config["extensions"]["purge"]
            self.__config["extensions"]["#purge"] = ""

        if self.queuesCheckBox.isChecked():
            self.__config["extensions"]["mq"] = ""
        else:
            if "mq" in self.__config["extensions"]:
                del self.__config["extensions"]["mq"]
            self.__config["extensions"]["#mq"] = ""

        if self.rebaseCheckBox.isChecked():
            self.__config["extensions"]["rebase"] = ""
        else:
            if "rebase" in self.__config["extensions"]:
                del self.__config["extensions"]["rebase"]
            self.__config["extensions"]["#rebase"] = ""

        if self.shelveCheckBox.isChecked():
            self.__config["extensions"]["shelve"] = ""
        else:
            if "shelve" in self.__config["extensions"]:
                del self.__config["extensions"]["shelve"]
            self.__config["extensions"]["#shelve"] = ""

        if self.stripCheckBox.isChecked():
            self.__config["extensions"]["strip"] = ""
        else:
            if "strip" in self.__config["extensions"]:
                del self.__config["extensions"]["strip"]
            self.__config["extensions"]["#strip"] = ""

        if self.histeditCheckBox.isChecked():
            self.__config["extensions"]["histedit"] = ""
        else:
            if "histedit" in self.__config["extensions"]:
                del self.__config["extensions"]["histedit"]
            self.__config["extensions"]["#histedit"] = ""

        if self.largefilesCheckBox.isChecked():
            self.__config["extensions"]["largefiles"] = ""
            ###############################################################
            ## largefiles section
            ###############################################################
            if "largefiles" not in self.__config:
                self.__config["largefiles"] = {}
            self.__config["largefiles"]["minsize"] = (str(
                self.lfFileSizeSpinBox.value()))
            lfFilePatterns = self.lfFilePatternsEdit.text()
            if lfFilePatterns:
                self.__config["largefiles"]["patterns"] = lfFilePatterns
            elif "patterns" in self.__config["largefiles"]:
                del self.__config["largefiles"]["patterns"]
            lfUserCache = self.lfUserCachePicker.text()
            if lfUserCache:
                self.__config["largefiles"]["usercache"] = lfUserCache
            elif "usercache" in self.__config["largefiles"]:
                del self.__config["largefiles"]["usercache"]
        else:
            if "largefiles" in self.__config["extensions"]:
                del self.__config["extensions"]["largefiles"]
            self.__config["extensions"]["#largefiles"] = ""

        if self.closeheadCheckBox.isChecked() and self.__version >= (4, 8, 0):
            self.__config["extensions"]["closehead"] = ""
        else:
            if "closehead" in self.__config["extensions"]:
                del self.__config["extensions"]["closehead"]
            self.__config["extensions"]["#closehead"] = ""
        ###################################################################
        ## http_proxy section
        ###################################################################
        if self.proxyHostEdit.text():
            self.__config["http_proxy"] = {
                "host": self.proxyHostEdit.text(),
                "user": self.proxyUserEdit.text(),
                "passwd": self.proxyPasswordEdit.text()
            }
            if self.proxyBypassEdit.text():
                self.__config["http_proxy"]["no"] = (
                    self.proxyBypassEdit.text())
        else:
            if "http_proxy" in self.__config:
                del self.__config["http_proxy"]
        ###################################################################
        ## hostfingerprints/hostsecurity section
        ###################################################################
        if self.__version < (3, 9, 0):
            #
            # delete hostsecurity section
            #
            if "hostsecurity" in self.__config:
                del self.__config["hostsecurity"]

            #
            # hostfingerprints section
            #
            if self.fingerprintsList.topLevelItemCount() > 0:
                self.__config["hostfingerprints"] = {}
                for row in range(self.fingerprintsList.topLevelItemCount()):
                    itm = self.fingerprintsList.topLevelItem(row)
                    fingerprint = itm.text(1)
                    if fingerprint.startswith("sha1:"):
                        fingerprint = fingerprint[5:]
                    self.__config["hostfingerprints"][itm.text(0)] = (
                        fingerprint)
            else:
                if "hostfingerprints" in self.__config:
                    del self.__config["hostfingerprints"]
        else:
            #
            # delete hostfingerprints section
            #
            if "hostfingerprints" in self.__config:
                del self.__config["hostfingerprints"]

            #
            # hostsecurity section
            #
            if "hostsecurity" not in self.__config:
                self.__config["hostsecurity"] = {}

            if self.fingerprintsList.topLevelItemCount() > 0:
                self.__clearFingerprints()
                fingerprints = self.__assembleFingerprints()
                for host in fingerprints:
                    key = "{0}:fingerprints".format(host)
                    self.__config["hostsecurity"][key] = (", ".join(
                        fingerprints[host]))
            else:
                self.__clearFingerprints()

            if self.disableTls10WarningCheckBox.isChecked():
                disabletls10warning = "true"
            else:
                disabletls10warning = "false"
            self.__config["hostsecurity"]["disabletls10warning"] = (
                disabletls10warning)

            if self.minimumProtocolComboBox.currentIndex() == 0:
                self.__config.remove_option("hostsecurity", "minimumprotocol")
            else:
                minimumProtocol = self.minimumProtocolComboBox.itemData(
                    self.minimumProtocolComboBox.currentIndex())
                self.__config["hostsecurity"]["minimumprotocol"] = (
                    minimumProtocol)

            if self.protocolsList.topLevelItemCount() > 0:
                self.__clearMinimumProtocols()
                minimumProtocols = self.__assembleMinimumProtocols()
                for host in minimumProtocols:
                    key = "{0}:minimumprotocol".format(host)
                    self.__config["hostsecurity"][key] = minimumProtocols[host]
            else:
                self.__clearMinimumProtocols()

            if len(self.__config.options("hostsecurity")) == 0:
                del self.__config["hostsecurity"]
        ###################################################################

        cfgFile = getConfigPath()
        with open(cfgFile, "w") as configFile:
            self.__config.write(configFile)

    def readUserConfig(self):
        """
        Public method to read the user configuration file.
        """
        cfgFile = getConfigPath()

        self.__config = ConfigParser(delimiters=("=", ))
        if self.__config.read(cfgFile):
            # step 1: extract user name and email
            try:
                username = self.__config["ui"]["username"].strip()
                if "<" in username and username.endswith(">"):
                    name, email = username[:-1].rsplit("<", 1)
                else:
                    name = username
                    email = ""
                self.userNameEdit.setText(name.strip()),
                self.emailEdit.setText(email.strip()),
            except KeyError:
                pass

            # step 2: extract extensions information
            if "extensions" in self.__config:
                self.fetchCheckBox.setChecked(
                    "fetch" in self.__config["extensions"])
                self.gpgCheckBox.setChecked(
                    "gpg" in self.__config["extensions"])
                self.purgeCheckBox.setChecked(
                    "purge" in self.__config["extensions"])
                self.queuesCheckBox.setChecked(
                    "mq" in self.__config["extensions"])
                self.rebaseCheckBox.setChecked(
                    "rebase" in self.__config["extensions"])
                self.shelveCheckBox.setChecked(
                    "shelve" in self.__config["extensions"])
                self.largefilesCheckBox.setChecked(
                    "largefiles" in self.__config["extensions"])
                self.stripCheckBox.setChecked(
                    "strip" in self.__config["extensions"])
                self.histeditCheckBox.setChecked(
                    "histedit" in self.__config["extensions"])
                self.closeheadCheckBox.setChecked(
                    "closehead" in self.__config["extensions"])
            self.closeheadCheckBox.setEnabled(self.__version >= (4, 8, 0))

            # step 3: extract large files information
            if "largefiles" in self.__config:
                if "minsize" in self.__config["largefiles"]:
                    self.lfFileSizeSpinBox.setValue(
                        self.__config.getint("largefiles", "minsize"))
                if "patterns" in self.__config["largefiles"]:
                    self.lfFilePatternsEdit.setText(
                        self.__config["largefiles"]["patterns"])
                if "usercache" in self.__config["largefiles"]:
                    self.lfUserCachePicker.setText(
                        self.__config["largefiles"]["usercache"])

            # step 4: extract http proxy information
            if "http_proxy" in self.__config:
                if "host" in self.__config["http_proxy"]:
                    self.proxyHostEdit.setText(
                        self.__config["http_proxy"]["host"])
                if "user" in self.__config["http_proxy"]:
                    self.proxyUserEdit.setText(
                        self.__config["http_proxy"]["user"])
                if "passwd" in self.__config["http_proxy"]:
                    self.proxyPasswordEdit.setText(
                        self.__config["http_proxy"]["passwd"])
                if "no" in self.__config["http_proxy"]:
                    self.proxyBypassEdit.setText(
                        self.__config["http_proxy"]["no"])

            # step 5a: extract host fingerprints
            if "hostfingerprints" in self.__config:
                for host in self.__config.options("hostfingerprints"):
                    if self.__version < (3, 9, 0):
                        QTreeWidgetItem(
                            self.fingerprintsList,
                            [host, self.__config["hostfingerprints"][host]])
                    else:
                        # convert to hostsecurity fingerprint
                        QTreeWidgetItem(self.fingerprintsList, [
                            host,
                            "sha1:" + self.__config["hostfingerprints"][host]
                        ])

            # step 5b: extract hostsecurity fingerprints
            if "hostsecurity" in self.__config:
                for key in self.__config.options("hostsecurity"):
                    if key.endswith(":fingerprints"):
                        host = key.replace(":fingerprints", "")
                        fingerprints = (
                            self.__config["hostsecurity"][key].split(","))
                        for fingerprint in fingerprints:
                            if self.__version < (3, 9, 0):
                                # downgrade from a newer version
                                if fingerprint.startswith("sha1:"):
                                    fingerprint = fingerprint[5:]
                                else:
                                    # Mercurial < 3.9.0 supports sha1
                                    # fingerprints only
                                    continue
                            QTreeWidgetItem(
                                self.fingerprintsList,
                                [host,
                                 fingerprint.replace("\\", "").strip()])

                    elif key == "disabletls10warning":
                        self.disableTls10WarningCheckBox.setChecked(
                            self.__config.getboolean("hostsecurity",
                                                     "disabletls10warning"))

                    elif key == "minimumprotocol":
                        minimumProtocol = self.__config["hostsecurity"][key]
                        index = self.minimumProtocolComboBox.findData(
                            minimumProtocol)
                        if index == -1:
                            index = 0
                        self.minimumProtocolComboBox.setCurrentIndex(index)

                    elif key.endswith(":minimumprotocol"):
                        host = key.replace(":minimumprotocol", "")
                        protocol = self.__config["hostsecurity"][key].strip()
                        if protocol in self.__minimumProtocols:
                            itm = QTreeWidgetItem(
                                self.protocolsList,
                                [host, self.__minimumProtocols[protocol]])
                            itm.setData(1, Qt.UserRole, protocol)

            self.__finalizeFingerprintsColumns()
            self.__finalizeProtocolsColumns()

    @pyqtSlot()
    def accept(self):
        """
        Public slot to accept the dialog.
        """
        self.writeUserConfig()

        super(HgUserConfigDialog, self).accept()

    def __clearDialog(self):
        """
        Private method to clear the data of the dialog.
        """
        self.userNameEdit.clear()
        self.emailEdit.clear()

        self.fetchCheckBox.setChecked(False)
        self.gpgCheckBox.setChecked(False)
        self.purgeCheckBox.setChecked(False)
        self.queuesCheckBox.setChecked(False)
        self.rebaseCheckBox.setChecked(False)
        self.shelveCheckBox.setChecked(False)
        self.stripCheckBox.setChecked(False)
        self.largefilesCheckBox.setChecked(False)
        self.lfFileSizeSpinBox.setValue(10)
        self.lfFilePatternsEdit.clear()
        self.lfUserCachePicker.clear()

        self.proxyHostEdit.clear()
        self.proxyUserEdit.clear()
        self.proxyPasswordEdit.clear()
        self.proxyBypassEdit.clear()

        self.fingerprintsList.clear()
        self.__finalizeFingerprintsColumns()
        self.__updateFingerprintsButtons()

        self.protocolsList.clear()
        self.__finalizeProtocolsColumns()
        self.__updateProtocolsButtons()

    #######################################################################
    ## Methods and slots for the host fingerprint handling below
    #######################################################################

    def __clearFingerprints(self):
        """
        Private method to clear the fingerprints from the hostsecurity section.
        """
        if "hostsecurity" in self.__config:
            for key in self.__config.options("hostsecurity"):
                if key.endswith(":fingerprints"):
                    self.__config.remove_option("hostsecurity", key)

    def __assembleFingerprints(self):
        """
        Private method to assemble a list of host fingerprints.
        
        @return dictionary with list of fingerprints per host
        @rtype dict with str as key and list of str as value
        """
        hostFingerprints = {}
        for row in range(self.fingerprintsList.topLevelItemCount()):
            itm = self.fingerprintsList.topLevelItem(row)
            host = itm.text(0)
            fingerprint = itm.text(1)
            if host in hostFingerprints:
                hostFingerprints[host].append(fingerprint)
            else:
                hostFingerprints[host] = [fingerprint]
        return hostFingerprints

    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
    def on_fingerprintsList_currentItemChanged(self, current, previous):
        """
        Private slot handling a change of the current fingerprints item.
        
        @param current reference to the current item
        @type QTreeWidgetItem
        @param previous reference to the previous current item
        @type QTreeWidgetItem
        """
        self.__updateFingerprintsButtons()

    @pyqtSlot()
    def on_fpAddButton_clicked(self):
        """
        Private slot to add a fingerprints entry.
        """
        dlg = HgUserConfigHostFingerprintDialog(self, version=self.__version)
        if dlg.exec_() == QDialog.Accepted:
            host, fingerprint = dlg.getData()
            itm = QTreeWidgetItem(self.fingerprintsList, [host, fingerprint])
            self.__finalizeFingerprintsColumns()
            self.fingerprintsList.setCurrentItem(itm)
            self.fingerprintsList.scrollToItem(itm)

    @pyqtSlot()
    def on_fpDeleteButton_clicked(self):
        """
        Private slot to delete the current fingerprints item.
        """
        itm = self.fingerprintsList.currentItem()
        if itm is not None:
            host = itm.text(0)
            yes = E5MessageBox.yesNo(
                self, self.tr("Delete Host Fingerprint"),
                self.tr("""<p>Shall the fingerprint for host <b>{0}</b>"""
                        """ really be deleted?</p>""").format(host))
            if yes:
                self.fingerprintsList.takeTopLevelItem(
                    self.fingerprintsList.indexOfTopLevelItem(itm))
                del itm
                self.__finalizeFingerprintsColumns()

    @pyqtSlot()
    def on_fpEditButton_clicked(self):
        """
        Private slot to edit the current fingerprints item.
        """
        itm = self.fingerprintsList.currentItem()
        if itm is not None:
            host = itm.text(0)
            fingerprint = itm.text(1)
            dlg = HgUserConfigHostFingerprintDialog(self,
                                                    host,
                                                    fingerprint,
                                                    version=self.__version)
            if dlg.exec_() == QDialog.Accepted:
                host, fingerprint = dlg.getData()
                itm.setText(0, host)
                itm.setText(1, fingerprint)
                self.__finalizeFingerprintsColumns()
                self.fingerprintsList.scrollToItem(itm)

    def __finalizeFingerprintsColumns(self):
        """
        Private method to resize and sort the host fingerprints columns.
        """
        for col in range(self.fingerprintsList.columnCount()):
            self.fingerprintsList.resizeColumnToContents(col)
        self.fingerprintsList.sortItems(0, Qt.AscendingOrder)

    def __updateFingerprintsButtons(self):
        """
        Private slot to update the host fingerprints edit buttons.
        """
        enable = self.fingerprintsList.currentItem() is not None
        self.fpDeleteButton.setEnabled(enable)
        self.fpEditButton.setEnabled(enable)

    #######################################################################
    ## Methods and slots for the host minimum protocol handling below
    #######################################################################

    def __clearMinimumProtocols(self):
        """
        Private method to clear the minimum protocols from the hostsecurity
        section.
        """
        if "hostsecurity" in self.__config:
            for key in self.__config.options("hostsecurity"):
                if key.endswith(":minimumprotocol"):
                    self.__config.remove_option("hostsecurity", key)

    def __assembleMinimumProtocols(self):
        """
        Private method to assemble a list of host minimum protocols.
        
        @return dictionary with list of minimum protocol per host
        @rtype dict with str as key and str as value
        """
        minimumProtocols = {}
        for row in range(self.protocolsList.topLevelItemCount()):
            itm = self.protocolsList.topLevelItem(row)
            host = itm.text(0)
            minimumProtocol = itm.data(1, Qt.UserRole)
            minimumProtocols[host] = minimumProtocol
        return minimumProtocols

    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
    def on_protocolsList_currentItemChanged(self, current, previous):
        """
        Private slot handling a change of the current minimum protocol item.
        
        @param current reference to the current item
        @type QTreeWidgetItem
        @param previous reference to the previous current item
        @type QTreeWidgetItem
        """
        self.__updateProtocolsButtons()

    @pyqtSlot()
    def on_protocolAddButton_clicked(self):
        """
        Private slot to add a minimum protocol entry.
        """
        dlg = HgUserConfigHostMinimumProtocolDialog(self.__minimumProtocols,
                                                    self)
        if dlg.exec_() == QDialog.Accepted:
            host, protocol = dlg.getData()
            itm = QTreeWidgetItem(self.protocolsList,
                                  [host, self.__minimumProtocols[protocol]])
            itm.setData(1, Qt.UserRole, protocol)
            self.__finalizeProtocolsColumns()
            self.protocolsList.setCurrentItem(itm)
            self.protocolsList.scrollToItem(itm)

    @pyqtSlot()
    def on_protocolDeleteButton_clicked(self):
        """
        Private slot to delete the current minimum protocol item.
        """
        itm = self.protocolsList.currentItem()
        if itm is not None:
            host = itm.text(0)
            yes = E5MessageBox.yesNo(
                self, self.tr("Delete Host Minimum Protocol"),
                self.tr("""<p>Shall the minimum protocol entry for host"""
                        """ <b>{0}</b> really be deleted?</p>""").format(host))
            if yes:
                self.protocolsList.takeTopLevelItem(
                    self.protocolsList.indexOfTopLevelItem(itm))
                del itm
                self.__finalizeProtocolsColumns()

    @pyqtSlot()
    def on_protocolEditButton_clicked(self):
        """
        Private slot to edit the current minimum protocol item.
        """
        itm = self.protocolsList.currentItem()
        if itm is not None:
            host = itm.text(0)
            protocol = itm.data(1, Qt.UserRole)
            dlg = HgUserConfigHostMinimumProtocolDialog(
                self.__minimumProtocols, self, host, protocol)
            if dlg.exec_() == QDialog.Accepted:
                host, protocol = dlg.getData()
                itm.setText(0, host)
                itm.setText(1, self.__minimumProtocols[protocol])
                itm.setData(1, Qt.UserRole, protocol)
                self.__finalizeProtocolsColumns()
                self.protocolsList.scrollToItem(itm)

    def __finalizeProtocolsColumns(self):
        """
        Private method to resize and sort the host fingerprints columns.
        """
        for col in range(self.protocolsList.columnCount()):
            self.protocolsList.resizeColumnToContents(col)
        self.protocolsList.sortItems(0, Qt.AscendingOrder)

    def __updateProtocolsButtons(self):
        """
        Private slot to update the host minimum protocol edit buttons.
        """
        enable = self.protocolsList.currentItem() is not None
        self.protocolDeleteButton.setEnabled(enable)
        self.protocolEditButton.setEnabled(enable)

    #######################################################################
    ## Slot to edit the user configuration in an editor below
    #######################################################################

    @pyqtSlot()
    def on_editorButton_clicked(self):
        """
        Private slot to open the user configuration file in a text editor.
        """
        from QScintilla.MiniEditor import MiniEditor
        cfgFile = getConfigPath()

        yes = E5MessageBox.yesNo(
            self,
            self.tr("Edit User Configuration"),
            self.tr("""You will loose all changes made in this dialog."""
                    """ Shall the data be saved first?"""),
            icon=E5MessageBox.Warning,
            yesDefault=True)
        if yes:
            self.writeUserConfig()

        self.__editor = MiniEditor(cfgFile, "Properties", self)
        self.__editor.setWindowModality(Qt.WindowModal)
        self.__editor.installEventFilter(self)
        self.__editor.show()

    def eventFilter(self, watched, event):
        """
        Public method called to filter the event queue.
        
        @param watched reference to the object being watched
        @type QObject
        @param event event to be handled
        @type QEvent
        @return flag indicating, if we handled the event
        @rtype bool
        """
        if watched is self.__editor and event.type() == QEvent.Close:
            self.__editor.closeEvent(event)
            if event.isAccepted():
                self.__clearDialog()
                self.readUserConfig()
                return True

        return False
Beispiel #18
0
class Pip(QObject):
    """
    Class implementing the pip GUI logic.
    """
    def __init__(self, plugin, parent=None):
        """
        Constructor
        
        @param plugin reference to the plugin object
        @param parent parent (QObject)
        """
        super(Pip, self).__init__(parent)

        self.__plugin = plugin
        self.__ui = parent

        self.__menus = {}  # dictionary with references to menus

        self.__plugin.currentPipChanged.connect(self.__handleTearOffMenu)

    def initActions(self):
        """
        Public method to define the Django actions.
        """
        self.actions = []

        self.selectExecutableAct = E5Action(self.tr('pip Executable'),
                                            self.tr('pip &Executable'), 0, 0,
                                            self, 'pip_select_executable')
        self.selectExecutableAct.setStatusTip(
            self.tr('Selects the pip executable to be used'))
        self.selectExecutableAct.setWhatsThis(
            self.tr(
                """<b>pip Executable</b>"""
                """<p>This selects the pip executable to be used. Multiple"""
                """ executables can be pre-configured via the configuration"""
                """ dialog.</p>"""))
        self.selectExecutableAct.triggered.connect(self.__selectPipExecutable)
        self.actions.append(self.selectExecutableAct)

        ##############################################
        ## Actions for listing packages
        ##############################################

        self.listPackagesAct = E5Action(self.tr('List Installed Packages'),
                                        self.tr('&List Installed Packages...'),
                                        0, 0, self, 'pip_list_packages')
        self.listPackagesAct.setStatusTip(
            self.tr('List all installed packages with versions'))
        self.listPackagesAct.setWhatsThis(
            self.tr("""<b>List Installed Packages</b>"""
                    """<p>This lists all the installed packages together"""
                    """ with their versions.</p>"""))
        self.listPackagesAct.triggered.connect(self.__listPackages)
        self.actions.append(self.listPackagesAct)

        self.listUptodatePackagesAct = E5Action(
            self.tr('List Up-to-date Packages'),
            self.tr('List Up-to-&date Packages...'), 0, 0, self,
            'pip_list_uptodate_packages')
        self.listUptodatePackagesAct.setStatusTip(
            self.tr('List all installed, up-to-date packages with versions'))
        self.listUptodatePackagesAct.setWhatsThis(
            self.
            tr("""<b>List Up-to-date Packages</b>"""
               """<p>This lists all the installed, up-to-date packages together"""
               """ with their versions.</p>"""))
        self.listUptodatePackagesAct.triggered.connect(
            self.__listUptodatePackages)
        self.actions.append(self.listUptodatePackagesAct)

        self.listOutdatedPackagesAct = E5Action(
            self.tr('List Outdated Packages'),
            self.tr('List &Outdated Packages...'), 0, 0, self,
            'pip_list_outdated_packages')
        self.listOutdatedPackagesAct.setStatusTip(
            self.tr('List all installed, outdated packages with versions'))
        self.listOutdatedPackagesAct.setWhatsThis(
            self.
            tr("""<b>List Up-to-date Packages</b>"""
               """<p>This lists all the installed, outdated packages together"""
               """ with their current and latest versions.</p>"""))
        self.listOutdatedPackagesAct.triggered.connect(
            self.__listOutdatedPackages)
        self.actions.append(self.listOutdatedPackagesAct)

        ##############################################
        ## Actions for installing packages
        ##############################################

        self.installPackagesAct = E5Action(self.tr('Install Packages'),
                                           self.tr('&Install Packages'), 0, 0,
                                           self, 'pip_install_packages')
        self.installPackagesAct.setStatusTip(
            self.tr('Install packages according to user input'))
        self.installPackagesAct.setWhatsThis(
            self.tr(
                """<b>Install Packages</b>"""
                """<p>This installs packages according to user input.</p>"""))
        self.installPackagesAct.triggered.connect(self.__installPackages)
        self.actions.append(self.installPackagesAct)

        self.installRequirementsAct = E5Action(self.tr('Install Requirements'),
                                               self.tr('Install Requirements'),
                                               0, 0, self,
                                               'pip_install_requirements')
        self.installRequirementsAct.setStatusTip(
            self.tr('Install packages according to a requirements file'))
        self.installRequirementsAct.setWhatsThis(
            self.tr("""<b>Install Requirements</b>"""
                    """<p>This installs packages according to a requirements"""
                    """ file.</p>"""))
        self.installRequirementsAct.triggered.connect(
            self.__installRequirements)
        self.actions.append(self.installRequirementsAct)

        self.installPipAct = E5Action(self.tr('Install Pip'),
                                      self.tr('Install Pip'), 0, 0, self,
                                      'pip_install_pip')
        self.installPipAct.setStatusTip(
            self.tr('Install the pip package itself'))
        self.installPipAct.setWhatsThis(
            self.tr("""<b>Install Pip</b>"""
                    """<p>This install the pip package itself.</p>"""))
        self.installPipAct.triggered.connect(self.__installPip)
        self.actions.append(self.installPipAct)

        self.upgradePipAct = E5Action(self.tr('Upgrade Pip'),
                                      self.tr('Upgrade &Pip'), 0, 0, self,
                                      'pip_upgrade_pip')
        self.upgradePipAct.setStatusTip(
            self.tr('Upgrade the pip package itself'))
        self.upgradePipAct.setWhatsThis(
            self.tr("""<b>Upgrade Pip</b>"""
                    """<p>This upgrades the pip package itself.</p>"""))
        self.upgradePipAct.triggered.connect(self.upgradePip)
        self.actions.append(self.upgradePipAct)

        self.upgradePackagesAct = E5Action(self.tr('Upgrade Packages'),
                                           self.tr('&Upgrade Packages'), 0, 0,
                                           self, 'pip_upgrade_packages')
        self.upgradePackagesAct.setStatusTip(
            self.tr('Upgrade packages according to user input'))
        self.upgradePackagesAct.setWhatsThis(
            self.tr(
                """<b>Upgrade Packages</b>"""
                """<p>This upgrades packages according to user input.</p>"""))
        self.upgradePackagesAct.triggered.connect(self.__upgradePackages)
        self.actions.append(self.upgradePackagesAct)

        ##############################################
        ## Actions for uninstalling packages
        ##############################################

        self.uninstallPackagesAct = E5Action(self.tr('Uninstall Packages'),
                                             self.tr('Uninstall Packages'), 0,
                                             0, self, 'pip_uninstall_packages')
        self.uninstallPackagesAct.setStatusTip(
            self.tr('Uninstall packages according to user input'))
        self.uninstallPackagesAct.setWhatsThis(
            self.
            tr("""<b>Uninstall Packages</b>"""
               """<p>This uninstalls packages according to user input.</p>"""))
        self.uninstallPackagesAct.triggered.connect(self.__uninstallPackages)
        self.actions.append(self.uninstallPackagesAct)

        self.uninstallRequirementsAct = E5Action(
            self.tr('Uninstall Requirements'),
            self.tr('Uninstall Requirements'), 0, 0, self,
            'pip_uninstall_requirements')
        self.uninstallRequirementsAct.setStatusTip(
            self.tr('Uninstall packages according to a requirements file'))
        self.uninstallRequirementsAct.setWhatsThis(
            self.tr(
                """<b>Uninstall Requirements</b>"""
                """<p>This uninstalls packages according to a requirements"""
                """ file.</p>"""))
        self.uninstallRequirementsAct.triggered.connect(
            self.__uninstallRequirements)
        self.actions.append(self.uninstallRequirementsAct)

        ##############################################
        ## Actions for generating requirements files
        ##############################################

        self.generateRequirementsAct = E5Action(
            self.tr('Generate Requirements'),
            self.tr('&Generate Requirements...'), 0, 0, self,
            'pip_generate_requirements')
        self.generateRequirementsAct.setStatusTip(
            self.tr('Generate the contents of a requirements file'))
        self.generateRequirementsAct.setWhatsThis(
            self.
            tr("""<b>Generate Requirements</b>"""
               """<p>This generates the contents of a requirements file.</p>"""
               ))
        self.generateRequirementsAct.triggered.connect(
            self.__generateRequirements)
        self.actions.append(self.generateRequirementsAct)

        ##############################################
        ## Actions for generating requirements files
        ##############################################

        self.searchPyPIAct = E5Action(self.tr('Search PyPI'),
                                      self.tr('&Search PyPI...'), 0, 0, self,
                                      'pip_search_pypi')
        self.searchPyPIAct.setStatusTip(
            self.tr('Open a dialog to search the Python Package Index'))
        self.searchPyPIAct.setWhatsThis(
            self.tr("""<b>Search PyPI</b>"""
                    """<p>This opens a dialog to search the Python Package"""
                    """ Index.</p>"""))
        self.searchPyPIAct.triggered.connect(self.__searchPyPI)
        self.actions.append(self.searchPyPIAct)

        ##############################################
        ## Actions for editing configuration files
        ##############################################

        self.editUserConfigAct = E5Action(
            self.tr('Edit User Configuration'),
            self.tr('Edit User Configuration...'), 0, 0, self,
            'pip_edit_user_config')
        self.editUserConfigAct.setStatusTip(
            self.tr('Open the per user configuration file in an editor'))
        self.editUserConfigAct.setWhatsThis(
            self.
            tr("""<b>Edit User Configuration</b>"""
               """<p>This opens the per user configuration file in an editor."""
               """</p>"""))
        self.editUserConfigAct.triggered.connect(self.__editUserConfiguration)
        self.actions.append(self.editUserConfigAct)

        self.editVirtualenvConfigAct = E5Action(
            self.tr('Edit Current Virtualenv Configuration'),
            self.tr('Edit Current Virtualenv Configuration...'), 0, 0, self,
            'pip_edit_virtualenv_config')
        self.editVirtualenvConfigAct.setStatusTip(
            self.tr(
                'Open the current virtualenv configuration file in an editor'))
        self.editVirtualenvConfigAct.setWhatsThis(
            self.
            tr("""<b>Edit Current Virtualenv Configuration</b>"""
               """<p>This opens the current virtualenv configuration file in"""
               """ an editor. </p>"""))
        self.editVirtualenvConfigAct.triggered.connect(
            self.__editVirtualenvConfiguration)
        self.actions.append(self.editVirtualenvConfigAct)

        self.pipConfigAct = E5Action(self.tr('Configure'),
                                     self.tr('Configure...'), 0, 0, self,
                                     'pip_configure')
        self.pipConfigAct.setStatusTip(
            self.
            tr('Show the configuration dialog with the Python Package Management'
               ' page selected'))
        self.pipConfigAct.setWhatsThis(
            self.tr(
                """<b>Configure</b>"""
                """<p>Show the configuration dialog with the Python Package"""
                """ Management page selected.</p>"""))
        self.pipConfigAct.triggered.connect(self.__pipConfigure)
        self.actions.append(self.pipConfigAct)

    def initMenu(self):
        """
        Public slot to initialize the Django menu.
        
        @return the menu generated (QMenu)
        """
        self.__menus = {}  # clear menus references

        menu = QMenu(self.tr('P&ython Package Management'), self.__ui)
        menu.setTearOffEnabled(True)

        menu.addAction(self.selectExecutableAct)
        menu.addSeparator()
        menu.addAction(self.listPackagesAct)
        menu.addAction(self.listUptodatePackagesAct)
        menu.addAction(self.listOutdatedPackagesAct)
        menu.addSeparator()
        menu.addAction(self.installPipAct)
        menu.addAction(self.installPackagesAct)
        menu.addAction(self.installRequirementsAct)
        menu.addSeparator()
        menu.addAction(self.upgradePipAct)
        menu.addAction(self.upgradePackagesAct)
        menu.addSeparator()
        menu.addAction(self.uninstallPackagesAct)
        menu.addAction(self.uninstallRequirementsAct)
        menu.addSeparator()
        menu.addAction(self.generateRequirementsAct)
        menu.addSeparator()
        menu.addAction(self.searchPyPIAct)
        menu.addSeparator()
        menu.addAction(self.editUserConfigAct)
        menu.addAction(self.editVirtualenvConfigAct)
        menu.addSeparator()
        menu.addAction(self.pipConfigAct)

        self.__menus["main"] = menu

        menu.aboutToShow.connect(self.__aboutToShowMenu)

        return menu

    def __aboutToShowMenu(self):
        """
        Private slot to set the action enabled status.
        """
        enable = bool(self.__plugin.getPreferences("CurrentPipExecutable"))
        for act in self.actions:
            if act not in [
                    self.selectExecutableAct, self.installPipAct,
                    self.editUserConfigAct, self.editVirtualenvConfigAct,
                    self.pipConfigAct
            ]:
                act.setEnabled(enable)

    def getMenu(self, name):
        """
        Public method to get a reference to the requested menu.
        
        @param name name of the menu (string)
        @return reference to the menu (QMenu) or None, if no
            menu with the given name exists
        """
        if name in self.__menus:
            return self.__menus[name]
        else:
            return None

    def getMenuNames(self):
        """
        Public method to get the names of all menus.
        
        @return menu names (list of string)
        """
        return list(self.__menus.keys())

    def __handleTearOffMenu(self, pip):
        """
        Private slot to handle a change of the pip executable.
        
        @param pip path of the pip executable
        @type str
        """
        if self.__menus["main"].isTearOffMenuVisible():
            # determine, if torn off menu needs to be refreshed
            enabled = self.listPackagesAct.isEnabled()
            if ((bool(pip) and not enabled) or (not bool(pip) and enabled)):
                self.__menus["main"].hideTearOffMenu()

    ##########################################################################
    ## Methods below implement some utility functions
    ##########################################################################

    def runProcess(self, args, cmd=""):
        """
        Public method to execute the current pip with the given arguments.
        
        The selected pip executable is called with the given arguments and
        waited for its end.
        
        @param args list of command line arguments (list of string)
        @param cmd pip command to be used (string)
        @return tuple containing a flag indicating success and the output
            of the process (string)
        """
        if not cmd:
            cmd = self.__plugin.getPreferences("CurrentPipExecutable")
        ioEncoding = Preferences.getSystem("IOEncoding")

        process = QProcess()
        process.start(cmd, args)
        procStarted = process.waitForStarted()
        if procStarted:
            finished = process.waitForFinished(30000)
            if finished:
                if process.exitCode() == 0:
                    output = str(process.readAllStandardOutput(), ioEncoding,
                                 'replace')
                    return True, output
                else:
                    return False, self.tr("pip exited with an error ({0}).")\
                        .format(process.exitCode())
            else:
                process.terminate()
                process.waitForFinished(2000)
                process.kill()
                process.waitForFinished(3000)
                return False, self.tr("pip did not finish within 30 seconds.")

        return False, self.tr("pip could not be started.")

    def __getUserConfig(self):
        """
        Private method to get the name of the user configuration file.
        
        @return path of the user configuration file (string)
        """
        # Unix:     ~/.config/pip/pip.conf
        # OS X:     ~/Library/Application Support/pip/pip.conf
        # Windows:  %APPDATA%\pip\pip.ini
        # Environment: $PIP_CONFIG_FILE

        try:
            return os.environ["PIP_CONFIG_FILE"]
        except KeyError:
            pass

        if Globals.isWindowsPlatform():
            config = os.path.join(os.environ["APPDATA"], "pip", "pip.ini")
        elif Globals.isMacPlatform():
            config = os.path.expanduser(
                "~/Library/Application Support/pip/pip.conf")
        else:
            config = os.path.expanduser("~/.config/pip/pip.conf")

        return config

    def __getVirtualenvConfig(self):
        """
        Private method to get the name of the virtualenv configuration file.
        
        @return path of the virtualenv configuration file (string)
        """
        # Unix, OS X:   $VIRTUAL_ENV/pip.conf
        # Windows:      %VIRTUAL_ENV%\pip.ini

        if Globals.isWindowsPlatform():
            pip = "pip.ini"
        else:
            pip = "pip.conf"
        try:
            virtualenv = os.environ["VIRTUAL_ENV"]
        except KeyError:
            # determine from pip executable file
            virtualenv = os.path.dirname(
                os.path.dirname(
                    self.__plugin.getPreferences("CurrentPipExecutable")))

        return os.path.join(virtualenv, pip)

    ##########################################################################
    ## Methods below implement the individual menu entries
    ##########################################################################

    def __selectPipExecutable(self):
        """
        Private method to select the pip executable to be used.
        """
        pipExecutables = sorted(self.__plugin.getPreferences("PipExecutables"))
        if pipExecutables:
            currentExecutable = self.__plugin.getPreferences(
                "CurrentPipExecutable")
            try:
                index = pipExecutables.index(currentExecutable)
            except ValueError:
                index = 0
            executable, ok = QInputDialog.getItem(
                None, self.tr("pip Executable"),
                self.tr("Select pip Executable to be used:"), pipExecutables,
                index, False)

            if ok and executable:
                self.__plugin.setPreferences("CurrentPipExecutable",
                                             executable)
        else:
            res = E5MessageBox.yesNo(
                None,
                self.tr("pip Executable"),
                self.tr("""No pip executables have been configured yet."""
                        """ Shall this be done now?"""),
                yesDefault=True)
            if res:
                e5App().getObject("UserInterface").showPreferences("pipPage")

    def __listPackages(self):
        """
        Private slot to list all installed packages.
        """
        from .PipListDialog import PipListDialog
        self.__listDialog = PipListDialog(self, "list", self.__plugin,
                                          self.tr("Installed Packages"))
        self.__listDialog.show()
        self.__listDialog.start()

    def __listUptodatePackages(self):
        """
        Private slot to list all installed, up-to-date packages.
        """
        from .PipListDialog import PipListDialog
        self.__listUptodateDialog = PipListDialog(
            self, "uptodate", self.__plugin, self.tr("Up-to-date Packages"))
        self.__listUptodateDialog.show()
        self.__listUptodateDialog.start()

    def __listOutdatedPackages(self):
        """
        Private slot to list all installed, up-to-date packages.
        """
        from .PipListDialog import PipListDialog
        self.__listOutdatedDialog = PipListDialog(self, "outdated",
                                                  self.__plugin,
                                                  self.tr("Outdated Packages"))
        self.__listOutdatedDialog.show()
        self.__listOutdatedDialog.start()

    def __editUserConfiguration(self):
        """
        Private slot to edit the user configuration.
        """
        self.__editConfiguration()

    def __editVirtualenvConfiguration(self):
        """
        Private slot to edit the current virtualenv configuration.
        """
        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 (boolean)
        """
        from QScintilla.MiniEditor import MiniEditor
        if virtualenv:
            cfgFile = self.__getVirtualenvConfig()
        else:
            cfgFile = self.__getUserConfig()
        cfgDir = os.path.dirname(cfgFile)
        if not cfgDir:
            E5MessageBox.critical(
                None, self.tr("Edit Configuration"),
                self.tr("""No valid configuartion 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 configuartion 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 __installPip(self):
        """
        Private slot to install pip.
        """
        python = E5FileDialog.getOpenFileName(
            None, self.tr("Select Python Executable"))
        if python:
            python = QDir.toNativeSeparators(python)
            dia = PipDialog(self.tr('Install PIP'))
            res = dia.startProcesses([
                (python, ["-m", "ensurepip"]),
                (python, ["-m", "pip", "install", "--upgrade", "pip"]),
            ])
            if res:
                dia.exec_()
                pip = E5FileDialog.getOpenFileName(
                    None, self.tr("Select PIP Executable"),
                    os.path.dirname(python))
                if pip:
                    pip = QDir.toNativeSeparators(pip)
                    pipExecutables = \
                        self.__plugin.getPreferences("PipExecutables")
                    if pip not in pipExecutables:
                        pipExecutables.append(pip)
                        self.__plugin.setPreferences("PipExecutables",
                                                     pipExecutables)

    @pyqtSlot()
    def upgradePip(self, pip=""):
        """
        Public method to upgrade pip itself.
        
        @param pip pip command to be used
        @type str
        @return flag indicating a successful execution
        @rtype bool
        """
        # Upgrading pip needs to be treated specially because
        # it must be done using the python executable

        if not pip:
            default = self.tr("<Default>")
            pipExecutables = sorted(
                self.__plugin.getPreferences("PipExecutables"))
            pip, ok = QInputDialog.getItem(None, self.tr("Upgrade pip"),
                                           self.tr("Select pip Executable:"),
                                           [default] + pipExecutables, 0,
                                           False)
            if not ok or not pip:
                return False

            if pip == default:
                pip = self.__plugin.getPreferences("CurrentPipExecutable")

        python = self.__getPython(pip)
        if not python:
            python = E5FileDialog.getOpenFileName(
                None, self.tr("Select Python Executable"),
                os.path.dirname(pip))
            if python:
                python = QDir.toNativeSeparators(python)
            else:
                return False

        args = ["-m", "pip", "install", "--upgrade", "pip"]
        dia = PipDialog(self.tr('Upgrade PIP'))
        res = dia.startProcess(python, args)
        if res:
            dia.exec_()
        return res

    def __getPython(self, cmd):
        """
        Private method to derive the path to the python executable given the
        path to the pip executable.
        
        @param cmd path of the pip executable
        @type str
        @return path of the python executable
        @rtype str
        """
        path, prog = os.path.split(cmd)
        paths = (path, os.path.split(path)[0])  # to try the parent directory
        if Globals.isWindowsPlatform():
            subPatterns = ((r"\d\.\d", ""), (r"\d\.", "."))
            for pyname in ("python", "pypy"):
                python = prog.replace("pip", pyname)
                for pattern, repl in subPatterns:
                    if re.search(pattern, python):
                        python = re.sub(pattern, repl, python)
                        break
                for path in paths:
                    pypath = os.path.join(path, python)
                    if os.path.exists(pypath):
                        return pypath
        else:
            subPatterns = ((r"\.\d$", ""), (r"\d\.\d$", ""), (r"\d$", ""))
            for pyname in ("python", "pypy"):
                python = prog.replace("pip", pyname)
                for path in paths:
                    pypath = os.path.join(path, python)
                    if os.path.exists(pypath):
                        return pypath

                    for pattern, repl in subPatterns:
                        if re.search(pattern, cmd):
                            newpy = re.sub(pattern, repl, python)
                            pypath = os.path.join(path, newpy)
                            if os.path.exists(pypath):
                                return pypath

        return ""

    def upgradePackages(self, packages, cmd=""):
        """
        Public method to upgrade the given list of packages.
        
        @param packages list of packages to upgrade (list of string)
        @param cmd pip command to be used (string)
        @return flag indicating a successful execution (boolean)
        """
        if not cmd:
            cmd = self.__plugin.getPreferences("CurrentPipExecutable")
        args = ["install", "--upgrade"] + packages
        dia = PipDialog(self.tr('Upgrade Packages'))
        res = dia.startProcess(cmd, args)
        if res:
            dia.exec_()
        return res

    def __upgradePackages(self):
        """
        Private slot to upgrade packages to be given by the user.
        """
        from .PipPackagesInputDialog import PipPackagesInputDialog
        dlg = PipPackagesInputDialog(self.__plugin,
                                     self.tr("Upgrade Packages"))
        if dlg.exec_() == QDialog.Accepted:
            command, packages = dlg.getData()
            if packages:
                self.upgradePackages(packages, cmd=command)

    def installPackages(self, packages, cmd=""):
        """
        Public method to install the given list of packages.
        
        @param packages list of packages to install (list of string)
        @param cmd pip command to be used (string)
        """
        if not cmd:
            cmd = self.__plugin.getPreferences("CurrentPipExecutable")
        args = ["install"] + packages
        dia = PipDialog(self.tr('Install Packages'))
        res = dia.startProcess(cmd, args)
        if res:
            dia.exec_()

    def __installPackages(self):
        """
        Private slot to install packages to be given by the user.
        """
        from .PipPackagesInputDialog import PipPackagesInputDialog
        dlg = PipPackagesInputDialog(self.__plugin,
                                     self.tr("Install Packages"))
        if dlg.exec_() == QDialog.Accepted:
            command, packages = dlg.getData()
            if packages:
                self.installPackages(packages, cmd=command)

    def __installRequirements(self):
        """
        Private slot to install packages as given in a requirements file.
        """
        from .PipRequirementsSelectionDialog import \
            PipRequirementsSelectionDialog
        dlg = PipRequirementsSelectionDialog(self.__plugin)
        if dlg.exec_() == QDialog.Accepted:
            command, requirements = dlg.getData()
            if requirements and os.path.exists(requirements):
                if not command:
                    command = self.__plugin.getPreferences(
                        "CurrentPipExecutable")
                args = ["install", "--requirement", requirements]
                dia = PipDialog(self.tr('Install Packages from Requirements'))
                res = dia.startProcess(command, args)
                if res:
                    dia.exec_()

    def uninstallPackages(self, packages, cmd=""):
        """
        Public method to uninstall the given list of packages.
        
        @param packages list of packages to uninstall (list of string)
        @param cmd pip command to be used (string)
        @return flag indicating a successful execution (boolean)
        """
        res = False
        if packages:
            from UI.DeleteFilesConfirmationDialog import \
                DeleteFilesConfirmationDialog
            dlg = DeleteFilesConfirmationDialog(
                self.parent(), self.tr("Uninstall Packages"),
                self.tr("Do you really want to uninstall these packages?"),
                packages)
            if dlg.exec_() == QDialog.Accepted:
                if not cmd:
                    cmd = self.__plugin.getPreferences("CurrentPipExecutable")
                args = ["uninstall", "--yes"] + packages
                dia = PipDialog(self.tr('Uninstall Packages'))
                res = dia.startProcess(cmd, args)
                if res:
                    dia.exec_()
        return res

    def __uninstallPackages(self):
        """
        Private slot to uninstall packages to be given by the user.
        """
        from .PipPackagesInputDialog import PipPackagesInputDialog
        dlg = PipPackagesInputDialog(self.__plugin,
                                     self.tr("Uninstall Packages"))
        if dlg.exec_() == QDialog.Accepted:
            command, packages = dlg.getData()
            if packages:
                self.uninstallPackages(packages, cmd=command)

    def __uninstallRequirements(self):
        """
        Private slot to uninstall packages as given in a requirements file.
        """
        from .PipRequirementsSelectionDialog import \
            PipRequirementsSelectionDialog
        dlg = PipRequirementsSelectionDialog(self.__plugin)
        if dlg.exec_() == QDialog.Accepted:
            command, requirements = dlg.getData()
            if requirements and os.path.exists(requirements):
                try:
                    f = open(requirements, "r")
                    reqs = f.read().splitlines()
                    f.close()
                except (OSError, IOError):
                    return

                from UI.DeleteFilesConfirmationDialog import \
                    DeleteFilesConfirmationDialog
                dlg = DeleteFilesConfirmationDialog(
                    self.parent(), self.tr("Uninstall Packages"),
                    self.tr("Do you really want to uninstall these packages?"),
                    reqs)
                if dlg.exec_() == QDialog.Accepted:
                    if not command:
                        command = self.__plugin.getPreferences(
                            "CurrentPipExecutable")
                    args = ["uninstall", "--requirement", requirements]
                    dia = PipDialog(
                        self.tr('Uninstall Packages from Requirements'))
                    res = dia.startProcess(command, args)
                    if res:
                        dia.exec_()

    def __generateRequirements(self):
        """
        Private slot to generate the contents for a requirements file.
        """
        from .PipFreezeDialog import PipFreezeDialog
        self.__freezeDialog = PipFreezeDialog(self, self.__plugin)
        self.__freezeDialog.show()
        self.__freezeDialog.start()

    def __searchPyPI(self):
        """
        Private slot to search the Python Package Index.
        """
        from .PipSearchDialog import PipSearchDialog
        self.__searchDialog = PipSearchDialog(self, self.__plugin)
        self.__searchDialog.show()

    def __pipConfigure(self):
        """
        Private slot to open the configuration page.
        """
        e5App().getObject("UserInterface").showPreferences("pipPage")
Beispiel #19
0
class PipPackagesWidget(QWidget, Ui_PipPackagesWidget):
    """
    Class implementing the pip packages management widget.
    """
    ShowProcessGeneralMode = 0
    ShowProcessClassifiersMode = 1
    ShowProcessEntryPointsMode = 2
    ShowProcessFilesListMode = 3

    SearchStopwords = {
        "a",
        "and",
        "are",
        "as",
        "at",
        "be",
        "but",
        "by",
        "for",
        "if",
        "in",
        "into",
        "is",
        "it",
        "no",
        "not",
        "of",
        "on",
        "or",
        "such",
        "that",
        "the",
        "their",
        "then",
        "there",
        "these",
        "they",
        "this",
        "to",
        "was",
        "will",
    }
    SearchVersionRole = Qt.UserRole + 1

    def __init__(self, pip, parent=None):
        """
        Constructor
        
        @param pip reference to the global pip interface
        @type Pip
        @param parent reference to the parent widget
        @type QWidget
        """
        super(PipPackagesWidget, self).__init__(parent)
        self.setupUi(self)

        self.pipMenuButton.setObjectName("pip_supermenu_button")
        self.pipMenuButton.setIcon(UI.PixmapCache.getIcon("superMenu"))
        self.pipMenuButton.setToolTip(self.tr("pip Menu"))
        self.pipMenuButton.setPopupMode(QToolButton.InstantPopup)
        self.pipMenuButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.pipMenuButton.setFocusPolicy(Qt.NoFocus)
        self.pipMenuButton.setAutoRaise(True)
        self.pipMenuButton.setShowMenuInside(True)

        self.refreshButton.setIcon(UI.PixmapCache.getIcon("reload"))
        self.upgradeButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
        self.upgradeAllButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
        self.uninstallButton.setIcon(UI.PixmapCache.getIcon("minus"))
        self.showPackageDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))
        self.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find"))
        self.searchButton.setIcon(UI.PixmapCache.getIcon("findNext"))
        self.installButton.setIcon(UI.PixmapCache.getIcon("plus"))
        self.installUserSiteButton.setIcon(UI.PixmapCache.getIcon("addUser"))
        self.showDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))

        self.__pip = pip
        self.__client = E5XmlRpcClient(self.__pip.getIndexUrlXml(), self)

        self.packagesList.header().setSortIndicator(0, Qt.AscendingOrder)

        self.__infoLabels = {
            "name": self.tr("Name:"),
            "version": self.tr("Version:"),
            "location": self.tr("Location:"),
            "requires": self.tr("Requires:"),
            "summary": self.tr("Summary:"),
            "home-page": self.tr("Homepage:"),
            "author": self.tr("Author:"),
            "author-email": self.tr("Author Email:"),
            "license": self.tr("License:"),
            "metadata-version": self.tr("Metadata Version:"),
            "installer": self.tr("Installer:"),
            "classifiers": self.tr("Classifiers:"),
            "entry-points": self.tr("Entry Points:"),
            "files": self.tr("Files:"),
        }
        self.infoWidget.setHeaderLabels(["Key", "Value"])

        venvManager = e5App().getObject("VirtualEnvManager")
        venvManager.virtualEnvironmentAdded.connect(
            self.on_refreshButton_clicked)
        venvManager.virtualEnvironmentRemoved.connect(
            self.on_refreshButton_clicked)

        project = e5App().getObject("Project")
        project.projectOpened.connect(self.on_refreshButton_clicked)
        project.projectClosed.connect(self.on_refreshButton_clicked)

        self.__initPipMenu()
        self.__populateEnvironments()
        self.__updateActionButtons()

        self.statusLabel.hide()
        self.searchWidget.hide()

        self.__queryName = []
        self.__querySummary = []

        self.__packageDetailsDialog = None

    def __populateEnvironments(self):
        """
        Private method to get a list of environments and populate the selector.
        """
        self.environmentsComboBox.addItem("")
        projectVenv = self.__pip.getProjectEnvironmentString()
        if projectVenv:
            self.environmentsComboBox.addItem(projectVenv)
        self.environmentsComboBox.addItems(
            self.__pip.getVirtualenvNames(noRemote=True))

    def __isPipAvailable(self):
        """
        Private method to check, if the pip package is available for the
        selected environment.
        
        @return flag indicating availability
        @rtype bool
        """
        available = False

        venvName = self.environmentsComboBox.currentText()
        if venvName:
            available = len(
                self.packagesList.findItems(
                    "pip", Qt.MatchExactly | Qt.MatchCaseSensitive)) == 1

        return available

    #######################################################################
    ## Slots handling widget signals below
    #######################################################################

    def __selectedUpdateableItems(self):
        """
        Private method to get a list of selected items that can be updated.
        
        @return list of selected items that can be updated
        @rtype list of QTreeWidgetItem
        """
        return [
            itm for itm in self.packagesList.selectedItems()
            if bool(itm.text(2))
        ]

    def __allUpdateableItems(self):
        """
        Private method to get a list of all items that can be updated.
        
        @return list of all items that can be updated
        @rtype list of QTreeWidgetItem
        """
        updateableItems = []
        for index in range(self.packagesList.topLevelItemCount()):
            itm = self.packagesList.topLevelItem(index)
            if itm.text(2):
                updateableItems.append(itm)

        return updateableItems

    def __updateActionButtons(self):
        """
        Private method to set the state of the action buttons.
        """
        if self.__isPipAvailable():
            self.upgradeButton.setEnabled(
                bool(self.__selectedUpdateableItems()))
            self.uninstallButton.setEnabled(
                bool(self.packagesList.selectedItems()))
            self.upgradeAllButton.setEnabled(bool(self.__allUpdateableItems()))
            self.showPackageDetailsButton.setEnabled(
                len(self.packagesList.selectedItems()) == 1)
        else:
            self.upgradeButton.setEnabled(False)
            self.uninstallButton.setEnabled(False)
            self.upgradeAllButton.setEnabled(False)
            self.showPackageDetailsButton.setEnabled(False)

    def __refreshPackagesList(self):
        """
        Private method to referesh the packages list.
        """
        self.packagesList.clear()
        venvName = self.environmentsComboBox.currentText()
        if venvName:
            interpreter = self.__pip.getVirtualenvInterpreter(venvName)
            if interpreter:
                QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                self.statusLabel.show()
                self.statusLabel.setText(
                    self.tr("Getting installed packages..."))
                QApplication.processEvents()

                # 1. populate with installed packages
                self.packagesList.setUpdatesEnabled(False)
                installedPackages = self.__pip.getInstalledPackages(
                    venvName,
                    localPackages=self.localCheckBox.isChecked(),
                    notRequired=self.notRequiredCheckBox.isChecked(),
                    usersite=self.userCheckBox.isChecked(),
                )
                for package, version in installedPackages:
                    QTreeWidgetItem(self.packagesList, [package, version])
                self.packagesList.setUpdatesEnabled(True)
                self.statusLabel.setText(
                    self.tr("Getting outdated packages..."))
                QApplication.processEvents()

                # 2. update with update information
                self.packagesList.setUpdatesEnabled(False)
                outdatedPackages = self.__pip.getOutdatedPackages(
                    venvName,
                    localPackages=self.localCheckBox.isChecked(),
                    notRequired=self.notRequiredCheckBox.isChecked(),
                    usersite=self.userCheckBox.isChecked(),
                )
                for package, _version, latest in outdatedPackages:
                    items = self.packagesList.findItems(
                        package, Qt.MatchExactly | Qt.MatchCaseSensitive)
                    if items:
                        itm = items[0]
                        itm.setText(2, latest)

                self.packagesList.sortItems(0, Qt.AscendingOrder)
                for col in range(self.packagesList.columnCount()):
                    self.packagesList.resizeColumnToContents(col)
                self.packagesList.setUpdatesEnabled(True)
                QApplication.restoreOverrideCursor()
                self.statusLabel.hide()

        self.__updateActionButtons()
        self.__updateSearchActionButtons()
        self.__updateSearchButton()

    @pyqtSlot(int)
    def on_environmentsComboBox_currentIndexChanged(self, index):
        """
        Private slot handling the selection of a Python environment.
        
        @param index index of the selected Python environment
        @type int
        """
        self.__refreshPackagesList()

    @pyqtSlot(bool)
    def on_localCheckBox_clicked(self, checked):
        """
        Private slot handling the switching of the local mode.
        
        @param checked state of the local check box
        @type bool
        """
        self.__refreshPackagesList()

    @pyqtSlot(bool)
    def on_notRequiredCheckBox_clicked(self, checked):
        """
        Private slot handling the switching of the 'not required' mode.
        
        @param checked state of the 'not required' check box
        @type bool
        """
        self.__refreshPackagesList()

    @pyqtSlot(bool)
    def on_userCheckBox_clicked(self, checked):
        """
        Private slot handling the switching of the 'user-site' mode.
        
        @param checked state of the 'user-site' check box
        @type bool
        """
        self.__refreshPackagesList()

    @pyqtSlot()
    def on_packagesList_itemSelectionChanged(self):
        """
        Private slot handling the selection of a package.
        """
        self.infoWidget.clear()

        if len(self.packagesList.selectedItems()) == 1:
            itm = self.packagesList.selectedItems()[0]

            environment = self.environmentsComboBox.currentText()
            interpreter = self.__pip.getVirtualenvInterpreter(environment)
            if not interpreter:
                return

            QApplication.setOverrideCursor(Qt.WaitCursor)

            args = ["-m", "pip", "show"]
            if self.verboseCheckBox.isChecked():
                args.append("--verbose")
            if self.installedFilesCheckBox.isChecked():
                args.append("--files")
            args.append(itm.text(0))
            success, output = self.__pip.runProcess(args, interpreter)

            if success and output:
                mode = self.ShowProcessGeneralMode
                for line in output.splitlines():
                    line = line.rstrip()
                    if line != "---":
                        if mode != self.ShowProcessGeneralMode:
                            if line[0] == " ":
                                QTreeWidgetItem(self.infoWidget,
                                                [" ", line.strip()])
                            else:
                                mode = self.ShowProcessGeneralMode
                        if mode == self.ShowProcessGeneralMode:
                            try:
                                label, info = line.split(": ", 1)
                            except ValueError:
                                label = line[:-1]
                                info = ""
                            label = label.lower()
                            if label in self.__infoLabels:
                                QTreeWidgetItem(
                                    self.infoWidget,
                                    [self.__infoLabels[label], info])
                            if label == "files":
                                mode = self.ShowProcessFilesListMode
                            elif label == "classifiers":
                                mode = self.ShowProcessClassifiersMode
                            elif label == "entry-points":
                                mode = self.ShowProcessEntryPointsMode
                self.infoWidget.scrollToTop()

            header = self.infoWidget.header()
            header.setStretchLastSection(False)
            header.resizeSections(QHeaderView.ResizeToContents)
            if header.sectionSize(0) + header.sectionSize(1) < header.width():
                header.setStretchLastSection(True)

            QApplication.restoreOverrideCursor()

        self.__updateActionButtons()

    @pyqtSlot(QTreeWidgetItem, int)
    def on_packagesList_itemActivated(self, item, column):
        """
        Private slot reacting on a package item activation.
        
        @param item reference to the activated item
        @type QTreeWidgetItem
        @param column activated column
        @type int
        """
        packageName = item.text(0)
        if column == 1:
            # show details for installed version
            packageVersion = item.text(1)
        else:
            # show details for available version or installed one
            if item.text(2):
                packageVersion = item.text(2)
            else:
                packageVersion = item.text(1)

        self.__showPackageDetails(packageName, packageVersion)

    @pyqtSlot(bool)
    def on_verboseCheckBox_clicked(self, checked):
        """
        Private slot to handle a change of the verbose package information
        checkbox.
        
        @param checked state of the checkbox
        @type bool
        """
        self.on_packagesList_itemSelectionChanged()

    @pyqtSlot(bool)
    def on_installedFilesCheckBox_clicked(self, checked):
        """
        Private slot to handle a change of the installed files information
        checkbox.
        
        @param checked state of the checkbox
        @type bool
        """
        self.on_packagesList_itemSelectionChanged()

    @pyqtSlot()
    def on_refreshButton_clicked(self):
        """
        Private slot to refresh the display.
        """
        currentEnvironment = self.environmentsComboBox.currentText()
        self.environmentsComboBox.clear()
        self.packagesList.clear()

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        QApplication.processEvents()

        self.__populateEnvironments()

        index = self.environmentsComboBox.findText(
            currentEnvironment, Qt.MatchExactly | Qt.MatchCaseSensitive)
        if index != -1:
            self.environmentsComboBox.setCurrentIndex(index)

        QApplication.restoreOverrideCursor()
        self.__updateActionButtons()

    @pyqtSlot()
    def on_upgradeButton_clicked(self):
        """
        Private slot to upgrade selected packages of the selected environment.
        """
        packages = [itm.text(0) for itm in self.__selectedUpdateableItems()]
        if packages:
            ok = self.__executeUpgradePackages(packages)
            if ok:
                self.on_refreshButton_clicked()

    @pyqtSlot()
    def on_upgradeAllButton_clicked(self):
        """
        Private slot to upgrade all packages of the selected environment.
        """
        packages = [itm.text(0) for itm in self.__allUpdateableItems()]
        if packages:
            ok = self.__executeUpgradePackages(packages)
            if ok:
                self.on_refreshButton_clicked()

    @pyqtSlot()
    def on_uninstallButton_clicked(self):
        """
        Private slot to remove selected packages of the selected environment.
        """
        packages = [itm.text(0) for itm in self.packagesList.selectedItems()]
        if packages:
            ok = self.__pip.uninstallPackages(
                packages, venvName=self.environmentsComboBox.currentText())
            if ok:
                self.on_refreshButton_clicked()

    def __executeUpgradePackages(self, packages):
        """
        Private method to execute the pip upgrade command.
        
        @param packages list of package names to be upgraded
        @type list of str
        @return flag indicating success
        @rtype bool
        """
        ok = self.__pip.upgradePackages(
            packages,
            venvName=self.environmentsComboBox.currentText(),
            userSite=self.userCheckBox.isChecked())
        return ok

    @pyqtSlot()
    def on_showPackageDetailsButton_clicked(self):
        """
        Private slot to show information for the selected package.
        """
        item = self.packagesList.selectedItems()[0]
        if item:
            packageName = item.text(0)
            # show details for available version or installed one
            if item.text(2):
                packageVersion = item.text(2)
            else:
                packageVersion = item.text(1)

            self.__showPackageDetails(packageName, packageVersion)

    #######################################################################
    ## Search widget related methods below
    #######################################################################

    def __updateSearchActionButtons(self):
        """
        Private method to update the action button states of the search widget.
        """
        installEnable = (len(self.searchResultList.selectedItems()) > 0
                         and self.environmentsComboBox.currentIndex() > 0
                         and self.__isPipAvailable())
        self.installButton.setEnabled(installEnable)
        self.installUserSiteButton.setEnabled(installEnable)

        self.showDetailsButton.setEnabled(
            len(self.searchResultList.selectedItems()) == 1
            and self.__isPipAvailable())

    def __updateSearchButton(self):
        """
        Private method to update the state of the search button.
        """
        self.searchButton.setEnabled((bool(self.searchEditName.text())
                                      or bool(self.searchEditSummary.text()))
                                     and self.__isPipAvailable())

    @pyqtSlot(bool)
    def on_searchToggleButton_toggled(self, checked):
        """
        Private slot to togle the search widget.
        
        @param checked state of the search widget button
        @type bool
        """
        self.searchWidget.setVisible(checked)

        if checked:
            self.searchEditName.setFocus(Qt.OtherFocusReason)
            self.searchEditName.selectAll()

            self.__updateSearchActionButtons()
            self.__updateSearchButton()

    @pyqtSlot(str)
    def on_searchEditName_textChanged(self, txt):
        """
        Private slot handling a change of the search term.
        
        @param txt search term
        @type str
        """
        self.__updateSearchButton()

    @pyqtSlot()
    def on_searchEditName_returnPressed(self):
        """
        Private slot initiating a search via a press of the Return key.
        """
        self.__search()

    @pyqtSlot(str)
    def on_searchEditSummary_textChanged(self, txt):
        """
        Private slot handling a change of the search term.
        
        @param txt search term
        @type str
        """
        self.__updateSearchButton()

    @pyqtSlot()
    def on_searchEditSummary_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.
        """
        self.__search()

    @pyqtSlot()
    def on_searchResultList_itemSelectionChanged(self):
        """
        Private slot handling changes of the search result selection.
        """
        self.__updateSearchActionButtons()

    def __search(self):
        """
        Private method to perform the search.
        """
        self.searchResultList.clear()
        self.searchInfoLabel.clear()

        self.searchButton.setEnabled(False)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

        self.__queryName = [
            term for term in self.searchEditName.text().strip().split()
            if term not in self.SearchStopwords
        ]
        self.__querySummary = [
            term for term in self.searchEditSummary.text().strip().split()
            if term not in self.SearchStopwords
        ]
        self.__client.call("search", ({
            "name": self.__queryName,
            "summary": self.__querySummary
        }, self.searchTermCombineComboBox.currentText()),
                           self.__processSearchResult, self.__searchError)

    def __processSearchResult(self, data):
        """
        Private method to process the search result data from PyPI.
        
        @param data result data with hits in the first element
        @type tuple
        """
        if data:
            packages = self.__transformHits(data[0])
            if packages:
                self.searchInfoLabel.setText(
                    self.tr("%n package(s) found.", "", len(packages)))
                wrapper = textwrap.TextWrapper(width=80)
                count = 0
                total = 0
                for package in packages:
                    itm = QTreeWidgetItem(self.searchResultList, [
                        package['name'].strip(), "{0:4d}".format(
                            package['score']), "\n".join([
                                wrapper.fill(line) for line in
                                package['summary'].strip().splitlines()
                            ])
                    ])
                    itm.setData(0, self.SearchVersionRole, package['version'])
                    count += 1
                    total += 1
                    if count == 100:
                        count = 0
                        QApplication.processEvents()
            else:
                QApplication.restoreOverrideCursor()
                E5MessageBox.warning(
                    self, self.tr("Search PyPI"),
                    self.tr("""<p>The package search did not return"""
                            """ anything.</p>"""))
                self.searchInfoLabel.setText(
                    self.tr("""<p>The package search did not return"""
                            """ anything.</p>"""))
        else:
            QApplication.restoreOverrideCursor()
            E5MessageBox.warning(
                self, self.tr("Search PyPI"),
                self.tr("""<p>The package search did not return anything."""
                        """</p>"""))
            self.searchInfoLabel.setText(
                self.tr("""<p>The package search did not return anything."""
                        """</p>"""))

        header = self.searchResultList.header()
        self.searchResultList.sortItems(1, Qt.DescendingOrder)
        header.setStretchLastSection(False)
        header.resizeSections(QHeaderView.ResizeToContents)
        headerSize = 0
        for col in range(header.count()):
            headerSize += header.sectionSize(col)
        if headerSize < header.width():
            header.setStretchLastSection(True)

        self.__finishSearch()

    def __finishSearch(self):
        """
        Private slot performing the search finishing actions.
        """
        QApplication.restoreOverrideCursor()

        self.__updateSearchActionButtons()
        self.__updateSearchButton()

        self.searchEditName.setFocus(Qt.OtherFocusReason)

    def __searchError(self, errorCode, errorString):
        """
        Private method handling a search error.
        
        @param errorCode code of the error
        @type int
        @param errorString error message
        @type str
        """
        self.__finish()
        E5MessageBox.warning(
            self, self.tr("Search PyPI"),
            self.tr("""<p>The package search failed.</p><p>Reason: {0}</p>""").
            format(errorString))
        self.searchInfoLabel.setText(self.tr("Error: {0}").format(errorString))

    def __transformHits(self, hits):
        """
        Private method to convert the list returned from pypi into a
        packages list.
        
        @param hits list returned from pypi
        @type list of dict
        @return list of packages
        @rtype list of dict
        """
        # we only include the record with the highest score
        packages = {}
        for hit in hits:
            name = hit['name'].strip()
            summary = (hit['summary'] or "").strip()
            version = hit['version'].strip()
            score = self.__score(name, summary)
            # cleanup the summary
            if summary in ["UNKNOWN", "."]:
                summary = ""

            if name not in packages:
                packages[name] = {
                    'name': name,
                    'summary': summary,
                    'version': [version.strip()],
                    'score': score
                }
            else:
                if score > packages[name]['score']:
                    packages[name]['score'] = score
                    packages[name]['summary'] = summary
                packages[name]['version'].append(version.strip())

        return list(packages.values())

    def __score(self, name, summary):
        """
        Private method to calculate some score for a search result.
        
        @param name name of the returned package
        @type str
        @param summary summary text for the package
        @type str
        @return score value
        @rtype int
        """
        score = 0
        for queryTerm in self.__queryName:
            if queryTerm.lower() in name.lower():
                score += 4
                if queryTerm.lower() == name.lower():
                    score += 4

        for queryTerm in self.__querySummary:
            if queryTerm.lower() in summary.lower():
                if QRegExp(r'\b{0}\b'.format(QRegExp.escape(queryTerm)),
                           Qt.CaseInsensitive).indexIn(summary) != -1:
                    # word match gets even higher score
                    score += 2
                else:
                    score += 1

        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.__showSearchedDetails()

    @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.__showSearchedDetails(item)

    def __showSearchedDetails(self, item=None):
        """
        Private slot to show details about the selected search result package.
        
        @param item reference to the search result item to show details for
        @type QTreeWidgetItem
        """
        self.showDetailsButton.setEnabled(False)

        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.__showPackageDetails(packageName, packageVersion)

    def __showPackageDetails(self, packageName, packageVersion):
        """
        Private method to populate the package details dialog.
        
        @param packageName name of the package to show details for
        @type str
        @param packageVersion version of the package
        @type str
        """
        QApplication.setOverrideCursor(Qt.WaitCursor)
        QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

        packageData = self.__pip.getPackageDetails(packageName, packageVersion)

        QApplication.restoreOverrideCursor()
        if packageData:
            from .PipPackageDetailsDialog import PipPackageDetailsDialog

            self.showDetailsButton.setEnabled(True)

            if self.__packageDetailsDialog is not None:
                self.__packageDetailsDialog.close()

            self.__packageDetailsDialog = (PipPackageDetailsDialog(
                packageData, self))
            self.__packageDetailsDialog.show()
        else:
            E5MessageBox.warning(
                self, self.tr("Search PyPI"),
                self.tr("""<p>No package details info for <b>{0}</b>"""
                        """ available.</p>""").format(packageName))

    #######################################################################
    ## Menu related methods below
    #######################################################################

    def __initPipMenu(self):
        """
        Private method to create the super menu and attach it to the super
        menu button.
        """
        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 Environment 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):
        """
        Private slot to install packages as given in a requirements file.
        """
        venvName = self.environmentsComboBox.currentText()
        if venvName:
            self.__pip.installRequirements(venvName)

    @pyqtSlot()
    def __uninstallRequirements(self):
        """
        Private slot to uninstall packages as given in a requirements file.
        """
        venvName = self.environmentsComboBox.currentText()
        if venvName:
            self.__pip.uninstallRequirements(venvName)

    @pyqtSlot()
    def __generateRequirements(self):
        """
        Private slot to generate the contents for a requirements file.
        """
        venvName = self.environmentsComboBox.currentText()
        if venvName:
            from .PipFreezeDialog import PipFreezeDialog
            self.__freezeDialog = PipFreezeDialog(self.__pip, self)
            self.__freezeDialog.show()
            self.__freezeDialog.start(venvName)

    @pyqtSlot()
    def __editUserConfiguration(self):
        """
        Private slot to edit the user configuration.
        """
        self.__editConfiguration()

    @pyqtSlot()
    def __editVirtualenvConfiguration(self):
        """
        Private slot to edit the configuration of the selected environment.
        """
        venvName = self.environmentsComboBox.currentText()
        if venvName:
            self.__editConfiguration(venvName=venvName)

    def __editConfiguration(self, venvName=""):
        """
        Private method to edit a configuration.
        
        @param venvName name of the environment to act upon
        @type str
        """
        from QScintilla.MiniEditor import MiniEditor
        if venvName:
            cfgFile = self.__pip.getVirtualenvConfig(venvName)
            if not cfgFile:
                return
        else:
            cfgFile = self.__pip.getUserConfig()
        cfgDir = os.path.dirname(cfgFile)
        if not cfgDir:
            E5MessageBox.critical(
                None, self.tr("Edit Configuration"),
                self.tr("""No valid configuration path determined."""
                        """ 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."""
                        """ 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 configuration path determined."""
                        """ 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")
class CondaPackagesWidget(QWidget, Ui_CondaPackagesWidget):
    """
    Class implementing the conda packages management widget.
    """
    # Role definition of packages list
    PackageVersionRole = Qt.UserRole + 1
    PackageBuildRole = Qt.UserRole + 2
    
    # Role definitions of search results list
    PackageDetailedDataRole = Qt.UserRole + 1
    
    def __init__(self, conda, parent=None):
        """
        Constructor
        
        @param conda reference to the conda interface
        @type Conda
        @param parent reference to the parent widget
        @type QWidget
        """
        super(CondaPackagesWidget, self).__init__(parent)
        self.setupUi(self)
        
        self.__conda = conda
        
        if not CondaInterface.isCondaAvailable():
            self.baseWidget.hide()
            self.searchWidget.hide()
        
        else:
            self.notAvailableWidget.hide()
            
            self.__initCondaInterface()
    
    def __initCondaInterface(self):
        """
        Private method to initialize the conda interface elements.
        """
        self.statusLabel.hide()
        
        self.condaMenuButton.setObjectName(
            "conda_supermenu_button")
        self.condaMenuButton.setIcon(UI.PixmapCache.getIcon("superMenu"))
        self.condaMenuButton.setToolTip(self.tr("Conda Menu"))
        self.condaMenuButton.setPopupMode(QToolButton.InstantPopup)
        self.condaMenuButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.condaMenuButton.setFocusPolicy(Qt.NoFocus)
        self.condaMenuButton.setAutoRaise(True)
        self.condaMenuButton.setShowMenuInside(True)
        
        self.refreshButton.setIcon(UI.PixmapCache.getIcon("reload"))
        self.upgradeButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
        self.upgradeAllButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
        self.uninstallButton.setIcon(UI.PixmapCache.getIcon("minus"))
        self.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find"))
        self.searchButton.setIcon(UI.PixmapCache.getIcon("findNext"))
        self.installButton.setIcon(UI.PixmapCache.getIcon("plus"))
        self.showDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))
        
        if CondaInterface.condaVersion() >= (4, 4, 0):
            self.searchOptionsWidget.hide()
        else:
            self.platformComboBox.addItems(sorted([
                "", "win-32", "win-64", "osx-64", "linux-32", "linux-64",
            ]))
        
        self.__initCondaMenu()
        self.__populateEnvironments()
        self.__updateActionButtons()
        
        self.searchWidget.hide()
        
        self.__conda.condaEnvironmentCreated.connect(
            self.on_refreshButton_clicked)
        self.__conda.condaEnvironmentRemoved.connect(
            self.on_refreshButton_clicked)
    
    def __populateEnvironments(self):
        """
        Private method to get a list of environments and populate the selector.
        """
        environments = [("", "")] + sorted(
            self.__conda.getCondaEnvironmentsList())
        for environment in environments:
            self.environmentsComboBox.addItem(environment[0], environment[1])
    
    def __initCondaMenu(self):
        """
        Private method to create the super menu and attach it to the super
        menu button.
        """
        self.__condaMenu = QMenu(self)
        self.__envActs = []
        
        self.__cleanMenu = QMenu(self.tr("Clean"), self)
        self.__cleanMenu.addAction(
            self.tr("All"), lambda: self.__conda.cleanConda("all"))
        self.__cleanMenu.addAction(
            self.tr("Cache"), lambda: self.__conda.cleanConda("index-cache"))
        self.__cleanMenu.addAction(
            self.tr("Lock Files"),
            lambda: self.__conda.cleanConda("lock"))
        self.__cleanMenu.addAction(
            self.tr("Packages"), lambda: self.__conda.cleanConda("packages"))
        self.__cleanMenu.addAction(
            self.tr("Tarballs"), lambda: self.__conda.cleanConda("tarballs"))
        
        self.__condaMenu.addAction(
            self.tr("About Conda..."), self.__aboutConda)
        self.__condaMenu.addSeparator()
        self.__condaMenu.addAction(
            self.tr("Update Conda"), self.__conda.updateConda)
        self.__condaMenu.addSeparator()
        self.__envActs.append(self.__condaMenu.addAction(
            self.tr("Install Packages"), self.__installPackages))
        self.__envActs.append(self.__condaMenu.addAction(
            self.tr("Install Requirements"), self.__installRequirements))
        self.__condaMenu.addSeparator()
        self.__envActs.append(self.__condaMenu.addAction(
            self.tr("Generate Requirements"), self.__generateRequirements))
        self.__condaMenu.addSeparator()
        self.__condaMenu.addAction(
            self.tr("Create Environment from Requirements"),
            self.__createEnvironment)
        self.__envActs.append(self.__condaMenu.addAction(
            self.tr("Clone Environment"), self.__cloneEnvironment))
        self.__deleteEnvAct = self.__condaMenu.addAction(
            self.tr("Delete Environment"), self.__deleteEnvironment)
        self.__condaMenu.addSeparator()
        self.__condaMenu.addMenu(self.__cleanMenu)
        self.__condaMenu.addSeparator()
        self.__condaMenu.addAction(
            self.tr("Edit User Configuration..."),
            self.__editUserConfiguration)
        self.__condaMenu.addSeparator()
        self.__condaMenu.addAction(
            self.tr("Configure..."), self.__condaConfigure)
        
        self.condaMenuButton.setMenu(self.__condaMenu)
        
        self.__condaMenu.aboutToShow.connect(self.__aboutToShowCondaMenu)
    
    def __selectedUpdateableItems(self):
        """
        Private method to get a list of selected items that can be updated.
        
        @return list of selected items that can be updated
        @rtype list of QTreeWidgetItem
        """
        return [
            itm for itm in self.packagesList.selectedItems()
            if bool(itm.text(2))
        ]
    
    def __allUpdateableItems(self):
        """
        Private method to get a list of all items that can be updated.
        
        @return list of all items that can be updated
        @rtype list of QTreeWidgetItem
        """
        updateableItems = []
        for index in range(self.packagesList.topLevelItemCount()):
            itm = self.packagesList.topLevelItem(index)
            if itm.text(2):
                updateableItems.append(itm)
        
        return updateableItems
    
    def __updateActionButtons(self):
        """
        Private method to set the state of the action buttons.
        """
        self.upgradeButton.setEnabled(
            bool(self.__selectedUpdateableItems()))
        self.uninstallButton.setEnabled(
            bool(self.packagesList.selectedItems()))
        self.upgradeAllButton.setEnabled(
            bool(self.__allUpdateableItems()))
    
    @pyqtSlot(int)
    def on_environmentsComboBox_currentIndexChanged(self, index):
        """
        Private slot handling the selection of a conda environment.
        
        @param index index of the selected conda environment
        @type int
        """
        self.packagesList.clear()
        prefix = self.environmentsComboBox.itemData(index)
        if prefix:
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            self.statusLabel.show()
            self.statusLabel.setText(self.tr("Getting installed packages..."))
            QApplication.processEvents()
            
            # 1. populate with installed packages
            self.packagesList.setUpdatesEnabled(False)
            installedPackages = self.__conda.getInstalledPackages(
                prefix=prefix)
            for package, version, build in installedPackages:
                itm = QTreeWidgetItem(self.packagesList, [package, version])
                itm.setData(1, self.PackageVersionRole, version)
                itm.setData(1, self.PackageBuildRole, build)
            self.packagesList.setUpdatesEnabled(True)
            self.statusLabel.setText(self.tr("Getting outdated packages..."))
            QApplication.processEvents()
            
            # 2. update with update information
            self.packagesList.setUpdatesEnabled(False)
            updateablePackages = self.__conda.getUpdateablePackages(
                prefix=prefix)
            for package, version, build in updateablePackages:
                items = self.packagesList.findItems(
                    package, Qt.MatchExactly | Qt.MatchCaseSensitive)
                if items:
                    itm = items[0]
                    itm.setText(2, version)
                    itm.setData(2, self.PackageVersionRole, version)
                    itm.setData(2, self.PackageBuildRole, build)
                    if itm.data(1, self.PackageVersionRole) == version:
                        # build must be different, show in version display
                        itm.setText(1, self.tr("{0} (Build: {1})").format(
                            itm.data(1, self.PackageVersionRole),
                            itm.data(1, self.PackageBuildRole),
                        ))
                        itm.setText(2, self.tr("{0} (Build: {1})").format(
                            itm.data(2, self.PackageVersionRole),
                            itm.data(2, self.PackageBuildRole),
                        ))
            
            self.packagesList.sortItems(0, Qt.AscendingOrder)
            for col in range(self.packagesList.columnCount()):
                self.packagesList.resizeColumnToContents(col)
            self.packagesList.setUpdatesEnabled(True)
            QApplication.restoreOverrideCursor()
            self.statusLabel.hide()
        
        self.__updateActionButtons()
        self.__updateSearchActionButtons()
    
    @pyqtSlot()
    def on_packagesList_itemSelectionChanged(self):
        """
        Private slot to handle the selection of some items..
        """
        self.__updateActionButtons()
    
    @pyqtSlot()
    def on_refreshButton_clicked(self):
        """
        Private slot to refresh the display.
        """
        currentEnvironment = self.environmentsComboBox.currentText()
        self.environmentsComboBox.clear()
        self.packagesList.clear()
        
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        QApplication.processEvents()
        
        self.__populateEnvironments()
        
        index = self.environmentsComboBox.findText(
            currentEnvironment, Qt.MatchExactly | Qt.MatchCaseSensitive)
        if index != -1:
            self.environmentsComboBox.setCurrentIndex(index)
        
        QApplication.restoreOverrideCursor()
        self.__updateActionButtons()
    
    @pyqtSlot()
    def on_upgradeButton_clicked(self):
        """
        Private slot to upgrade selected packages of the selected environment.
        """
        packages = [itm.text(0) for itm in self.__selectedUpdateableItems()]
        if packages:
            prefix = self.environmentsComboBox.itemData(
                self.environmentsComboBox.currentIndex())
            ok = self.__conda.updatePackages(packages, prefix=prefix)
            if ok:
                self.on_refreshButton_clicked()
    
    @pyqtSlot()
    def on_upgradeAllButton_clicked(self):
        """
        Private slot to upgrade all packages of the selected environment.
        """
        prefix = self.environmentsComboBox.itemData(
            self.environmentsComboBox.currentIndex())
        ok = self.__conda.updateAllPackages(prefix=prefix)
        if ok:
            self.on_refreshButton_clicked()
    
    @pyqtSlot()
    def on_uninstallButton_clicked(self):
        """
        Private slot to remove selected packages of the selected environment.
        """
        packages = [itm.text(0) for itm in self.packagesList.selectedItems()]
        if packages:
            prefix = self.environmentsComboBox.itemData(
                self.environmentsComboBox.currentIndex())
            ok = self.__conda.uninstallPackages(packages, prefix=prefix)
            if ok:
                self.on_refreshButton_clicked()
    
    #######################################################################
    ## Search widget related methods below
    #######################################################################
    
    def __updateSearchActionButtons(self):
        """
        Private method to update the action button states of the search widget.
        """
        enable = len(self.searchResultList.selectedItems()) == 1
        self.installButton.setEnabled(
            enable and self.environmentsComboBox.currentIndex() > 0)
        self.showDetailsButton.setEnabled(
            enable and bool(self.searchResultList.selectedItems()[0].parent()))
    
    def __doSearch(self):
        """
        Private method to search for packages.
        """
        self.searchResultList.clear()
        pattern = self.searchEdit.text()
        if pattern:
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            QApplication.processEvents()
            
            if CondaInterface.condaVersion() >= (4, 4, 0):
                prefix = ""
            else:
                prefix = self.environmentsComboBox.itemData(
                    self.environmentsComboBox.currentIndex())
            ok, result = self.__conda.searchPackages(
                pattern,
                fullNameOnly=self.fullNameButton.isChecked(),
                packageSpec=self.packageSpecButton.isChecked(),
                platform=self.platformComboBox.currentText(),
                prefix=prefix,
            )
            
            if result:
                if ok:
                    self.searchResultList.setUpdatesEnabled(False)
                    for package in result:
                        itm = QTreeWidgetItem(self.searchResultList, [package])
                        itm.setExpanded(False)
                        for detail in result[package]:
                            version = detail["version"]
                            build = detail["build"]
                            if "subdir" in detail:
                                platform = detail["subdir"]
                            elif "platform" in detail:
                                platform = detail["platform"]
                            else:
                                platform = ""
                            citm = QTreeWidgetItem(
                                itm, ["", version, build, platform])
                            citm.setData(0, self.PackageDetailedDataRole,
                                         detail)
                
                    self.searchResultList.sortItems(0, Qt.AscendingOrder)
                    self.searchResultList.resizeColumnToContents(0)
                    self.searchResultList.setUpdatesEnabled(True)
                else:
                    QApplication.restoreOverrideCursor()
                    try:
                        message = result["message"]
                    except KeyError:
                        message = result["error"]
                    E5MessageBox.warning(
                        self,
                        self.tr("Conda Search Package Error"),
                        message)
            QApplication.restoreOverrideCursor()
    
    def __showDetails(self, item):
        """
        Private method to show a dialog with details about a package item.
        
        @param item reference to the package item
        @type QTreeWidgetItem
        """
        details = item.data(0, self.PackageDetailedDataRole)
        if details:
            from .CondaPackageDetailsWidget import CondaPackageDetailsDialog
            dlg = CondaPackageDetailsDialog(details, self)
            dlg.exec_()
    
    @pyqtSlot(str)
    def on_searchEdit_textChanged(self, txt):
        """
        Private slot handling changes of the entered search specification.
        
        @param txt current search entry
        @type str
        """
        self.searchButton.setEnabled(bool(txt))
    
    @pyqtSlot()
    def on_searchEdit_returnPressed(self):
        """
        Private slot handling the user pressing the Return button in the
        search edit.
        """
        self.__doSearch()
    
    @pyqtSlot()
    def on_searchButton_clicked(self):
        """
        Private slot handling the press of the search button.
        """
        self.__doSearch()
    
    @pyqtSlot()
    def on_installButton_clicked(self):
        """
        Private slot to install a selected package.
        """
        if len(self.searchResultList.selectedItems()) == 1:
            item = self.searchResultList.selectedItems()[0]
            if item.parent() is None:
                # it is just the package item
                package = item.text(0)
            else:
                # item with version and build
                package = "{0}={1}={2}".format(
                    item.parent().text(0),
                    item.text(1),
                    item.text(2),
                )
            
            prefix = self.environmentsComboBox.itemData(
                self.environmentsComboBox.currentIndex())
            ok = self.__conda.installPackages([package], prefix=prefix)
            if ok:
                self.on_refreshButton_clicked()
    
    @pyqtSlot()
    def on_showDetailsButton_clicked(self):
        """
        Private slot handling the 'Show Details' button.
        """
        item = self.searchResultList.selectedItems()[0]
        self.__showDetails(item)
    
    @pyqtSlot()
    def on_searchResultList_itemSelectionChanged(self):
        """
        Private slot handling a change of selected search results.
        """
        self.__updateSearchActionButtons()
    
    @pyqtSlot(QTreeWidgetItem)
    def on_searchResultList_itemExpanded(self, item):
        """
        Private slot handling the expansion of an item.
        
        @param item reference to the expanded item
        @type QTreeWidgetItem
        """
        for col in range(1, self.searchResultList.columnCount()):
            self.searchResultList.resizeColumnToContents(col)
    
    @pyqtSlot(QTreeWidgetItem, int)
    def on_searchResultList_itemDoubleClicked(self, item, column):
        """
        Private slot handling a double click of an item.
        
        @param item reference to the item that was double clicked
        @type QTreeWidgetItem
        @param column column of the double click
        @type int
        """
        if item.parent() is not None:
            self.__showDetails(item)
    
    @pyqtSlot(bool)
    def on_searchToggleButton_toggled(self, checked):
        """
        Private slot to togle the search widget.
        
        @param checked state of the search widget button
        @type bool
        """
        self.searchWidget.setVisible(checked)
        
        if checked:
            self.searchEdit.setFocus(Qt.OtherFocusReason)
            self.searchEdit.selectAll()
            
            self.__updateSearchActionButtons()
    
    #######################################################################
    ## Menu related methods below
    #######################################################################
    
    @pyqtSlot()
    def __aboutToShowCondaMenu(self):
        """
        Private slot to handle the conda menu about to be shown.
        """
        selectedEnvironment = self.environmentsComboBox.currentText()
        enable = selectedEnvironment not in [""]
        for act in self.__envActs:
            act.setEnabled(enable)
        
        self.__deleteEnvAct.setEnabled(
            selectedEnvironment not in ["", self.__conda.RootName])
    
    @pyqtSlot()
    def __aboutConda(self):
        """
        Private slot to show some information about the conda installation.
        """
        infoDict = self.__conda.getCondaInformation()
        
        from .CondaInfoDialog import CondaInfoDialog
        dlg = CondaInfoDialog(infoDict, self)
        dlg.exec_()
    
    @pyqtSlot()
    def __installPackages(self):
        """
        Private slot to install packages.
        """
        prefix = self.environmentsComboBox.itemData(
            self.environmentsComboBox.currentIndex())
        if prefix:
            ok, packageSpecs = E5TextInputDialog.getText(
                self,
                self.tr("Install Packages"),
                self.tr("Package Specifications (separated by whitespace):"),
                QLineEdit.Normal,
                minimumWidth=600)
            if ok and packageSpecs.strip():
                packages = [p.strip() for p in packageSpecs.split()]
                ok = self.__conda.installPackages(packages, prefix=prefix)
                if ok:
                    self.on_refreshButton_clicked()
    
    @pyqtSlot()
    def __installRequirements(self):
        """
        Private slot to install packages from requirements files.
        """
        prefix = self.environmentsComboBox.itemData(
            self.environmentsComboBox.currentIndex())
        if prefix:
            requirements = E5FileDialog.getOpenFileNames(
                self,
                self.tr("Install Packages"),
                "",
                self.tr("Text Files (*.txt);;All Files (*)"))
            if requirements:
                args = []
                for requirement in requirements:
                    args.extend(["--file", requirement])
                ok = self.__conda.installPackages(args, prefix=prefix)
                if ok:
                    self.on_refreshButton_clicked()
    
    @pyqtSlot()
    def __generateRequirements(self):
        """
        Private slot to generate a requirements file.
        """
        prefix = self.environmentsComboBox.itemData(
            self.environmentsComboBox.currentIndex())
        if prefix:
            env = self.environmentsComboBox.currentText()
            
            from .CondaExportDialog import CondaExportDialog
            
            self.__requirementsDialog = CondaExportDialog(
                self.__conda, env, prefix)
            self.__requirementsDialog.show()
            QApplication.processEvents()
            self.__requirementsDialog.start()
    
    @pyqtSlot()
    def __cloneEnvironment(self):
        """
        Private slot to clone a conda environment.
        """
        from .CondaNewEnvironmentDataDialog import (
            CondaNewEnvironmentDataDialog)
        
        prefix = self.environmentsComboBox.itemData(
            self.environmentsComboBox.currentIndex())
        if prefix:
            dlg = CondaNewEnvironmentDataDialog(self.tr("Clone Environment"),
                                                False, self)
            if dlg.exec_() == QDialog.Accepted:
                virtEnvName, envName, _ = dlg.getData()
                args = [
                    "--name",
                    envName.strip(),
                    "--clone",
                    prefix,
                ]
                ok, prefix, interpreter = self.__conda.createCondaEnvironment(
                    args)
                if ok:
                    e5App().getObject("VirtualEnvManager").addVirtualEnv(
                        virtEnvName, prefix, interpreter, isConda=True)
    
    @pyqtSlot()
    def __createEnvironment(self):
        """
        Private slot to create a conda environment from a requirements file.
        """
        from .CondaNewEnvironmentDataDialog import (
            CondaNewEnvironmentDataDialog)
        
        dlg = CondaNewEnvironmentDataDialog(self.tr("Create Environment"),
                                            True, self)
        if dlg.exec_() == QDialog.Accepted:
            virtEnvName, envName, requirements = dlg.getData()
            args = [
                "--name",
                envName.strip(),
                "--file",
                requirements,
            ]
            ok, prefix, interpreter = self.__conda.createCondaEnvironment(args)
            if ok:
                e5App().getObject("VirtualEnvManager").addVirtualEnv(
                    virtEnvName, prefix, interpreter, isConda=True)
    
    @pyqtSlot()
    def __deleteEnvironment(self):
        """
        Private slot to delete a conda environment.
        """
        envName = self.environmentsComboBox.currentText()
        ok = E5MessageBox.yesNo(
            self,
            self.tr("Delete Environment"),
            self.tr("""<p>Shall the environment <b>{0}</b> really be"""
                    """ deleted?</p>""").format(envName)
        )
        if ok:
            self.__conda.removeCondaEnvironment(name=envName)
    
    @pyqtSlot()
    def __editUserConfiguration(self):
        """
        Private slot to edit the user configuration.
        """
        from QScintilla.MiniEditor import MiniEditor
        
        cfgFile = CondaInterface.userConfiguration()
        if not cfgFile:
            return
        
        if not os.path.exists(cfgFile):
            self.__conda.writeDefaultConfiguration()
        
        # check, if the destination is writeable
        if not os.access(cfgFile, os.W_OK):
            E5MessageBox.critical(
                None,
                self.tr("Edit Configuration"),
                self.tr("""The configuration file "{0}" does not exist"""
                        """ or is not writable."""))
            return
        
        self.__editor = MiniEditor(cfgFile, "YAML")
        self.__editor.show()
    
    @pyqtSlot()
    def __condaConfigure(self):
        """
        Private slot to open the configuration page.
        """
        e5App().getObject("UserInterface").showPreferences("condaPage")
    
    @pyqtSlot()
    def on_recheckButton_clicked(self):
        """
        Private slot to re-check the availability of conda and adjust the
        interface if it became available.
        """
        if CondaInterface.isCondaAvailable():
            self.__initCondaInterface()
            
            self.notAvailableWidget.hide()
            self.baseWidget.show()