Sat, 26 Apr 2025 12:34:32 +0200
MicroPython
- Added a configuration option to disable the support for the no longer produced Pimoroni Pico Wireless Pack.
# -*- coding: utf-8 -*- # Copyright (c) 2018 - 2025 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a dialog to manage the list of defined virtual environments. """ import datetime import os from PyQt6.QtCore import Qt, pyqtSlot from PyQt6.QtWidgets import ( QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem, QVBoxLayout, QWidget, ) from eric7.EricGui import EricPixmapCache from eric7.EricWidgets import EricMessageBox from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog from eric7.EricWidgets.EricMainWindow import EricMainWindow from eric7.EricWidgets.EricPathPicker import EricPathPickerModes from eric7.SystemUtilities import OSUtilities from .Ui_VirtualenvManagerWidget import Ui_VirtualenvManagerWidget from .VirtualenvMeta import VirtualenvMetaData class VirtualenvManagerWidget(QWidget, Ui_VirtualenvManagerWidget): """ Class implementing a widget to manage the list of defined virtual environments. """ MetadataRole = Qt.ItemDataRole.UserRole + 1 def __init__(self, manager, parent=None): """ Constructor @param manager reference to the virtual environment manager @type VirtualenvManager @param parent reference to the parent widget @type QWidget """ super().__init__(parent) self.setupUi(self) self.layout().setContentsMargins(0, 3, 0, 0) self.__manager = manager self.refreshButton.setIcon(EricPixmapCache.getIcon("reload")) self.addButton.setIcon(EricPixmapCache.getIcon("plus")) self.newButton.setIcon(EricPixmapCache.getIcon("new")) self.searchNewButton.setIcon(EricPixmapCache.getIcon("question")) self.editButton.setIcon(EricPixmapCache.getIcon("edit")) self.upgradeButton.setIcon(EricPixmapCache.getIcon("upgrade")) self.removeButton.setIcon(EricPixmapCache.getIcon("minus")) self.removeAllButton.setIcon(EricPixmapCache.getIcon("minus_3")) self.deleteButton.setIcon(EricPixmapCache.getIcon("fileDelete")) self.deleteAllButton.setIcon(EricPixmapCache.getIcon("fileDeleteList")) self.saveButton.setIcon(EricPixmapCache.getIcon("fileSave")) baseDir = self.__manager.getVirtualEnvironmentsBaseDir() if not baseDir: baseDir = OSUtilities.getHomeDir() self.envBaseDirectoryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) self.envBaseDirectoryPicker.setWindowTitle(self.tr("Virtualenv Base Directory")) self.envBaseDirectoryPicker.setText(baseDir) self.__populateVenvList() self.__updateButtons() self.venvList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) self.__manager.virtualEnvironmentsListChanged.connect(self.__refresh) def __updateButtons(self): """ Private method to update the enabled state of the various buttons. """ selectedItemsCount = len(self.venvList.selectedItems()) topLevelItemCount = self.venvList.topLevelItemCount() deletableSelectedItemCount = 0 for itm in self.venvList.selectedItems(): if ( itm.text(0) != "<default>" and bool(itm.text(1)) and not itm.data(0, VirtualenvManagerWidget.MetadataRole).is_global and itm.data(0, VirtualenvManagerWidget.MetadataRole).environment_type != "remote" ): deletableSelectedItemCount += 1 deletableItemCount = 0 for index in range(topLevelItemCount): itm = self.venvList.topLevelItem(index) if ( itm.text(0) != "<default>" and bool(itm.text(1)) and itm.data(0, VirtualenvManagerWidget.MetadataRole).environment_type != "remote" ): deletableItemCount += 1 canBeRemoved = ( selectedItemsCount == 1 and self.venvList.selectedItems()[0].text(0) != "<default>" ) canAllBeRemoved = ( topLevelItemCount == 1 and self.venvList.topLevelItem(0).text(0) != "<default>" ) self.editButton.setEnabled(selectedItemsCount == 1) self.removeButton.setEnabled(selectedItemsCount > 1 or canBeRemoved) self.removeAllButton.setEnabled(topLevelItemCount > 1 or canAllBeRemoved) self.deleteButton.setEnabled(deletableSelectedItemCount) self.deleteAllButton.setEnabled(deletableItemCount) if selectedItemsCount == 1: venvName = self.venvList.selectedItems()[0].text(0) venvDirectory = self.__manager.getVirtualenvDirectory(venvName) self.upgradeButton.setEnabled( os.path.exists(os.path.join(venvDirectory, "pyvenv.cfg")) ) else: self.upgradeButton.setEnabled(False) @pyqtSlot() def on_refreshButton_clicked(self): """ Private slot to refresh the list of virtual environments. """ self.__manager.reloadSettings() self.__refresh() @pyqtSlot() def on_addButton_clicked(self): """ Private slot to add a new entry. """ from .VirtualenvAddEditDialog import VirtualenvAddEditDialog dlg = VirtualenvAddEditDialog( self.__manager, baseDir=self.envBaseDirectoryPicker.text(), parent=self ) if dlg.exec() == QDialog.DialogCode.Accepted: metadata = dlg.getMetaData() self.__manager.addVirtualEnv(metadata) @pyqtSlot() def on_newButton_clicked(self): """ Private slot to create a new virtual environment. """ self.__manager.createVirtualEnv(baseDir=self.envBaseDirectoryPicker.text()) @pyqtSlot() def on_searchNewButton_clicked(self): """ Private slot to search for new (not yet registered) Python interpreters. """ potentialInterpreters = self.__manager.searchUnregisteredInterpreters() if not bool(potentialInterpreters): EricMessageBox.information( self, self.tr("Search Virtual Environments"), self.tr("""No unregistered virtual environments were found."""), ) return baseDir = self.__manager.getVirtualEnvironmentsBaseDir() if not baseDir: baseDir = OSUtilities.getHomeDir() selectionList = [] for interpreter in potentialInterpreters: if not interpreter.startswith(baseDir): realpath = os.path.realpath(interpreter) if realpath != interpreter: # interpreter is a link selectionList.append( ( self.tr("{0}\n(=> {1})").format(interpreter, realpath), interpreter, ) ) continue selectionList.append((interpreter, interpreter)) dlg = EricListSelectionDialog( sorted(selectionList), title=self.tr("Search Virtual Environments"), message=self.tr( "Select the interpreters to create environment entries for:" ), checkBoxSelection=True, emptySelectionOk=True, showSelectAll=True, parent=self, ) if dlg.exec() == QDialog.DialogCode.Accepted: selectedInterpreters = [env[1] for env in dlg.getSelection()] nameTemplate = "Environment #{0} added " + datetime.datetime.now().strftime( # noqa: M-305 "%Y-%m-%d %H:%M" ) for interpreter in selectedInterpreters: metadata = VirtualenvMetaData( name=nameTemplate.format( selectedInterpreters.index(interpreter) + 1 ), path=( os.path.abspath(os.path.join(interpreter, "..", "..")) if interpreter.startswith(baseDir) else "" ), interpreter=interpreter, is_global=not interpreter.startswith(baseDir), ) self.__manager.addVirtualEnv(metadata) @pyqtSlot() def on_editButton_clicked(self): """ Private slot to edit the selected entry. """ from .VirtualenvAddEditDialog import VirtualenvAddEditDialog selectedItem = self.venvList.selectedItems()[0] oldVenvName = selectedItem.text(0) dlg = VirtualenvAddEditDialog( self.__manager, selectedItem.data(0, VirtualenvManagerWidget.MetadataRole), baseDir=self.envBaseDirectoryPicker.text(), parent=self, ) if dlg.exec() == QDialog.DialogCode.Accepted: metadata = dlg.getMetaData() if metadata.name != oldVenvName: self.__manager.renameVirtualEnv(oldVenvName, metadata) else: self.__manager.setVirtualEnv(metadata) @pyqtSlot() def on_upgradeButton_clicked(self): """ Private slot to upgrade a virtual environment. """ self.__manager.upgradeVirtualEnv(self.venvList.selectedItems()[0].text(0)) @pyqtSlot() def on_removeButton_clicked(self): """ Private slot to remove all selected entries from the list but keep their directories. """ selectedVenvs = [] for itm in self.venvList.selectedItems(): selectedVenvs.append(itm.text(0)) if selectedVenvs: self.__manager.removeVirtualEnvs(selectedVenvs) @pyqtSlot() def on_removeAllButton_clicked(self): """ Private slot to remove all entries from the list but keep their directories. """ venvNames = [] for index in range(self.venvList.topLevelItemCount()): itm = self.venvList.topLevelItem(index) venvNames.append(itm.text(0)) if venvNames: self.__manager.removeVirtualEnvs(venvNames) @pyqtSlot() def on_deleteButton_clicked(self): """ Private slot to delete all selected entries from the list and disk. """ selectedVenvs = [] for itm in self.venvList.selectedItems(): selectedVenvs.append(itm.text(0)) if selectedVenvs: self.__manager.deleteVirtualEnvs(selectedVenvs) @pyqtSlot() def on_deleteAllButton_clicked(self): """ Private slot to delete all entries from the list and disk. """ venvNames = [] for index in range(self.venvList.topLevelItemCount()): itm = self.venvList.topLevelItem(index) venvNames.append(itm.text(0)) if venvNames: self.__manager.deleteVirtualEnvs(venvNames) @pyqtSlot() def on_venvList_itemSelectionChanged(self): """ Private slot handling a change of the selected items. """ self.__updateButtons() selectedItems = self.venvList.selectedItems() if len(selectedItems) == 1: envMetaData = selectedItems[0].data(0, VirtualenvManagerWidget.MetadataRole) self.descriptionEdit.setPlainText(envMetaData.description) self.interpreterEdit.setText(envMetaData.interpreter) else: self.descriptionEdit.clear() self.interpreterEdit.clear() @pyqtSlot() def __refresh(self): """ Private slot to refresh the list of shown items. """ # 1. remember selected entries selectedVenvs = [] for itm in self.venvList.selectedItems(): selectedVenvs.append(itm.text(0)) # 2. clear the list self.venvList.clear() # 3. re-populate the list self.__populateVenvList() # 4. re-establish selection for venvName in selectedVenvs: itms = self.venvList.findItems(venvName, Qt.MatchFlag.MatchExactly, 0) if itms: itms[0].setSelected(True) def __populateVenvList(self): """ Private method to populate the list of virtual environments. """ typeNamesMapping = { n[0]: n[1] for n in self.__manager.getEnvironmentTypeNames() } for environment in self.__manager.getEnvironmentEntries(): try: environmentType = typeNamesMapping[environment.environment_type] except KeyError: # Environment type was not registered or has been deactivated. environmentType = environment.environment_type itm = QTreeWidgetItem( self.venvList, [environment.name, environmentType, environment.path], ) itm.setData(0, VirtualenvManagerWidget.MetadataRole, environment) self.__resizeSections() def __resizeSections(self): """ Private method to resize the sections of the environment list to their contents. """ self.venvList.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) self.venvList.header().setStretchLastSection(True) @pyqtSlot() def on_saveButton_clicked(self): """ Private slot to save the base directory name. """ baseDir = self.envBaseDirectoryPicker.text() self.__manager.setVirtualEnvironmentsBaseDir(baseDir) class VirtualenvManagerDialog(QDialog): """ Class implementing the virtual environments manager dialog variant. """ def __init__(self, manager, parent=None): """ Constructor @param manager reference to the virtual environment manager @type VirtualenvManager @param parent reference to the parent widget @type QWidget """ super().__init__(parent) self.setSizeGripEnabled(True) self.__layout = QVBoxLayout(self) self.setLayout(self.__layout) self.cw = VirtualenvManagerWidget(manager, self) self.__layout.addWidget(self.cw) self.buttonBox = QDialogButtonBox( QDialogButtonBox.StandardButton.Close, Qt.Orientation.Horizontal, self ) self.__layout.addWidget(self.buttonBox) self.resize(700, 500) self.setWindowTitle(self.tr("Manage Virtual Environments")) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) class VirtualenvManagerWindow(EricMainWindow): """ Main window class for the standalone virtual environments manager. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ from eric7.CondaInterface.Conda import Conda from eric7.EricWidgets.EricApplication import ericApp from eric7.PluginManager.PluginManager import PluginManager from eric7.VirtualEnv.VirtualenvManager import VirtualenvManager super().__init__(parent) self.__virtualenvManager = VirtualenvManager(self) ericApp().registerObject("VirtualEnvManager", self.__virtualenvManager) self.__pluginManager = PluginManager(self) ericApp().registerObject("PluginManager", self.__pluginManager) self.__pluginManager.activateTypedPlugins("virtualenv") self.__conda = Conda(self) self.__centralWidget = QWidget(self) self.__layout = QVBoxLayout(self.__centralWidget) self.__centralWidget.setLayout(self.__layout) self.__virtualenvManagerWidget = VirtualenvManagerWidget( self.__virtualenvManager, self.__centralWidget ) self.__layout.addWidget(self.__virtualenvManagerWidget) self.__buttonBox = QDialogButtonBox( QDialogButtonBox.StandardButton.Close, Qt.Orientation.Horizontal, self ) self.__layout.addWidget(self.__buttonBox) self.setCentralWidget(self.__centralWidget) self.resize(700, 600) self.setWindowTitle(self.tr("Manage Virtual Environments")) self.__buttonBox.accepted.connect(self.close) self.__buttonBox.rejected.connect(self.close)