Sun, 10 Feb 2019 19:46:34 +0100
Conda: started implementing the conda menu functionality
- About Conda
- Edit User Configuration
- Configure
--- a/CondaInterface/Conda.py Sun Feb 10 17:24:10 2019 +0100 +++ b/CondaInterface/Conda.py Sun Feb 10 19:46:34 2019 +0100 @@ -609,7 +609,7 @@ def updateConda(self): """ - Private method to update conda itself. + Public method to update conda itself. """ args = [ "update", @@ -624,3 +624,49 @@ ok, _ = dlg.getResult() return ok + + def writeDefaultConfiguration(self): + """ + Public method to create a conda configuration with default values. + """ + args = [ + "config", + "--write-default", + "--quiet" + ] + + exe = Preferences.getConda("CondaExecutable") + if not exe: + exe = "conda" + + proc = QProcess() + proc.start(exe, args) + proc.waitForStarted(15000) + proc.waitForFinished(30000) + + def getCondaInformation(self): + """ + Public method to get a dictionary containing information about conda. + + @return dictionary containing information about conda + @rtype dict + """ + exe = Preferences.getConda("CondaExecutable") + if not exe: + exe = "conda" + + infoDict = {} + + proc = QProcess() + proc.start(exe, ["info", "--json"]) + if proc.waitForStarted(15000): + if proc.waitForFinished(15000): + output = str(proc.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + 'replace').strip() + try: + infoDict = json.loads(output) + except Exception: + infoDict = {} + + return infoDict
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CondaInterface/CondaInfoDialog.py Sun Feb 10 19:46:34 2019 +0100 @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the conda information dialog. +""" + +from __future__ import unicode_literals + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QDialog + +from .Ui_CondaInfoDialog import Ui_CondaInfoDialog + +import UI.PixmapCache + + +class CondaInfoDialog(QDialog, Ui_CondaInfoDialog): + """ + Class implementing the conda information dialog. + """ + def __init__(self, infoDict, parent=None): + """ + Constructor + + @param infoDict dictionary containing the information to be shown + @type dict + @param parent reference to the parent widget + @type QWidget + """ + super(CondaInfoDialog, self).__init__(parent) + self.setupUi(self) + self.setWindowFlags(Qt.Window) + + self.iconLabel.setPixmap( + UI.PixmapCache.getPixmap("miniconda48.png")) + + if "conda_version" in infoDict: + self.condaVersionLabel.setText( + infoDict["conda_version"]) + if "conda_build_version" in infoDict: + self.condaBuildVersionLabel.setText( + infoDict["conda_build_version"]) + if "python_version" in infoDict: + self.pythonVersionLabel.setText( + infoDict["python_version"]) + if "active_prefix" in infoDict and "active_prefix_name" in infoDict: + if infoDict["active_prefix_name"] and infoDict["active_prefix"]: + self.activeEnvironmentEdit.setText( + "{0} ({1})".format(infoDict["active_prefix_name"], + infoDict["active_prefix"])) + elif infoDict["active_prefix"]: + self.activeEnvironmentEdit.setText( + infoDict["active_prefix"]) + elif infoDict["active_prefix_name"]: + self.activeEnvironmentEdit.setText( + infoDict["active_prefix_name"]) + else: + self.activeEnvironmentEdit.setText( + self.tr("None")) + if "rc_path" in infoDict: + self.userConfigEdit.setText( + infoDict["rc_path"]) + if "user_rc_path" in infoDict: + # overwrite with more specific info + self.userConfigEdit.setText( + infoDict["user_rc_path"]) + if "sys_rc_path" in infoDict: + self.systemConfigEdit.setText( + infoDict["sys_rc_path"]) + if "config_files" in infoDict: + self.configurationsEdit.setPlainText( + "\n".join(infoDict["config_files"])) + if "root_prefix" in infoDict: + if "root_writable" in infoDict and infoDict["root_writable"]: + self.baseEnvironmentEdit.setText( + self.tr("{0} (writable)").format(infoDict["root_prefix"])) + else: + self.baseEnvironmentEdit.setText( + infoDict["root_prefix"]) + if "channels" in infoDict: + self.channelsEdit.setPlainText( + "\n".join(infoDict["channels"])) + if "pkgs_dirs" in infoDict: + self.cachesEdit.setPlainText( + "\n".join(infoDict["pkgs_dirs"])) + if "envs_dirs" in infoDict: + self.envDirsEdit.setPlainText( + "\n".join(infoDict["envs_dirs"])) + if "platform" in infoDict: + self.platformLabel.setText( + infoDict["platform"]) + if "user_agent" in infoDict: + self.useragentEdit.setText( + infoDict["user_agent"]) + if "UID" in infoDict and "GID" in infoDict: + self.uidGidLabel.setText( + "{0}:{1}".format(infoDict["UID"], infoDict["GID"])) + if "netrc_file" in infoDict: + if infoDict["netrc_file"]: + self.netrcEdit.setText( + infoDict["netrc_file"]) + else: + self.netrcEdit.setText( + self.tr("None")) + if "offline" in infoDict: + self.offlineCheckBox.setChecked( + infoDict["offline"])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CondaInterface/CondaInfoDialog.ui Sun Feb 10 19:46:34 2019 +0100 @@ -0,0 +1,291 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CondaInfoDialog</class> + <widget class="QDialog" name="CondaInfoDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>650</width> + <height>893</height> + </rect> + </property> + <property name="windowTitle"> + <string>Conda Information</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="iconLabel"> + <property name="text"> + <string>icon</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string><h2>Conda Information</h2></string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>conda Version:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="condaVersionLabel"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>conda-build Version:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="condaBuildVersionLabel"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>python Version:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="pythonVersionLabel"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Active Environment:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="activeEnvironmentEdit"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>User Configuration:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="userConfigEdit"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>System Configuration:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="systemConfigEdit"/> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Populated Configurations:</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QPlainTextEdit" name="configurationsEdit"/> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Base Environment:</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QLineEdit" name="baseEnvironmentEdit"/> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="label_9"> + <property name="text"> + <string>Channel URLs:</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QPlainTextEdit" name="channelsEdit"/> + </item> + <item row="9" column="0"> + <widget class="QLabel" name="label_10"> + <property name="text"> + <string>Package Cache:</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="9" column="1"> + <widget class="QPlainTextEdit" name="cachesEdit"/> + </item> + <item row="10" column="0"> + <widget class="QLabel" name="label_11"> + <property name="text"> + <string>Environment Directories:</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="10" column="1"> + <widget class="QPlainTextEdit" name="envDirsEdit"/> + </item> + <item row="11" column="0"> + <widget class="QLabel" name="label_12"> + <property name="text"> + <string>Platform:</string> + </property> + </widget> + </item> + <item row="11" column="1"> + <widget class="QLabel" name="platformLabel"/> + </item> + <item row="12" column="0"> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>User-Agent:</string> + </property> + </widget> + </item> + <item row="12" column="1"> + <widget class="QLineEdit" name="useragentEdit"/> + </item> + <item row="13" column="0"> + <widget class="QLabel" name="label_14"> + <property name="text"> + <string>UID:GID:</string> + </property> + </widget> + </item> + <item row="13" column="1"> + <widget class="QLabel" name="uidGidLabel"/> + </item> + <item row="14" column="0"> + <widget class="QLabel" name="label_15"> + <property name="text"> + <string>netrc File:</string> + </property> + </widget> + </item> + <item row="14" column="1"> + <widget class="QLineEdit" name="netrcEdit"/> + </item> + <item row="15" column="0"> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>Offline Mode:</string> + </property> + </widget> + </item> + <item row="15" column="1"> + <widget class="QCheckBox" name="offlineCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>CondaInfoDialog</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>CondaInfoDialog</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/CondaPackagesWidget.py Sun Feb 10 17:24:10 2019 +0100 +++ b/CondaInterface/CondaPackagesWidget.py Sun Feb 10 19:46:34 2019 +0100 @@ -9,12 +9,15 @@ from __future__ import unicode_literals +import os + 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 E5Gui.E5Application import e5App from .Ui_CondaPackagesWidget import Ui_CondaPackagesWidget @@ -93,10 +96,51 @@ menu button. """ self.__condaMenu = QMenu(self) - # TODO: implement Conda menu + self.__envActs = [] + + self.__cleanMenu = QMenu(self.tr("Clean"), self) + self.__cleanMenu.addAction( + self.tr("All"), lambda: self.__cleanMenuAction("all")) + self.__cleanMenu.addAction( + self.tr("Cache"), lambda: self.__cleanMenuAction("index-cache")) + self.__cleanMenu.addAction( + self.tr("Lock Files"), + lambda: self.__cleanMenuAction("lock")) + self.__cleanMenu.addAction( + self.tr("Packages"), lambda: self.__cleanMenuAction("packages")) + self.__cleanMenu.addAction( + self.tr("Tarballs"), lambda: self.__cleanMenuAction("tarballs")) + self.__cleanMenu.addAction( + self.tr("Temporary Files"), + lambda: self.__cleanMenuAction("temp_files")) + + 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 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.__envActs.append(self.__condaMenu.addAction( + self.tr("Clone Environment"), self.__cloneEnvironment)) + self.__condaMenu.addSeparator() + self.__envActs.append(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) @@ -154,16 +198,19 @@ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() + # 1. populate with installed packages 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) + self.packagesList.setUpdatesEnabled(True) + QApplication.processEvents() # 2. update with update information + self.packagesList.setUpdatesEnabled(False) updateablePackages = \ self.__conda.getUpdateablePackages(prefix=prefix) for package, version, build in updateablePackages: @@ -186,7 +233,8 @@ )) self.packagesList.sortItems(0, Qt.AscendingOrder) - self.packagesList.resizeColumnToContents(0) + for col in range(self.packagesList.columnCount()): + self.packagesList.resizeColumnToContents(col) self.packagesList.setUpdatesEnabled(True) QApplication.restoreOverrideCursor() @@ -448,9 +496,115 @@ 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) + + @pyqtSlot() + def __aboutConda(self): + """ + Private slot to + """ + # TODO: implement this menu function + # conda info --all + infoDict = self.__conda.getCondaInformation() + + from .CondaInfoDialog import CondaInfoDialog + dlg = CondaInfoDialog(infoDict, self) + dlg.exec_() + + @pyqtSlot() + def __installPackages(self): + """ + Private slot to + """ + # TODO: implement this menu function + # conda install ... + + @pyqtSlot() + def __installLocalPackage(self): + """ + Private slot to + """ + # TODO: implement this menu function + # conda install --use-local + + @pyqtSlot() + def __installRequirements(self): + """ + Private slot to + """ + # TODO: implement this menu function + # conda install --file FILE1 --file FILE2 ... + + @pyqtSlot() + def __generateRequirements(self): + """ + Private slot to + """ + # TODO: implement this menu function + # conda list --export + + @pyqtSlot() + def __cloneEnvironment(self): + """ + Private slot to + """ + # TODO: implement this menu function + # conda create --clone ENV + + def __cleanMenuAction(self, cleanAction): + """ + Private method to + """ + # 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') + + @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")
--- a/CondaInterface/__init__.py Sun Feb 10 17:24:10 2019 +0100 +++ b/CondaInterface/__init__.py Sun Feb 10 19:46:34 2019 +0100 @@ -22,6 +22,7 @@ __CondaVersion = tuple() __CondaVersionStr = "" __CondaRootPrefix = "" +__CondaUserConfig = "" __initialized = False @@ -30,7 +31,8 @@ """ Private module function to (re-)initialize the conda interface. """ - global __CondaVersionStr, __CondaVersion, __CondaRootPrefix, __initialized + global __CondaVersionStr, __CondaVersion, __CondaRootPrefix, \ + __CondaUserConfig, __initialized if not __initialized: exe = Preferences.getConda("CondaExecutable") @@ -61,6 +63,7 @@ int(i) for i in __CondaVersionStr.split(".") ) __CondaRootPrefix = jsonDict["root_prefix"] + __CondaUserConfig = jsonDict["rc_path"] __initialized = True @@ -98,6 +101,17 @@ return __CondaRootPrefix +def userConfiguration(): + """ + Module function to get the path of the user configuration file. + + @return path of the user configuration file + @rtype str + """ + __initializeCondaInterface() + return __CondaUserConfig + + def resetInterface(): """ Module function to reset the conda interface.
--- a/eric6.e4p Sun Feb 10 17:24:10 2019 +0100 +++ b/eric6.e4p Sun Feb 10 19:46:34 2019 +0100 @@ -18,6 +18,7 @@ <Sources> <Source>CondaInterface/Conda.py</Source> <Source>CondaInterface/CondaExecDialog.py</Source> + <Source>CondaInterface/CondaInfoDialog.py</Source> <Source>CondaInterface/CondaPackageDetailsWidget.py</Source> <Source>CondaInterface/CondaPackagesWidget.py</Source> <Source>CondaInterface/__init__.py</Source> @@ -1720,6 +1721,7 @@ </Sources> <Forms> <Form>CondaInterface/CondaExecDialog.ui</Form> + <Form>CondaInterface/CondaInfoDialog.ui</Form> <Form>CondaInterface/CondaPackageDetailsWidget.ui</Form> <Form>CondaInterface/CondaPackagesWidget.ui</Form> <Form>Cooperation/ChatWidget.ui</Form>