eric7/Project/PropertiesDialog.py

Thu, 09 Jun 2022 16:13:18 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 09 Jun 2022 16:13:18 +0200
branch
eric7
changeset 9141
7085ece52151
parent 9129
44f8c8c3d37e
child 9175
21e2be5f0b41
permissions
-rw-r--r--

Completed the SBOM generation support by adding a metadata dialog.

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

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

"""
Module implementing the project properties dialog.
"""

import contextlib
import os

from PyQt6.QtCore import QDir, pyqtSlot
from PyQt6.QtWidgets import QDialog, QDialogButtonBox

from EricWidgets import EricMessageBox
from EricWidgets.EricApplication import ericApp
from EricWidgets.EricPathPicker import EricPathPickerModes

from .Ui_PropertiesDialog import Ui_PropertiesDialog

from QScintilla.DocstringGenerator import getSupportedDocstringTypes

from Testing.Interfaces import FrameworkNames

import Utilities
import Preferences
import UI.PixmapCache


class PropertiesDialog(QDialog, Ui_PropertiesDialog):
    """
    Class implementing the project properties dialog.
    """
    def __init__(self, project, new=True, parent=None, name=None):
        """
        Constructor
        
        @param project reference to the project object
        @param new flag indicating the generation of a new project
        @param parent parent widget of this dialog (QWidget)
        @param name name of this dialog (string)
        """
        super().__init__(parent)
        if name:
            self.setObjectName(name)
        self.setupUi(self)
        
        self.dirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE)
        self.mainscriptPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
        
        self.makeButton.setIcon(UI.PixmapCache.getIcon("makefile"))
        
        self.docstringStyleComboBox.addItem(self.tr("None"), "")
        for docstringType, docstringStyle in sorted(
            getSupportedDocstringTypes()
        ):
            self.docstringStyleComboBox.addItem(docstringStyle, docstringType)
        
        self.project = project
        self.newProject = new
        self.transPropertiesDlg = None
        self.spellPropertiesDlg = None
        self.makePropertiesDlg = None
        
        patterns = []
        for pattern, filetype in self.project.pdata["FILETYPES"].items():
            if filetype == "SOURCES":
                patterns.append(pattern)
        filters = self.tr("Source Files ({0});;All Files (*)"
                          ).format(" ".join(sorted(patterns)))
        self.mainscriptPicker.setFilters(filters)
        
        self.languageComboBox.addItems(project.getProgrammingLanguages())
        
        projectTypes = []
        for projectTypeItem in project.getProjectTypes().items():
            projectTypes.append((projectTypeItem[1], projectTypeItem[0]))
        self.projectTypeComboBox.clear()
        for projectType in sorted(projectTypes):
            self.projectTypeComboBox.addItem(
                projectType[0], projectType[1])
        
        ipath = (
            Preferences.getMultiProject("Workspace") or
            Utilities.getHomeDir()
        )
        self.__initPaths = [
            Utilities.fromNativeSeparators(ipath),
            Utilities.fromNativeSeparators(ipath) + "/",
        ]
        
        self.licenseComboBox.lineEdit().setClearButtonEnabled(True)
        self.__populateLicenseComboBox()
        
        if not new:
            name = os.path.splitext(self.project.pfile)[0]
            self.nameEdit.setText(os.path.basename(name))
            self.languageComboBox.setCurrentIndex(
                self.languageComboBox.findText(
                    self.project.pdata["PROGLANGUAGE"]))
            self.mixedLanguageCheckBox.setChecked(
                self.project.pdata["MIXEDLANGUAGE"])
            curIndex = self.projectTypeComboBox.findData(
                self.project.pdata["PROJECTTYPE"])
            if curIndex == -1:
                curIndex = self.projectTypeComboBox.findData("PyQt6")
            self.projectTypeComboBox.setCurrentIndex(curIndex)
            self.dirPicker.setText(self.project.ppath)
            self.versionEdit.setText(self.project.pdata["VERSION"])
            self.mainscriptPicker.setText(self.project.pdata["MAINSCRIPT"])
            self.authorEdit.setText(self.project.pdata["AUTHOR"])
            self.emailEdit.setText(self.project.pdata["EMAIL"])
            self.descriptionEdit.setPlainText(
                self.project.pdata["DESCRIPTION"])
            self.eolComboBox.setCurrentIndex(self.project.pdata["EOL"])
            self.vcsLabel.show()
            if self.project.vcs is not None:
                vcsSystemsDict = (
                    ericApp().getObject("PluginManager")
                    .getPluginDisplayStrings("version_control")
                )
                try:
                    vcsSystemDisplay = vcsSystemsDict[
                        self.project.pdata["VCS"]]
                except KeyError:
                    vcsSystemDisplay = "None"
                self.vcsLabel.setText(
                    self.tr(
                        "The project is version controlled by <b>{0}</b>.")
                    .format(vcsSystemDisplay))
                self.vcsInfoButton.show()
            else:
                self.vcsLabel.setText(
                    self.tr("The project is not version controlled."))
                self.vcsInfoButton.hide()
            self.vcsCheckBox.hide()
            self.makeCheckBox.setChecked(
                self.project.pdata["MAKEPARAMS"]["MakeEnabled"])
            cindex = self.docstringStyleComboBox.findData(
                self.project.pdata["DOCSTRING"])
            self.docstringStyleComboBox.setCurrentIndex(cindex)
            with contextlib.suppress(KeyError):
                cindex = self.testingFrameworkComboBox.findData(
                    self.project.pdata["TESTING_FRAMEWORK"])
                self.testingFrameworkComboBox.setCurrentIndex(cindex)
            with contextlib.suppress(KeyError):
                self.licenseComboBox.setCurrentText(
                    self.project.pdata["LICENSE"])
        else:
            self.languageComboBox.setCurrentText("Python3")
            self.projectTypeComboBox.setCurrentIndex(
                self.projectTypeComboBox.findData("PyQt6"))
            self.dirPicker.setText(self.__initPaths[0])
            self.versionEdit.setText('0.1')
            self.vcsLabel.hide()
            self.vcsInfoButton.hide()
            if not self.project.vcsSoftwareAvailable():
                self.vcsCheckBox.hide()
        
        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(
            bool(self.dirPicker.text()) and
            self.dirPicker.text() not in self.__initPaths)
    
    def __populateLicenseComboBox(self):
        """
        Private method to populate the license selector with the list of trove
        license types.
        
        Note: The trove licanese list file was created from querying
        "https://pypi.org/pypi?%3Aaction=list_classifiers".
        """
        filename = os.path.join(
            os.path.dirname(__file__), "..", "data",
            "trove_license_classifiers.txt")
        try:
            with open(filename, "r") as f:
                lines = f.readlines()
        except OSError as err:
            EricMessageBox.warning(
                self,
                self.tr("Reading Trove License Classifiers"),
                self.tr("""<p>The Trove License Classifiers file <b>{0}</b>"""
                        """ could not be read.</p><p>Reason: {1}</p>""")
                .format(filename, str(err)))
            return
        
        self.licenseComboBox.addItem("")
        self.licenseComboBox.addItems(sorted(
            line.split("::")[-1].strip()
            for line in lines
            if line.startswith("License ")      # play it safe
        ))
    
    @pyqtSlot(str)
    def on_languageComboBox_currentTextChanged(self, language):
        """
        Private slot handling the selection of a programming language.
        
        @param language text of the current item
        @type str
        """
        curProjectType = self.getProjectType()
        
        self.projectTypeComboBox.clear()
        for projectType in sorted(
            self.project.getProjectTypes(language).items(),
            key=lambda k: k[1]
        ):
            self.projectTypeComboBox.addItem(
                projectType[1], projectType[0])
        
        index = self.projectTypeComboBox.findData(curProjectType)
        if index == -1:
            index = 0
        self.projectTypeComboBox.setCurrentIndex(index)
        
        curTestingFramework = self.testingFrameworkComboBox.currentText()
        self.testingFrameworkComboBox.clear()
        self.testingFrameworkComboBox.addItem(self.tr("None"), "")
        with contextlib.suppress(KeyError):
            for framework in sorted(FrameworkNames[language]):
                self.testingFrameworkComboBox.addItem(framework, framework)
        self.testingFrameworkComboBox.setCurrentText(curTestingFramework)
    
    @pyqtSlot(str)
    def on_dirPicker_textChanged(self, txt):
        """
        Private slot to handle a change of the project directory.
        
        @param txt name of the project directory (string)
        """
        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(
            bool(txt) and
            Utilities.fromNativeSeparators(txt) not in self.__initPaths)
    
    @pyqtSlot()
    def on_spellPropertiesButton_clicked(self):
        """
        Private slot to display the spelling properties dialog.
        """
        if self.spellPropertiesDlg is None:
            from .SpellingPropertiesDialog import SpellingPropertiesDialog
            self.spellPropertiesDlg = SpellingPropertiesDialog(
                self.project, self.newProject, self)
        res = self.spellPropertiesDlg.exec()
        if res == QDialog.DialogCode.Rejected:
            self.spellPropertiesDlg.initDialog()  # reset the dialogs contents
    
    @pyqtSlot()
    def on_transPropertiesButton_clicked(self):
        """
        Private slot to display the translations properties dialog.
        """
        if self.transPropertiesDlg is None:
            from .TranslationPropertiesDialog import (
                TranslationPropertiesDialog
            )
            self.transPropertiesDlg = TranslationPropertiesDialog(
                self.project, self.newProject, self)
        else:
            self.transPropertiesDlg.initFilters()
        res = self.transPropertiesDlg.exec()
        if res == QDialog.DialogCode.Rejected:
            self.transPropertiesDlg.initDialog()  # reset the dialogs contents
    
    @pyqtSlot()
    def on_makeButton_clicked(self):
        """
        Private slot to display the make properties dialog.
        """
        if self.makePropertiesDlg is None:
            from .MakePropertiesDialog import MakePropertiesDialog
            self.makePropertiesDlg = MakePropertiesDialog(
                self.project, self.newProject, self)
        res = self.makePropertiesDlg.exec()
        if res == QDialog.DialogCode.Rejected:
            self.makePropertiesDlg.initDialog()
    
    @pyqtSlot(str)
    def on_mainscriptPicker_pathSelected(self, script):
        """
        Private slot to check the selected main script name.
        
        @param script name of the main script
        @type str
        """
        if script:
            ppath = self.dirPicker.text()
            if ppath:
                ppath = QDir(ppath).absolutePath() + QDir.separator()
                script = script.replace(ppath, "")
            self.mainscriptPicker.setText(script)
    
    @pyqtSlot()
    def on_mainscriptPicker_aboutToShowPathPickerDialog(self):
        """
        Private slot to perform actions before the main script selection dialog
        is shown.
        """
        path = self.dirPicker.text()
        if not path:
            path = QDir.currentPath()
        self.mainscriptPicker.setDefaultDirectory(path)
    
    @pyqtSlot()
    def on_vcsInfoButton_clicked(self):
        """
        Private slot to display a vcs information dialog.
        """
        if self.project.vcs is None:
            return
            
        from VCS.RepositoryInfoDialog import VcsRepositoryInfoDialog
        info = self.project.vcs.vcsRepositoryInfos(self.project.ppath)
        dlg = VcsRepositoryInfoDialog(self, info)
        dlg.exec()
    
    def getProjectType(self):
        """
        Public method to get the selected project type.
        
        @return selected UI type (string)
        """
        return self.projectTypeComboBox.itemData(
            self.projectTypeComboBox.currentIndex())
    
    def getPPath(self):
        """
        Public method to get the project path.
        
        @return data of the project directory edit (string)
        """
        return os.path.abspath(self.dirPicker.text())
    
    def storeData(self):
        """
        Public method to store the entered/modified data.
        """
        self.project.ppath = os.path.abspath(self.dirPicker.text())
        fn = self.nameEdit.text()
        if fn:
            self.project.name = fn
            fn = "{0}.epj".format(fn)
            self.project.pfile = os.path.join(self.project.ppath, fn)
        else:
            self.project.pfile = ""
        self.project.pdata["VERSION"] = self.versionEdit.text()
        fn = self.mainscriptPicker.text()
        if fn:
            fn = self.project.getRelativePath(fn)
            self.project.pdata["MAINSCRIPT"] = fn
            self.project.translationsRoot = os.path.splitext(fn)[0]
        else:
            self.project.pdata["MAINSCRIPT"] = ""
            self.project.translationsRoot = ""
        self.project.pdata["AUTHOR"] = self.authorEdit.text()
        self.project.pdata["EMAIL"] = self.emailEdit.text()
        self.project.pdata["DESCRIPTION"] = self.descriptionEdit.toPlainText()
        self.project.pdata["PROGLANGUAGE"] = (
            self.languageComboBox.currentText()
        )
        self.project.pdata["MIXEDLANGUAGE"] = (
            self.mixedLanguageCheckBox.isChecked()
        )
        projectType = self.getProjectType()
        if projectType is not None:
            self.project.pdata["PROJECTTYPE"] = projectType
        self.project.pdata["EOL"] = self.eolComboBox.currentIndex()
        
        self.project.vcsRequested = self.vcsCheckBox.isChecked()
        
        if self.spellPropertiesDlg is not None:
            self.spellPropertiesDlg.storeData()
        
        if self.transPropertiesDlg is not None:
            self.transPropertiesDlg.storeData()
        
        self.project.pdata["MAKEPARAMS"]["MakeEnabled"] = (
            self.makeCheckBox.isChecked()
        )
        if self.makePropertiesDlg is not None:
            self.makePropertiesDlg.storeData()
        
        self.project.pdata["DOCSTRING"] = (
            self.docstringStyleComboBox.currentData()
        )
        
        self.project.pdata["TESTING_FRAMEWORK"] = (
            self.testingFrameworkComboBox.currentData()
        )
        
        self.project.pdata["LICENSE"] = self.licenseComboBox.currentText()

eric ide

mercurial