diff -r 24c847222774 -r 67295777d9fe src/eric7/Project/Project.py --- a/src/eric7/Project/Project.py Mon Oct 31 14:07:57 2022 +0100 +++ b/src/eric7/Project/Project.py Wed Nov 30 09:19:51 2022 +0100 @@ -7,6 +7,7 @@ Module implementing the project management functionality. """ +import collections import contextlib import copy import fnmatch @@ -18,46 +19,48 @@ import time import zipfile +from PyQt6.Qsci import QsciScintilla from PyQt6.QtCore import ( - pyqtSlot, + QByteArray, + QCryptographicHash, QFile, - pyqtSignal, - QCryptographicHash, QIODevice, - QByteArray, QObject, QProcess, + pyqtSignal, + pyqtSlot, ) -from PyQt6.QtGui import QKeySequence, QAction -from PyQt6.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, QMenu -from PyQt6.Qsci import QsciScintilla - +from PyQt6.QtGui import QAction, QKeySequence +from PyQt6.QtWidgets import QDialog, QInputDialog, QLineEdit, QMenu, QToolBar + +from eric7 import Globals, Preferences, Utilities +from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction +from eric7.CodeFormatting.BlackUtilities import aboutBlack +from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction +from eric7.CodeFormatting.IsortUtilities import aboutIsort +from eric7.EricGui import EricPixmapCache +from eric7.EricGui.EricAction import EricAction, createActionGroup +from eric7.EricGui.EricOverrideCursor import EricOverrideCursor, EricOverridenCursor +from eric7.EricWidgets import EricFileDialog, EricMessageBox from eric7.EricWidgets.EricApplication import ericApp -from eric7.EricWidgets import EricFileDialog, EricMessageBox from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog from eric7.EricWidgets.EricProgressDialog import EricProgressDialog -from eric7.EricGui.EricOverrideCursor import EricOverrideCursor, EricOverridenCursor - +from eric7.EricXML.DebuggerPropertiesReader import DebuggerPropertiesReader +from eric7.EricXML.ProjectReader import ProjectReader +from eric7.EricXML.SessionReader import SessionReader +from eric7.EricXML.TasksReader import TasksReader +from eric7.EricXML.UserProjectReader import UserProjectReader from eric7.Globals import recentNameProject - -from eric7.EricGui import EricPixmapCache +from eric7.Sessions.SessionFile import SessionFile +from eric7.Tasks.TasksFile import TasksFile from eric7.UI import Config from eric7.UI.NotificationWidget import NotificationTypes -from eric7.EricGui.EricAction import EricAction, createActionGroup - -from eric7 import Globals, Preferences, Utilities - +from .DebuggerPropertiesFile import DebuggerPropertiesFile +from .FileCategoryRepositoryItem import FileCategoryRepositoryItem +from .ProjectBrowserModel import ProjectBrowserModel from .ProjectFile import ProjectFile from .UserProjectFile import UserProjectFile -from .DebuggerPropertiesFile import DebuggerPropertiesFile - -from eric7.Sessions.SessionFile import SessionFile - -from eric7.Tasks.TasksFile import TasksFile - -from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction -from eric7.CodeFormatting.BlackUtilities import aboutBlack class Project(QObject): @@ -65,26 +68,11 @@ Class implementing the project management functionality. @signal dirty(bool) emitted when the dirty state changes - @signal projectLanguageAdded(str) emitted after a new language was added + @signal projectFileAdded(str, str) emitted after a new file was added + @signal projectFileRemoved(str, str) emitted after a file of the project was removed + @signal projectFileCompiled(str, str) emitted after a form was compiled @signal projectLanguageAddedByCode(str) emitted after a new language was added. The language code is sent by this signal. - @signal projectLanguageRemoved(str) emitted after a language was removed - @signal projectFormAdded(str) emitted after a new form was added - @signal projectFormRemoved(str) emitted after a form was removed - @signal projectFormCompiled(str) emitted after a form was compiled - @signal projectSourceAdded(str) emitted after a new source file was added - @signal projectSourceRemoved(str) emitted after a source was removed - @signal projectInterfaceAdded(str) emitted after a new IDL file was added - @signal projectInterfaceRemoved(str) emitted after a IDL file was removed - @signal projectProtocolAdded(str) emitted after a new proto file was added - @signal projectProtocolRemoved(str) emitted after a proto file was removed - @signal projectResourceAdded(str) emitted after a new resource file was - added - @signal projectResourceRemoved(str) emitted after a resource was removed - @signal projectOthersAdded(str) emitted after a file or directory was added - to the OTHERS project data area - @signal projectOthersRemoved(str) emitted after a file was removed from the - OTHERS project data area @signal projectAboutToBeCreated() emitted just before the project will be created @signal newProjectHooks() emitted after a new project was generated but @@ -129,25 +117,15 @@ a QProcess on stdout @signal appendStderr(str) emitted after something was received from a QProcess on stderr + @signal processChangedProjectFiles() emitted to indicate, that changed project files + should be processed """ dirty = pyqtSignal(bool) - projectLanguageAdded = pyqtSignal(str) + projectFileAdded = pyqtSignal(str, str) + projectFileRemoved = pyqtSignal(str, str) + projectFileCompiled = pyqtSignal(str, str) projectLanguageAddedByCode = pyqtSignal(str) - projectLanguageRemoved = pyqtSignal(str) - projectFormAdded = pyqtSignal(str) - projectFormRemoved = pyqtSignal(str) - projectFormCompiled = pyqtSignal(str) - projectSourceAdded = pyqtSignal(str) - projectSourceRemoved = pyqtSignal(str) - projectInterfaceAdded = pyqtSignal(str) - projectInterfaceRemoved = pyqtSignal(str) - projectProtocolAdded = pyqtSignal(str) - projectProtocolRemoved = pyqtSignal(str) - projectResourceAdded = pyqtSignal(str) - projectResourceRemoved = pyqtSignal(str) - projectOthersAdded = pyqtSignal(str) - projectOthersRemoved = pyqtSignal(str) projectAboutToBeCreated = pyqtSignal() newProjectHooks = pyqtSignal() newProject = pyqtSignal() @@ -174,6 +152,7 @@ projectChanged = pyqtSignal() appendStdout = pyqtSignal(str) appendStderr = pyqtSignal(str) + processChangedProjectFiles = pyqtSignal() eols = [os.linesep, "\n", "\r", "\r\n"] @@ -204,6 +183,11 @@ ), } + self.__fileCategoriesRepository = {} + # This dictionary will be populated by the various project browsers with + # classes of type 'FileCategoryRepositoryItem' using the 'addFileCategory() + # and removeFileCategory() methods. + self.vcsMenu = None self.__makeProcess = None @@ -225,8 +209,6 @@ else: self.vcs = self.initVCS() - from .ProjectBrowserModel import ProjectBrowserModel - self.__model = ProjectBrowserModel(self) self.codemetrics = None @@ -236,6 +218,52 @@ self.loadedDiagram = None self.__findProjectFileDialog = None + self.processChangedProjectFiles.connect(self.__autoExecuteMake) + + def addFileCategory(self, category, categoryItem): + """ + Public method to add a file category to the categories repository. + + Note: The given category must not be contained in the repository already. + + @param category file category (must be unique) + @type str + @param categoryItem data class instance containing the category data + @type FileCategoryRepositoryItem + @exception TypeError raised to signal a wrong type for the category item + """ + if not isinstance(categoryItem, FileCategoryRepositoryItem): + raise TypeError( + "'categoryItem' must be an instance of 'FileCategoryRepositoryItem'." + ) + + if category in self.__fileCategoriesRepository: + EricMessageBox.critical( + self.ui, + self.tr("Add File Category"), + self.tr( + "<p>The file category <b>{0}</b> has already been added. This" + " attempt will be ignored.</p>" + ), + ) + else: + self.__fileCategoriesRepository[category] = categoryItem + with contextlib.suppress(AttributeError): + self.__pdata[category] = [] + + def removeFileCategory(self, category): + """ + Public method to remove a category from the categories repository. + + Note: If the category is not contained in the repository, the request to + remove it will be ignored silently. + + @param category file category + @type str + """ + with contextlib.suppress(KeyError): + del self.__fileCategoriesRepository[category] + def __sourceExtensions(self, language): """ Private method to get the source extensions of a programming language. @@ -484,16 +512,9 @@ "redirect": True, } - self.pdata = { + self.__pdata = { "DESCRIPTION": "", "VERSION": "", - "SOURCES": [], - "FORMS": [], - "RESOURCES": [], - "INTERFACES": [], - "PROTOCOLS": [], - "OTHERS": [], - "TRANSLATIONS": [], "TRANSLATIONEXCEPTIONS": [], "TRANSLATIONPATTERN": "", "TRANSLATIONSBINPATH": "", @@ -548,6 +569,8 @@ "LICENSE": "", "EMBEDDED_VENV": False, } + for category in self.__fileCategoriesRepository: + self.__pdata[category] = [] self.__initDebugProperties() @@ -560,6 +583,52 @@ self.__initVenvConfiguration() + def getProjectData(self, dataKey=None, default=None): + """ + Public method to get the data associated with the given data key. + + Note: If dataKey is None, a copy of the project data structure + is returned. + + @param dataKey key of the data to get (defaults to None) + @type str (optional) + @param default default value for non-existent keys (defaults to None) + @type Any (optional) + @return requested data or None if the data key doesn't exist or + a copy of the project data dictionary + @rtype Any + """ + if dataKey is None: + return copy.deepcopy(self.__pdata) + + try: + return self.__pdata[dataKey] + except KeyError: + return default + + def setProjectData(self, data, dataKey=None, setDirty=True): + """ + Public method to set data associated with the given data key in the project + dictionary. + + Note: If no data key is given or is None, the data must be a dictionary used + to update the project data. + + @param data data to be set or a dictionary to update the project data + @type Any + @param dataKey key of the data to set (defaults to None) + @type str (optional) + @param setDirty flag indicating to set the dirty flag if the data is different + from the current one (defaults to True) + @type bool (optional) + """ + if dataKey is None: + self.__pdata.update(data) + else: + if self.__pdata[dataKey] != data and setDirty: + self.setDirty(True) + self.__pdata[dataKey] = data + def getData(self, category, key): """ Public method to get data out of the project data store. @@ -580,9 +649,9 @@ "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS", ] - and key in self.pdata[category] + and key in self.__pdata[category] ): - return copy.deepcopy(self.pdata[category][key]) + return copy.deepcopy(self.__pdata[category][key]) else: return None @@ -609,29 +678,124 @@ # test for changes of data and save them in the project # 1. there were none, now there are - if key not in self.pdata[category] and len(data) > 0: - self.pdata[category][key] = copy.deepcopy(data) + if key not in self.__pdata[category] and len(data) > 0: + self.__pdata[category][key] = copy.deepcopy(data) self.setDirty(True) # 2. there were some, now there aren't - elif key in self.pdata[category] and len(data) == 0: - del self.pdata[category][key] + elif key in self.__pdata[category] and len(data) == 0: + del self.__pdata[category][key] self.setDirty(True) # 3. there were some and still are - elif key in self.pdata[category] and len(data) > 0: - if data != self.pdata[category][key]: - self.pdata[category][key] = copy.deepcopy(data) + elif key in self.__pdata[category] and len(data) > 0: + if data != self.__pdata[category][key]: + self.__pdata[category][key] = copy.deepcopy(data) self.setDirty(True) # 4. there were none and none are given else: return False return True + def getFileCategories(self): + """ + Public method to get the list of known file categories. + + @return list of known file categories + @rtype list of str + """ + return list(self.__fileCategoriesRepository.keys()) + + def getFileCategoryFilterString( + self, categories=None, withOthers=False, withAll=True + ): + """ + Public method to get a file selection string for the given categories. + + @param categories list of file type categories (defaults to None). + A value of None means all categories except 'OTHERS'. + @type list of str (optional) + @param withOthers flag indicating to include the 'OTHERS' category + (defaults to False) + @type bool (optional) + @param withAll flag indicating to include a filter for 'All Files' + (defaults to True) + @type bool (optional) + @return file selection filter string + @rtype str + """ + if categories is None: + categories = [c for c in self.__fileCategoriesRepository if c != "OTHERS"] + if withOthers: + categories.append("OTHERS") + + patterns = collections.defaultdict(list) + for pattern, filetype in self.__pdata["FILETYPES"].items(): + if filetype in categories and filetype in self.__fileCategoriesRepository: + patterns[filetype].append(pattern) + + filters = [] + for filetype in patterns: + filters.append( + self.__fileCategoriesRepository[ + filetype + ].fileCategoryFilterTemplate.format( + " ".join(sorted(patterns[filetype])) + ) + ) + filterString = ";;".join(sorted(filters)) + if withAll: + filterString += ";;" + self.tr("All Files (*)") + + return filterString + + def getFileCategoryString(self, category): + """ + Public method to get a user string for the given category. + + @param category file type category + @type str + @return user string for the category + @rtype str + """ + return self.__fileCategoriesRepository[category].fileCategoryUserString + + def getFileCategoryType(self, category): + """ + Public method to get a user type string for the given category. + + @param category file type category + @type str + @return user type string for the category + @rtype str + """ + return self.__fileCategoriesRepository[category].fileCategoryTyeString + + def getFileCategoryExtension(self, category, reverse=False): + """ + Public method to get a list of default file extensions for the given category. + + @param category file type category + @type str + @param reverse flag indicating to get all other extensions except the one of + the given category + @type bool + @return list of default file extensions for the category + @rtype list of str + """ + if reverse: + extensions = [] + for cat, item in self.__fileCategoriesRepository.items(): + if cat != category: + extensions += item.fileCategoryExtensions[:] + return extensions + else: + return self.__fileCategoriesRepository[category].fileCategoryExtensions[:] + def initFileTypes(self): """ Public method to initialize the filetype associations with default values. """ - self.pdata["FILETYPES"] = { + self.__pdata["FILETYPES"] = { "*.txt": "OTHERS", "*.md": "OTHERS", "*.rst": "OTHERS", @@ -646,29 +810,29 @@ # Sources sourceKey = ( - "Mixed" if self.pdata["MIXEDLANGUAGE"] else self.pdata["PROGLANGUAGE"] + "Mixed" if self.__pdata["MIXEDLANGUAGE"] else self.__pdata["PROGLANGUAGE"] ) for ext in self.__sourceExtensions(sourceKey): - self.pdata["FILETYPES"]["*{0}".format(ext)] = "SOURCES" + self.__pdata["FILETYPES"]["*{0}".format(ext)] = "SOURCES" # IDL interfaces - self.pdata["FILETYPES"]["*.idl"] = "INTERFACES" + self.__pdata["FILETYPES"]["*.idl"] = "INTERFACES" # Protobuf Files - self.pdata["FILETYPES"]["*.proto"] = "PROTOCOLS" + self.__pdata["FILETYPES"]["*.proto"] = "PROTOCOLS" # Forms - if self.pdata["PROJECTTYPE"] in [ + if self.__pdata["PROJECTTYPE"] in [ "E7Plugin", "PyQt5", "PyQt6", "PySide2", "PySide6", ]: - self.pdata["FILETYPES"]["*.ui"] = "FORMS" + self.__pdata["FILETYPES"]["*.ui"] = "FORMS" # Resources - if self.pdata["PROJECTTYPE"] in [ + if self.__pdata["PROJECTTYPE"] in [ "PyQt5", "PyQt5C", "PySide2", @@ -676,10 +840,10 @@ "PySide6", "PySide6C", ]: - self.pdata["FILETYPES"]["*.qrc"] = "RESOURCES" + self.__pdata["FILETYPES"]["*.qrc"] = "RESOURCES" # Translations - if self.pdata["PROJECTTYPE"] in [ + if self.__pdata["PROJECTTYPE"] in [ "E7Plugin", "PyQt5", "PyQt5C", @@ -690,14 +854,14 @@ "PySide6", "PySide6C", ]: - self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" - self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" + self.__pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" + self.__pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" # Project type specific ones with contextlib.suppress(KeyError): - if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]] is not None: - ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]]() - self.pdata["FILETYPES"].update(ftypes) + if self.__fileTypeCallbacks[self.__pdata["PROJECTTYPE"]] is not None: + ftypes = self.__fileTypeCallbacks[self.__pdata["PROJECTTYPE"]]() + self.__pdata["FILETYPES"].update(ftypes) self.setDirty(True) @@ -706,7 +870,7 @@ Public method to update the filetype associations with new default values. """ - if self.pdata["PROJECTTYPE"] in [ + if self.__pdata["PROJECTTYPE"] in [ "E7Plugin", "PyQt5", "PyQt5C", @@ -717,16 +881,16 @@ "PySide6", "PySide6C", ]: - if "*.ts" not in self.pdata["FILETYPES"]: - self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" - if "*.qm" not in self.pdata["FILETYPES"]: - self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" + if "*.ts" not in self.__pdata["FILETYPES"]: + self.__pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" + if "*.qm" not in self.__pdata["FILETYPES"]: + self.__pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" with contextlib.suppress(KeyError): - if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]] is not None: - ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]]() + if self.__fileTypeCallbacks[self.__pdata["PROJECTTYPE"]] is not None: + ftypes = self.__fileTypeCallbacks[self.__pdata["PROJECTTYPE"]]() for pattern, ftype in list(ftypes.items()): - if pattern not in self.pdata["FILETYPES"]: - self.pdata["FILETYPES"][pattern] = ftype + if pattern not in self.__pdata["FILETYPES"]: + self.__pdata["FILETYPES"][pattern] = ftype self.setDirty(True) def __loadRecent(self): @@ -845,14 +1009,14 @@ """ removed = False removelist = [] - for file in self.pdata[index]: + for file in self.__pdata[index]: if not os.path.exists(os.path.join(self.ppath, file)): removelist.append(file) removed = True if removed: for file in removelist: - self.pdata[index].remove(file) + self.__pdata[index].remove(file) self.setDirty(True) def __readProject(self, fn): @@ -870,15 +1034,13 @@ # old XML based format f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): - from eric7.EricXML.ProjectReader import ProjectReader - reader = ProjectReader(f, self) reader.readXML() res = not reader.hasError() f.close() # create hash value, if it doesn't have one - if reader.version.startswith("5.") and not self.pdata["HASH"]: + if reader.version.startswith("5.") and not self.__pdata["HASH"]: hashStr = str( QCryptographicHash.hash( QByteArray(self.ppath.encode("utf-8")), @@ -886,7 +1048,7 @@ ).toHex(), encoding="utf-8", ) - self.pdata["HASH"] = hashStr + self.__pdata["HASH"] = hashStr self.setDirty(True) else: EricMessageBox.critical( @@ -905,12 +1067,12 @@ # insert filename into list of recently opened projects self.__syncRecent() - if self.pdata["TRANSLATIONPATTERN"]: - self.translationsRoot = self.pdata["TRANSLATIONPATTERN"].split( + if self.__pdata["TRANSLATIONPATTERN"]: + self.translationsRoot = self.__pdata["TRANSLATIONPATTERN"].split( "%language%" )[0] - elif self.pdata["MAINSCRIPT"]: - self.translationsRoot = os.path.splitext(self.pdata["MAINSCRIPT"])[0] + elif self.__pdata["MAINSCRIPT"]: + self.translationsRoot = os.path.splitext(self.__pdata["MAINSCRIPT"])[0] if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)): dn = self.translationsRoot else: @@ -922,29 +1084,18 @@ # check, if the files of the project still exist in the # project directory - self.__checkFilesExist("SOURCES") - self.__checkFilesExist("FORMS") - self.__checkFilesExist("INTERFACES") - self.__checkFilesExist("PROTOCOLS") - self.__checkFilesExist("TRANSLATIONS") - self.__checkFilesExist("RESOURCES") - self.__checkFilesExist("OTHERS") + for fileCategory in self.getFileCategories(): + self.__checkFilesExist(fileCategory) # get the names of subdirectories the files are stored in - for fn in ( - self.pdata["SOURCES"] - + self.pdata["FORMS"] - + self.pdata["INTERFACES"] - + self.pdata["PROTOCOLS"] - + self.pdata["RESOURCES"] - + self.pdata["TRANSLATIONS"] - ): - dn = os.path.dirname(fn) - if dn not in self.subdirs: - self.subdirs.append(dn) + for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: + for fn in self.__pdata[fileCategory]: + dn = os.path.dirname(fn) + if dn not in self.subdirs: + self.subdirs.append(dn) # get the names of other subdirectories - for fn in self.pdata["OTHERS"]: + for fn in self.__pdata["OTHERS"]: dn = os.path.dirname(fn) if dn not in self.otherssubdirs: self.otherssubdirs.append(dn) @@ -963,10 +1114,10 @@ @return flag indicating success """ if self.vcs is not None: - self.pdata["VCSOPTIONS"] = copy.deepcopy(self.vcs.vcsGetOptions()) - self.pdata["VCSOTHERDATA"] = copy.deepcopy(self.vcs.vcsGetOtherData()) - - if not self.pdata["HASH"]: + self.__pdata["VCSOPTIONS"] = copy.deepcopy(self.vcs.vcsGetOptions()) + self.__pdata["VCSOTHERDATA"] = copy.deepcopy(self.vcs.vcsGetOtherData()) + + if not self.__pdata["HASH"]: hashStr = str( QCryptographicHash.hash( QByteArray(self.ppath.encode("utf-8")), @@ -974,7 +1125,7 @@ ).toHex(), encoding="utf-8", ) - self.pdata["HASH"] = hashStr + self.__pdata["HASH"] = hashStr if fn is None: fn = self.pfile @@ -1012,8 +1163,6 @@ if os.path.exists(fn): f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): - from eric7.EricXML.UserProjectReader import UserProjectReader - reader = UserProjectReader(f, self) reader.readXML() f.close() @@ -1088,8 +1237,6 @@ if os.path.exists(fn): f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): - from eric7.EricXML.SessionReader import SessionReader - reader = SessionReader(f, False) reader.readXML(quiet=quiet) f.close() @@ -1183,8 +1330,6 @@ if os.path.exists(fn): f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): - from eric7.EricXML.TasksReader import TasksReader - reader = TasksReader(f, True) reader.readXML() f.close() @@ -1262,10 +1407,6 @@ f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): - from eric7.EricXML.DebuggerPropertiesReader import ( - DebuggerPropertiesReader, - ) - reader = DebuggerPropertiesReader(f, self) reader.readXML(quiet=quiet) f.close() @@ -1366,7 +1507,7 @@ @return load status of debug properties (boolean) """ - return self.debugPropertiesLoaded or self.pdata["EMBEDDED_VENV"] + return self.debugPropertiesLoaded or self.__pdata["EMBEDDED_VENV"] def __showDebugProperties(self): """ @@ -1471,7 +1612,7 @@ @return translation pattern (string) """ - return self.pdata["TRANSLATIONPATTERN"] + return self.__pdata["TRANSLATIONPATTERN"] def setTranslationPattern(self, pattern): """ @@ -1480,13 +1621,15 @@ @param pattern translation pattern @type str """ - self.pdata["TRANSLATIONPATTERN"] = pattern + self.__pdata["TRANSLATIONPATTERN"] = pattern def addLanguage(self): """ Public slot used to add a language to the project. """ - if not self.pdata["TRANSLATIONPATTERN"]: + from .AddLanguageDialog import AddLanguageDialog + + if not self.__pdata["TRANSLATIONPATTERN"]: EricMessageBox.critical( self.ui, self.tr("Add Language"), @@ -1494,12 +1637,10 @@ ) return - from .AddLanguageDialog import AddLanguageDialog - dlg = AddLanguageDialog(self.parent()) if dlg.exec() == QDialog.DialogCode.Accepted: lang = dlg.getSelectedLanguage() - if self.pdata["PROJECTTYPE"] in [ + if self.__pdata["PROJECTTYPE"] in [ "PyQt5", "PyQt5C", "PyQt6", @@ -1510,7 +1651,9 @@ "PySide6", "PySide6C", ]: - langFile = self.pdata["TRANSLATIONPATTERN"].replace("%language%", lang) + langFile = self.__pdata["TRANSLATIONPATTERN"].replace( + "%language%", lang + ) self.appendFile(langFile) self.projectLanguageAddedByCode.emit(lang) @@ -1525,12 +1668,12 @@ qmFile = "" try: if ( - self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"]] + self.__binaryTranslationsCallbacks[self.__pdata["PROJECTTYPE"]] is not None ): - qmFile = self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"]]( - langFile - ) + qmFile = self.__binaryTranslationsCallbacks[ + self.__pdata["PROJECTTYPE"] + ](langFile) except KeyError: qmFile = langFile.replace(".ts", ".qm") if qmFile == langFile: @@ -1541,17 +1684,17 @@ """ Public slot to check the language files after a release process. """ - tbPath = self.pdata["TRANSLATIONSBINPATH"] - for langFile in self.pdata["TRANSLATIONS"][:]: + tbPath = self.__pdata["TRANSLATIONSBINPATH"] + for langFile in self.__pdata["TRANSLATIONS"][:]: qmFile = self.__binaryTranslationFile(langFile) if qmFile: - if qmFile not in self.pdata["TRANSLATIONS"] and os.path.exists( + if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists( os.path.join(self.ppath, qmFile) ): self.appendFile(qmFile) if tbPath: qmFile = os.path.join(tbPath, os.path.basename(qmFile)) - if qmFile not in self.pdata["TRANSLATIONS"] and os.path.exists( + if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists( os.path.join(self.ppath, qmFile) ): self.appendFile(qmFile) @@ -1566,17 +1709,18 @@ """ langFile = self.getRelativePath(langFile) qmFile = self.__binaryTranslationFile(langFile) - self.pdata["TRANSLATIONS"].remove(langFile) + self.__pdata["TRANSLATIONS"].remove(langFile) self.__model.removeItem(langFile) if qmFile: with contextlib.suppress(ValueError): - if self.pdata["TRANSLATIONSBINPATH"]: + if self.__pdata["TRANSLATIONSBINPATH"]: qmFile = self.getRelativePath( os.path.join( - self.pdata["TRANSLATIONSBINPATH"], os.path.basename(qmFile) + self.__pdata["TRANSLATIONSBINPATH"], + os.path.basename(qmFile), ) ) - self.pdata["TRANSLATIONS"].remove(qmFile) + self.__pdata["TRANSLATIONS"].remove(qmFile) self.__model.removeItem(qmFile) self.setDirty(True) @@ -1587,7 +1731,7 @@ @param langFile the translation file to be removed (string) """ try: - from send2trash import send2trash as s2t + from send2trash import send2trash as s2t # __IGNORE_WARNING_I10__ except ImportError: s2t = os.remove @@ -1614,10 +1758,11 @@ # now get rid of the .qm file if qmFile: try: - if self.pdata["TRANSLATIONSBINPATH"]: + if self.__pdata["TRANSLATIONSBINPATH"]: qmFile = self.getRelativePath( os.path.join( - self.pdata["TRANSLATIONSBINPATH"], os.path.basename(qmFile) + self.__pdata["TRANSLATIONSBINPATH"], + os.path.basename(qmFile), ) ) fn = os.path.join(self.ppath, qmFile) @@ -1659,68 +1804,39 @@ if fnmatch.fnmatch(bfn, "*.ts") or fnmatch.fnmatch(bfn, "*.qm"): filetype = "TRANSLATIONS" else: - for pattern in sorted(self.pdata["FILETYPES"].keys(), reverse=True): + for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True): if fnmatch.fnmatch(bfn, pattern): - filetype = self.pdata["FILETYPES"][pattern] + filetype = self.__pdata["FILETYPES"][pattern] break if filetype == "__IGNORE__": return - if filetype in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES"]: - if filetype == "SOURCES": - if newfn not in self.pdata["SOURCES"]: - self.pdata["SOURCES"].append(newfn) - self.projectSourceAdded.emit(newfn) - updateModel and self.__model.addNewItem("SOURCES", newfn) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "FORMS": - if newfn not in self.pdata["FORMS"]: - self.pdata["FORMS"].append(newfn) - self.projectFormAdded.emit(newfn) - updateModel and self.__model.addNewItem("FORMS", newfn) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "INTERFACES": - if newfn not in self.pdata["INTERFACES"]: - self.pdata["INTERFACES"].append(newfn) - self.projectInterfaceAdded.emit(newfn) - (updateModel and self.__model.addNewItem("INTERFACES", newfn)) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "PROTOCOLS": - if newfn not in self.pdata["PROTOCOLS"]: - self.pdata["PROTOCOLS"].append(newfn) - self.projectProtocolAdded.emit(newfn) - (updateModel and self.__model.addNewItem("PROTOCOLS", newfn)) - dirty = True - else: - updateModel and self.repopulateItem(newfn) - elif filetype == "RESOURCES": - if newfn not in self.pdata["RESOURCES"]: - self.pdata["RESOURCES"].append(newfn) - self.projectResourceAdded.emit(newfn) - updateModel and self.__model.addNewItem("RESOURCES", newfn) - dirty = True - else: - updateModel and self.repopulateItem(newfn) + if filetype in ( + category + for category in self.getFileCategories() + if category not in ("TRANSLATIONS", "OTHERS") + ): + if newfn not in self.__pdata[filetype]: + self.__pdata[filetype].append(newfn) + self.projectFileAdded.emit(newfn, filetype) + updateModel and self.__model.addNewItem(filetype, newfn) + dirty = True + else: + updateModel and self.repopulateItem(newfn) if newdir not in self.subdirs: self.subdirs.append(newdir) elif filetype == "TRANSLATIONS": - if newfn not in self.pdata["TRANSLATIONS"]: - self.pdata["TRANSLATIONS"].append(newfn) + if newfn not in self.__pdata["TRANSLATIONS"]: + self.__pdata["TRANSLATIONS"].append(newfn) updateModel and self.__model.addNewItem("TRANSLATIONS", newfn) - self.projectLanguageAdded.emit(newfn) + self.projectFileAdded.emit(newfn, "TRANSLATIONS") dirty = True else: updateModel and self.repopulateItem(newfn) - else: # filetype == "OTHERS" - if newfn not in self.pdata["OTHERS"]: - self.pdata["OTHERS"].append(newfn) + elif filetype == "OTHERS": + if newfn not in self.__pdata["OTHERS"]: + self.__pdata["OTHERS"].append(newfn) self.othersAdded(newfn, updateModel) dirty = True else: @@ -1741,9 +1857,10 @@ @param startdir start directory for the selection dialog @type str """ + from .AddFileDialog import AddFileDialog + if startdir is None: startdir = self.ppath - from .AddFileDialog import AddFileDialog dlg = AddFileDialog(self, self.parent(), fileTypeFilter, startdir=startdir) if dlg.exec() == QDialog.DialogCode.Accepted: @@ -1803,7 +1920,7 @@ # get all relevant filename patterns patterns = [] ignorePatterns = [] - for pattern, patterntype in list(self.pdata["FILETYPES"].items()): + for pattern, patterntype in list(self.__pdata["FILETYPES"].items()): if patterntype == filetype: patterns.append(pattern) elif patterntype == "__IGNORE__": @@ -1883,7 +2000,7 @@ ignore_patterns = [ pattern - for pattern, filetype in self.pdata["FILETYPES"].items() + for pattern, filetype in self.__pdata["FILETYPES"].items() if filetype == "__IGNORE__" ] @@ -1912,9 +2029,10 @@ @param startdir start directory for the selection dialog @type str """ + from .AddDirectoryDialog import AddDirectoryDialog + if startdir is None: startdir = self.ppath - from .AddDirectoryDialog import AddDirectoryDialog dlg = AddDirectoryDialog(self, fileTypeFilter, self.parent(), startdir=startdir) if dlg.exec() == QDialog.DialogCode.Accepted: @@ -1958,90 +2076,14 @@ if fn.endswith(os.sep): fn = fn[:-1] - if fn not in self.pdata["OTHERS"]: - self.pdata["OTHERS"].append(fn) + if fn not in self.__pdata["OTHERS"]: + self.__pdata["OTHERS"].append(fn) self.othersAdded(fn) self.setDirty(True) if os.path.isdir(fn) and fn not in self.otherssubdirs: self.otherssubdirs.append(fn) - def addSourceFiles(self): - """ - Public slot to add source files to the current project. - """ - self.addFiles("source") - - def addUiFiles(self): - """ - Public slot to add forms to the current project. - """ - self.addFiles("form") - - def addIdlFiles(self): - """ - Public slot to add IDL interfaces to the current project. - """ - self.addFiles("interface") - - def addProtoFiles(self): - """ - Public slot to add protocol files to the current project. - """ - self.addFiles("protocol") - - def addResourceFiles(self): - """ - Public slot to add Qt resources to the current project. - """ - self.addFiles("resource") - - def addOthersFiles(self): - """ - Public slot to add files to the OTHERS project data. - """ - self.addFiles("others") - - def addSourceDir(self): - """ - Public slot to add all source files of a directory to the current - project. - """ - self.addDirectory("source") - - def addUiDir(self): - """ - Public slot to add all forms of a directory to the current project. - """ - self.addDirectory("form") - - def addIdlDir(self): - """ - Public slot to add all IDL interfaces of a directory to the current - project. - """ - self.addDirectory("interface") - - def addProtoDir(self): - """ - Public slot to add all protocol files of a directory to the current - project. - """ - self.addDirectory("protocol") - - def addResourceDir(self): - """ - Public slot to add all Qt resource files of a directory to the current - project. - """ - self.addDirectory("resource") - - def addOthersDir(self): - """ - Public slot to add a directory to the OTHERS project data. - """ - self.addDirectory("others") - def renameMainScript(self, oldfn, newfn): """ Public method to rename the main script. @@ -2049,13 +2091,13 @@ @param oldfn old filename (string) @param newfn new filename of the main script (string) """ - if self.pdata["MAINSCRIPT"]: + if self.__pdata["MAINSCRIPT"]: ofn = self.getRelativePath(oldfn) - if ofn != self.pdata["MAINSCRIPT"]: + if ofn != self.__pdata["MAINSCRIPT"]: return fn = self.getRelativePath(newfn) - self.pdata["MAINSCRIPT"] = fn + self.__pdata["MAINSCRIPT"] = fn self.setDirty(True) def renameFile(self, oldfn, newfn=None): @@ -2067,7 +2109,7 @@ @return flag indicating success """ fn = self.getRelativePath(oldfn) - isSourceFile = fn in self.pdata["SOURCES"] + isSourceFile = fn in self.__pdata["SOURCES"] if newfn is None: newfn = EricFileDialog.getSaveFileName( @@ -2107,22 +2149,14 @@ ) return False - if ( - fn in self.pdata["SOURCES"] - or fn in self.pdata["FORMS"] - or fn in self.pdata["TRANSLATIONS"] - or fn in self.pdata["INTERFACES"] - or fn in self.pdata["PROTOCOLS"] - or fn in self.pdata["RESOURCES"] - or fn in self.pdata["OTHERS"] - ): + if any(fn in self.__pdata[category] for category in self.getFileCategories()): self.renameFileInPdata(oldfn, newfn, isSourceFile) return True def renameFileInPdata(self, oldname, newname, isSourceFile=False): """ - Public method to rename a file in the pdata structure. + Public method to rename a file in the __pdata structure. @param oldname old filename (string) @param newname new filename (string) @@ -2151,15 +2185,10 @@ """ filelist = [] start = self.getRelativePath(start) - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "OTHERS", + for fileCategory in [ + c for c in self.getFileCategories() if c != "TRANSLATIONS" ]: - for entry in self.pdata[key][:]: + for entry in self.__pdata[fileCategory][:]: if entry.startswith(start): filelist.append(os.path.join(self.ppath, entry)) return filelist @@ -2172,52 +2201,37 @@ # init data store for the reorganization newPdata = {} - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "OTHERS", - "TRANSLATIONS", - ]: - newPdata[key] = [] + for fileCategory in self.getFileCategories(): + newPdata[fileCategory] = [] # iterate over all files checking for a reassignment - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "OTHERS", - "TRANSLATIONS", - ]: - for fn in self.pdata[key][:]: - filetype = key + for fileCategory in self.getFileCategories(): + for fn in self.__pdata[fileCategory][:]: + filetype = fileCategory bfn = os.path.basename(fn) - for pattern in sorted(self.pdata["FILETYPES"].keys(), reverse=True): + for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True): if fnmatch.fnmatch(bfn, pattern): - filetype = self.pdata["FILETYPES"][pattern] + filetype = self.__pdata["FILETYPES"][pattern] break if filetype != "__IGNORE__": newPdata[filetype].append(fn) - if filetype != key: + if filetype != fileCategory: reorganized = True if reorganized: # copy the reorganized files back to the project - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "OTHERS", - "TRANSLATIONS", - ]: - self.pdata[key] = newPdata[key][:] + ##for key in [ + ##"SOURCES", + ##"FORMS", + ##"INTERFACES", + ##"PROTOCOLS", + ##"RESOURCES", + ##"OTHERS", + ##"TRANSLATIONS", + ##]: + for fileCategory in self.getFileCategories(): + self.__pdata[fileCategory] = newPdata[fileCategory][:] # repopulate the model self.__model.projectClosed(False) @@ -2232,18 +2246,23 @@ """ olddn = self.getRelativePath(olddn) newdn = self.getRelativePath(newdn) - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "OTHERS", + ##for key in [ + ##"SOURCES", + ##"FORMS", + ##"INTERFACES", + ##"PROTOCOLS", + ##"RESOURCES", + ##"OTHERS", + ##]: + for fileCategory in [ + c for c in self.getFileCategories() if c != "TRANSLATIONS" ]: - for entry in self.pdata[key][:]: + for entry in self.__pdata[fileCategory][:]: if entry.startswith(olddn): entry = entry.replace(olddn, newdn) - self.appendFile(os.path.join(self.ppath, entry), key == "SOURCES") + self.appendFile( + os.path.join(self.ppath, entry), fileCategory == "SOURCES" + ) self.setDirty(True) def moveDirectory(self, olddn, newdn): @@ -2256,22 +2275,25 @@ olddn = self.getRelativePath(olddn) newdn = self.getRelativePath(newdn) typeStrings = [] - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "OTHERS", + ##for key in [ + ##"SOURCES", + ##"FORMS", + ##"INTERFACES", + ##"PROTOCOLS", + ##"RESOURCES", + ##"OTHERS", + ##]: + for fileCategory in [ + c for c in self.getFileCategories() if c != "TRANSLATIONS" ]: - for entry in self.pdata[key][:]: + for entry in self.__pdata[fileCategory][:]: if entry.startswith(olddn): - if key not in typeStrings: - typeStrings.append(key) - self.pdata[key].remove(entry) + if fileCategory not in typeStrings: + typeStrings.append(fileCategory) + self.__pdata[fileCategory].remove(entry) entry = entry.replace(olddn, newdn) - self.pdata[key].append(entry) - if key == "OTHERS": + self.__pdata[fileCategory].append(entry) + if fileCategory == "OTHERS": if newdn not in self.otherssubdirs: self.otherssubdirs.append(newdn) else: @@ -2299,33 +2321,14 @@ requested (boolean) """ fn = self.getRelativePath(fn) - dirty = True - if fn in self.pdata["SOURCES"]: - self.pdata["SOURCES"].remove(fn) - self.projectSourceRemoved.emit(fn) - elif fn in self.pdata["FORMS"]: - self.pdata["FORMS"].remove(fn) - self.projectFormRemoved.emit(fn) - elif fn in self.pdata["INTERFACES"]: - self.pdata["INTERFACES"].remove(fn) - self.projectInterfaceRemoved.emit(fn) - elif fn in self.pdata["PROTOCOLS"]: - self.pdata["PROTOCOLS"].remove(fn) - self.projectProtocolRemoved.emit(fn) - elif fn in self.pdata["RESOURCES"]: - self.pdata["RESOURCES"].remove(fn) - self.projectResourceRemoved.emit(fn) - elif fn in self.pdata["OTHERS"]: - self.pdata["OTHERS"].remove(fn) - self.projectOthersRemoved.emit(fn) - elif fn in self.pdata["TRANSLATIONS"]: - self.pdata["TRANSLATIONS"].remove(fn) - self.projectLanguageRemoved.emit(fn) - else: - dirty = False - updateModel and self.__model.removeItem(fn) - if dirty: - self.setDirty(True) + for fileCategory in self.getFileCategories(): + if fn in self.__pdata[fileCategory]: + self.__pdata[fileCategory].remove(fn) + self.projectFileRemoved.emit(fn, fileCategory) + self.setDirty(True) + if updateModel: + self.__model.removeItem(fn) + break def removeDirectory(self, dn): """ @@ -2337,22 +2340,15 @@ """ dirty = False dn = self.getRelativePath(dn) - for entry in self.pdata["OTHERS"][:]: + for entry in self.__pdata["OTHERS"][:]: if entry.startswith(dn): - self.pdata["OTHERS"].remove(entry) + self.__pdata["OTHERS"].remove(entry) dirty = True dn2 = dn if dn.endswith(os.sep) else dn + os.sep - for key in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "TRANSLATIONS", - ]: - for entry in self.pdata[key][:]: + for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: + for entry in self.__pdata[fileCategory][:]: if entry.startswith(dn2): - self.pdata[key].remove(entry) + self.__pdata[fileCategory].remove(entry) dirty = True self.__model.removeItem(dn) if dirty: @@ -2367,7 +2363,7 @@ @return flag indicating success (boolean) """ try: - from send2trash import send2trash as s2t + from send2trash import send2trash as s2t # __IGNORE_WARNING_I10__ except ImportError: s2t = os.remove @@ -2415,7 +2411,7 @@ dn = os.path.join(self.ppath, dn) try: try: - from send2trash import send2trash + from send2trash import send2trash # __IGNORE_WARNING_I10__ send2trash(dn) except ImportError: @@ -2442,13 +2438,10 @@ @return flag indicating, if the project contains the file (boolean) """ fn = self.getRelativePath(fn) - return ( - fn in self.pdata["SOURCES"] - or fn in self.pdata["FORMS"] - or fn in self.pdata["INTERFACES"] - or fn in self.pdata["PROTOCOLS"] - or fn in self.pdata["RESOURCES"] - or fn in self.pdata["OTHERS"] + return any( + fn in self.__pdata[category] + for category in self.getFileCategories() + if category != "TRANSLATIONS" ) def createNewProject(self): @@ -2458,20 +2451,23 @@ This method displays the new project dialog and initializes the project object with the data entered. """ + from eric7.VCS.CommandOptionsDialog import VcsCommandOptionsDialog + + from .PropertiesDialog import PropertiesDialog + if not self.checkDirty(): return - from .PropertiesDialog import PropertiesDialog - dlg = PropertiesDialog(self, True) if dlg.exec() == QDialog.DialogCode.Accepted: self.closeProject() dlg.storeData() - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.opened = True - if not self.pdata["FILETYPES"]: + if not self.__pdata["FILETYPES"]: self.initFileTypes() self.setDirty(True) + self.reloadAct.setEnabled(True) self.closeAct.setEnabled(True) self.saveasAct.setEnabled(True) self.actGrp2.setEnabled(True) @@ -2488,10 +2484,10 @@ self.menuDiagramAct.setEnabled(True) self.menuApidocAct.setEnabled(True) self.menuPackagersAct.setEnabled(True) - self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"] in ["E7Plugin"]) - self.addLanguageAct.setEnabled(bool(self.pdata["TRANSLATIONPATTERN"])) - self.makeGrp.setEnabled(self.pdata["MAKEPARAMS"]["MakeEnabled"]) - self.menuMakeAct.setEnabled(self.pdata["MAKEPARAMS"]["MakeEnabled"]) + self.pluginGrp.setEnabled(self.__pdata["PROJECTTYPE"] in ["E7Plugin"]) + self.addLanguageAct.setEnabled(bool(self.__pdata["TRANSLATIONPATTERN"])) + self.makeGrp.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) + self.menuMakeAct.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) self.menuOtherToolsAct.setEnabled(True) self.menuFormattingAct.setEnabled(True) @@ -2504,11 +2500,11 @@ ).toHex(), encoding="utf-8", ) - self.pdata["HASH"] = hashStr - - if self.pdata["PROGLANGUAGE"] == "MicroPython": + self.__pdata["HASH"] = hashStr + + if self.__pdata["PROGLANGUAGE"] == "MicroPython": # change the lexer association for *.py files - self.pdata["LEXERASSOCS"] = { + self.__pdata["LEXERASSOCS"] = { "*.py": "MicroPython", } @@ -2530,25 +2526,25 @@ # create an empty __init__.py file to make it a Python package # (only for Python and Python3) - if self.pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: + if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: fn = os.path.join(self.ppath, "__init__.py") with open(fn, "w", encoding="utf-8"): pass self.appendFile(fn, True) # create an empty main script file, if a name was given - if self.pdata["MAINSCRIPT"]: - if not os.path.isabs(self.pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + if self.__pdata["MAINSCRIPT"]: + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) else: - ms = self.pdata["MAINSCRIPT"] + ms = self.__pdata["MAINSCRIPT"] os.makedirs(os.path.dirname(ms), exist_ok=True) with open(ms, "w"): pass self.appendFile(ms, True) - if self.pdata["MAKEPARAMS"]["MakeEnabled"]: - mf = self.pdata["MAKEPARAMS"]["MakeFile"] + if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: + mf = self.__pdata["MAKEPARAMS"]["MakeFile"] if mf: if not os.path.isabs(mf): mf = os.path.join(self.ppath, mf) @@ -2564,8 +2560,8 @@ tpd = os.path.dirname(tpd) if not os.path.isdir(tpd): os.makedirs(tpd, exist_ok=True) - if self.pdata["TRANSLATIONSBINPATH"]: - tpd = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"]) + if self.__pdata["TRANSLATIONSBINPATH"]: + tpd = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) if not os.path.isdir(tpd): os.makedirs(tpd, exist_ok=True) @@ -2589,11 +2585,11 @@ ) return - if self.pdata["MAINSCRIPT"]: - if not os.path.isabs(self.pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + if self.__pdata["MAINSCRIPT"]: + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) else: - ms = self.pdata["MAINSCRIPT"] + ms = self.__pdata["MAINSCRIPT"] if not os.path.exists(ms): try: os.makedirs(os.path.dirname(ms)) @@ -2612,8 +2608,8 @@ else: ms = "" - if self.pdata["MAKEPARAMS"]["MakeEnabled"]: - mf = self.pdata["MAKEPARAMS"]["MakeFile"] + if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: + mf = self.__pdata["MAKEPARAMS"]["MakeFile"] if mf: if not os.path.isabs(mf): mf = os.path.join(self.ppath, mf) @@ -2647,7 +2643,7 @@ addAllToVcs = res # create an empty __init__.py file to make it a Python package # if none exists (only for Python and Python3) - if self.pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: + if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: fn = os.path.join(self.ppath, "__init__.py") if not os.path.exists(fn): with open(fn, "w", encoding="utf-8"): @@ -2685,7 +2681,7 @@ vcsSystem = "None" else: vcsSystem = vcsData[0][1] - self.pdata["VCS"] = vcsSystem + self.__pdata["VCS"] = vcsSystem self.vcs = self.initVCS() self.setDirty(True) if self.vcs is not None: @@ -2702,10 +2698,6 @@ else: vcores = False if vcores: - from eric7.VCS.CommandOptionsDialog import ( - VcsCommandOptionsDialog, - ) - codlg = VcsCommandOptionsDialog(self.vcs) if codlg.exec() == QDialog.DialogCode.Accepted: self.vcs.vcsSetOptions(codlg.getOptions()) @@ -2724,7 +2716,7 @@ self.saveProject() self.vcs.vcsAdd(self.pfile) else: - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.saveProject() break @@ -2750,19 +2742,19 @@ if ok and vcsSelected != self.tr("None"): for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items(): if vcsSystemDisplay == vcsSelected: - self.pdata["VCS"] = vcsSystem + self.__pdata["VCS"] = vcsSystem break else: - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" else: - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.vcs = self.initVCS() if self.vcs is not None: vcsdlg = self.vcs.vcsOptionsDialog(self, self.name) if vcsdlg.exec() == QDialog.DialogCode.Accepted: vcsDataDict = vcsdlg.getData() else: - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.vcs = self.initVCS() self.setDirty(True) if self.vcs is not None: @@ -2779,10 +2771,6 @@ else: vcores = False if vcores: - from eric7.VCS.CommandOptionsDialog import ( - VcsCommandOptionsDialog, - ) - codlg = VcsCommandOptionsDialog(self.vcs) if codlg.exec() == QDialog.DialogCode.Accepted: self.vcs.vcsSetOptions(codlg.getOptions()) @@ -2799,9 +2787,9 @@ self.newProjectHooks.emit() self.newProject.emit() - if self.pdata["EMBEDDED_VENV"]: + if self.__pdata["EMBEDDED_VENV"]: self.__createEmbeddedEnvironment() - self.menuEnvironmentAct.setEnabled(self.pdata["EMBEDDED_VENV"]) + self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) def newProjectAddFiles(self, mainscript): """ @@ -2814,7 +2802,7 @@ with EricOverrideCursor(): # search the project directory for files with known extensions - filespecs = list(self.pdata["FILETYPES"].keys()) + filespecs = list(self.__pdata["FILETYPES"].keys()) for filespec in filespecs: files = Utilities.direntries(self.ppath, True, filespec) for file in files: @@ -2828,12 +2816,12 @@ else: tpd = self.ppath tslist = [] - if self.pdata["TRANSLATIONPATTERN"]: - pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"]) + if self.__pdata["TRANSLATIONPATTERN"]: + pattern = os.path.basename(self.__pdata["TRANSLATIONPATTERN"]) if "%language%" in pattern: pattern = pattern.replace("%language%", "*") else: - tpd = self.pdata["TRANSLATIONPATTERN"].split("%language%")[0] + tpd = self.__pdata["TRANSLATIONPATTERN"].split("%language%")[0] else: pattern = "*.ts" tslist.extend(Utilities.direntries(tpd, True, pattern)) @@ -2847,7 +2835,7 @@ os.path.splitext(mainscript)[0] or os.path.basename(tslist[0]).split("_")[0] ) - self.pdata["TRANSLATIONPATTERN"] = os.path.join( + self.__pdata["TRANSLATIONPATTERN"] = os.path.join( os.path.dirname(tslist[0]), "{0}_%language%{1}".format( os.path.basename(tslist[0]).split("_")[0], @@ -2868,35 +2856,35 @@ tslist[0], ) if pattern: - self.pdata["TRANSLATIONPATTERN"] = pattern - if self.pdata["TRANSLATIONPATTERN"]: - self.pdata["TRANSLATIONPATTERN"] = self.getRelativePath( - self.pdata["TRANSLATIONPATTERN"] + self.__pdata["TRANSLATIONPATTERN"] = pattern + if self.__pdata["TRANSLATIONPATTERN"]: + self.__pdata["TRANSLATIONPATTERN"] = self.getRelativePath( + self.__pdata["TRANSLATIONPATTERN"] ) - pattern = self.pdata["TRANSLATIONPATTERN"].replace( + pattern = self.__pdata["TRANSLATIONPATTERN"].replace( "%language%", "*" ) for ts in tslist: if fnmatch.fnmatch(ts, pattern): - self.pdata["TRANSLATIONS"].append(ts) - self.projectLanguageAdded.emit(ts) - if self.pdata["TRANSLATIONSBINPATH"]: + self.__pdata["TRANSLATIONS"].append(ts) + self.projectFileAdded.emit(ts, "TRANSLATIONS") + if self.__pdata["TRANSLATIONSBINPATH"]: tpd = os.path.join( - self.ppath, self.pdata["TRANSLATIONSBINPATH"] + self.ppath, self.__pdata["TRANSLATIONSBINPATH"] ) pattern = os.path.basename( - self.pdata["TRANSLATIONPATTERN"] + self.__pdata["TRANSLATIONPATTERN"] ).replace("%language%", "*") pattern = self.__binaryTranslationFile(pattern) qmlist = Utilities.direntries(tpd, True, pattern) for qm in qmlist: - self.pdata["TRANSLATIONS"].append(qm) - self.projectLanguageAdded.emit(qm) - if not self.pdata["MAINSCRIPT"] and bool(mainscriptname): - if self.pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: - self.pdata["MAINSCRIPT"] = "{0}.py".format(mainscriptname) - elif self.pdata["PROGLANGUAGE"] == "Ruby": - self.pdata["MAINSCRIPT"] = "{0}.rb".format(mainscriptname) + self.__pdata["TRANSLATIONS"].append(qm) + self.projectFileAdded.emit(qm, "TRANSLATIONS") + if not self.__pdata["MAINSCRIPT"] and bool(mainscriptname): + if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: + self.__pdata["MAINSCRIPT"] = "{0}.py".format(mainscriptname) + elif self.__pdata["PROGLANGUAGE"] == "Ruby": + self.__pdata["MAINSCRIPT"] = "{0}.rb".format(mainscriptname) self.setDirty(True) def __showProperties(self): @@ -2907,19 +2895,19 @@ dlg = PropertiesDialog(self, False) if dlg.exec() == QDialog.DialogCode.Accepted: - projectType = self.pdata["PROJECTTYPE"] + projectType = self.__pdata["PROJECTTYPE"] dlg.storeData() self.setDirty(True) - if self.pdata["MAINSCRIPT"]: - if not os.path.isabs(self.pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + if self.__pdata["MAINSCRIPT"]: + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) else: - ms = self.pdata["MAINSCRIPT"] + ms = self.__pdata["MAINSCRIPT"] if os.path.exists(ms): self.appendFile(ms) - if self.pdata["MAKEPARAMS"]["MakeEnabled"]: - mf = self.pdata["MAKEPARAMS"]["MakeFile"] + if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: + mf = self.__pdata["MAKEPARAMS"]["MakeFile"] if mf: if not os.path.isabs(mf): mf = os.path.join(self.ppath, mf) @@ -2940,7 +2928,7 @@ ) self.appendFile(mf) - if self.pdata["PROJECTTYPE"] != projectType: + if self.__pdata["PROJECTTYPE"] != projectType: # reinitialize filetype associations self.initFileTypes() @@ -2955,39 +2943,39 @@ if tp != self.ppath and tp not in self.subdirs: self.subdirs.append(tp) - if self.pdata["TRANSLATIONSBINPATH"]: - tp = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"]) + if self.__pdata["TRANSLATIONSBINPATH"]: + tp = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) if not os.path.isdir(tp): os.makedirs(tp) if tp != self.ppath and tp not in self.subdirs: self.subdirs.append(tp) - self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"] in ["E7Plugin"]) + self.pluginGrp.setEnabled(self.__pdata["PROJECTTYPE"] in ["E7Plugin"]) self.__model.projectPropertiesChanged() self.projectPropertiesChanged.emit() - if self.pdata["PROJECTTYPE"] != projectType: + if self.__pdata["PROJECTTYPE"] != projectType: self.__reorganizeFiles() - if self.pdata["EMBEDDED_VENV"] and not self.__findEmbeddedEnvironment(): + if self.__pdata["EMBEDDED_VENV"] and not self.__findEmbeddedEnvironment(): self.__createEmbeddedEnvironment() def __showUserProperties(self): """ Private slot to display the user specific properties dialog. """ - vcsSystem = self.pdata["VCS"] or None + from .UserPropertiesDialog import UserPropertiesDialog + + vcsSystem = self.__pdata["VCS"] or None vcsSystemOverride = self.pudata["VCSOVERRIDE"] or None - from .UserPropertiesDialog import UserPropertiesDialog - dlg = UserPropertiesDialog(self) if dlg.exec() == QDialog.DialogCode.Accepted: dlg.storeData() if ( - (self.pdata["VCS"] and self.pdata["VCS"] != vcsSystem) + (self.__pdata["VCS"] and self.__pdata["VCS"] != vcsSystem) or ( self.pudata["VCSOVERRIDE"] and self.pudata["VCSOVERRIDE"] != vcsSystemOverride @@ -3031,17 +3019,16 @@ Public method to get the list of file type associations for the given association type. - @param associationType type of the association (one of FORMS, - INTERFACES, OTHERS, PROTOCOLS, RESOURCES, SOURCES, - TRANSLATIONS or __IGNORE__) + @param associationType type of the association (one of the known file categories + or __IGNORE__) @type str @return list of file patterns for the given type @rtype list of str """ return [ assoc - for assoc in self.pdata["FILETYPES"] - if self.pdata["FILETYPES"][assoc] == associationType + for assoc in self.__pdata["FILETYPES"] + if self.__pdata["FILETYPES"][assoc] == associationType ] def __showLexerAssociations(self): @@ -3065,12 +3052,12 @@ @return the requested lexer language (string) """ # try user settings first - for pattern, language in list(self.pdata["LEXERASSOCS"].items()): + for pattern, language in list(self.__pdata["LEXERASSOCS"].items()): if fnmatch.fnmatch(filename, pattern): return language # try project type specific defaults next - projectType = self.pdata["PROJECTTYPE"] + projectType = self.__pdata["PROJECTTYPE"] with contextlib.suppress(KeyError): if self.__lexerAssociationCallbacks[projectType] is not None: return self.__lexerAssociationCallbacks[projectType](filename) @@ -3115,7 +3102,7 @@ ok = self.__readProject(fn) if ok: self.opened = True - if not self.pdata["FILETYPES"]: + if not self.__pdata["FILETYPES"]: self.initFileTypes() else: self.updateFileTypes() @@ -3172,15 +3159,16 @@ vcsSystem = "None" else: vcsSystem = vcsData[0][0] - self.pdata["VCS"] = vcsSystem + self.__pdata["VCS"] = vcsSystem self.vcs = self.initVCS() self.setDirty(True) if self.vcs is not None and ( self.vcs.vcsRegisteredState(self.ppath) != self.vcs.canBeCommitted ): - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.vcs = self.initVCS() + self.reloadAct.setEnabled(True) self.closeAct.setEnabled(True) self.saveasAct.setEnabled(True) self.actGrp2.setEnabled(True) @@ -3197,12 +3185,16 @@ self.menuDiagramAct.setEnabled(True) self.menuApidocAct.setEnabled(True) self.menuPackagersAct.setEnabled(True) - self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"] in ["E7Plugin"]) + self.pluginGrp.setEnabled( + self.__pdata["PROJECTTYPE"] in ["E7Plugin"] + ) self.addLanguageAct.setEnabled( - bool(self.pdata["TRANSLATIONPATTERN"]) + bool(self.__pdata["TRANSLATIONPATTERN"]) ) - self.makeGrp.setEnabled(self.pdata["MAKEPARAMS"]["MakeEnabled"]) - self.menuMakeAct.setEnabled(self.pdata["MAKEPARAMS"]["MakeEnabled"]) + self.makeGrp.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) + self.menuMakeAct.setEnabled( + self.__pdata["MAKEPARAMS"]["MakeEnabled"] + ) self.menuOtherToolsAct.setEnabled(True) self.menuFormattingAct.setEnabled(True) @@ -3213,7 +3205,7 @@ self.__model.projectOpened() - if self.pdata["EMBEDDED_VENV"]: + if self.__pdata["EMBEDDED_VENV"]: envPath = self.__findEmbeddedEnvironment() if bool(envPath): self.__loadEnvironmentConfiguration() @@ -3225,7 +3217,7 @@ self.__configureEnvironment(envPath) else: self.__createEmbeddedEnvironment() - self.menuEnvironmentAct.setEnabled(self.pdata["EMBEDDED_VENV"]) + self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) self.projectOpenedHooks.emit() self.projectOpened.emit() @@ -3242,11 +3234,11 @@ if restoreSession: # open the main script - if self.pdata["MAINSCRIPT"]: - if not os.path.isabs(self.pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + if self.__pdata["MAINSCRIPT"]: + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) else: - ms = self.pdata["MAINSCRIPT"] + ms = self.__pdata["MAINSCRIPT"] self.sourceFile.emit(ms) # open a project session file being quiet about errors @@ -3459,6 +3451,7 @@ self.__closeAllWindows() self.__initData() + self.reloadAct.setEnabled(False) self.closeAct.setEnabled(False) self.saveasAct.setEnabled(False) self.saveAct.setEnabled(False) @@ -3502,7 +3495,7 @@ filesWithSyntaxErrors = 0 for fn in vm.getOpenFilenames(): rfn = self.getRelativePath(fn) - if rfn in self.pdata["SOURCES"] or rfn in self.pdata["OTHERS"]: + if rfn in self.__pdata["SOURCES"] or rfn in self.__pdata["OTHERS"]: editor = vm.getOpenEditor(fn) success &= vm.saveEditorEd(editor) if reportSyntaxErrors and editor.hasSyntaxErrors(): @@ -3536,7 +3529,7 @@ filesWithSyntaxErrors = 0 for fn in vm.getOpenFilenames(): rfn = self.getRelativePath(fn) - if rfn in self.pdata["SOURCES"] or rfn in self.pdata["OTHERS"]: + if rfn in self.__pdata["SOURCES"] or rfn in self.__pdata["OTHERS"]: editor = vm.getOpenEditor(fn) success &= editor.checkDirty() if reportSyntaxErrors and editor.hasSyntaxErrors(): @@ -3568,11 +3561,11 @@ @return filename of the projects main script @rtype str """ - if self.pdata["MAINSCRIPT"]: + if self.__pdata["MAINSCRIPT"]: if normalized: - return os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + return os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) else: - return self.pdata["MAINSCRIPT"] + return self.__pdata["MAINSCRIPT"] else: return "" @@ -3591,8 +3584,7 @@ """ Public method to get the file entries of the given type. - @param fileType project file type (one of SOURCES, FORMS, RESOURCES, - INTERFACES, PROTOCOLS, OTHERS, TRANSLATIONS) + @param fileType project file type (one of the known file categories) @type str @param normalized flag indicating normalized file names are wanted @type boolean @@ -3600,21 +3592,13 @@ @rtype list of str @exception ValueError raised when an unsupported file type is given """ - if fileType not in [ - "SOURCES", - "FORMS", - "RESOURCES", - "INTERFACES", - "PROTOCOLS", - "OTHERS", - "TRANSLATIONS", - ]: + if fileType not in self.getFileCategories(): raise ValueError("Given file type has incorrect value.") if normalized: - return [os.path.join(self.ppath, fn) for fn in self.pdata[fileType]] + return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] else: - return self.pdata[fileType] + return self.__pdata[fileType] def getProjectType(self): """ @@ -3622,7 +3606,7 @@ @return UI type of the project (string) """ - return self.pdata["PROJECTTYPE"] + return self.__pdata["PROJECTTYPE"] def getProjectLanguage(self): """ @@ -3630,7 +3614,7 @@ @return programming language (string) """ - return self.pdata["PROGLANGUAGE"] + return self.__pdata["PROGLANGUAGE"] def isMixedLanguageProject(self): """ @@ -3639,7 +3623,7 @@ @return flag indicating a mixed language project @rtype bool """ - return self.pdata["MIXEDLANGUAGE"] + return self.__pdata["MIXEDLANGUAGE"] def isPythonProject(self): """ @@ -3648,7 +3632,7 @@ @return flag indicating a Python project (boolean) """ - return self.pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"] + return self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"] def isPy3Project(self): """ @@ -3656,7 +3640,7 @@ @return flag indicating a Python3 project (boolean) """ - return self.pdata["PROGLANGUAGE"] == "Python3" + return self.__pdata["PROGLANGUAGE"] == "Python3" def isMicroPythonProject(self): """ @@ -3665,7 +3649,7 @@ @return flag indicating a MicroPython project @rtype bool """ - return self.pdata["PROGLANGUAGE"] == "MicroPython" + return self.__pdata["PROGLANGUAGE"] == "MicroPython" def isRubyProject(self): """ @@ -3673,7 +3657,7 @@ @return flag indicating a Ruby project (boolean) """ - return self.pdata["PROGLANGUAGE"] == "Ruby" + return self.__pdata["PROGLANGUAGE"] == "Ruby" def isJavaScriptProject(self): """ @@ -3681,7 +3665,7 @@ @return flag indicating a JavaScript project (boolean) """ - return self.pdata["PROGLANGUAGE"] == "JavaScript" + return self.__pdata["PROGLANGUAGE"] == "JavaScript" def getProjectSpellLanguage(self): """ @@ -3689,7 +3673,7 @@ @return programming language (string) """ - return self.pdata["SPELLLANGUAGE"] + return self.__pdata["SPELLLANGUAGE"] def getProjectDictionaries(self): """ @@ -3699,14 +3683,14 @@ project specific word and exclude list """ pwl = "" - if self.pdata["SPELLWORDS"]: - pwl = os.path.join(self.ppath, self.pdata["SPELLWORDS"]) + if self.__pdata["SPELLWORDS"]: + pwl = os.path.join(self.ppath, self.__pdata["SPELLWORDS"]) if not os.path.isfile(pwl): pwl = "" pel = "" - if self.pdata["SPELLEXCLUDES"]: - pel = os.path.join(self.ppath, self.pdata["SPELLEXCLUDES"]) + if self.__pdata["SPELLEXCLUDES"]: + pel = os.path.join(self.ppath, self.__pdata["SPELLEXCLUDES"]) if not os.path.isfile(pel): pel = "" @@ -3719,7 +3703,7 @@ @return default extension (including the dot) (string) """ - lang = self.pdata["PROGLANGUAGE"] + lang = self.__pdata["PROGLANGUAGE"] if lang in ("", "Python"): lang = "Python3" return self.__sourceExtensions(lang)[0] @@ -3795,7 +3779,7 @@ @return project hash as a hex string (string) """ - return self.pdata["HASH"] + return self.__pdata["HASH"] def getRelativePath(self, path): """ @@ -3852,8 +3836,8 @@ @return eol string (string) """ - if self.pdata["EOL"] >= 0: - return self.eols[self.pdata["EOL"]] + if self.__pdata["EOL"] >= 0: + return self.eols[self.__pdata["EOL"]] else: eolMode = Preferences.getEditor("EOLMode") if eolMode == QsciScintilla.EolMode.EolWindows: @@ -3872,7 +3856,7 @@ @return flag indicating the usage of system eol (boolean) """ - return self.pdata["EOL"] == 0 + return self.__pdata["EOL"] == 0 def getProjectVersion(self): """ @@ -3881,7 +3865,7 @@ @return version number @rtype str """ - return self.pdata["VERSION"] + return self.__pdata["VERSION"] def getProjectAuthor(self): """ @@ -3890,7 +3874,7 @@ @return author name @rtype str """ - return self.pdata["AUTHOR"] + return self.__pdata["AUTHOR"] def getProjectAuthorEmail(self): """ @@ -3899,7 +3883,7 @@ @return project author email @rtype str """ - return self.pdata["EMAIL"] + return self.__pdata["EMAIL"] def getProjectDescription(self): """ @@ -3908,7 +3892,7 @@ @return project description @rtype str """ - return self.pdata["DESCRIPTION"] + return self.__pdata["DESCRIPTION"] def getProjectVenv(self, resolveDebugger=True): """ @@ -3923,7 +3907,7 @@ """ venvName = ( self.__venvConfiguration["name"] - if self.pdata["EMBEDDED_VENV"] and bool(self.__venvConfiguration["name"]) + if self.__pdata["EMBEDDED_VENV"] and bool(self.__venvConfiguration["name"]) else self.getDebugProperty("VIRTUALENV") ) if ( @@ -3948,7 +3932,7 @@ """ interpreter = ( self.__venvConfiguration["interpreter"] - if self.pdata["EMBEDDED_VENV"] + if self.__pdata["EMBEDDED_VENV"] else "" ) if not interpreter: @@ -3971,7 +3955,7 @@ @return executable search path prefix @rtype str """ - if self.pdata["EMBEDDED_VENV"]: + if self.__pdata["EMBEDDED_VENV"]: execPath = self.__venvConfiguration["exec_path"] else: execPath = "" @@ -3993,7 +3977,7 @@ @rtype str """ try: - return self.pdata["TESTING_FRAMEWORK"] + return self.__pdata["TESTING_FRAMEWORK"] except KeyError: return "" @@ -4005,7 +3989,7 @@ @rtype str """ try: - return self.pdata["LICENSE"] + return self.__pdata["LICENSE"] except KeyError: return "" @@ -4022,16 +4006,7 @@ newfn = os.path.abspath(fn) newfn = self.getRelativePath(newfn) return any( - newfn in self.pdata[group] - for group in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "TRANSLATIONS", - "OTHERS", - ] + newfn in self.__pdata[category] for category in self.getFileCategories() ) def isProjectFile(self, fn): @@ -4043,16 +4018,8 @@ @return flag indicating membership (boolean) """ return any( - self.__checkProjectFileGroup(fn, group) - for group in [ - "SOURCES", - "FORMS", - "INTERFACES", - "PROTOCOLS", - "RESOURCES", - "TRANSLATIONS", - "OTHERS", - ] + self.__checkProjectFileGroup(fn, category) + for category in self.getFileCategories() ) def __checkProjectFileGroup(self, fn, group): @@ -4066,75 +4033,41 @@ """ newfn = os.path.abspath(fn) newfn = self.getRelativePath(newfn) - if newfn in self.pdata[group] or ( + if newfn in self.__pdata[group] or ( group == "OTHERS" - and any(newfn.startswith(entry) for entry in self.pdata[group]) + and any(newfn.startswith(entry) for entry in self.__pdata[group]) ): return True if Utilities.isWindowsPlatform(): # try the above case-insensitive newfn = newfn.lower() - if any(entry.lower() == newfn for entry in self.pdata[group]): + if any(entry.lower() == newfn for entry in self.__pdata[group]): return True - elif group == "OTHERS" and any( - newfn.startswith(entry.lower()) for entry in self.pdata[group] - ): - return True + + elif group == "OTHERS" and any( + newfn.startswith(entry.lower()) for entry in self.__pdata[group] + ): + return True return False - def isProjectSource(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project sources. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "SOURCES") - - def isProjectForm(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project forms. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "FORMS") - - def isProjectInterface(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project interfaces. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "INTERFACES") - - def isProjectProtocol(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project protocols. + def isProjectCategory(self, fn, category): + """ + Public method to check, if the passed in filename belongs to the given + category. @param fn filename to be checked @type str + @param category file category to check against + @type str @return flag indicating membership @rtype bool """ - return self.__checkProjectFileGroup(fn, "PROTOCOLS") - - def isProjectResource(self, fn): - """ - Public method used to check, if the passed in filename belongs to the - project resources. - - @param fn filename to be checked (string) - @return flag indicating membership (boolean) - """ - return self.__checkProjectFileGroup(fn, "RESOURCES") + if category in self.getFileCategories(): + return self.__checkProjectFileGroup(fn, category) + else: + return False # unknown category always returns False def initActions(self): """ @@ -4179,11 +4112,27 @@ ) act.setStatusTip(self.tr("Open an existing project")) act.setWhatsThis( - self.tr("""<b>Open...</b>""" """<p>This opens an existing project.</p>""") + self.tr("""<b>Open...</b><p>This opens an existing project.</p>""") ) act.triggered.connect(self.openProject) self.actions.append(act) + self.reloadAct = EricAction( + self.tr("Reload project"), + EricPixmapCache.getIcon("projectReload"), + self.tr("&Reload"), + 0, + 0, + self.actGrp1, + "project_reload", + ) + self.reloadAct.setStatusTip(self.tr("Reload the current project")) + self.reloadAct.setWhatsThis( + self.tr("""<b>Reload</b><p>This reloads the current project.</p>""") + ) + self.reloadAct.triggered.connect(self.reopenProject) + self.actions.append(self.reloadAct) + self.closeAct = EricAction( self.tr("Close project"), EricPixmapCache.getIcon("projectClose"), @@ -4195,7 +4144,7 @@ ) self.closeAct.setStatusTip(self.tr("Close the current project")) self.closeAct.setWhatsThis( - self.tr("""<b>Close</b>""" """<p>This closes the current project.</p>""") + self.tr("""<b>Close</b><p>This closes the current project.</p>""") ) self.closeAct.triggered.connect(self.closeProject) self.actions.append(self.closeAct) @@ -4211,7 +4160,7 @@ ) self.saveAct.setStatusTip(self.tr("Save the current project")) self.saveAct.setWhatsThis( - self.tr("""<b>Save</b>""" """<p>This saves the current project.</p>""") + self.tr("""<b>Save</b><p>This saves the current project.</p>""") ) self.saveAct.triggered.connect(self.saveProject) self.actions.append(self.saveAct) @@ -4867,7 +4816,7 @@ self.actions.append(self.createSBOMAct) ################################################################### - ## Project Tools - code formatting actions + ## Project Tools - code formatting actions - Black ################################################################### self.blackFormattingGrp = createActionGroup(self) @@ -4993,6 +4942,108 @@ self.actions.append(self.blackConfigureAct) ################################################################### + ## Project Tools - code formatting actions - isort + ################################################################### + + self.isortFormattingGrp = createActionGroup(self) + + self.isortAboutAct = EricAction( + self.tr("About isort"), + self.tr("&isort"), + 0, + 0, + self.isortFormattingGrp, + "project_isort_about", + ) + self.isortAboutAct.setStatusTip(self.tr("Show some information about 'isort'.")) + self.isortAboutAct.setWhatsThis( + self.tr( + "<b>isort</b>" + "<p>This shows some information about the installed 'isort' tool.</p>" + ) + ) + self.isortAboutAct.triggered.connect(aboutIsort) + self.actions.append(self.isortAboutAct) + font = self.isortAboutAct.font() + font.setBold(True) + self.isortAboutAct.setFont(font) + + self.isortSortImportsAct = EricAction( + self.tr("Sort Imports"), + self.tr("Sort Imports"), + 0, + 0, + self.isortFormattingGrp, + "project_isort_sort_imports", + ) + self.isortSortImportsAct.setStatusTip( + self.tr("Sort the import statements of the project sources with 'isort'.") + ) + self.isortSortImportsAct.setWhatsThis( + self.tr( + "<b>Sort Imports</b>" + "<p>This shows a dialog to enter parameters for the imports sorting" + " run and sorts the import statements of the project sources using" + " 'isort'.</p>" + ) + ) + self.isortSortImportsAct.triggered.connect( + lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Sort) + ) + self.actions.append(self.isortSortImportsAct) + + self.isortDiffSortingAct = EricAction( + self.tr("Imports Sorting Diff"), + self.tr("Imports Sorting Diff"), + 0, + 0, + self.isortFormattingGrp, + "project_isort_diff_code", + ) + self.isortDiffSortingAct.setStatusTip( + self.tr( + "Generate a unified diff of potential project source imports" + " resorting with 'isort'." + ) + ) + self.isortDiffSortingAct.setWhatsThis( + self.tr( + "<b>Imports Sorting Diff</b>" + "<p>This shows a dialog to enter parameters for the imports sorting" + " diff run and generates a unified diff of potential project source" + " changes using 'isort'.</p>" + ) + ) + self.isortDiffSortingAct.triggered.connect( + lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Diff) + ) + self.actions.append(self.isortDiffSortingAct) + + self.isortConfigureAct = EricAction( + self.tr("Configure"), + self.tr("Configure"), + 0, + 0, + self.isortFormattingGrp, + "project_isort_configure", + ) + self.isortConfigureAct.setStatusTip( + self.tr( + "Enter the parameters for resorting the project sources import" + " statements with 'isort'." + ) + ) + self.isortConfigureAct.setWhatsThis( + self.tr( + "<b>Configure</b>" + "<p>This shows a dialog to enter the parameters for resorting the" + " import statements of the project sources with 'isort'.</p>" + ) + ) + self.isortConfigureAct.triggered.connect(self.__configureIsort) + self.actions.append(self.isortConfigureAct) + + ################################################################### ## Project - embedded environment actions ################################################################### @@ -5081,6 +5132,7 @@ self.recreateVenvAct.triggered.connect(self.__createEmbeddedEnvironment) self.actions.append(self.recreateVenvAct) + self.reloadAct.setEnabled(False) self.closeAct.setEnabled(False) self.saveAct.setEnabled(False) self.saveasAct.setEnabled(False) @@ -5205,6 +5257,8 @@ self.formattingMenu.setTearOffEnabled(True) self.formattingMenu.addActions(self.blackFormattingGrp.actions()) self.formattingMenu.addSeparator() + self.formattingMenu.addActions(self.isortFormattingGrp.actions()) + self.formattingMenu.addSeparator() # build the project main menu menu.setTearOffEnabled(True) @@ -5275,6 +5329,8 @@ (EricToolBarManager) @return tuple of the generated toolbars (tuple of two QToolBar) """ + from eric7 import VCS + tb = QToolBar(self.tr("Project"), self.ui) tb.setIconSize(Config.ToolBarIconSize) tb.setObjectName("ProjectToolbar") @@ -5293,8 +5349,6 @@ toolbarManager.addAction(self.propsAct, tb.windowTitle()) toolbarManager.addAction(self.userPropsAct, tb.windowTitle()) - from eric7 import VCS - vcstb = VCS.getBasicHelper(self).initBasicToolbar(self.ui, toolbarManager) return tb, vcstb @@ -5304,7 +5358,7 @@ Private method to set up the project menu. """ self.menuRecentAct.setEnabled(len(self.recent) > 0) - self.menuEnvironmentAct.setEnabled(self.pdata["EMBEDDED_VENV"]) + self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) self.showMenu.emit("Main", self.__menus["Main"]) @@ -5381,9 +5435,9 @@ """ Private slot to show the Find Project File dialog. """ + from .QuickFindFileDialog import QuickFindFileDialog + if self.__findProjectFileDialog is None: - from .QuickFindFileDialog import QuickFindFileDialog - self.__findProjectFileDialog = QuickFindFileDialog(self) self.__findProjectFileDialog.sourceFile.connect(self.sourceFile) self.__findProjectFileDialog.designerFile.connect(self.designerFile) @@ -5406,13 +5460,15 @@ @param onUserDemand flag indicating whether this method was requested by the user via a menu action (boolean) """ + from .AddFoundFilesDialog import AddFoundFilesDialog + autoInclude = Preferences.getProject("AutoIncludeNewFiles") recursiveSearch = Preferences.getProject("SearchNewFilesRecursively") newFiles = [] ignore_patterns = [ pattern - for pattern, filetype in self.pdata["FILETYPES"].items() + for pattern, filetype in self.__pdata["FILETYPES"].items() if filetype == "__IGNORE__" ] @@ -5432,8 +5488,8 @@ except OSError: newSources = [] pattern = ( - self.pdata["TRANSLATIONPATTERN"].replace("%language%", "*") - if self.pdata["TRANSLATIONPATTERN"] + self.__pdata["TRANSLATIONPATTERN"].replace("%language%", "*") + if self.__pdata["TRANSLATIONPATTERN"] else "*.ts" ) binpattern = self.__binaryTranslationFile(pattern) @@ -5465,24 +5521,22 @@ filetype = "" bfn = os.path.basename(fn) - for pattern in sorted(self.pdata["FILETYPES"].keys(), reverse=True): + for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True): if fnmatch.fnmatch(bfn, pattern): - filetype = self.pdata["FILETYPES"][pattern] + filetype = self.__pdata["FILETYPES"][pattern] break if ( - (filetype == "SOURCES" and fn not in self.pdata["SOURCES"]) - or (filetype == "FORMS" and fn not in self.pdata["FORMS"]) - or (filetype == "INTERFACES" and fn not in self.pdata["INTERFACES"]) - or (filetype == "PROTOCOLS" and fn not in self.pdata["PROTOCOLS"]) - or (filetype == "RESOURCES" and fn not in self.pdata["RESOURCES"]) - or (filetype == "OTHERS" and fn not in self.pdata["OTHERS"]) - or ( - filetype == "TRANSLATIONS" - and fn not in self.pdata["TRANSLATIONS"] - and ( - fnmatch.fnmatch(ns, pattern) - or fnmatch.fnmatch(ns, binpattern) + filetype in self.getFileCategories() + and fn not in self.__pdata[filetype] + and ( + filetype != "TRANSLATIONS" + or ( + filetype == "TRANSLATIONS" + and ( + fnmatch.fnmatch(ns, pattern) + or fnmatch.fnmatch(ns, binpattern) + ) ) ) ): @@ -5506,8 +5560,6 @@ return # autoInclude is not set, show a dialog - from .AddFoundFilesDialog import AddFoundFilesDialog - dlg = AddFoundFilesDialog(newFiles, self.parent(), None) res = dlg.exec() @@ -5531,7 +5583,7 @@ @param updateModel flag indicating an update of the model is requested (boolean) """ - self.projectOthersAdded.emit(fn) + self.projectFileAdded.emit(fn, "OTHERS") updateModel and self.__model.addNewItem("OTHERS", fn) def getActions(self): @@ -5600,20 +5652,22 @@ (boolean) @return a reference to the vcs object """ + from eric7 import VCS + vcs = None forProject = True override = False if vcsSystem is None: - if self.pdata["VCS"] and self.pdata["VCS"] != "None": - vcsSystem = self.pdata["VCS"] + if self.__pdata["VCS"] and self.__pdata["VCS"] != "None": + vcsSystem = self.__pdata["VCS"] else: forProject = False if ( forProject - and self.pdata["VCS"] - and self.pdata["VCS"] != "None" + and self.__pdata["VCS"] + and self.__pdata["VCS"] != "None" and self.pudata["VCSOVERRIDE"] and not nooverride ): @@ -5621,8 +5675,6 @@ override = True if vcsSystem is not None: - from eric7 import VCS - try: vcs = VCS.factory(vcsSystem) except ImportError: @@ -5661,7 +5713,7 @@ ) vcs = None if forProject: - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.setDirty(True) else: vcs.vcsInitConfig(self) @@ -5670,17 +5722,15 @@ # set the vcs options if vcs.vcsSupportCommandOptions(): with contextlib.suppress(LookupError): - vcsopt = copy.deepcopy(self.pdata["VCSOPTIONS"]) + vcsopt = copy.deepcopy(self.__pdata["VCSOPTIONS"]) vcs.vcsSetOptions(vcsopt) # set vcs specific data with contextlib.suppress(LookupError): - vcsother = copy.deepcopy(self.pdata["VCSOTHERDATA"]) + vcsother = copy.deepcopy(self.__pdata["VCSOTHERDATA"]) vcs.vcsSetOtherData(vcsother) if forProject: if vcs is None: - from eric7 import VCS - self.vcsProjectHelper = VCS.getBasicHelper(self) self.vcsBasicHelper = True else: @@ -5696,7 +5746,7 @@ """ Public method to reset the VCS. """ - self.pdata["VCS"] = "None" + self.__pdata["VCS"] = "None" self.vcs = self.initVCS() ericApp().getObject("PluginManager").deactivateVcsPlugins() @@ -5781,13 +5831,13 @@ """ Private slot used to calculate some code metrics for the project files. """ + from eric7.DataViews.CodeMetricsDialog import CodeMetricsDialog + files = [ os.path.join(self.ppath, file) - for file in self.pdata["SOURCES"] + for file in self.__pdata["SOURCES"] if file.endswith(".py") ] - from eric7.DataViews.CodeMetricsDialog import CodeMetricsDialog - self.codemetrics = CodeMetricsDialog() self.codemetrics.show() self.codemetrics.prepare(files) @@ -5797,6 +5847,8 @@ Private slot used to show the code coverage information for the project files. """ + from eric7.DataViews.PyCoverageDialog import PyCoverageDialog + fn = self.getMainScript(True) if fn is None: EricMessageBox.critical( @@ -5829,11 +5881,9 @@ files = [ os.path.join(self.ppath, file) - for file in self.pdata["SOURCES"] + for file in self.__pdata["SOURCES"] if os.path.splitext(file)[1].startswith(".py") ] - from eric7.DataViews.PyCoverageDialog import PyCoverageDialog - self.codecoverage = PyCoverageDialog() self.codecoverage.show() self.codecoverage.start(fn, files) @@ -5842,6 +5892,8 @@ """ Private slot used to show the profiling information for the project. """ + from eric7.DataViews.PyProfileDialog import PyProfileDialog + fn = self.getMainScript(True) if fn is None: EricMessageBox.critical( @@ -5872,8 +5924,6 @@ else: return - from eric7.DataViews.PyProfileDialog import PyProfileDialog - self.profiledata = PyProfileDialog() self.profiledata.show() self.profiledata.start(fn) @@ -5909,6 +5959,8 @@ """ Public method to handle the application diagram context menu action. """ + from eric7.Graphics.UMLDialog import UMLDialog, UMLDialogType + res = EricMessageBox.yesNo( self.ui, self.tr("Application Diagram"), @@ -5916,8 +5968,6 @@ yesDefault=True, ) - from eric7.Graphics.UMLDialog import UMLDialog, UMLDialogType - self.applicationDiagram = UMLDialog( UMLDialogType.APPLICATION_DIAGRAM, self, self.parent(), noModules=not res ) @@ -6059,16 +6109,8 @@ # build the list of entries lst_ = [] - for key in [ - "SOURCES", - "FORMS", - "RESOURCES", - "TRANSLATIONS", - "INTERFACES", - "PROTOCOLS", - "OTHERS", - ]: - lst_.extend(self.pdata[key]) + for key in self.getFileCategories(): + lst_.extend(self.__pdata[key]) lst = [] for entry in lst_: if os.path.isdir(self.getAbsolutePath(entry)): @@ -6095,7 +6137,7 @@ # write the file try: - newline = None if self.pdata["EOL"] == 0 else self.getEolString() + newline = None if self.__pdata["EOL"] == 0 else self.getEolString() with open(pkglist, "w", encoding="utf-8", newline=newline) as pkglistFile: pkglistFile.write("\n".join(header) + "\n") pkglistFile.write( @@ -6114,7 +6156,7 @@ ) return - if "PKGLIST" not in self.pdata["OTHERS"]: + if "PKGLIST" not in self.__pdata["OTHERS"]: self.appendFile("PKGLIST") @pyqtSlot() @@ -6124,7 +6166,7 @@ @param snapshot flag indicating snapshot archives (boolean) """ - if not self.pdata["MAINSCRIPT"]: + if not self.__pdata["MAINSCRIPT"]: EricMessageBox.critical( self.ui, self.tr("Create Plugin Archive"), @@ -6238,7 +6280,7 @@ os.path.join(self.ppath, archiveName) if archiveName else os.path.join( - self.ppath, self.pdata["MAINSCRIPT"].replace(".py", ".zip") + self.ppath, self.__pdata["MAINSCRIPT"].replace(".py", ".zip") ) ) try: @@ -6260,16 +6302,16 @@ if name: try: self.__createZipDirEntries(os.path.split(name)[0], archiveFile) - if snapshot and name == self.pdata["MAINSCRIPT"]: + if snapshot and name == self.__pdata["MAINSCRIPT"]: snapshotSource, version = self.__createSnapshotSource( - os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) ) archiveFile.writestr(name, snapshotSource) else: archiveFile.write(os.path.join(self.ppath, name), name) - if name == self.pdata["MAINSCRIPT"]: + if name == self.__pdata["MAINSCRIPT"]: version = self.__pluginExtractVersion( - os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) + os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) ) if archiveVersion and ( self.__pluginVersionToTuple(version) @@ -6289,7 +6331,7 @@ archiveFile.writestr("VERSION", version.encode("utf-8")) archiveFile.close() - if archive not in self.pdata["OTHERS"]: + if archive not in self.__pdata["OTHERS"]: self.appendFile(archive) progress.setValue(len(selectedLists)) @@ -6434,7 +6476,7 @@ @return flag indicating default parameter set @rtype bool """ - return self.pdata["MAKEPARAMS"] == { + return self.__pdata["MAKEPARAMS"] == { "MakeEnabled": False, "MakeExecutable": "", "MakeFile": "", @@ -6450,17 +6492,19 @@ @return flag indicating enabled make support @rtype bool """ - return self.pdata["MAKEPARAMS"]["MakeEnabled"] + return self.__pdata["MAKEPARAMS"]["MakeEnabled"] @pyqtSlot() - def executeMake(self): - """ - Public slot to execute a project specific make run (auto-run) + def __autoExecuteMake(self): + """ + Private slot to execute a project specific make run (auto-run) (execute or question). """ - self.__executeMake( - questionOnly=self.pdata["MAKEPARAMS"]["MakeTestOnly"], interactive=False - ) + if Preferences.getProject("AutoExecuteMake"): + self.__executeMake( + questionOnly=self.__pdata["MAKEPARAMS"]["MakeTestOnly"], + interactive=False, + ) @pyqtSlot() def __executeMake(self, questionOnly=False, interactive=True): @@ -6474,31 +6518,33 @@ @type bool """ if ( - not self.pdata["MAKEPARAMS"]["MakeEnabled"] + not self.__pdata["MAKEPARAMS"]["MakeEnabled"] or self.__makeProcess is not None ): return prog = ( - self.pdata["MAKEPARAMS"]["MakeExecutable"] - if self.pdata["MAKEPARAMS"]["MakeExecutable"] + self.__pdata["MAKEPARAMS"]["MakeExecutable"] + if self.__pdata["MAKEPARAMS"]["MakeExecutable"] else Project.DefaultMake ) args = [] - if self.pdata["MAKEPARAMS"]["MakeParameters"]: + if self.__pdata["MAKEPARAMS"]["MakeParameters"]: args.extend( - Utilities.parseOptionString(self.pdata["MAKEPARAMS"]["MakeParameters"]) + Utilities.parseOptionString( + self.__pdata["MAKEPARAMS"]["MakeParameters"] + ) ) - if self.pdata["MAKEPARAMS"]["MakeFile"]: - args.append("--makefile={0}".format(self.pdata["MAKEPARAMS"]["MakeFile"])) + if self.__pdata["MAKEPARAMS"]["MakeFile"]: + args.append("--makefile={0}".format(self.__pdata["MAKEPARAMS"]["MakeFile"])) if questionOnly: args.append("--question") - if self.pdata["MAKEPARAMS"]["MakeTarget"]: - args.append(self.pdata["MAKEPARAMS"]["MakeTarget"]) + if self.__pdata["MAKEPARAMS"]["MakeTarget"]: + args.append(self.__pdata["MAKEPARAMS"]["MakeTarget"]) self.__makeProcess = QProcess(self) self.__makeProcess.readyReadStandardOutput.connect(self.__makeReadStdOut) @@ -6571,11 +6617,11 @@ # a rebuild is needed title = self.tr("Test for Changes") - if self.pdata["MAKEPARAMS"]["MakeTarget"]: + if self.__pdata["MAKEPARAMS"]["MakeTarget"]: message = self.tr( """<p>There are changes that require the configured""" """ make target <b>{0}</b> to be rebuilt.</p>""" - ).format(self.pdata["MAKEPARAMS"]["MakeTarget"]) + ).format(self.__pdata["MAKEPARAMS"]["MakeTarget"]) else: message = self.tr( """<p>There are changes that require the default""" @@ -6620,7 +6666,7 @@ @return flag indicating default parameter set @rtype bool """ - return self.pdata["IDLPARAMS"] == { + return self.__pdata["IDLPARAMS"] == { "IncludeDirs": [], "DefinedNames": [], "UndefinedNames": [], @@ -6638,7 +6684,7 @@ @return flag indicating default parameter set @rtype bool """ - return self.pdata["UICPARAMS"] == { + return self.__pdata["UICPARAMS"] == { "Package": "", "RcSuffix": "", "PackagesRoot": "", @@ -6653,8 +6699,8 @@ @return value of the given parameter @rtype any, None in case on non-existence """ - if name in self.pdata["UICPARAMS"]: - return self.pdata["UICPARAMS"][name] + if name in self.__pdata["UICPARAMS"]: + return self.__pdata["UICPARAMS"][name] else: return None @@ -6670,7 +6716,7 @@ @return flag indicating default parameter set @rtype bool """ - return self.pdata["RCCPARAMS"] == self.getDefaultRccCompilerParameters() + return self.__pdata["RCCPARAMS"] == self.getDefaultRccCompilerParameters() def getDefaultRccCompilerParameters(self): """ @@ -6698,7 +6744,7 @@ @return flag indicating default parameter @rtype bool """ - return self.pdata["DOCSTRING"] == "" + return self.__pdata["DOCSTRING"] == "" def getDocstringType(self): """ @@ -6707,7 +6753,7 @@ @return configured docstring style @rtype str """ - return self.pdata["DOCSTRING"] + return self.__pdata["DOCSTRING"] ######################################################################### ## Below are methods implementing the 'SBOM' support @@ -6724,7 +6770,7 @@ """ Private slot to create a SBOM file of the project dependencies. """ - import CycloneDXInterface + import CycloneDXInterface # __IGNORE_WARNING_I102__ CycloneDXInterface.createCycloneDXFile("<project>") @@ -6787,6 +6833,56 @@ dlg.getConfiguration(saveToProject=True) # The data is saved to the project as a side effect. + def __performImportSortingWithIsort(self, action): + """ + Private method to format the project sources import statements using the + 'isort' tool. + + Following actions are supported. + <ul> + <li>IsortFormattingAction.Format - the imports reformatting is performed</li> + <li>IsortFormattingAction.Check - a check is performed, if imports formatting + is necessary</li> + <li>IsortFormattingAction.Diff - a unified diff of potential imports formatting + changes is generated</li> + </ul> + + @param action formatting operation to be performed + @type IsortFormattingAction + """ + from eric7.CodeFormatting.IsortConfigurationDialog import ( + IsortConfigurationDialog, + ) + from eric7.CodeFormatting.IsortFormattingDialog import IsortFormattingDialog + + if ericApp().getObject("ViewManager").checkAllDirty(): + dlg = IsortConfigurationDialog(withProject=True) + if dlg.exec() == QDialog.DialogCode.Accepted: + config = dlg.getConfiguration(saveToProject=True) + + isortDialog = IsortFormattingDialog( + config, + self.getProjectFiles("SOURCES", normalized=True), + project=self, + action=action, + ) + isortDialog.exec() + + @pyqtSlot() + def __configureIsort(self): + """ + Private slot to enter the parameters for formatting the import statements of the + project sources with 'isort'. + """ + from eric7.CodeFormatting.IsortConfigurationDialog import ( + IsortConfigurationDialog, + ) + + dlg = IsortConfigurationDialog(withProject=True, onlyProject=True) + if dlg.exec() == QDialog.DialogCode.Accepted: + dlg.getConfiguration(saveToProject=True) + # The data is saved to the project as a side effect. + ######################################################################### ## Below are methods implementing the 'Embedded Environment' support ######################################################################### @@ -6820,8 +6916,8 @@ @param value flag indicating an embedded environment @type bool """ - if value != self.pdata["EMBEDDED_VENV"]: - self.pdata["EMBEDDED_VENV"] = value + if value != self.__pdata["EMBEDDED_VENV"]: + self.__pdata["EMBEDDED_VENV"] = value self.setDirty(True) def __initVenvConfiguration(self): @@ -6843,6 +6939,7 @@ @type bool (optional) """ from eric7.VirtualEnv.VirtualenvExecDialog import VirtualenvExecDialog + from .ProjectVenvCreationParametersDialog import ( ProjectVenvCreationParametersDialog, )