Conda: continued implementing the conda menu functionality conda

Mon, 11 Feb 2019 19:57:53 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 11 Feb 2019 19:57:53 +0100
branch
conda
changeset 6728
ba077788a882
parent 6727
e235150f016c
child 6729
fea66bcabb33

Conda: continued implementing the conda menu functionality
- Install ...
- Generate Requirements
- Clone Environment
- Delete Environment
- Clean ...

CondaInterface/Conda.py file | annotate | diff | comparison | revisions
CondaInterface/CondaExportDialog.py file | annotate | diff | comparison | revisions
CondaInterface/CondaExportDialog.ui file | annotate | diff | comparison | revisions
CondaInterface/CondaInfoDialog.ui file | annotate | diff | comparison | revisions
CondaInterface/CondaPackageDetailsWidget.py file | annotate | diff | comparison | revisions
CondaInterface/CondaPackageDetailsWidget.ui file | annotate | diff | comparison | revisions
CondaInterface/CondaPackagesWidget.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- a/CondaInterface/Conda.py	Mon Feb 11 19:33:12 2019 +0100
+++ b/CondaInterface/Conda.py	Mon Feb 11 19:57:53 2019 +0100
@@ -24,7 +24,7 @@
 import Globals
 import Preferences
 
-from . import rootPrefix
+from . import rootPrefix, condaVersion
 from .CondaExecDialog import CondaExecDialog
 
 
@@ -456,8 +456,6 @@
         if not name and not prefix:
             raise RuntimeError("One of 'name' or 'prefix' must be given.")
         
-        # TODO: not implemented yet
-        
         if packages:
             args = [
                 "install",
@@ -517,8 +515,9 @@
                     "remove",
                     "--json",
                     "--yes",
-                    "--prune",
                 ]
+                if condaVersion() >= (4, 4, 0):
+                    args.append("--prune",)
                 if name:
                     args.extend(["--name", name])
                 elif prefix:
@@ -610,6 +609,9 @@
     def updateConda(self):
         """
         Public method to update conda itself.
+        
+        @return flag indicating success
+        @rtype bool
         """
         args = [
             "update",
@@ -630,7 +632,7 @@
         Public method to create a conda configuration with default values.
         """
         args = [
-            "config", 
+            "config",
             "--write-default",
             "--quiet"
         ]
@@ -660,7 +662,7 @@
         proc = QProcess()
         proc.start(exe, ["info", "--json"])
         if proc.waitForStarted(15000):
-            if proc.waitForFinished(15000):
+            if proc.waitForFinished(30000):
                 output = str(proc.readAllStandardOutput(),
                              Preferences.getSystem("IOEncoding"),
                              'replace').strip()
@@ -670,3 +672,63 @@
                     infoDict = {}
         
         return infoDict
+    
+    def runProcess(self, args):
+        """
+        Public method to execute the conda with the given arguments.
+        
+        The conda executable is called with the given arguments and
+        waited for its end.
+        
+        @param args list of command line arguments
+        @type list of str
+        @return tuple containing a flag indicating success and the output
+            of the process
+        @rtype tuple of (bool, str)
+        """
+        exe = Preferences.getConda("CondaExecutable")
+        if not exe:
+            exe = "conda"
+        
+        process = QProcess()
+        process.start(exe, args)
+        procStarted = process.waitForStarted(15000)
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished:
+                if process.exitCode() == 0:
+                    output = str(process.readAllStandardOutput(),
+                                 Preferences.getSystem("IOEncoding"),
+                                 'replace').strip()
+                    return True, output
+                else:
+                    return (False,
+                            self.tr("conda exited with an error ({0}).")
+                            .format(process.exitCode()))
+            else:
+                process.terminate()
+                process.waitForFinished(2000)
+                process.kill()
+                process.waitForFinished(3000)
+                return False, self.tr("conda did not finish within"
+                                      " 30 seconds.")
+        
+        return False, self.tr("conda could not be started.")
+    
+    def cleanConda(self, cleanAction):
+        """
+        Public method to update conda itself.
+        
+        @param cleanAction cleaning action to be performed (must be one of
+            the command line parameters without '--')
+        @type str
+        """
+        args = [
+            "clean",
+            "--yes",
+            "--{0}".format(cleanAction),
+        ]
+        
+        dlg = CondaExecDialog("clean", self.__ui)
+        dlg.start(args)
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CondaInterface/CondaExportDialog.py	Mon Feb 11 19:57:53 2019 +0100
@@ -0,0 +1,274 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to generate a requirements file for conda.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \
+    QApplication
+
+from E5Gui import E5MessageBox, E5FileDialog
+from E5Gui.E5PathPicker import E5PathPickerModes
+from E5Gui.E5Application import e5App
+
+from .Ui_CondaExportDialog import Ui_CondaExportDialog
+
+
+class CondaExportDialog(QDialog, Ui_CondaExportDialog):
+    """
+    Class implementing a dialog to generate a requirements file for conda.
+    """
+    def __init__(self, conda, envName, envPrefix, parent=None):
+        """
+        Constructor
+        
+        @param conda reference to the master object
+        @type Conda
+        @param envName name of the environment to create the requirements
+            file from
+        @type str
+        @param envPrefix prefix of the environment to create the requirements
+            file from
+        @type str
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(CondaExportDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__refreshButton = self.buttonBox.addButton(
+            self.tr("&Refresh"), QDialogButtonBox.ActionRole)
+        
+        self.requirementsFilePicker.setMode(E5PathPickerModes.SaveFileMode)
+        self.requirementsFilePicker.setFilters(
+            self.tr("Text Files (*.txt);;All Files (*)"))
+        
+        self.__conda = conda
+        self.__prefix = envPrefix
+        
+        self.environmentLabel.setText("<b>{0}</b>".format(envName))
+        
+        self.__requirementsEdited = False
+        self.__requirementsAvailable = False
+        
+        self.__updateButtons()
+    
+    def closeEvent(self, e):
+        """
+        Protected slot implementing a close event handler.
+        
+        @param e close event
+        @type QCloseEvent
+        """
+        QApplication.restoreOverrideCursor()
+        e.accept()
+    
+    @pyqtSlot(str)
+    def on_requirementsFilePicker_textChanged(self, txt):
+        """
+        Private slot handling a change of the requirements file name.
+        
+        @param txt name of the requirements file
+        @type str
+        """
+        self.__updateButtons()
+    
+    @pyqtSlot()
+    def on_requirementsEdit_textChanged(self):
+        """
+        Private slot handling changes of the requirements text.
+        """
+        self.__requirementsEdited = True
+    
+    @pyqtSlot(QAbstractButton)
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot called by a button of the button box clicked.
+        
+        @param button button that was clicked
+        @type QAbstractButton
+        """
+        if button == self.buttonBox.button(QDialogButtonBox.Close):
+            self.close()
+        elif button == self.__refreshButton:
+            self.__refresh()
+    
+    def __refresh(self):
+        """
+        Private slot to refresh the displayed list.
+        """
+        if self.__requirementsEdited:
+            ok = E5MessageBox.yesNo(
+                self,
+                self.tr("Generate Requirements"),
+                self.tr("""The requirements were changed. Do you want"""
+                        """ to overwrite these changes?"""))
+        else:
+            ok = True
+        if ok:
+            self.start()
+    
+    def start(self):
+        """
+        Public method to start the command.
+        """
+        self.requirementsEdit.clear()
+        self.__requirementsAvailable = False
+        
+        args = [
+            "list",
+            "--export",
+            "--prefix",
+            self.__prefix,
+        ]
+        
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        success, output = self.__conda.runProcess(args)
+        
+        if success:
+            self.requirementsEdit.setPlainText(output)
+            self.__requirementsAvailable = True
+        else:
+            self.requirementsEdit.setPlainText(
+                self.tr("No output generated by conda."))
+        
+        QApplication.restoreOverrideCursor()
+        self.__updateButtons()
+        
+        self.__requirementsEdited = False
+    
+    def __updateButtons(self):
+        """
+        Private method to set the state of the various buttons.
+        """
+        self.saveButton.setEnabled(
+            self.__requirementsAvailable and
+            bool(self.requirementsFilePicker.text())
+        )
+        self.saveToButton.setEnabled(self.__requirementsAvailable)
+        self.copyButton.setEnabled(self.__requirementsAvailable)
+        
+        aw = e5App().getObject("ViewManager").activeWindow()
+        if aw and self.__requirementsAvailable:
+            self.insertButton.setEnabled(True)
+            self.replaceAllButton.setEnabled(True)
+            self.replaceSelectionButton.setEnabled(
+                aw.hasSelectedText())
+        else:
+            self.insertButton.setEnabled(False)
+            self.replaceAllButton.setEnabled(False)
+            self.replaceSelectionButton.setEnabled(False)
+    
+    def __writeToFile(self, fileName):
+        """
+        Private method to write the requirements text to a file.
+        
+        @param fileName name of the file to write to
+        @type str
+        """
+        if os.path.exists(fileName):
+            ok = E5MessageBox.warning(
+                self,
+                self.tr("Generate Requirements"),
+                self.tr("""The file <b>{0}</b> already exists. Do you want"""
+                        """ to overwrite it?""").format(fileName))
+            if not ok:
+                return
+        
+        try:
+            f = open(fileName, "w")
+            f.write(self.requirementsEdit.toPlainText())
+            f.close()
+        except (OSError, IOError) as err:
+            E5MessageBox.critical(
+                self,
+                self.tr("Generate Requirements"),
+                self.tr("""<p>The requirements could not be written"""
+                        """ to <b>{0}</b>.</p><p>Reason: {1}</p>""")
+                .format(fileName, str(err)))
+    
+    @pyqtSlot()
+    def on_saveButton_clicked(self):
+        """
+        Private slot to save the requirements text to the requirements file.
+        """
+        fileName = self.requirementsFilePicker.text()
+        self.__writeToFile(fileName)
+    
+    @pyqtSlot()
+    def on_saveToButton_clicked(self):
+        """
+        Private slot to write the requirements text to a new file.
+        """
+        fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
+            self,
+            self.tr("Generate Requirements"),
+            os.path.expanduser("~"),
+            self.tr("Text Files (*.txt);;All Files (*)"),
+            None,
+            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)
+        )
+        if fileName:
+            ext = os.path.splitext(fileName)[1]
+            if not ext:
+                ex = selectedFilter.split("(*")[1].split(")")[0]
+                if ex:
+                    fileName += ex
+            self.__writeToFile(fileName)
+    
+    @pyqtSlot()
+    def on_copyButton_clicked(self):
+        """
+        Private slot to copy the requirements text to the clipboard.
+        """
+        txt = self.requirementsEdit.toPlainText()
+        cb = QApplication.clipboard()
+        cb.setText(txt)
+    
+    @pyqtSlot()
+    def on_insertButton_clicked(self):
+        """
+        Private slot to insert the requirements text at the cursor position
+        of the current editor.
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        if aw:
+            aw.beginUndoAction()
+            cline, cindex = aw.getCursorPosition()
+            aw.insertAt(self.requirementsEdit.toPlainText(), cline, cindex)
+            aw.endUndoAction()
+    
+    @pyqtSlot()
+    def on_replaceSelectionButton_clicked(self):
+        """
+        Private slot to replace the selected text of the current editor
+        with the requirements text.
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        if aw:
+            aw.beginUndoAction()
+            aw.replaceSelectedText(self.requirementsEdit.toPlainText())
+            aw.endUndoAction()
+    
+    @pyqtSlot()
+    def on_replaceAllButton_clicked(self):
+        """
+        Private slot to replace the text of the current editor with the
+        requirements text.
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        if aw:
+            aw.setText(self.requirementsEdit.toPlainText())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CondaInterface/CondaExportDialog.ui	Mon Feb 11 19:57:53 2019 +0100
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CondaExportDialog</class>
+ <widget class="QDialog" name="CondaExportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>600</width>
+    <height>550</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Generate Requirements</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Conda Environment:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="environmentLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Requirements File:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="E5PathPicker" name="requirementsFilePicker" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="focusPolicy">
+        <enum>Qt::StrongFocus</enum>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="1">
+      <widget class="QPushButton" name="saveButton">
+       <property name="toolTip">
+        <string>Press to save to the requirements file</string>
+       </property>
+       <property name="text">
+        <string>Save</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="saveToButton">
+       <property name="toolTip">
+        <string>Save to a new file</string>
+       </property>
+       <property name="text">
+        <string>Save To</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QPushButton" name="copyButton">
+       <property name="toolTip">
+        <string>Copy the requirements text to the clipboard</string>
+       </property>
+       <property name="text">
+        <string>Copy</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QPushButton" name="insertButton">
+       <property name="toolTip">
+        <string>Insert the requirements text at the cursor position</string>
+       </property>
+       <property name="text">
+        <string>Insert</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QPushButton" name="replaceSelectionButton">
+       <property name="toolTip">
+        <string>Replace the current selection with the requirements text</string>
+       </property>
+       <property name="text">
+        <string>Replace Selection</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QPushButton" name="replaceAllButton">
+       <property name="toolTip">
+        <string>Replace all text with the requirements text</string>
+       </property>
+       <property name="text">
+        <string>Replace All</string>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="1">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="0" column="0" rowspan="7">
+      <widget class="QPlainTextEdit" name="requirementsEdit">
+       <property name="tabChangesFocus">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>requirementsFilePicker</tabstop>
+  <tabstop>requirementsEdit</tabstop>
+  <tabstop>saveButton</tabstop>
+  <tabstop>saveToButton</tabstop>
+  <tabstop>copyButton</tabstop>
+  <tabstop>insertButton</tabstop>
+  <tabstop>replaceSelectionButton</tabstop>
+  <tabstop>replaceAllButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CondaExportDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CondaExportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/CondaInterface/CondaInfoDialog.ui	Mon Feb 11 19:33:12 2019 +0100
+++ b/CondaInterface/CondaInfoDialog.ui	Mon Feb 11 19:57:53 2019 +0100
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>650</width>
-    <height>893</height>
+    <height>650</height>
    </rect>
   </property>
   <property name="windowTitle">
--- a/CondaInterface/CondaPackageDetailsWidget.py	Mon Feb 11 19:33:12 2019 +0100
+++ b/CondaInterface/CondaPackageDetailsWidget.py	Mon Feb 11 19:57:53 2019 +0100
@@ -60,7 +60,7 @@
             dt = QDateTime.fromMSecsSinceEpoch(details["timestamp"], Qt.UTC)
             self.timestampLabel.setText(dt.toString("yyyy-MM-dd hh:mm:ss t"))
         
-        self.resize(600, 500)
+        self.resize(600, 450)
 
 
 class CondaPackageDetailsDialog(QDialog):
--- a/CondaInterface/CondaPackageDetailsWidget.ui	Mon Feb 11 19:33:12 2019 +0100
+++ b/CondaInterface/CondaPackageDetailsWidget.ui	Mon Feb 11 19:57:53 2019 +0100
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>612</width>
-    <height>492</height>
+    <width>600</width>
+    <height>450</height>
    </rect>
   </property>
   <layout class="QGridLayout" name="gridLayout">
--- a/CondaInterface/CondaPackagesWidget.py	Mon Feb 11 19:33:12 2019 +0100
+++ b/CondaInterface/CondaPackagesWidget.py	Mon Feb 11 19:57:53 2019 +0100
@@ -14,9 +14,9 @@
 from PyQt5.QtCore import pyqtSlot, Qt
 from PyQt5.QtGui import QCursor
 from PyQt5.QtWidgets import QWidget, QToolButton, QMenu, QTreeWidgetItem, \
-    QApplication
+    QApplication, QLineEdit
 
-from E5Gui import E5MessageBox
+from E5Gui import E5FileDialog, E5MessageBox, E5TextInputDialog
 from E5Gui.E5Application import e5App
 
 from .Ui_CondaPackagesWidget import Ui_CondaPackagesWidget
@@ -100,19 +100,16 @@
         
         self.__cleanMenu = QMenu(self.tr("Clean"), self)
         self.__cleanMenu.addAction(
-            self.tr("All"), lambda: self.__cleanMenuAction("all"))
+            self.tr("All"), lambda: self.__conda.cleanConda("all"))
         self.__cleanMenu.addAction(
-            self.tr("Cache"), lambda: self.__cleanMenuAction("index-cache"))
+            self.tr("Cache"), lambda: self.__conda.cleanConda("index-cache"))
         self.__cleanMenu.addAction(
             self.tr("Lock Files"),
-            lambda: self.__cleanMenuAction("lock"))
-        self.__cleanMenu.addAction(
-            self.tr("Packages"), lambda: self.__cleanMenuAction("packages"))
+            lambda: self.__conda.cleanConda("lock"))
         self.__cleanMenu.addAction(
-            self.tr("Tarballs"), lambda: self.__cleanMenuAction("tarballs"))
+            self.tr("Packages"), lambda: self.__conda.cleanConda("packages"))
         self.__cleanMenu.addAction(
-            self.tr("Temporary Files"),
-            lambda: self.__cleanMenuAction("temp_files"))
+            self.tr("Tarballs"), lambda: self.__conda.cleanConda("tarballs"))
         
         self.__condaMenu.addAction(
             self.tr("About Conda..."), self.__aboutConda)
@@ -123,17 +120,20 @@
         self.__envActs.append(self.__condaMenu.addAction(
             self.tr("Install Packages"), self.__installPackages))
         self.__envActs.append(self.__condaMenu.addAction(
-            self.tr("Install Local Packages"), self.__installLocalPackage))
-        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.__envActs.append(self.__condaMenu.addMenu(self.__cleanMenu))
+        self.__condaMenu.addMenu(self.__cleanMenu)
         self.__condaMenu.addSeparator()
         self.__condaMenu.addAction(
             self.tr("Edit User Configuration..."),
@@ -509,14 +509,15 @@
         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
+        Private slot to show some information about the conda installation.
         """
-        # TODO: implement this menu function
-        # conda info --all
         infoDict = self.__conda.getCondaInformation()
         
         from .CondaInfoDialog import CondaInfoDialog
@@ -526,55 +527,106 @@
     @pyqtSlot()
     def __installPackages(self):
         """
-        Private slot to
+        Private slot to install packages.
         """
-        # TODO: implement this menu function
-        # conda install ...
-    
-    @pyqtSlot()
-    def __installLocalPackage(self):
-        """
-        Private slot to
-        """
-        # TODO: implement this menu function
-        # conda install --use-local
+        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
+        Private slot to install packages from requirements files.
         """
-        # TODO: implement this menu function
-        # conda install --file FILE1 --file FILE2 ...
+        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
+        Private slot to generate a requirements file.
         """
-        # TODO: implement this menu function
-        # conda list --export
+        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
-        """
-        # TODO: implement this menu function
-        # conda create --clone ENV
-    
-    def __cleanMenuAction(self, cleanAction):
-        """
-        Private method to
+        Private slot to clone a conda environment.
         """
-        # TODO: implement this menu function
-        # conda clean
-        #   --all                   (act = 'all')
-        #   --index-cache           (act = 'index-cache')
-        #   --lock                  (act = 'lock')
-        #   --packages              (act = 'packages')
-        #   --tarballs              (act = 'tarballs')
-        #   -c prefix1 prefix2 ...  (act = 'temp_files')
+        prefix = self.environmentsComboBox.itemData(
+            self.environmentsComboBox.currentIndex())
+        if prefix:
+            ok, envName = E5TextInputDialog.getText(
+                self,
+                self.tr("Clone Environment"),
+                self.tr("Enter name for the cloned environment:"),
+                QLineEdit.Normal)
+            if ok and envName.strip():
+                args = [
+                    "--name",
+                    envName.strip(),
+                    "--clone",
+                    prefix,
+                ]
+                self.__conda.createCondaEnvironment(args)
+        # TODO: add code to register the cloned env with the virt env manager
+    
+    @pyqtSlot()
+    def __createEnvironment(self):
+        """
+        Private slot to create a conda environment from a requirements file.
+        """
+        # TODO: implement this
+    
+    @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>Shal the environment <b>{0}</b> really be"""
+                    """ deleted?</p>""").format(envName)
+        )
+        if ok:
+            self.__conda.removeCondaEnvironment(name=envName)
     
     @pyqtSlot()
     def __editUserConfiguration(self):
--- a/eric6.e4p	Mon Feb 11 19:33:12 2019 +0100
+++ b/eric6.e4p	Mon Feb 11 19:57:53 2019 +0100
@@ -18,6 +18,7 @@
   <Sources>
     <Source>CondaInterface/Conda.py</Source>
     <Source>CondaInterface/CondaExecDialog.py</Source>
+    <Source>CondaInterface/CondaExportDialog.py</Source>
     <Source>CondaInterface/CondaInfoDialog.py</Source>
     <Source>CondaInterface/CondaPackageDetailsWidget.py</Source>
     <Source>CondaInterface/CondaPackagesWidget.py</Source>
@@ -1721,6 +1722,7 @@
   </Sources>
   <Forms>
     <Form>CondaInterface/CondaExecDialog.ui</Form>
+    <Form>CondaInterface/CondaExportDialog.ui</Form>
     <Form>CondaInterface/CondaInfoDialog.ui</Form>
     <Form>CondaInterface/CondaPackageDetailsWidget.ui</Form>
     <Form>CondaInterface/CondaPackagesWidget.ui</Form>
@@ -2257,14 +2259,14 @@
   </Resources>
   <Others>
     <Other>.hgignore</Other>
-    <Other>APIs/Python/zope-2.10.7.api</Other>
-    <Other>APIs/Python/zope-2.11.2.api</Other>
-    <Other>APIs/Python/zope-3.3.1.api</Other>
     <Other>APIs/Python3/PyQt4.bas</Other>
     <Other>APIs/Python3/PyQt5.bas</Other>
     <Other>APIs/Python3/QScintilla2.bas</Other>
     <Other>APIs/Python3/eric6.api</Other>
     <Other>APIs/Python3/eric6.bas</Other>
+    <Other>APIs/Python/zope-2.10.7.api</Other>
+    <Other>APIs/Python/zope-2.11.2.api</Other>
+    <Other>APIs/Python/zope-3.3.1.api</Other>
     <Other>APIs/QSS/qss.api</Other>
     <Other>APIs/Ruby/Ruby-1.8.7.api</Other>
     <Other>APIs/Ruby/Ruby-1.8.7.bas</Other>

eric ide

mercurial