src/eric7/MicroPython/Devices/CircuitPythonUpdater/RequirementsDialog.py

branch
eric7
changeset 9756
9854647c8c5c
parent 9741
901caff48307
child 10439
21c28b0f9e41
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/MicroPython/Devices/CircuitPythonUpdater/RequirementsDialog.py	Mon Feb 13 17:49:52 2023 +0100
@@ -0,0 +1,265 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to generate a requirements file.
+"""
+
+import os
+
+import circup
+
+from PyQt6.QtCore import pyqtSlot
+from PyQt6.QtGui import QGuiApplication
+from PyQt6.QtWidgets import QAbstractButton, QDialog, QDialogButtonBox
+
+from eric7.EricWidgets import EricFileDialog, EricMessageBox
+from eric7.EricWidgets.EricApplication import ericApp
+from eric7.EricWidgets.EricPathPicker import EricPathPickerModes
+from eric7.SystemUtilities import FileSystemUtilities
+
+from .Ui_RequirementsDialog import Ui_RequirementsDialog
+
+
+class RequirementsDialog(QDialog, Ui_RequirementsDialog):
+    """
+    Class implementing a dialog to generate a requirements file.
+    """
+
+    def __init__(self, devicePath, parent=None):
+        """
+        Constructor
+
+        @param devicePath path to the connected board
+        @type str
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+        self.__title = self.tr("Generate Requirements")
+
+        self.__refreshButton = self.buttonBox.addButton(
+            self.tr("&Refresh"), QDialogButtonBox.ButtonRole.ActionRole
+        )
+
+        self.requirementsFilePicker.setMode(EricPathPickerModes.SAVE_FILE_MODE)
+        self.requirementsFilePicker.setFilters(
+            self.tr("Text Files (*.txt);;All Files (*)")
+        )
+
+        self.__devicePath = devicePath
+
+        self.__requirementsEdited = False
+        self.__requirementsAvailable = False
+
+        self.__generateRequirements()
+
+    def __updateButtons(self):
+        """
+        Private method to set the state of the various buttons.
+        """
+        self.saveButton.setEnabled(
+            self.__requirementsAvailable
+            and bool(self.requirementsFilePicker.text())
+            and os.path.isabs(self.requirementsFilePicker.text())
+        )
+        self.saveToButton.setEnabled(self.__requirementsAvailable)
+        self.copyButton.setEnabled(self.__requirementsAvailable)
+
+        aw = ericApp().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)
+
+    @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.StandardButton.Close):
+            self.close()
+        elif button == self.__refreshButton:
+            self.__generateRequirements()
+
+    def __generateRequirements(self):
+        """
+        Private slot to generate the requirements specifiers list.
+        """
+        ok = (
+            EricMessageBox.yesNo(
+                self,
+                self.__title,
+                self.tr(
+                    """The requirements were changed. Do you want"""
+                    """ to overwrite these changes?"""
+                ),
+            )
+            if self.__requirementsEdited
+            else True
+        )
+        if ok:
+            self.requirementsEdit.clear()
+            self.__requirementsAvailable = False
+
+            if not bool(self.requirementsFilePicker.text()):
+                self.requirementsFilePicker.setText("requirements.txt")
+
+            fileName = FileSystemUtilities.toNativeSeparators(
+                self.requirementsFilePicker.text()
+            )
+            if fileName and not os.path.isabs(fileName):
+                fileName = ""
+
+            modules = circup.find_modules(self.__devicePath, circup.get_bundles_list())
+            specifiers = []
+            if modules:
+                for module in modules:
+                    specifiers.append(
+                        "{0}=={1}".format(module.name, module.device_version)
+                    )
+
+            if specifiers:
+                self.requirementsEdit.setPlainText("\n".join(specifiers) + "\n")
+                self.__requirementsAvailable = True
+            else:
+                self.requirementsEdit.setPlainText(
+                    self.tr("No package specifiers generated.")
+                )
+
+            self.__updateButtons()
+
+            self.__requirementsEdited = 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 = EricMessageBox.warning(
+                self,
+                self.__title,
+                self.tr(
+                    """The file <b>{0}</b> already exists. Do you want"""
+                    """ to overwrite it?"""
+                ).format(fileName),
+            )
+            if not ok:
+                return
+
+        txt = self.requirementsEdit.toPlainText()
+        try:
+            with open(fileName, "w") as f:
+                f.write(txt)
+        except OSError as err:
+            EricMessageBox.critical(
+                self,
+                self.__title,
+                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 = EricFileDialog.getSaveFileNameAndFilter(
+            self,
+            self.__title,
+            os.path.expanduser("~"),
+            self.tr("Text Files (*.txt);;All Files (*)"),
+            None,
+            EricFileDialog.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 = QGuiApplication.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 = ericApp().getObject("ViewManager").activeWindow()
+        if aw:
+            aw.beginUndoAction()
+            aw.insert(self.requirementsEdit.toPlainText())
+            aw.endUndoAction()
+
+    @pyqtSlot()
+    def on_replaceSelectionButton_clicked(self):
+        """
+        Private slot to replace the selected text of the current editor
+        with the requirements text.
+        """
+        aw = ericApp().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 = ericApp().getObject("ViewManager").activeWindow()
+        if aw:
+            aw.setText(self.requirementsEdit.toPlainText())

eric ide

mercurial