CondaInterface/CondaPackagesWidget.py

Sat, 09 Feb 2019 18:28:33 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 09 Feb 2019 18:28:33 +0100
branch
conda
changeset 6715
f1bf1985434e
parent 6712
91fa67e8ebbc
child 6724
ca89c7d94c94
permissions
-rw-r--r--

Conda, CondaPackagesWidget: implemented install functionality and strted with Conda menu.

# -*- coding: utf-8 -*-

# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the conda packages management widget.
"""

from __future__ import unicode_literals

from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QWidget, QToolButton, QMenu, QTreeWidgetItem, \
    QApplication

from E5Gui import E5MessageBox

from .Ui_CondaPackagesWidget import Ui_CondaPackagesWidget

import UI.PixmapCache

import CondaInterface


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
        
        self.condaMenuButton.setObjectName(
            "navigation_supermenu_button")
        self.condaMenuButton.setIcon(UI.PixmapCache.getIcon("superMenu.png"))
        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.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find.png"))
        
        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)
        # TODO: implement Conda menu
        self.__condaMenu.addAction(
            self.tr("Update Conda"), self.__conda.updateConda)
        self.__condaMenu.addSeparator()
        
        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))
            QApplication.processEvents()
            
            self.packagesList.setUpdatesEnabled(False)
            # 1. populate with installed packages
            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)
            
            # 2. update with update information
            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)
            self.packagesList.resizeColumnToContents(0)
            self.packagesList.setUpdatesEnabled(True)
            QApplication.restoreOverrideCursor()
        
        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()
    
    @pyqtSlot()
    def __aboutToShowCondaMenu(self):
        """
        Private slot to handle the conda menu about to be shown.
        """
        selectedEnvironment = self.environmentsComboBox.currentText()

eric ide

mercurial