diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Project/Project.py --- a/src/eric7/Project/Project.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Project/Project.py Wed Jul 13 14:55:47 2022 +0200 @@ -18,13 +18,17 @@ import pathlib from PyQt6.QtCore import ( - pyqtSlot, QFile, pyqtSignal, QCryptographicHash, QIODevice, QByteArray, - QObject, QProcess + pyqtSlot, + QFile, + pyqtSignal, + QCryptographicHash, + QIODevice, + QByteArray, + QObject, + QProcess, ) from PyQt6.QtGui import QKeySequence, QAction -from PyQt6.QtWidgets import ( - QLineEdit, QToolBar, QDialog, QInputDialog, QMenu -) +from PyQt6.QtWidgets import QLineEdit, QToolBar, QDialog, QInputDialog, QMenu from PyQt6.Qsci import QsciScintilla from EricWidgets.EricApplication import ericApp @@ -58,7 +62,7 @@ class Project(QObject): """ 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 projectLanguageAddedByCode(str) emitted after a new language was @@ -125,6 +129,7 @@ @signal appendStderr(str) emitted after something was received from a QProcess on stderr """ + dirty = pyqtSignal(bool) projectLanguageAdded = pyqtSignal(str) projectLanguageAddedByCode = pyqtSignal(str) @@ -168,71 +173,72 @@ projectChanged = pyqtSignal() appendStdout = pyqtSignal(str) appendStderr = pyqtSignal(str) - + eols = [os.linesep, "\n", "\r", "\r\n"] - + DefaultMake = "make" DefaultMakefile = "makefile" - + def __init__(self, parent=None, filename=None): """ Constructor - + @param parent parent widget (usually the ui object) (QWidget) @param filename optional filename of a project file to open (string) """ super().__init__(parent) - + self.ui = parent - + self.__progLanguages = [ "Python3", "MicroPython", "Ruby", "JavaScript", ] - + self.__dbgFilters = { "Python3": self.tr( - "Python3 Files (*.py *.py3);;" - "Python3 GUI Files (*.pyw *.pyw3);;"), + "Python3 Files (*.py *.py3);;" "Python3 GUI Files (*.pyw *.pyw3);;" + ), } - + self.vcsMenu = None self.__makeProcess = None - + self.__initProjectTypes() - + self.__initData() - + self.__projectFile = ProjectFile(self) self.__userProjectFile = UserProjectFile(self) self.__debuggerPropertiesFile = DebuggerPropertiesFile(self) self.__sessionFile = SessionFile(False) self.__tasksFile = TasksFile(False) - + self.recent = [] self.__loadRecent() - + if filename is not None: self.openProject(filename) else: self.vcs = self.initVCS() - + from .ProjectBrowserModel import ProjectBrowserModel + self.__model = ProjectBrowserModel(self) - + self.codemetrics = None self.codecoverage = None self.profiledata = None self.applicationDiagram = None self.loadedDiagram = None self.__findProjectFileDialog = None - + def __sourceExtensions(self, language): """ Private method to get the source extensions of a programming language. - + @param language programming language (string) @return source extensions (list of string) """ @@ -254,23 +260,21 @@ return { "Ruby": [".rb"], "JavaScript": [".js"], - "Mixed": ( - Preferences.getPython("Python3Extensions") + - ['.rb', '.js']), + "Mixed": (Preferences.getPython("Python3Extensions") + [".rb", ".js"]), }.get(language, "") - + def getProgrammingLanguages(self): """ Public method to get the programming languages supported by project. - + @return list of supported programming languages (list of string) """ return self.__progLanguages[:] - + def getDebuggerFilters(self, language): """ Public method to get the debugger filters for a programming language. - + @param language programming language @type str @return filter string @@ -280,7 +284,7 @@ return self.__dbgFilters[language] except KeyError: return "" - + def __initProjectTypes(self): """ Private method to initialize the list of supported project types. @@ -288,7 +292,7 @@ self.__fileTypeCallbacks = {} self.__lexerAssociationCallbacks = {} self.__binaryTranslationsCallbacks = {} - + self.__projectTypes = { "PyQt5": self.tr("PyQt5 GUI"), "PyQt5C": self.tr("PyQt5 Console"), @@ -298,31 +302,36 @@ "Console": self.tr("Console"), "Other": self.tr("Other"), } - + self.__projectProgLanguages = { - "Python3": ["PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin", - "Console", "Other"], + "Python3": [ + "PyQt5", + "PyQt5C", + "PyQt6", + "PyQt6C", + "E7Plugin", + "Console", + "Other", + ], "MicroPython": ["Console", "Other"], "Ruby": ["Console", "Other"], "JavaScript": ["Other"], } - + if Utilities.checkPyside(variant=2): self.__projectTypes["PySide2"] = self.tr("PySide2 GUI") self.__projectTypes["PySide2C"] = self.tr("PySide2 Console") - self.__projectProgLanguages["Python3"].extend( - ["PySide2", "PySide2C"]) - + self.__projectProgLanguages["Python3"].extend(["PySide2", "PySide2C"]) + if Utilities.checkPyside(variant=6): self.__projectTypes["PySide6"] = self.tr("PySide6 GUI") self.__projectTypes["PySide6C"] = self.tr("PySide6 Console") - self.__projectProgLanguages["Python3"].extend( - ["PySide6", "PySide6C"]) - + self.__projectProgLanguages["Python3"].extend(["PySide6", "PySide6C"]) + def getProjectTypes(self, progLanguage=""): """ Public method to get the list of supported project types. - + @param progLanguage programming language to get project types for (string) @return reference to the dictionary of project types. @@ -334,29 +343,35 @@ return ptypes else: return self.__projectTypes - + def hasProjectType(self, type_, progLanguage=""): """ Public method to check, if a project type is already registered. - + @param type_ internal type designator (string) @param progLanguage programming language of the project type (string) @return flag indicating presence of the project type (boolean) """ if progLanguage: return ( - progLanguage in self.__projectProgLanguages and - type_ in self.__projectProgLanguages[progLanguage] + progLanguage in self.__projectProgLanguages + and type_ in self.__projectProgLanguages[progLanguage] ) else: return type_ in self.__projectTypes - - def registerProjectType(self, type_, description, fileTypeCallback=None, - binaryTranslationsCallback=None, - lexerAssociationCallback=None, progLanguages=None): + + def registerProjectType( + self, + type_, + description, + fileTypeCallback=None, + binaryTranslationsCallback=None, + lexerAssociationCallback=None, + progLanguages=None, + ): """ Public method to register a project type. - + @param type_ internal type designator to be registered (string) @param description more verbose type name (display string) (string) @param fileTypeCallback reference to a method returning a dictionary @@ -378,11 +393,11 @@ self.tr("Registering Project Type"), self.tr( """<p>The Programming Language <b>{0}</b> is not""" - """ supported (project type: {1}).</p>""") - .format(progLanguage, type_) + """ supported (project type: {1}).</p>""" + ).format(progLanguage, type_), ) return - + if type_ in self.__projectProgLanguages[progLanguage]: EricMessageBox.critical( self.ui, @@ -390,25 +405,25 @@ self.tr( """<p>The Project type <b>{0}</b> is already""" """ registered with Programming Language""" - """ <b>{1}</b>.</p>""") - .format(type_, progLanguage) + """ <b>{1}</b>.</p>""" + ).format(type_, progLanguage), ) return - + if type_ in self.__projectTypes: EricMessageBox.critical( self.ui, self.tr("Registering Project Type"), - self.tr("""<p>The Project type <b>{0}</b> is already""" - """ registered.</p>""").format(type_) + self.tr( + """<p>The Project type <b>{0}</b> is already""" + """ registered.</p>""" + ).format(type_), ) else: self.__projectTypes[type_] = description self.__fileTypeCallbacks[type_] = fileTypeCallback self.__lexerAssociationCallbacks[type_] = lexerAssociationCallback - self.__binaryTranslationsCallbacks[type_] = ( - binaryTranslationsCallback - ) + self.__binaryTranslationsCallbacks[type_] = binaryTranslationsCallback if progLanguages: for progLanguage in progLanguages: self.__projectProgLanguages[progLanguage].append(type_) @@ -416,11 +431,11 @@ # no specific programming languages given -> add to all for progLanguage in self.__projectProgLanguages: self.__projectProgLanguages[progLanguage].append(type_) - + def unregisterProjectType(self, type_): """ Public method to unregister a project type. - + @param type_ internal type designator to be unregistered (string) """ for progLanguage in self.__projectProgLanguages: @@ -434,15 +449,15 @@ del self.__lexerAssociationCallbacks[type_] if type_ in self.__binaryTranslationsCallbacks: del self.__binaryTranslationsCallbacks[type_] - + def __initData(self): """ Private method to initialize the project data part. """ - self.loaded = False # flag for the loaded status - self.__dirty = False # dirty flag - self.pfile = "" # name of the project file - self.ppath = "" # name of the project directory + self.loaded = False # flag for the loaded status + self.__dirty = False # dirty flag + self.pfile = "" # name of the project file + self.ppath = "" # name of the project directory self.translationsRoot = "" # the translations prefix self.name = "" self.opened = False @@ -451,10 +466,10 @@ self.otherssubdirs = [] self.vcs = None self.vcsRequested = False - self.dbgVirtualEnv = '' - self.dbgCmdline = '' - self.dbgWd = '' - self.dbgEnv = '' + self.dbgVirtualEnv = "" + self.dbgCmdline = "" + self.dbgWd = "" + self.dbgEnv = "" self.dbgReportExceptions = True self.dbgExcList = [] self.dbgExcIgnoreList = [] @@ -467,7 +482,7 @@ "enable": False, "redirect": True, } - + self.pdata = { "DESCRIPTION": "", "VERSION": "", @@ -485,16 +500,15 @@ "VCS": "None", "VCSOPTIONS": {}, "VCSOTHERDATA": {}, - "AUTHOR": '', - "EMAIL": '', - "HASH": '', + "AUTHOR": "", + "EMAIL": "", + "HASH": "", "PROGLANGUAGE": "Python3", "MIXEDLANGUAGE": False, "PROJECTTYPE": "PyQt5", - "SPELLLANGUAGE": - Preferences.getEditor("SpellCheckingDefaultLanguage"), - "SPELLWORDS": '', - "SPELLEXCLUDES": '', + "SPELLLANGUAGE": Preferences.getEditor("SpellCheckingDefaultLanguage"), + "SPELLWORDS": "", + "SPELLEXCLUDES": "", "FILETYPES": {}, "LEXERASSOCS": {}, "PROJECTTYPESPECIFICDATA": {}, @@ -521,8 +535,8 @@ "PackagesRoot": "", }, "RCCPARAMS": { - "CompressionThreshold": 70, # default value - "CompressLevel": 0, # use zlib default + "CompressionThreshold": 70, # default value + "CompressLevel": 0, # use zlib default "CompressionDisable": False, "PathPrefix": "", }, @@ -531,20 +545,20 @@ "TESTING_FRAMEWORK": "", "LICENSE": "", } - + self.__initDebugProperties() - + self.pudata = { "VCSOVERRIDE": "", "VCSSTATUSMONITORINTERVAL": 0, } - + self.vcs = self.initVCS() - + def getData(self, category, key): """ Public method to get data out of the project data store. - + @param category category of the data to get (string, one of PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS or OTHERTOOLSPARMS) @@ -553,19 +567,24 @@ """ # __IGNORE_WARNING_D202__ if ( - category in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS", - "PACKAGERSPARMS", "DOCUMENTATIONPARMS", - "OTHERTOOLSPARMS"] and - key in self.pdata[category] + category + in [ + "PROJECTTYPESPECIFICDATA", + "CHECKERSPARMS", + "PACKAGERSPARMS", + "DOCUMENTATIONPARMS", + "OTHERTOOLSPARMS", + ] + and key in self.pdata[category] ): return copy.deepcopy(self.pdata[category][key]) else: return None - + def setData(self, category, key, data): """ Public method to store data in the project data store. - + @param category category of the data to get (string, one of PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS or OTHERTOOLSPARMS) @@ -574,11 +593,15 @@ @return flag indicating success (boolean) """ # __IGNORE_WARNING_D202__ - if category not in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS", - "PACKAGERSPARMS", "DOCUMENTATIONPARMS", - "OTHERTOOLSPARMS"]: + if category not in [ + "PROJECTTYPESPECIFICDATA", + "CHECKERSPARMS", + "PACKAGERSPARMS", + "DOCUMENTATIONPARMS", + "OTHERTOOLSPARMS", + ]: return False - + # 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: @@ -597,7 +620,7 @@ else: return False return True - + def initFileTypes(self): """ Public method to initialize the filetype associations with default @@ -615,77 +638,92 @@ "makefile": "OTHERS", "Makefile": "OTHERS", } - + # 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" - + # IDL interfaces self.pdata["FILETYPES"]["*.idl"] = "INTERFACES" - + # Protobuf Files self.pdata["FILETYPES"]["*.proto"] = "PROTOCOLS" - + # Forms - if self.pdata["PROJECTTYPE"] in ["E7Plugin", - "PyQt5", - "PyQt6", - "PySide2", - "PySide6"]: + if self.pdata["PROJECTTYPE"] in [ + "E7Plugin", + "PyQt5", + "PyQt6", + "PySide2", + "PySide6", + ]: self.pdata["FILETYPES"]["*.ui"] = "FORMS" - + # Resources - if self.pdata["PROJECTTYPE"] in ["PyQt5", "PyQt5C", - "PySide2", "PySide2C", - "PySide6", "PySide6C"]: + if self.pdata["PROJECTTYPE"] in [ + "PyQt5", + "PyQt5C", + "PySide2", + "PySide2C", + "PySide6", + "PySide6C", + ]: self.pdata["FILETYPES"]["*.qrc"] = "RESOURCES" - + # Translations - if self.pdata["PROJECTTYPE"] in ["E7Plugin", - "PyQt5", "PyQt5C", - "PyQt6", "PyQt6C", - "PySide2", "PySide2C", - "PySide6", "PySide6C"]: + if self.pdata["PROJECTTYPE"] in [ + "E7Plugin", + "PyQt5", + "PyQt5C", + "PyQt6", + "PyQt6C", + "PySide2", + "PySide2C", + "PySide6", + "PySide6C", + ]: 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: + if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]] is not None: ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"]]() self.pdata["FILETYPES"].update(ftypes) - + self.setDirty(True) - + def updateFileTypes(self): """ Public method to update the filetype associations with new default values. """ - if self.pdata["PROJECTTYPE"] in ["E7Plugin", - "PyQt5", "PyQt5C", - "PyQt6", "PyQt6C", - "PySide2", "PySide2C", - "PySide6", "PySide6C"]: + if self.pdata["PROJECTTYPE"] in [ + "E7Plugin", + "PyQt5", + "PyQt5C", + "PyQt6", + "PyQt6C", + "PySide2", + "PySide2C", + "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" with contextlib.suppress(KeyError): - if self.__fileTypeCallbacks[ - self.pdata["PROJECTTYPE"]] is not None: + 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 self.setDirty(True) - + def __loadRecent(self): """ Private method to load the recently opened project filenames. @@ -697,72 +735,70 @@ for f in rp: if pathlib.Path(f).exists(): self.recent.append(f) - + def __saveRecent(self): """ Private method to save the list of recently opened filenames. """ Preferences.Prefs.rsettings.setValue(recentNameProject, self.recent) Preferences.Prefs.rsettings.sync() - + def getMostRecent(self): """ Public method to get the most recently opened project. - + @return path of the most recently opened project (string) """ if len(self.recent): return self.recent[0] else: return None - + def getModel(self): """ Public method to get a reference to the project browser model. - + @return reference to the project browser model (ProjectBrowserModel) """ return self.__model - + def startFileSystemMonitoring(self): """ Public method to (re)start monitoring the project file system. """ self.__model.startFileSystemMonitoring() - + def stopFileSystemMonitoring(self): """ Public method to stop monitoring the project file system. """ self.__model.stopFileSystemMonitoring() - + def getVcs(self): """ Public method to get a reference to the VCS object. - + @return reference to the VCS object """ return self.vcs - + def handlePreferencesChanged(self): """ Public slot used to handle the preferencesChanged signal. """ if self.pudata["VCSSTATUSMONITORINTERVAL"]: - self.setStatusMonitorInterval( - self.pudata["VCSSTATUSMONITORINTERVAL"]) + self.setStatusMonitorInterval(self.pudata["VCSSTATUSMONITORINTERVAL"]) else: - self.setStatusMonitorInterval( - Preferences.getVCS("StatusMonitorInterval")) - + self.setStatusMonitorInterval(Preferences.getVCS("StatusMonitorInterval")) + self.__model.preferencesChanged() - + def setDirty(self, dirty): """ Public method to set the dirty state. - + It emits the signal dirty(bool). - + @param dirty dirty state @type bool """ @@ -771,31 +807,31 @@ self.dirty.emit(dirty) if self.__dirty: self.projectChanged.emit() - + def isDirty(self): """ Public method to return the dirty state. - + @return dirty state (boolean) """ return self.__dirty - + def isOpen(self): """ Public method to return the opened state. - + @return open state (boolean) """ return self.opened - + def __checkFilesExist(self, index): """ Private method to check, if the files in a list exist. - + The files in the indicated list are checked for existance in the filesystem. Non existant files are removed from the list and the dirty state of the project is changed accordingly. - + @param index key of the list to be checked (string) """ removed = False @@ -804,16 +840,16 @@ 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.setDirty(True) - + def __readProject(self, fn): """ Private method to read in a project (.epj or .e4p) file. - + @param fn filename of the project file to be read (string) @return flag indicating success """ @@ -826,17 +862,21 @@ f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): from 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"]: - hashStr = str(QCryptographicHash.hash( - QByteArray(self.ppath.encode("utf-8")), - QCryptographicHash.Algorithm.Sha1).toHex(), - encoding="utf-8") + hashStr = str( + QCryptographicHash.hash( + QByteArray(self.ppath.encode("utf-8")), + QCryptographicHash.Algorithm.Sha1, + ).toHex(), + encoding="utf-8", + ) self.pdata["HASH"] = hashStr self.setDirty(True) else: @@ -844,33 +884,33 @@ self.ui, self.tr("Read Project File"), self.tr( - "<p>The project file <b>{0}</b> could not be read." - "</p>") - .format(fn)) + "<p>The project file <b>{0}</b> could not be read." "</p>" + ).format(fn), + ) res = False - + if res: self.pfile = os.path.abspath(fn) self.ppath = os.path.abspath(os.path.dirname(fn)) - + # insert filename into list of recently opened projects self.__syncRecent() - + if self.pdata["TRANSLATIONPATTERN"]: self.translationsRoot = self.pdata["TRANSLATIONPATTERN"].split( - "%language%")[0] + "%language%" + )[0] elif self.pdata["MAINSCRIPT"]: - self.translationsRoot = os.path.splitext( - self.pdata["MAINSCRIPT"])[0] + self.translationsRoot = os.path.splitext(self.pdata["MAINSCRIPT"])[0] if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)): dn = self.translationsRoot else: dn = os.path.dirname(self.translationsRoot) if dn not in self.subdirs: self.subdirs.append(dn) - + self.name = os.path.splitext(os.path.basename(fn))[0] - + # check, if the files of the project still exist in the # project directory self.__checkFilesExist("SOURCES") @@ -880,32 +920,32 @@ self.__checkFilesExist("TRANSLATIONS") self.__checkFilesExist("RESOURCES") self.__checkFilesExist("OTHERS") - + # 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"] + 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) - + # get the names of other subdirectories for fn in self.pdata["OTHERS"]: dn = os.path.dirname(fn) if dn not in self.otherssubdirs: self.otherssubdirs.append(dn) - + return res def __writeProject(self, fn=None): """ Private method to save the project infos to a project file. - + @param fn optional filename of the project file to be written (string). If fn is None, the filename stored in the project object is used. This is the 'save' action. If fn is given, this filename @@ -915,33 +955,35 @@ """ if self.vcs is not None: self.pdata["VCSOPTIONS"] = copy.deepcopy(self.vcs.vcsGetOptions()) - self.pdata["VCSOTHERDATA"] = copy.deepcopy( - self.vcs.vcsGetOtherData()) - + self.pdata["VCSOTHERDATA"] = copy.deepcopy(self.vcs.vcsGetOtherData()) + if not self.pdata["HASH"]: - hashStr = str(QCryptographicHash.hash( - QByteArray(self.ppath.encode("utf-8")), - QCryptographicHash.Algorithm.Sha1).toHex(), - encoding="utf-8") + hashStr = str( + QCryptographicHash.hash( + QByteArray(self.ppath.encode("utf-8")), + QCryptographicHash.Algorithm.Sha1, + ).toHex(), + encoding="utf-8", + ) self.pdata["HASH"] = hashStr - + if fn is None: fn = self.pfile - + with EricOverrideCursor(): res = self.__projectFile.writeFile(fn) - + if res: self.pfile = os.path.abspath(fn) self.ppath = os.path.abspath(os.path.dirname(fn)) self.name = os.path.splitext(os.path.basename(fn))[0] self.setDirty(False) - + # insert filename into list of recently opened projects self.__syncRecent() - + return res - + def __readUserProperties(self): """ Private method to read in the user specific project file (.eqj or @@ -949,21 +991,20 @@ """ if self.pfile is None: return - + fn1, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), - '{0}.eqj'.format(fn1)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.eqj".format(fn1)) if os.path.exists(fn): # try the new JSON based format first self.__userProjectFile.readFile(fn) else: # try the old XML based format second - fn = os.path.join(self.getProjectManagementDir(), - '{0}.e4q'.format(fn1)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.e4q".format(fn1)) if os.path.exists(fn): f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): from EricXML.UserProjectReader import UserProjectReader + reader = UserProjectReader(f, self) reader.readXML() f.close() @@ -973,21 +1014,23 @@ self.tr("Read User Project Properties"), self.tr( "<p>The user specific project properties file" - " <b>{0}</b> could not be read.</p>").format(fn)) - + " <b>{0}</b> could not be read.</p>" + ).format(fn), + ) + def __writeUserProperties(self): """ Private method to write the user specific project data to a JSON file. """ if self.pfile is None: return - + fn, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), '{0}.eqj'.format(fn)) - + fn = os.path.join(self.getProjectManagementDir(), "{0}.eqj".format(fn)) + with EricOverrideCursor(): self.__userProjectFile.writeFile(fn) - + def __showContextMenuSession(self): """ Private slot called before the Session menu is shown. @@ -997,21 +1040,17 @@ enable = False else: fn, ext = os.path.splitext(os.path.basename(self.pfile)) - fn_new = os.path.join(self.getProjectManagementDir(), - '{0}.esj'.format(fn)) - fn_old = os.path.join(self.getProjectManagementDir(), - '{0}.e5s'.format(fn)) + fn_new = os.path.join(self.getProjectManagementDir(), "{0}.esj".format(fn)) + fn_old = os.path.join(self.getProjectManagementDir(), "{0}.e5s".format(fn)) enable = os.path.exists(fn_new) or os.path.exists(fn_old) - self.sessActGrp.findChild( - QAction, "project_load_session").setEnabled(enable) - self.sessActGrp.findChild( - QAction, "project_delete_session").setEnabled(enable) - + self.sessActGrp.findChild(QAction, "project_load_session").setEnabled(enable) + self.sessActGrp.findChild(QAction, "project_delete_session").setEnabled(enable) + @pyqtSlot() def __readSession(self, quiet=False, indicator=""): """ Private method to read in the project session file (.esj or .e5s). - + @param quiet flag indicating quiet operations. If this flag is true, no errors are reported. @param indicator indicator string (string) @@ -1021,23 +1060,27 @@ EricMessageBox.critical( self.ui, self.tr("Read Project Session"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + fn1, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), - '{0}{1}.esj'.format(fn1, indicator)) + fn = os.path.join( + self.getProjectManagementDir(), "{0}{1}.esj".format(fn1, indicator) + ) if os.path.exists(fn): # try the new JSON based format first self.__sessionFile.readFile(fn) else: # try the old XML based format second - fn = os.path.join(self.getProjectManagementDir(), - '{0}{1}.e5s'.format(fn1, indicator)) + fn = os.path.join( + self.getProjectManagementDir(), "{0}{1}.e5s".format(fn1, indicator) + ) if os.path.exists(fn): f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): from EricXML.SessionReader import SessionReader + reader = SessionReader(f, False) reader.readXML(quiet=quiet) f.close() @@ -1048,13 +1091,15 @@ self.tr("Read project session"), self.tr( "<p>The project session file <b>{0}</b> could" - " not be read.</p>").format(fn)) - + " not be read.</p>" + ).format(fn), + ) + @pyqtSlot() def __writeSession(self, quiet=False, indicator=""): """ Private method to write the session data to an XML file (.esj). - + @param quiet flag indicating quiet operations. If this flag is true, no errors are reported. @param indicator indicator string (string) @@ -1064,15 +1109,17 @@ EricMessageBox.critical( self.ui, self.tr("Save Project Session"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + fn, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), - '{0}{1}.esj'.format(fn, indicator)) - + fn = os.path.join( + self.getProjectManagementDir(), "{0}{1}.esj".format(fn, indicator) + ) + self.__sessionFile.writeFile(fn) - + def __deleteSession(self): """ Private method to delete the session file. @@ -1081,14 +1128,16 @@ EricMessageBox.critical( self.ui, self.tr("Delete Project Session"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + fname, ext = os.path.splitext(os.path.basename(self.pfile)) - + for ext in (".esj", ".e5s", ".e4s"): fn = os.path.join( - self.getProjectManagementDir(), "{0}{1}".format(fname, ext)) + self.getProjectManagementDir(), "{0}{1}".format(fname, ext) + ) if os.path.exists(fn): try: os.remove(fn) @@ -1098,8 +1147,10 @@ self.tr("Delete Project Session"), self.tr( "<p>The project session file <b>{0}</b> could" - " not be deleted.</p>").format(fn)) - + " not be deleted.</p>" + ).format(fn), + ) + def __readTasks(self): """ Private method to read in the project tasks file (.etj or .e6t). @@ -1108,23 +1159,23 @@ EricMessageBox.critical( self.ui, self.tr("Read Tasks"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + base, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), - '{0}.etj'.format(base)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.etj".format(base)) if os.path.exists(fn): # try new style JSON file first self.__tasksFile.readFile(fn) else: # try old style XML file second - fn = os.path.join(self.getProjectManagementDir(), - '{0}.e6t'.format(base)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.e6t".format(base)) if os.path.exists(fn): f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): from EricXML.TasksReader import TasksReader + reader = TasksReader(f, True) reader.readXML() f.close() @@ -1133,23 +1184,22 @@ self.ui, self.tr("Read Tasks"), self.tr( - "<p>The tasks file <b>{0}</b> could not be read." - "</p>") - .format(fn)) - + "<p>The tasks file <b>{0}</b> could not be read." "</p>" + ).format(fn), + ) + def writeTasks(self): """ Public method to write the tasks data to a JSON file (.etj). """ if self.pfile is None: return - + fn, ext = os.path.splitext(os.path.basename(self.pfile)) - - fn = os.path.join(self.getProjectManagementDir(), - '{0}.etj'.format(fn)) + + fn = os.path.join(self.getProjectManagementDir(), "{0}.etj".format(fn)) self.__tasksFile.writeFile(fn) - + def __showContextMenuDebugger(self): """ Private slot called before the Debugger menu is shown. @@ -1160,24 +1210,24 @@ else: fn, ext = os.path.splitext(os.path.basename(self.pfile)) # try new style file first - fn = os.path.join(self.getProjectManagementDir(), - '{0}.edj'.format(fn)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn)) if not os.path.exists(fn): # try old style file second - fn = os.path.join(self.getProjectManagementDir(), - '{0}.e4d'.format(fn)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.e4d".format(fn)) enable = os.path.exists(fn) self.dbgActGrp.findChild( - QAction, "project_debugger_properties_load").setEnabled(enable) + QAction, "project_debugger_properties_load" + ).setEnabled(enable) self.dbgActGrp.findChild( - QAction, "project_debugger_properties_delete").setEnabled(enable) - + QAction, "project_debugger_properties_delete" + ).setEnabled(enable) + @pyqtSlot() def __readDebugProperties(self, quiet=False): """ Private method to read in the project debugger properties file (.edj or .e4d). - + @param quiet flag indicating quiet operations. If this flag is true, no errors are reported. """ @@ -1186,12 +1236,12 @@ EricMessageBox.critical( self.ui, self.tr("Read Debugger Properties"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + fn1, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), - '{0}.edj'.format(fn1)) + fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn1)) if os.path.exists(fn): # try the new JSON based format first if self.__debuggerPropertiesFile.readFile(fn): @@ -1199,14 +1249,12 @@ self.debugPropertiesChanged = False else: # try the old XML based format second - fn = os.path.join(self.getProjectManagementDir(), - '{0}.e4d'.format(fn1)) - + fn = os.path.join(self.getProjectManagementDir(), "{0}.e4d".format(fn1)) + f = QFile(fn) if f.open(QIODevice.OpenModeFlag.ReadOnly): - from EricXML.DebuggerPropertiesReader import ( - DebuggerPropertiesReader - ) + from EricXML.DebuggerPropertiesReader import DebuggerPropertiesReader + reader = DebuggerPropertiesReader(f, self) reader.readXML(quiet=quiet) f.close() @@ -1219,13 +1267,15 @@ self.tr("Read Debugger Properties"), self.tr( "<p>The project debugger properties file" - " <b>{0}</b> could not be read.</p>").format(fn)) - + " <b>{0}</b> could not be read.</p>" + ).format(fn), + ) + @pyqtSlot() def __writeDebugProperties(self, quiet=False): """ Private method to write the project debugger properties file (.edj). - + @param quiet flag indicating quiet operations. If this flag is true, no errors are reported. """ @@ -1234,15 +1284,16 @@ EricMessageBox.critical( self.ui, self.tr("Save Debugger Properties"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + fn, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), '{0}.edj'.format(fn)) - + fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn)) + with EricOverrideCursor(): self.__debuggerPropertiesFile.writeFile(fn) - + def __deleteDebugProperties(self): """ Private method to delete the project debugger properties file @@ -1252,14 +1303,16 @@ EricMessageBox.critical( self.ui, self.tr("Delete Debugger Properties"), - self.tr("Please save the project first.")) + self.tr("Please save the project first."), + ) return - + fname, ext = os.path.splitext(os.path.basename(self.pfile)) - + for ext in (".edj", ".e4d"): - fn = os.path.join(self.getProjectManagementDir(), - "{0}{1}".format(fname, ext)) + fn = os.path.join( + self.getProjectManagementDir(), "{0}{1}".format(fname, ext) + ) if os.path.exists(fn): try: os.remove(fn) @@ -1269,9 +1322,10 @@ self.tr("Delete Debugger Properties"), self.tr( "<p>The project debugger properties file" - " <b>{0}</b> could not be deleted.</p>") - .format(fn)) - + " <b>{0}</b> could not be deleted.</p>" + ).format(fn), + ) + def __initDebugProperties(self): """ Private method to initialize the debug properties. @@ -1294,46 +1348,60 @@ "REDIRECT": False, "NOENCODING": False, } - + def isDebugPropertiesLoaded(self): """ Public method to return the status of the debug properties. - + @return load status of debug properties (boolean) """ return self.debugPropertiesLoaded - + def __showDebugProperties(self): """ Private slot to display the debugger properties dialog. """ from .DebuggerPropertiesDialog import DebuggerPropertiesDialog + dlg = DebuggerPropertiesDialog(self) if dlg.exec() == QDialog.DialogCode.Accepted: dlg.storeData() - + def getDebugProperty(self, key): """ Public method to retrieve a debugger property. - + @param key key of the property (string) @return value of the property """ if key == "INTERPRETER": return ( - ericApp().getObject("VirtualEnvManager") + ericApp() + .getObject("VirtualEnvManager") .getVirtualenvInterpreter(self.debugProperties["VIRTUALENV"]) ) else: return self.debugProperties[key] - - def setDbgInfo(self, venvName, argv, wd, env, excReporting, excList, - excIgnoreList, autoClearShell, tracePython=None, - autoContinue=None, enableMultiprocess=None, - multiprocessNoDebug=None, configOverride=None): + + def setDbgInfo( + self, + venvName, + argv, + wd, + env, + excReporting, + excList, + excIgnoreList, + autoClearShell, + tracePython=None, + autoContinue=None, + enableMultiprocess=None, + multiprocessNoDebug=None, + configOverride=None, + ): """ Public method to set the debugging information. - + @param venvName name of the virtual environment used @type str @param argv command line arguments to be used @@ -1372,8 +1440,8 @@ self.dbgWd = wd self.dbgEnv = env self.dbgReportExceptions = excReporting - self.dbgExcList = excList[:] # keep a copy of the list - self.dbgExcIgnoreList = excIgnoreList[:] # keep a copy of the list + self.dbgExcList = excList[:] # keep a copy of the list + self.dbgExcIgnoreList = excIgnoreList[:] # keep a copy of the list self.dbgAutoClearShell = autoClearShell if tracePython is not None: self.dbgTracePython = tracePython @@ -1385,24 +1453,24 @@ self.dbgMultiprocessNoDebug = multiprocessNoDebug if configOverride is not None: self.dbgGlobalConfigOverride = copy.deepcopy(configOverride) - + def getTranslationPattern(self): """ Public method to get the translation pattern. - + @return translation pattern (string) """ return self.pdata["TRANSLATIONPATTERN"] - + def setTranslationPattern(self, pattern): """ Public method to set the translation pattern. - + @param pattern translation pattern @type str """ self.pdata["TRANSLATIONPATTERN"] = pattern - + def addLanguage(self): """ Public slot used to add a language to the project. @@ -1411,43 +1479,53 @@ EricMessageBox.critical( self.ui, self.tr("Add Language"), - self.tr( - "You have to specify a translation pattern first.")) + self.tr("You have to specify a translation pattern first."), + ) return - + from .AddLanguageDialog import AddLanguageDialog + dlg = AddLanguageDialog(self.parent()) if dlg.exec() == QDialog.DialogCode.Accepted: lang = dlg.getSelectedLanguage() if self.pdata["PROJECTTYPE"] in [ - "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin", - "PySide2", "PySide2C", "PySide6", "PySide6C" + "PyQt5", + "PyQt5C", + "PyQt6", + "PyQt6C", + "E7Plugin", + "PySide2", + "PySide2C", + "PySide6", + "PySide6C", ]: - langFile = self.pdata["TRANSLATIONPATTERN"].replace( - "%language%", lang) + langFile = self.pdata["TRANSLATIONPATTERN"].replace("%language%", lang) self.appendFile(langFile) self.projectLanguageAddedByCode.emit(lang) - + def __binaryTranslationFile(self, langFile): """ Private method to calculate the filename of the binary translations file given the name of the raw translations file. - + @param langFile name of the raw translations file (string) @return name of the binary translations file (string) """ qmFile = "" try: - if self.__binaryTranslationsCallbacks[ - self.pdata["PROJECTTYPE"]] is not None: - qmFile = self.__binaryTranslationsCallbacks[ - self.pdata["PROJECTTYPE"]](langFile) + if ( + self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"]] + is not None + ): + qmFile = self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"]]( + langFile + ) except KeyError: - qmFile = langFile.replace('.ts', '.qm') + qmFile = langFile.replace(".ts", ".qm") if qmFile == langFile: qmFile = "" return qmFile - + def checkLanguageFiles(self): """ Public slot to check the language files after a release process. @@ -1456,25 +1534,23 @@ for langFile in self.pdata["TRANSLATIONS"][:]: qmFile = self.__binaryTranslationFile(langFile) if qmFile: - if ( - qmFile not in self.pdata["TRANSLATIONS"] and - os.path.exists(os.path.join(self.ppath, qmFile)) + 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(os.path.join(self.ppath, qmFile)) + if qmFile not in self.pdata["TRANSLATIONS"] and os.path.exists( + os.path.join(self.ppath, qmFile) ): self.appendFile(qmFile) - + def removeLanguageFile(self, langFile): """ Public slot to remove a translation from the project. - + The translation file is not deleted from the project directory. - + @param langFile the translation file to be removed (string) """ langFile = self.getRelativePath(langFile) @@ -1485,26 +1561,28 @@ with contextlib.suppress(ValueError): if self.pdata["TRANSLATIONSBINPATH"]: qmFile = self.getRelativePath( - os.path.join(self.pdata["TRANSLATIONSBINPATH"], - os.path.basename(qmFile))) + os.path.join( + self.pdata["TRANSLATIONSBINPATH"], os.path.basename(qmFile) + ) + ) self.pdata["TRANSLATIONS"].remove(qmFile) self.__model.removeItem(qmFile) self.setDirty(True) - + def deleteLanguageFile(self, langFile): """ Public slot to delete a translation from the project directory. - + @param langFile the translation file to be removed (string) """ try: from send2trash import send2trash as s2t except ImportError: s2t = os.remove - + langFile = self.getRelativePath(langFile) qmFile = self.__binaryTranslationFile(langFile) - + try: fn = os.path.join(self.ppath, langFile) if os.path.exists(fn): @@ -1515,19 +1593,22 @@ self.tr("Delete translation"), self.tr( "<p>The selected translation file <b>{0}</b> could not be" - " deleted.</p><p>Reason: {1}</p>").format( - langFile, str(err))) + " deleted.</p><p>Reason: {1}</p>" + ).format(langFile, str(err)), + ) return - + self.removeLanguageFile(langFile) - + # now get rid of the .qm file if qmFile: try: if self.pdata["TRANSLATIONSBINPATH"]: qmFile = self.getRelativePath( - os.path.join(self.pdata["TRANSLATIONSBINPATH"], - os.path.basename(qmFile))) + os.path.join( + self.pdata["TRANSLATIONSBINPATH"], os.path.basename(qmFile) + ) + ) fn = os.path.join(self.ppath, qmFile) if os.path.exists(fn): s2t(fn) @@ -1537,14 +1618,15 @@ self.tr("Delete translation"), self.tr( "<p>The selected translation file <b>{0}</b> could" - " not be deleted.</p><p>Reason: {1}</p>").format( - qmFile, str(err))) + " not be deleted.</p><p>Reason: {1}</p>" + ).format(qmFile, str(err)), + ) return - + def appendFile(self, fn, isSourceFile=False, updateModel=True): """ Public method to append a file to the project. - + @param fn filename to be added to the project (string) @param isSourceFile flag indicating that this is a source file even if it doesn't have the source extension (boolean) @@ -1552,31 +1634,29 @@ requested (boolean) """ dirty = False - + # make it relative to the project root, if it starts with that path # assume relative paths are relative to the project root newfn = self.getRelativePath(fn) if os.path.isabs(fn) else fn newdir = os.path.dirname(newfn) - + if isSourceFile: filetype = "SOURCES" else: filetype = "OTHERS" bfn = os.path.basename(newfn) - if fnmatch.fnmatch(bfn, '*.ts') or fnmatch.fnmatch(bfn, '*.qm'): + 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] break - + if filetype == "__IGNORE__": return - - if filetype in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", - "RESOURCES"]: + + if filetype in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES"]: if filetype == "SOURCES": if newfn not in self.pdata["SOURCES"]: self.pdata["SOURCES"].append(newfn) @@ -1597,8 +1677,7 @@ if newfn not in self.pdata["INTERFACES"]: self.pdata["INTERFACES"].append(newfn) self.projectInterfaceAdded.emit(newfn) - (updateModel and - self.__model.addNewItem("INTERFACES", newfn)) + (updateModel and self.__model.addNewItem("INTERFACES", newfn)) dirty = True else: updateModel and self.repopulateItem(newfn) @@ -1606,8 +1685,7 @@ if newfn not in self.pdata["PROTOCOLS"]: self.pdata["PROTOCOLS"].append(newfn) self.projectProtocolAdded.emit(newfn) - (updateModel and - self.__model.addNewItem("PROTOCOLS", newfn)) + (updateModel and self.__model.addNewItem("PROTOCOLS", newfn)) dirty = True else: updateModel and self.repopulateItem(newfn) @@ -1629,24 +1707,24 @@ dirty = True else: updateModel and self.repopulateItem(newfn) - else: # filetype == "OTHERS" + else: # filetype == "OTHERS" if newfn not in self.pdata["OTHERS"]: - self.pdata['OTHERS'].append(newfn) + self.pdata["OTHERS"].append(newfn) self.othersAdded(newfn, updateModel) dirty = True else: updateModel and self.repopulateItem(newfn) if newdir not in self.otherssubdirs: self.otherssubdirs.append(newdir) - + if dirty: self.setDirty(True) - + @pyqtSlot() def addFiles(self, fileTypeFilter=None, startdir=None): """ Public slot used to add files to the project. - + @param fileTypeFilter filter to be used by the add file dialog @type str out of source, form, resource, interface, protocol, others @param startdir start directory for the selection dialog @@ -1655,30 +1733,31 @@ if startdir is None: startdir = self.ppath from .AddFileDialog import AddFileDialog - dlg = AddFileDialog(self, self.parent(), fileTypeFilter, - startdir=startdir) + + dlg = AddFileDialog(self, self.parent(), fileTypeFilter, startdir=startdir) if dlg.exec() == QDialog.DialogCode.Accepted: fnames, target, isSource = dlg.getData() - if target != '': + if target != "": for fn in fnames: targetfile = os.path.join(target, os.path.basename(fn)) if not Utilities.samepath(os.path.dirname(fn), target): try: if not os.path.isdir(target): os.makedirs(target) - + if os.path.exists(targetfile): res = EricMessageBox.yesNo( self.ui, self.tr("Add file"), self.tr( "<p>The file <b>{0}</b> already" - " exists.</p><p>Overwrite it?</p>") - .format(targetfile), - icon=EricMessageBox.Warning) + " exists.</p><p>Overwrite it?</p>" + ).format(targetfile), + icon=EricMessageBox.Warning, + ) if not res: return # don't overwrite - + shutil.copy(fn, target) except OSError as why: EricMessageBox.critical( @@ -1687,23 +1766,24 @@ self.tr( "<p>The selected file <b>{0}</b> could" " not be added to <b>{1}</b>.</p>" - "<p>Reason: {2}</p>") - .format(fn, target, str(why))) + "<p>Reason: {2}</p>" + ).format(fn, target, str(why)), + ) continue - - self.appendFile(targetfile, - isSource or fileTypeFilter == 'source') + + self.appendFile(targetfile, isSource or fileTypeFilter == "source") else: EricMessageBox.critical( self.ui, self.tr("Add file"), - self.tr("The target directory must not be empty.")) - + self.tr("The target directory must not be empty."), + ) + def __addSingleDirectory(self, filetype, source, target, quiet=False): """ Private method used to add all files of a single directory to the project. - + @param filetype type of files to add (string) @param source source directory (string) @param target target directory (string) @@ -1717,12 +1797,12 @@ patterns.append(pattern) elif patterntype == "__IGNORE__": ignorePatterns.append(pattern) - + files = [] for pattern in patterns: sstring = "{0}{1}{2}".format(source, os.sep, pattern) files.extend(glob.glob(sstring)) - + if len(files) == 0: if not quiet: EricMessageBox.information( @@ -1730,13 +1810,12 @@ self.tr("Add directory"), self.tr( "<p>The source directory doesn't contain" - " any files belonging to the selected category.</p>")) + " any files belonging to the selected category.</p>" + ), + ) return - - if ( - not Utilities.samepath(target, source) and - not os.path.isdir(target) - ): + + if not Utilities.samepath(target, source) and not os.path.isdir(target): try: os.makedirs(target) except OSError as why: @@ -1745,15 +1824,16 @@ self.tr("Add directory"), self.tr( "<p>The target directory <b>{0}</b> could not be" - " created.</p><p>Reason: {1}</p>") - .format(target, str(why))) + " created.</p><p>Reason: {1}</p>" + ).format(target, str(why)), + ) return - + for file in files: for pattern in ignorePatterns: if fnmatch.fnmatch(file, pattern): continue - + targetfile = os.path.join(target, os.path.basename(file)) if not Utilities.samepath(target, source): try: @@ -1763,36 +1843,39 @@ self.tr("Add directory"), self.tr( "<p>The file <b>{0}</b> already exists.</p>" - "<p>Overwrite it?</p>") - .format(targetfile), - icon=EricMessageBox.Warning) + "<p>Overwrite it?</p>" + ).format(targetfile), + icon=EricMessageBox.Warning, + ) if not res: continue # don't overwrite, carry on with next file - + shutil.copy(file, target) except OSError: continue self.appendFile(targetfile) - + def __addRecursiveDirectory(self, filetype, source, target): """ Private method used to add all files of a directory tree. - + The tree is rooted at source to another one rooted at target. This method decents down to the lowest subdirectory. - + @param filetype type of files to add (string) @param source source directory (string) @param target target directory (string) """ # first perform the addition of source self.__addSingleDirectory(filetype, source, target, True) - - ignore_patterns = [pattern for pattern, filetype in - self.pdata["FILETYPES"].items() - if filetype == '__IGNORE__'] - + + ignore_patterns = [ + pattern + for pattern, filetype in self.pdata["FILETYPES"].items() + if filetype == "__IGNORE__" + ] + # now recurse into subdirectories for name in os.listdir(source): ns = os.path.join(source, name) @@ -1804,15 +1887,15 @@ break if skip: continue - + nt = os.path.join(target, name) self.__addRecursiveDirectory(filetype, ns, nt) - + @pyqtSlot() def addDirectory(self, fileTypeFilter=None, startdir=None): """ Public method used to add all files of a directory to the project. - + @param fileTypeFilter filter to be used by the add directory dialog @type str out of source, form, resource, interface, protocol, others @param startdir start directory for the selection dialog @@ -1821,135 +1904,137 @@ if startdir is None: startdir = self.ppath from .AddDirectoryDialog import AddDirectoryDialog - dlg = AddDirectoryDialog( - self, fileTypeFilter, self.parent(), startdir=startdir) + + dlg = AddDirectoryDialog(self, fileTypeFilter, self.parent(), startdir=startdir) if dlg.exec() == QDialog.DialogCode.Accepted: filetype, source, target, recursive = dlg.getData() - if target == '': + if target == "": EricMessageBox.critical( self.ui, self.tr("Add directory"), - self.tr("The target directory must not be empty.")) + self.tr("The target directory must not be empty."), + ) return - - if filetype == 'OTHERS': + + if filetype == "OTHERS": self.addToOthers(source) return - - if source == '': + + if source == "": EricMessageBox.critical( self.ui, self.tr("Add directory"), - self.tr("The source directory must not be empty.")) + self.tr("The source directory must not be empty."), + ) return - + if recursive: self.__addRecursiveDirectory(filetype, source, target) else: self.__addSingleDirectory(filetype, source, target) - + def addToOthers(self, fn): """ Public method to add a file/directory to the OTHERS project data. - + @param fn file name or directory name to add (string) """ if fn: # if it is below the project directory, make it relative to that fn = self.getRelativePath(fn) - + # if it ends with the directory separator character, remove it if fn.endswith(os.sep): fn = fn[:-1] - + if fn not in self.pdata["OTHERS"]: - self.pdata['OTHERS'].append(fn) + 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') - + self.addFiles("source") + def addUiFiles(self): """ Public slot to add forms to the current project. """ - self.addFiles('form') - + self.addFiles("form") + def addIdlFiles(self): """ Public slot to add IDL interfaces to the current project. """ - self.addFiles('interface') - + self.addFiles("interface") + def addProtoFiles(self): """ Public slot to add protocol files to the current project. """ - self.addFiles('protocol') - + self.addFiles("protocol") + def addResourceFiles(self): """ Public slot to add Qt resources to the current project. """ - self.addFiles('resource') - + self.addFiles("resource") + def addOthersFiles(self): """ Public slot to add files to the OTHERS project data. """ - self.addFiles('others') - + self.addFiles("others") + def addSourceDir(self): """ Public slot to add all source files of a directory to the current project. """ - self.addDirectory('source') - + self.addDirectory("source") + def addUiDir(self): """ Public slot to add all forms of a directory to the current project. """ - self.addDirectory('form') - + self.addDirectory("form") + def addIdlDir(self): """ Public slot to add all IDL interfaces of a directory to the current project. """ - self.addDirectory('interface') - + self.addDirectory("interface") + def addProtoDir(self): """ Public slot to add all protocol files of a directory to the current project. """ - self.addDirectory('protocol') - + self.addDirectory("protocol") + def addResourceDir(self): """ Public slot to add all Qt resource files of a directory to the current project. """ - self.addDirectory('resource') - + self.addDirectory("resource") + def addOthersDir(self): """ Public slot to add a directory to the OTHERS project data. """ - self.addDirectory('others') - + self.addDirectory("others") + def renameMainScript(self, oldfn, newfn): """ Public method to rename the main script. - + @param oldfn old filename (string) @param newfn new filename of the main script (string) """ @@ -1957,44 +2042,47 @@ ofn = self.getRelativePath(oldfn) if ofn != self.pdata["MAINSCRIPT"]: return - + fn = self.getRelativePath(newfn) self.pdata["MAINSCRIPT"] = fn self.setDirty(True) - + def renameFile(self, oldfn, newfn=None): """ Public slot to rename a file of the project. - + @param oldfn old filename of the file (string) @param newfn new filename of the file (string) @return flag indicating success """ fn = self.getRelativePath(oldfn) isSourceFile = fn in self.pdata["SOURCES"] - + if newfn is None: newfn = EricFileDialog.getSaveFileName( None, self.tr("Rename file"), oldfn, "", - EricFileDialog.DontConfirmOverwrite) + EricFileDialog.DontConfirmOverwrite, + ) if not newfn: return False newfn = Utilities.toNativeSeparators(newfn) - + if os.path.exists(newfn): res = EricMessageBox.yesNo( self.ui, self.tr("Rename File"), - self.tr("""<p>The file <b>{0}</b> already exists.""" - """ Overwrite it?</p>""") - .format(newfn), - icon=EricMessageBox.Warning) + self.tr( + """<p>The file <b>{0}</b> already exists.""" + """ Overwrite it?</p>""" + ).format(newfn), + icon=EricMessageBox.Warning, + ) if not res: return False - + try: os.rename(oldfn, newfn) except OSError as msg: @@ -2003,26 +2091,28 @@ self.tr("Rename File"), self.tr( """<p>The file <b>{0}</b> could not be renamed.<br />""" - """Reason: {1}</p>""").format(oldfn, str(msg))) + """Reason: {1}</p>""" + ).format(oldfn, str(msg)), + ) 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"] + 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"] ): self.renameFileInPdata(oldfn, newfn, isSourceFile) - + return True - + def renameFileInPdata(self, oldname, newname, isSourceFile=False): """ Public method to rename a file in the pdata structure. - + @param oldname old filename (string) @param newname new filename (string) @param isSourceFile flag indicating that this is a source file @@ -2038,94 +2128,131 @@ self.removeFile(oldname) self.appendFile(newname, isSourceFile) self.projectFileRenamed.emit(oldname, newname) - + self.renameMainScript(fn, newname) - + def getFiles(self, start): """ Public method to get all files starting with a common prefix. - + @param start prefix (string) @return list of files starting with a common prefix (list of strings) """ filelist = [] start = self.getRelativePath(start) - for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", - "OTHERS"]: + for key in [ + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "OTHERS", + ]: for entry in self.pdata[key][:]: if entry.startswith(start): filelist.append(os.path.join(self.ppath, entry)) return filelist - + def __reorganizeFiles(self): """ Private method to reorganize files stored in the project. """ reorganized = False - + # init data store for the reorganization newPdata = {} - for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", - "OTHERS", "TRANSLATIONS"]: + for key in [ + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "OTHERS", + "TRANSLATIONS", + ]: newPdata[key] = [] - + # iterate over all files checking for a reassignment - for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", - "OTHERS", "TRANSLATIONS"]: + for key in [ + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "OTHERS", + "TRANSLATIONS", + ]: for fn in self.pdata[key][:]: filetype = key 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] break - + if filetype != "__IGNORE__": newPdata[filetype].append(fn) if filetype != key: reorganized = True - + if reorganized: # copy the reorganized files back to the project - for key in ["SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", - "RESOURCES", "OTHERS", "TRANSLATIONS"]: + for key in [ + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "OTHERS", + "TRANSLATIONS", + ]: self.pdata[key] = newPdata[key][:] - + # repopulate the model self.__model.projectClosed(False) self.__model.projectOpened() - + def copyDirectory(self, olddn, newdn): """ Public slot to copy a directory. - + @param olddn original directory name (string) @param newdn new directory name (string) """ 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 entry in self.pdata[key][:]: 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), key == "SOURCES") self.setDirty(True) - + def moveDirectory(self, olddn, newdn): """ Public slot to move a directory. - + @param olddn old directory name (string) @param newdn new directory name (string) """ 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 entry in self.pdata[key][:]: if entry.startswith(olddn): if key not in typeStrings: @@ -2149,13 +2276,13 @@ else: self.__model.renameItem(olddn, self.getAbsolutePath(newdn)) self.directoryRemoved.emit(olddn) - + def removeFile(self, fn, updateModel=True): """ Public slot to remove a file from the project. - + The file is not deleted from the project directory. - + @param fn filename to be removed from the project @param updateModel flag indicating an update of the model is requested (boolean) @@ -2188,13 +2315,13 @@ updateModel and self.__model.removeItem(fn) if dirty: self.setDirty(True) - + def removeDirectory(self, dn): """ Public method to remove a directory from the project. - + The directory is not deleted from the project directory. - + @param dn directory name to be removed from the project """ dirty = False @@ -2204,8 +2331,14 @@ 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 key in [ + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "TRANSLATIONS", + ]: for entry in self.pdata[key][:]: if entry.startswith(dn2): self.pdata[key].remove(entry) @@ -2214,11 +2347,11 @@ if dirty: self.setDirty(True) self.directoryRemoved.emit(dn) - + def deleteFile(self, fn): """ Public method to delete a file from the project directory. - + @param fn filename to be deleted from the project @return flag indicating success (boolean) """ @@ -2226,22 +2359,22 @@ from send2trash import send2trash as s2t except ImportError: s2t = os.remove - + try: s2t(os.path.join(self.ppath, fn)) path, ext = os.path.splitext(fn) - if ext == '.ui': - fn2 = os.path.join(self.ppath, '{0}.h'.format(fn)) + if ext == ".ui": + fn2 = os.path.join(self.ppath, "{0}.h".format(fn)) if os.path.isfile(fn2): s2t(fn2) head, tail = os.path.split(path) - for ext in ['.pyc', '.pyo']: + for ext in [".pyc", ".pyo"]: fn2 = os.path.join(self.ppath, path + ext) if os.path.isfile(fn2): s2t(fn2) pat = os.path.join( - self.ppath, head, - "__pycache__", "{0}.*{1}".format(tail, ext)) + self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext) + ) for f in glob.glob(pat): s2t(f) except OSError as err: @@ -2250,19 +2383,20 @@ self.tr("Delete file"), self.tr( "<p>The selected file <b>{0}</b> could not be" - " deleted.</p><p>Reason: {1}</p>").format( - fn, str(err))) + " deleted.</p><p>Reason: {1}</p>" + ).format(fn, str(err)), + ) return False - + self.removeFile(fn) - if ext == '.ui': - self.removeFile(fn + '.h') + if ext == ".ui": + self.removeFile(fn + ".h") return True - + def deleteDirectory(self, dn): """ Public method to delete a directory from the project directory. - + @param dn directory name to be removed from the project @return flag indicating success (boolean) """ @@ -2271,6 +2405,7 @@ try: try: from send2trash import send2trash + send2trash(dn) except ImportError: shutil.rmtree(dn, True) @@ -2280,45 +2415,48 @@ self.tr("Delete directory"), self.tr( "<p>The selected directory <b>{0}</b> could not be" - " deleted.</p><p>Reason: {1}</p>").format(dn, str(err))) + " deleted.</p><p>Reason: {1}</p>" + ).format(dn, str(err)), + ) return False - + self.removeDirectory(dn) return True - + def hasEntry(self, fn): """ Public method to check the project for a file. - + @param fn filename to be checked (string) @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"] + 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"] ) - + def createNewProject(self): """ Public slot to built a new project. - + This method displays the new project dialog and initializes the project object with the data entered. """ 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"]: self.initFileTypes() @@ -2339,31 +2477,30 @@ 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) - + self.projectAboutToBeCreated.emit() - - hashStr = str(QCryptographicHash.hash( - QByteArray(self.ppath.encode("utf-8")), - QCryptographicHash.Algorithm.Sha1).toHex(), - encoding="utf-8") + + hashStr = str( + QCryptographicHash.hash( + QByteArray(self.ppath.encode("utf-8")), + QCryptographicHash.Algorithm.Sha1, + ).toHex(), + encoding="utf-8", + ) self.pdata["HASH"] = hashStr - + if self.pdata["PROGLANGUAGE"] == "MicroPython": # change the lexer association for *.py files self.pdata["LEXERASSOCS"] = { "*.py": "MicroPython", } - + # create the project directory if it doesn't exist already if not os.path.isdir(self.ppath): try: @@ -2374,33 +2511,31 @@ self.tr("Create project directory"), self.tr( "<p>The project directory <b>{0}</b> could not" - " be created.</p>") - .format(self.ppath)) + " be created.</p>" + ).format(self.ppath), + ) self.vcs = self.initVCS() return - + # 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"]) + ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) else: 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 mf: @@ -2412,21 +2547,20 @@ with open(mf, "w"): pass self.appendFile(mf) - + tpd = os.path.join(self.ppath, self.translationsRoot) if not self.translationsRoot.endswith(os.sep): 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"]) + tpd = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"]) if not os.path.isdir(tpd): os.makedirs(tpd, exist_ok=True) - + # create management directory if not present self.createProjectManagementDir() - + self.saveProject() addAllToVcs = True else: @@ -2439,14 +2573,14 @@ self.tr("Create project management directory"), self.tr( "<p>The project directory <b>{0}</b> is not" - " writable.</p>") - .format(self.ppath)) + " writable.</p>" + ).format(self.ppath), + ) return - + if self.pdata["MAINSCRIPT"]: if not os.path.isabs(self.pdata["MAINSCRIPT"]): - ms = os.path.join( - self.ppath, self.pdata["MAINSCRIPT"]) + ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) else: ms = self.pdata["MAINSCRIPT"] if not os.path.exists(ms): @@ -2460,12 +2594,13 @@ self.tr("Create main script"), self.tr( "<p>The mainscript <b>{0}</b> could not" - " be created.<br/>Reason: {1}</p>") - .format(ms, str(err))) + " be created.<br/>Reason: {1}</p>" + ).format(ms, str(err)), + ) self.appendFile(ms, True) else: ms = "" - + if self.pdata["MAKEPARAMS"]["MakeEnabled"]: mf = self.pdata["MAKEPARAMS"]["MakeFile"] if mf: @@ -2484,36 +2619,37 @@ self.tr("Create Makefile"), self.tr( "<p>The makefile <b>{0}</b> could not" - " be created.<br/>Reason: {1}</p>") - .format(mf, str(err))) + " be created.<br/>Reason: {1}</p>" + ).format(mf, str(err)), + ) self.appendFile(mf) - + # add existing files to the project res = EricMessageBox.yesNo( self.ui, self.tr("New Project"), self.tr("""Add existing files to the project?"""), - yesDefault=True) + yesDefault=True, + ) if res: self.newProjectAddFiles(ms) 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"): pass self.appendFile(fn, True) self.saveProject() - + # check, if the existing project directory is already under # VCS control pluginManager = ericApp().getObject("PluginManager") for indicator, vcsData in list( - pluginManager.getVcsSystemIndicators().items()): + pluginManager.getVcsSystemIndicators().items() + ): if os.path.exists(os.path.join(self.ppath, indicator)): if len(vcsData) > 1: vcsList = [] @@ -2524,7 +2660,9 @@ self.tr("New Project"), self.tr("Select Version Control System"), vcsList, - 0, False) + 0, + False, + ) if vcs_ok: for vcsSystemStr, vcsSystemDisplay in vcsData: if res == vcsSystemDisplay: @@ -2547,13 +2685,16 @@ self.tr("New Project"), self.tr( """Would you like to edit the VCS""" - """ command options?""")) + """ command options?""" + ), + ) else: vcores = False if vcores: from VCS.CommandOptionsDialog import ( - VcsCommandOptionsDialog + VcsCommandOptionsDialog, ) + codlg = VcsCommandOptionsDialog(self.vcs) if codlg.exec() == QDialog.DialogCode.Accepted: self.vcs.vcsSetOptions(codlg.getOptions()) @@ -2564,24 +2705,23 @@ self.tr("New project"), self.tr( "Shall the project file be added" - " to the repository?"), - yesDefault=True) + " to the repository?" + ), + yesDefault=True, + ) if apres: self.saveProject() self.vcs.vcsAdd(self.pfile) else: - self.pdata["VCS"] = 'None' + self.pdata["VCS"] = "None" self.saveProject() break - + # put the project under VCS control - if ( - self.vcs is None and - self.vcsSoftwareAvailable() and - self.vcsRequested - ): + if self.vcs is None and self.vcsSoftwareAvailable() and self.vcsRequested: vcsSystemsDict = ( - ericApp().getObject("PluginManager") + ericApp() + .getObject("PluginManager") .getPluginDisplayStrings("version_control") ) vcsSystemsDisplay = [self.tr("None")] @@ -2591,26 +2731,27 @@ vcsSelected, ok = QInputDialog.getItem( None, self.tr("New Project"), - self.tr( - "Select version control system for the project"), + self.tr("Select version control system for the project"), vcsSystemsDisplay, - 0, False) + 0, + False, + ) if ok and vcsSelected != self.tr("None"): for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items(): if vcsSystemDisplay == vcsSelected: 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: @@ -2621,26 +2762,26 @@ self.tr("New Project"), self.tr( """Would you like to edit the VCS command""" - """ options?""")) + """ options?""" + ), + ) else: vcores = False if vcores: - from VCS.CommandOptionsDialog import ( - VcsCommandOptionsDialog - ) + from VCS.CommandOptionsDialog import VcsCommandOptionsDialog + codlg = VcsCommandOptionsDialog(self.vcs) if codlg.exec() == QDialog.DialogCode.Accepted: self.vcs.vcsSetOptions(codlg.getOptions()) - + # create the project in the VCS self.vcs.vcsSetDataFromDict(vcsDataDict) self.saveProject() - self.vcs.vcsConvertProject(vcsDataDict, self, - addAll=addAllToVcs) + self.vcs.vcsConvertProject(vcsDataDict, self, addAll=addAllToVcs) else: self.newProjectHooks.emit() self.newProject.emit() - + else: self.newProjectHooks.emit() self.newProject.emit() @@ -2648,12 +2789,12 @@ def newProjectAddFiles(self, mainscript): """ Public method to add files to a new project. - + @param mainscript name of the mainscript (string) """ # Show the file type associations for the user to change self.__showFiletypeAssociations() - + with EricOverrideCursor(): # search the project directory for files with known extensions filespecs = list(self.pdata["FILETYPES"].keys()) @@ -2661,7 +2802,7 @@ files = Utilities.direntries(self.ppath, True, filespec) for file in files: self.appendFile(file) - + # special handling for translation files if self.translationsRoot: tpd = os.path.join(self.ppath, self.translationsRoot) @@ -2675,8 +2816,7 @@ 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)) @@ -2684,17 +2824,19 @@ if pattern: tslist.extend(Utilities.direntries(tpd, True, pattern)) if tslist: - if '_' in os.path.basename(tslist[0]): + if "_" in os.path.basename(tslist[0]): # the first entry determines the mainscript name mainscriptname = ( - os.path.splitext(mainscript)[0] or - os.path.basename(tslist[0]).split('_')[0] + os.path.splitext(mainscript)[0] + or os.path.basename(tslist[0]).split("_")[0] ) self.pdata["TRANSLATIONPATTERN"] = os.path.join( os.path.dirname(tslist[0]), "{0}_%language%{1}".format( - os.path.basename(tslist[0]).split('_')[0], - os.path.splitext(tslist[0])[1])) + os.path.basename(tslist[0]).split("_")[0], + os.path.splitext(tslist[0])[1], + ), + ) else: mainscriptname = "" pattern, ok = QInputDialog.getText( @@ -2703,47 +2845,49 @@ self.tr( "Enter the path pattern for translation files " "(use '%language%' in place of the language" - " code):"), + " code):" + ), QLineEdit.EchoMode.Normal, - tslist[0]) + tslist[0], + ) if pattern: self.pdata["TRANSLATIONPATTERN"] = pattern if self.pdata["TRANSLATIONPATTERN"]: self.pdata["TRANSLATIONPATTERN"] = self.getRelativePath( - self.pdata["TRANSLATIONPATTERN"]) + self.pdata["TRANSLATIONPATTERN"] + ) pattern = self.pdata["TRANSLATIONPATTERN"].replace( - "%language%", "*") + "%language%", "*" + ) for ts in tslist: if fnmatch.fnmatch(ts, pattern): self.pdata["TRANSLATIONS"].append(ts) self.projectLanguageAdded.emit(ts) if self.pdata["TRANSLATIONSBINPATH"]: - tpd = os.path.join(self.ppath, - self.pdata["TRANSLATIONSBINPATH"]) + tpd = os.path.join( + self.ppath, self.pdata["TRANSLATIONSBINPATH"] + ) pattern = os.path.basename( - self.pdata["TRANSLATIONPATTERN"]).replace( - "%language%", "*") + 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) + 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["MAINSCRIPT"] = "{0}.rb".format(mainscriptname) self.setDirty(True) - + def __showProperties(self): """ Private slot to display the properties dialog. """ from .PropertiesDialog import PropertiesDialog + dlg = PropertiesDialog(self, False) if dlg.exec() == QDialog.DialogCode.Accepted: projectType = self.pdata["PROJECTTYPE"] @@ -2751,13 +2895,12 @@ self.setDirty(True) if self.pdata["MAINSCRIPT"]: if not os.path.isabs(self.pdata["MAINSCRIPT"]): - ms = os.path.join( - self.ppath, self.pdata["MAINSCRIPT"]) + ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) else: ms = self.pdata["MAINSCRIPT"] if os.path.exists(ms): self.appendFile(ms) - + if self.pdata["MAKEPARAMS"]["MakeEnabled"]: mf = self.pdata["MAKEPARAMS"]["MakeFile"] if mf: @@ -2775,14 +2918,15 @@ self.tr("Create Makefile"), self.tr( "<p>The makefile <b>{0}</b> could not" - " be created.<br/>Reason: {1}</p>") - .format(mf, str(err))) + " be created.<br/>Reason: {1}</p>" + ).format(mf, str(err)), + ) self.appendFile(mf) - + if self.pdata["PROJECTTYPE"] != projectType: # reinitialize filetype associations self.initFileTypes() - + if self.translationsRoot: tp = os.path.join(self.ppath, self.translationsRoot) if not self.translationsRoot.endswith(os.sep): @@ -2793,43 +2937,42 @@ os.makedirs(tp) 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"]) + 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: self.__reorganizeFiles() - + def __showUserProperties(self): """ Private slot to display the user specific properties dialog. """ 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) or - (self.pudata["VCSOVERRIDE"] and - self.pudata["VCSOVERRIDE"] != vcsSystemOverride) or - (vcsSystemOverride is not None and - not self.pudata["VCSOVERRIDE"]) + (self.pdata["VCS"] and self.pdata["VCS"] != vcsSystem) + or ( + self.pudata["VCSOVERRIDE"] + and self.pudata["VCSOVERRIDE"] != vcsSystemOverride + ) + or (vcsSystemOverride is not None and not self.pudata["VCSOVERRIDE"]) ): # stop the VCS monitor thread and shutdown VCS if self.vcs is not None: @@ -2843,30 +2986,31 @@ # start the VCS monitor thread self.__vcsConnectStatusMonitor() self.reinitVCS.emit() - + if self.pudata["VCSSTATUSMONITORINTERVAL"]: - self.setStatusMonitorInterval( - self.pudata["VCSSTATUSMONITORINTERVAL"]) + self.setStatusMonitorInterval(self.pudata["VCSSTATUSMONITORINTERVAL"]) else: self.setStatusMonitorInterval( - Preferences.getVCS("StatusMonitorInterval")) - + Preferences.getVCS("StatusMonitorInterval") + ) + def __showFiletypeAssociations(self): """ Private slot to display the filetype association dialog. """ from .FiletypeAssociationDialog import FiletypeAssociationDialog + dlg = FiletypeAssociationDialog(self) if dlg.exec() == QDialog.DialogCode.Accepted: dlg.transferData() self.setDirty(True) self.__reorganizeFiles() - + def getFiletypeAssociations(self, associationType): """ 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__) @@ -2874,24 +3018,28 @@ @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] - + return [ + assoc + for assoc in self.pdata["FILETYPES"] + if self.pdata["FILETYPES"][assoc] == associationType + ] + def __showLexerAssociations(self): """ Private slot to display the lexer association dialog. """ from .LexerAssociationDialog import LexerAssociationDialog + dlg = LexerAssociationDialog(self) if dlg.exec() == QDialog.DialogCode.Accepted: dlg.transferData() self.setDirty(True) self.lexerAssociationsChanged.emit() - + def getEditorLexerAssoc(self, filename): """ Public method to retrieve a lexer association. - + @param filename filename used to determine the associated lexer language (string) @return the requested lexer language (string) @@ -2900,32 +3048,32 @@ 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"] with contextlib.suppress(KeyError): if self.__lexerAssociationCallbacks[projectType] is not None: return self.__lexerAssociationCallbacks[projectType](filename) - + # return empty string to signal to use the global setting return "" - + def getIgnorePatterns(self): """ Public method to get the list of file name patterns for files to be ignored. - + @return list of ignore file name patterns @rtype list of str """ return self.getFiletypeAssociations("__IGNORE__") - + @pyqtSlot() @pyqtSlot(str) def openProject(self, fn=None, restoreSession=True, reopen=False): """ Public slot to open a project. - + @param fn optional filename of the project file to be read @param restoreSession flag indicating to restore the project session (boolean) @@ -2933,15 +3081,15 @@ """ if not self.checkDirty(): return - + if fn is None: fn = EricFileDialog.getOpenFileName( self.parent(), self.tr("Open project"), - Preferences.getMultiProject("Workspace") or - Utilities.getHomeDir(), - self.tr("Project Files (*.epj);;XML Project Files (*.e4p)")) - + Preferences.getMultiProject("Workspace") or Utilities.getHomeDir(), + self.tr("Project Files (*.epj);;XML Project Files (*.e4p)"), + ) + if fn and self.closeProject(): with EricOverrideCursor(): ok = self.__readProject(fn) @@ -2951,7 +3099,7 @@ self.initFileTypes() else: self.updateFileTypes() - + try: # create management directory if not present self.createProjectManagementDir() @@ -2961,43 +3109,40 @@ self.tr("Create project management directory"), self.tr( "<p>The project directory <b>{0}</b> is not" - " writable.</p>") - .format(self.ppath)) + " writable.</p>" + ).format(self.ppath), + ) return - + # read a user specific project file self.__readUserProperties() - + with EricOverrideCursor(): oldState = self.isDirty() self.vcs = self.initVCS() if self.vcs is None and self.isDirty() == oldState: # check, if project is version controlled pluginManager = ericApp().getObject("PluginManager") - for indicator, vcsData in ( - pluginManager.getVcsSystemIndicators().items() - ): - if os.path.exists( - os.path.join(self.ppath, indicator)): + for ( + indicator, + vcsData, + ) in pluginManager.getVcsSystemIndicators().items(): + if os.path.exists(os.path.join(self.ppath, indicator)): if len(vcsData) > 1: vcsList = [] - for ( - _vcsSystemStr, vcsSystemDisplay - ) in vcsData: + for (_vcsSystemStr, vcsSystemDisplay) in vcsData: vcsList.append(vcsSystemDisplay) with EricOverridenCursor(): res, vcs_ok = QInputDialog.getItem( None, self.tr("New Project"), - self.tr( - "Select Version Control" - " System"), + self.tr("Select Version Control" " System"), vcsList, - 0, False) + 0, + False, + ) if vcs_ok: - for ( - vcsSystemStr, vcsSystemDisplay - ) in vcsData: + for (vcsSystemStr, vcsSystemDisplay) in vcsData: if res == vcsSystemDisplay: vcsSystem = vcsSystemStr break @@ -3010,12 +3155,11 @@ 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) + 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.closeAct.setEnabled(True) self.saveasAct.setEnabled(True) @@ -3033,56 +3177,52 @@ 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"])) - self.makeGrp.setEnabled( - self.pdata["MAKEPARAMS"]["MakeEnabled"]) - self.menuMakeAct.setEnabled( - self.pdata["MAKEPARAMS"]["MakeEnabled"]) + 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) - + # open a project debugger properties file being quiet # about errors if Preferences.getProject("AutoLoadDbgProperties"): self.__readDebugProperties(True) - + self.__model.projectOpened() self.projectOpenedHooks.emit() self.projectOpened.emit() - + if Preferences.getProject("SearchNewFiles"): self.__doSearchNewFiles() - + # read a project tasks file self.__readTasks() self.ui.taskViewer.setProjectOpen(True) # rescan project tasks if Preferences.getProject("TasksProjectRescanOnOpen"): - ericApp().getObject("TaskViewer" - ).regenerateProjectTasks(quiet=True) - + ericApp().getObject("TaskViewer").regenerateProjectTasks(quiet=True) + 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"]) + ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) else: ms = self.pdata["MAINSCRIPT"] self.sourceFile.emit(ms) - + # open a project session file being quiet about errors if reopen: self.__readSession(quiet=True, indicator="_tmp") elif Preferences.getProject("AutoLoadSession"): self.__readSession(quiet=True) - + # start the VCS monitor thread self.__vcsConnectStatusMonitor() - + def reopenProject(self): """ Public slot to reopen the current project. @@ -3091,11 +3231,11 @@ res = self.closeProject(reopen=True) if res: self.openProject(projectFile, reopen=True) - + def saveProject(self): """ Public slot to save the current project. - + @return flag indicating success """ if self.isDirty(): @@ -3111,19 +3251,18 @@ self.sessActGrp.setEnabled(ok) self.menuSessionAct.setEnabled(ok) return ok - + def saveProjectAs(self): """ Public slot to save the current project to a different file. - + @return flag indicating success (boolean) """ defaultFilter = self.tr("Project Files (*.epj)") defaultPath = ( self.ppath - if self.ppath else - (Preferences.getMultiProject("Workspace") or - Utilities.getHomeDir()) + if self.ppath + else (Preferences.getMultiProject("Workspace") or Utilities.getHomeDir()) ) fn, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( self.parent(), @@ -3131,8 +3270,9 @@ defaultPath, self.tr("Project Files (*.epj)"), defaultFilter, - EricFileDialog.DontConfirmOverwrite) - + EricFileDialog.DontConfirmOverwrite, + ) + if fn: fpath = pathlib.Path(fn) if not fpath.suffix: @@ -3143,22 +3283,25 @@ res = EricMessageBox.yesNo( self.ui, self.tr("Save File"), - self.tr("""<p>The file <b>{0}</b> already exists.""" - """ Overwrite it?</p>""").format(fpath), - icon=EricMessageBox.Warning) + self.tr( + """<p>The file <b>{0}</b> already exists.""" + """ Overwrite it?</p>""" + ).format(fpath), + icon=EricMessageBox.Warning, + ) if not res: return False - + self.name = fpath.stem ok = self.__writeProject(str(fpath)) - + if ok: # create management directory if not present self.createProjectManagementDir() - + # now save the tasks self.writeTasks() - + self.sessActGrp.setEnabled(ok) self.menuSessionAct.setEnabled(ok) self.projectClosedHooks.emit() @@ -3168,11 +3311,11 @@ return ok else: return False - + def checkDirty(self): """ Public method to check dirty status and open a message window. - + @return flag indicating whether this operation was successful (boolean) """ if self.isDirty(): @@ -3180,13 +3323,14 @@ self.parent(), self.tr("Close Project"), self.tr("The current project has unsaved changes."), - self.saveProject) + self.saveProject, + ) if res: self.setDirty(False) return res - + return True - + def __closeAllWindows(self): """ Private method to close all project related windows. @@ -3196,12 +3340,12 @@ self.profiledata and self.profiledata.close() self.applicationDiagram and self.applicationDiagram.close() self.loadedDiagram and self.loadedDiagram.close() - + @pyqtSlot() def closeProject(self, reopen=False, noSave=False, shutdown=False): """ Public slot to close the current project. - + @param reopen flag indicating a reopening of the project @type bool @param noSave flag indicating to not perform save actions @@ -3213,35 +3357,36 @@ """ # save the list of recently opened projects self.__saveRecent() - + if not self.isOpen(): return True - + if not self.checkDirty(): return False - + ericApp().getObject("TaskViewer").stopProjectTaskExtraction() - + # save the user project properties if not noSave: self.__writeUserProperties() - + # save the project session file being quiet about error if reopen: self.__writeSession(quiet=True, indicator="_tmp") elif Preferences.getProject("AutoSaveSession") and not noSave: self.__writeSession(quiet=True) - + # save the project debugger properties file being quiet about error if ( - Preferences.getProject("AutoSaveDbgProperties") and - self.isDebugPropertiesLoaded() and - not noSave and self.debugPropertiesChanged + Preferences.getProject("AutoSaveDbgProperties") + and self.isDebugPropertiesLoaded() + and not noSave + and self.debugPropertiesChanged ): self.__writeDebugProperties(True) - + vm = ericApp().getObject("ViewManager") - + # check dirty status of all project files first for fn in vm.getOpenFilenames(): if self.isProjectFile(fn): @@ -3249,7 +3394,7 @@ if not reset: # abort shutting down return False - + # close all project related editors success = True for fn in vm.getOpenFilenames(): @@ -3257,27 +3402,27 @@ success &= vm.closeWindow(fn, ignoreDirty=True) if not success: return False - + # stop the VCS monitor thread if self.vcs is not None: self.vcs.stopStatusMonitor() - + # now save the tasks if not noSave: self.writeTasks() self.ui.taskViewer.clearProjectTasks() self.ui.taskViewer.setProjectOpen(False) - + # now shutdown the vcs interface if self.vcs: self.vcs.vcsShutdown() self.vcs.deleteLater() self.vcs = None ericApp().getObject("PluginManager").deactivateVcsPlugins() - + # now close all project related tool windows self.__closeAllWindows() - + self.__initData() self.closeAct.setEnabled(False) self.saveasAct.setEnabled(False) @@ -3301,17 +3446,17 @@ self.menuMakeAct.setEnabled(False) self.menuOtherToolsAct.setEnabled(False) self.menuFormattingAct.setEnabled(False) - + self.__model.projectClosed() self.projectClosedHooks.emit() self.projectClosed.emit(shutdown) - + return True def saveAllScripts(self, reportSyntaxErrors=False): """ Public method to save all scripts belonging to the project. - + @param reportSyntaxErrors flag indicating special reporting for syntax errors (boolean) @return flag indicating success (boolean) @@ -3326,24 +3471,26 @@ success &= vm.saveEditorEd(editor) if reportSyntaxErrors and editor.hasSyntaxErrors(): filesWithSyntaxErrors += 1 - + if reportSyntaxErrors and filesWithSyntaxErrors > 0: EricMessageBox.critical( self.ui, self.tr("Syntax errors detected"), self.tr( """The project contains %n file(s) with syntax errors.""", - "", filesWithSyntaxErrors) + "", + filesWithSyntaxErrors, + ), ) return False else: return success - + def checkAllScriptsDirty(self, reportSyntaxErrors=False): """ Public method to check all scripts belonging to the project for their dirty status. - + @param reportSyntaxErrors flag indicating special reporting for syntax errors (boolean) @return flag indicating success (boolean) @@ -3358,14 +3505,16 @@ success &= editor.checkDirty() if reportSyntaxErrors and editor.hasSyntaxErrors(): filesWithSyntaxErrors += 1 - + if reportSyntaxErrors and filesWithSyntaxErrors > 0: EricMessageBox.critical( self.ui, self.tr("Syntax errors detected"), self.tr( """The project contains %n file(s) with syntax errors.""", - "", filesWithSyntaxErrors) + "", + filesWithSyntaxErrors, + ), ) return False else: @@ -3374,10 +3523,10 @@ def getMainScript(self, normalized=False): """ Public method to return the main script filename. - + The normalized name is the name of the main script prepended with the project path. - + @param normalized flag indicating a normalized filename is wanted @type bool @return filename of the projects main script @@ -3390,22 +3539,22 @@ return self.pdata["MAINSCRIPT"] else: return "" - + def getSources(self, normalized=False): """ Public method to return the source script files. - + @param normalized flag indicating a normalized filename is wanted @type bool @return list of the projects scripts @rtype list of str """ return self.getProjectFiles("SOURCES", normalized=normalized) - + def getProjectFiles(self, fileType, normalized=False): """ 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) @type str @@ -3415,95 +3564,101 @@ @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 [ + "SOURCES", + "FORMS", + "RESOURCES", + "INTERFACES", + "PROTOCOLS", + "OTHERS", + "TRANSLATIONS", + ]: 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] - + def getProjectType(self): """ Public method to get the type of the project. - + @return UI type of the project (string) """ return self.pdata["PROJECTTYPE"] - + def getProjectLanguage(self): """ Public method to get the project's programming language. - + @return programming language (string) """ return self.pdata["PROGLANGUAGE"] - + def isMixedLanguageProject(self): """ Public method to check, if this is a mixed language project. - + @return flag indicating a mixed language project @rtype bool """ return self.pdata["MIXEDLANGUAGE"] - + def isPythonProject(self): """ Public method to check, if this project is a Python3 or MicroPython project. - + @return flag indicating a Python project (boolean) """ return self.pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"] - + def isPy3Project(self): """ Public method to check, if this project is a Python3 project. - + @return flag indicating a Python3 project (boolean) """ return self.pdata["PROGLANGUAGE"] == "Python3" - + def isMicroPythonProject(self): """ Public method to check, if this project is a MicroPython project. - + @return flag indicating a MicroPython project @rtype bool """ return self.pdata["PROGLANGUAGE"] == "MicroPython" - + def isRubyProject(self): """ Public method to check, if this project is a Ruby project. - + @return flag indicating a Ruby project (boolean) """ return self.pdata["PROGLANGUAGE"] == "Ruby" - + def isJavaScriptProject(self): """ Public method to check, if this project is a JavaScript project. - + @return flag indicating a JavaScript project (boolean) """ return self.pdata["PROGLANGUAGE"] == "JavaScript" - + def getProjectSpellLanguage(self): """ Public method to get the project's programming language. - + @return programming language (string) """ return self.pdata["SPELLLANGUAGE"] - + def getProjectDictionaries(self): """ Public method to get the names of the project specific dictionaries. - + @return tuple of two strings giving the absolute path names of the project specific word and exclude list """ @@ -3512,67 +3667,65 @@ 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 not os.path.isfile(pel): pel = "" - + return (pwl, pel) - + def getDefaultSourceExtension(self): """ Public method to get the default extension for the project's programming language. - + @return default extension (including the dot) (string) """ lang = self.pdata["PROGLANGUAGE"] if lang in ("", "Python"): lang = "Python3" return self.__sourceExtensions(lang)[0] - + def getProjectPath(self): """ Public method to get the project path. - + @return project path (string) """ return self.ppath - + def startswithProjectPath(self, path): """ Public method to check, if a path starts with the project path. - + @param path path to be checked @type str @return flag indicating that the path starts with the project path @rtype bool """ - return ( - bool(self.ppath) and - (path == self.ppath or - Utilities.normcasepath(Utilities.toNativeSeparators(path)) - .startswith(Utilities.normcasepath( - Utilities.toNativeSeparators(self.ppath + "/"))) - ) + return bool(self.ppath) and ( + path == self.ppath + or Utilities.normcasepath(Utilities.toNativeSeparators(path)).startswith( + Utilities.normcasepath(Utilities.toNativeSeparators(self.ppath + "/")) + ) ) - + def getProjectFile(self): """ Public method to get the path of the project file. - + @return path of the project file (string) """ return self.pfile - + def getProjectName(self): """ Public method to get the name of the project. - + The project name is determined from the name of the project file. - + @return name of the project (string) """ if self.pfile: @@ -3580,39 +3733,39 @@ return os.path.basename(name) else: return "" - + def getProjectManagementDir(self): """ Public method to get the path of the management directory. - + @return path of the management directory (string) """ return os.path.join(self.ppath, ".eric7project") - + def createProjectManagementDir(self): """ Public method to create the project management directory. - + It does nothing, if it already exists. """ # create management directory if not present mgmtDir = self.getProjectManagementDir() if not os.path.exists(mgmtDir): os.makedirs(mgmtDir) - + def getHash(self): """ Public method to get the project hash. - + @return project hash as a hex string (string) """ return self.pdata["HASH"] - + def getRelativePath(self, path): """ Public method to convert a file path to a project relative file path. - + @param path file or directory name to convert (string) @return project relative path or unchanged path, if path doesn't belong to the project (string) @@ -3621,46 +3774,46 @@ return str(pathlib.Path(path).relative_to(self.ppath)) except ValueError: return path - + def getRelativeUniversalPath(self, path): """ Public method to convert a file path to a project relative file path with universal separators. - + @param path file or directory name to convert (string) @return project relative path or unchanged path, if path doesn't belong to the project (string) """ return Utilities.fromNativeSeparators(self.getRelativePath(path)) - + def getAbsolutePath(self, fn): """ Public method to convert a project relative file path to an absolute file path. - + @param fn file or directory name to convert (string) @return absolute path (string) """ if not os.path.isabs(fn): fn = os.path.join(self.ppath, fn) return fn - + def getAbsoluteUniversalPath(self, fn): """ Public method to convert a project relative file path with universal separators to an absolute file path. - + @param fn file or directory name to convert (string) @return absolute path (string) """ if not os.path.isabs(fn): fn = os.path.join(self.ppath, Utilities.toNativeSeparators(fn)) return fn - + def getEolString(self): """ Public method to get the EOL-string to be used by the project. - + @return eol string (string) """ if self.pdata["EOL"] >= 0: @@ -3668,64 +3821,64 @@ else: eolMode = Preferences.getEditor("EOLMode") if eolMode == QsciScintilla.EolMode.EolWindows: - eol = '\r\n' + eol = "\r\n" elif eolMode == QsciScintilla.EolMode.EolUnix: - eol = '\n' + eol = "\n" elif eolMode == QsciScintilla.EolMode.EolMac: - eol = '\r' + eol = "\r" else: eol = os.linesep return eol - + def useSystemEol(self): """ Public method to check, if the project uses the system eol setting. - + @return flag indicating the usage of system eol (boolean) """ return self.pdata["EOL"] == 0 - + def getProjectVersion(self): """ Public mehod to get the version number of the project. - + @return version number @rtype str """ return self.pdata["VERSION"] - + def getProjectAuthor(self): """ Public method to get the author of the project. - + @return author name @rtype str """ return self.pdata["AUTHOR"] - + def getProjectAuthorEmail(self): """ Public method to get the email address of the project author. - + @return project author email @rtype str """ return self.pdata["EMAIL"] - + def getProjectDescription(self): """ Public method to get the description of the project. - + @return project description @rtype str """ return self.pdata["DESCRIPTION"] - + def getProjectVenv(self, resolveDebugger=True): """ Public method to get the name of the virtual environment used by the project. - + @param resolveDebugger flag indicating to resolve the virtual environment name via the debugger settings if none was configured @type bool @@ -3734,18 +3887,18 @@ """ venvName = self.getDebugProperty("VIRTUALENV") if ( - not venvName and - resolveDebugger and - self.getProjectLanguage() in ("Python3", "MicroPython", "Cython") + not venvName + and resolveDebugger + and self.getProjectLanguage() in ("Python3", "MicroPython", "Cython") ): venvName = Preferences.getDebugger("Python3VirtualEnv") - + return venvName - + def getProjectInterpreter(self, resolveGlobal=True): """ Public method to get the path of the interpreter used by the project. - + @param resolveGlobal flag indicating to resolve the interpreter using the global interpreter if no project of debugger specific environment was configured @@ -3757,18 +3910,19 @@ venvName = self.getProjectVenv() if venvName: interpreter = ( - ericApp().getObject("VirtualEnvManager") + ericApp() + .getObject("VirtualEnvManager") .getVirtualenvInterpreter(venvName) ) if not interpreter and resolveGlobal: interpreter = Globals.getPythonExecutable() - + return interpreter - + def getProjectExecPath(self): """ Public method to get the executable search path prefix of the project. - + @return executable search path prefix @rtype str """ @@ -3776,16 +3930,15 @@ venvName = self.getProjectVenv() if venvName: execPath = ( - ericApp().getObject("VirtualEnvManager") - .getVirtualenvExecPath(venvName) + ericApp().getObject("VirtualEnvManager").getVirtualenvExecPath(venvName) ) - + return execPath - + def getProjectTestingFramework(self): """ Public method to get the testing framework name of the project. - + @return testing framework name of the project @rtype str """ @@ -3793,11 +3946,11 @@ return self.pdata["TESTING_FRAMEWORK"] except KeyError: return "" - + def getProjectLicense(self): """ Public method to get the license type used by the project. - + @return license type of the project @rtype str """ @@ -3805,12 +3958,12 @@ return self.pdata["LICENSE"] except KeyError: return "" - + def __isInPdata(self, fn): """ Private method used to check, if the passed in filename is project controlled.. - + @param fn filename to be checked @type str @return flag indicating membership @@ -3821,500 +3974,652 @@ return any( newfn in self.pdata[group] for group in [ - "SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", - "TRANSLATIONS", "OTHERS" + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "TRANSLATIONS", + "OTHERS", ] ) - + def isProjectFile(self, fn): """ Public method used to check, if the passed in filename belongs to the project. - + @param fn filename to be checked (string) @return flag indicating membership (boolean) """ return any( self.__checkProjectFileGroup(fn, group) for group in [ - "SOURCES", "FORMS", "INTERFACES", "PROTOCOLS", "RESOURCES", - "TRANSLATIONS", "OTHERS" + "SOURCES", + "FORMS", + "INTERFACES", + "PROTOCOLS", + "RESOURCES", + "TRANSLATIONS", + "OTHERS", ] ) - + def __checkProjectFileGroup(self, fn, group): """ Private method to check, if a file is in a specific file group of the project. - + @param fn filename to be checked (string) @param group group to check (string) @return flag indicating membership (boolean) """ newfn = os.path.abspath(fn) newfn = self.getRelativePath(newfn) - if ( - newfn in self.pdata[group] or - (group == "OTHERS" and - any(newfn.startswith(entry) for entry in self.pdata[group])) + if newfn in self.pdata[group] or ( + group == "OTHERS" + 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]): return True - elif ( - group == "OTHERS" and - any(newfn.startswith(entry.lower()) - for entry in self.pdata[group]) + 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. - + @param fn filename to be checked @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") - + def initActions(self): """ Public slot to initialize the project related actions. """ self.actions = [] - + ################################################################### ## Project actions ################################################################### - + self.actGrp1 = createActionGroup(self) - + act = EricAction( - self.tr('New project'), + self.tr("New project"), UI.PixmapCache.getIcon("projectNew"), - self.tr('&New...'), 0, 0, - self.actGrp1, 'project_new') - act.setStatusTip(self.tr('Generate a new project')) - act.setWhatsThis(self.tr( - """<b>New...</b>""" - """<p>This opens a dialog for entering the info for a""" - """ new project.</p>""" - )) + self.tr("&New..."), + 0, + 0, + self.actGrp1, + "project_new", + ) + act.setStatusTip(self.tr("Generate a new project")) + act.setWhatsThis( + self.tr( + """<b>New...</b>""" + """<p>This opens a dialog for entering the info for a""" + """ new project.</p>""" + ) + ) act.triggered.connect(self.createNewProject) self.actions.append(act) act = EricAction( - self.tr('Open project'), + self.tr("Open project"), UI.PixmapCache.getIcon("projectOpen"), - self.tr('&Open...'), 0, 0, - self.actGrp1, 'project_open') - act.setStatusTip(self.tr('Open an existing project')) - act.setWhatsThis(self.tr( - """<b>Open...</b>""" - """<p>This opens an existing project.</p>""" - )) + self.tr("&Open..."), + 0, + 0, + self.actGrp1, + "project_open", + ) + act.setStatusTip(self.tr("Open an existing project")) + act.setWhatsThis( + self.tr("""<b>Open...</b>""" """<p>This opens an existing project.</p>""") + ) act.triggered.connect(self.openProject) self.actions.append(act) self.closeAct = EricAction( - self.tr('Close project'), + self.tr("Close project"), UI.PixmapCache.getIcon("projectClose"), - self.tr('&Close'), 0, 0, self, 'project_close') - 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("&Close"), + 0, + 0, + self, + "project_close", + ) + 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.closeAct.triggered.connect(self.closeProject) self.actions.append(self.closeAct) self.saveAct = EricAction( - self.tr('Save project'), + self.tr("Save project"), UI.PixmapCache.getIcon("projectSave"), - self.tr('&Save'), 0, 0, self, 'project_save') - 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("&Save"), + 0, + 0, + self, + "project_save", + ) + 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.saveAct.triggered.connect(self.saveProject) self.actions.append(self.saveAct) self.saveasAct = EricAction( - self.tr('Save project as'), + self.tr("Save project as"), UI.PixmapCache.getIcon("projectSaveAs"), - self.tr('Save &as...'), 0, 0, self, 'project_save_as') - self.saveasAct.setStatusTip(self.tr( - 'Save the current project to a new file')) - self.saveasAct.setWhatsThis(self.tr( - """<b>Save as</b>""" - """<p>This saves the current project to a new file.</p>""" - )) + self.tr("Save &as..."), + 0, + 0, + self, + "project_save_as", + ) + self.saveasAct.setStatusTip(self.tr("Save the current project to a new file")) + self.saveasAct.setWhatsThis( + self.tr( + """<b>Save as</b>""" + """<p>This saves the current project to a new file.</p>""" + ) + ) self.saveasAct.triggered.connect(self.saveProjectAs) self.actions.append(self.saveasAct) - + ################################################################### ## Project management actions ################################################################### - + self.actGrp2 = createActionGroup(self) - + self.addFilesAct = EricAction( - self.tr('Add files to project'), + self.tr("Add files to project"), UI.PixmapCache.getIcon("fileMisc"), - self.tr('Add &files...'), 0, 0, - self.actGrp2, 'project_add_file') - self.addFilesAct.setStatusTip(self.tr( - 'Add files to the current project')) - self.addFilesAct.setWhatsThis(self.tr( - """<b>Add files...</b>""" - """<p>This opens a dialog for adding files""" - """ to the current project. The place to add is""" - """ determined by the file extension.</p>""" - )) + self.tr("Add &files..."), + 0, + 0, + self.actGrp2, + "project_add_file", + ) + self.addFilesAct.setStatusTip(self.tr("Add files to the current project")) + self.addFilesAct.setWhatsThis( + self.tr( + """<b>Add files...</b>""" + """<p>This opens a dialog for adding files""" + """ to the current project. The place to add is""" + """ determined by the file extension.</p>""" + ) + ) self.addFilesAct.triggered.connect(self.addFiles) self.actions.append(self.addFilesAct) self.addDirectoryAct = EricAction( - self.tr('Add directory to project'), + self.tr("Add directory to project"), UI.PixmapCache.getIcon("dirOpen"), - self.tr('Add directory...'), 0, 0, - self.actGrp2, 'project_add_directory') + self.tr("Add directory..."), + 0, + 0, + self.actGrp2, + "project_add_directory", + ) self.addDirectoryAct.setStatusTip( - self.tr('Add a directory to the current project')) - self.addDirectoryAct.setWhatsThis(self.tr( - """<b>Add directory...</b>""" - """<p>This opens a dialog for adding a directory""" - """ to the current project.</p>""" - )) + self.tr("Add a directory to the current project") + ) + self.addDirectoryAct.setWhatsThis( + self.tr( + """<b>Add directory...</b>""" + """<p>This opens a dialog for adding a directory""" + """ to the current project.</p>""" + ) + ) self.addDirectoryAct.triggered.connect(self.addDirectory) self.actions.append(self.addDirectoryAct) self.addLanguageAct = EricAction( - self.tr('Add translation to project'), + self.tr("Add translation to project"), UI.PixmapCache.getIcon("linguist4"), - self.tr('Add &translation...'), 0, 0, - self.actGrp2, 'project_add_translation') + self.tr("Add &translation..."), + 0, + 0, + self.actGrp2, + "project_add_translation", + ) self.addLanguageAct.setStatusTip( - self.tr('Add a translation to the current project')) - self.addLanguageAct.setWhatsThis(self.tr( - """<b>Add translation...</b>""" - """<p>This opens a dialog for add a translation""" - """ to the current project.</p>""" - )) + self.tr("Add a translation to the current project") + ) + self.addLanguageAct.setWhatsThis( + self.tr( + """<b>Add translation...</b>""" + """<p>This opens a dialog for add a translation""" + """ to the current project.</p>""" + ) + ) self.addLanguageAct.triggered.connect(self.addLanguage) self.actions.append(self.addLanguageAct) act = EricAction( - self.tr('Search new files'), - self.tr('Searc&h new files...'), 0, 0, - self.actGrp2, 'project_search_new_files') - act.setStatusTip(self.tr( - 'Search new files in the project directory.')) - act.setWhatsThis(self.tr( - """<b>Search new files...</b>""" - """<p>This searches for new files (sources, *.ui, *.idl,""" - """ *.proto) in the project directory and registered""" - """ subdirectories.</p>""" - )) + self.tr("Search new files"), + self.tr("Searc&h new files..."), + 0, + 0, + self.actGrp2, + "project_search_new_files", + ) + act.setStatusTip(self.tr("Search new files in the project directory.")) + act.setWhatsThis( + self.tr( + """<b>Search new files...</b>""" + """<p>This searches for new files (sources, *.ui, *.idl,""" + """ *.proto) in the project directory and registered""" + """ subdirectories.</p>""" + ) + ) act.triggered.connect(self.__searchNewFiles) self.actions.append(act) act = EricAction( - self.tr('Search Project File'), - self.tr('Search Project File...'), + self.tr("Search Project File"), + self.tr("Search Project File..."), QKeySequence(self.tr("Alt+Ctrl+P", "Project|Search Project File")), 0, - self.actGrp2, 'project_search_project_file') - act.setStatusTip(self.tr( - 'Search for a file in the project list of files.')) - act.setWhatsThis(self.tr( - """<b>Search Project File</b>""" - """<p>This searches for a file in the project list of files.</p>""" - )) + self.actGrp2, + "project_search_project_file", + ) + act.setStatusTip(self.tr("Search for a file in the project list of files.")) + act.setWhatsThis( + self.tr( + """<b>Search Project File</b>""" + """<p>This searches for a file in the project list of files.</p>""" + ) + ) act.triggered.connect(self.__searchProjectFile) self.actions.append(act) - + self.propsAct = EricAction( - self.tr('Project properties'), + self.tr("Project properties"), UI.PixmapCache.getIcon("projectProps"), - self.tr('&Properties...'), 0, 0, self, - 'project_properties') - self.propsAct.setStatusTip(self.tr('Show the project properties')) - self.propsAct.setWhatsThis(self.tr( - """<b>Properties...</b>""" - """<p>This shows a dialog to edit the project properties.</p>""" - )) + self.tr("&Properties..."), + 0, + 0, + self, + "project_properties", + ) + self.propsAct.setStatusTip(self.tr("Show the project properties")) + self.propsAct.setWhatsThis( + self.tr( + """<b>Properties...</b>""" + """<p>This shows a dialog to edit the project properties.</p>""" + ) + ) self.propsAct.triggered.connect(self.__showProperties) self.actions.append(self.propsAct) self.userPropsAct = EricAction( - self.tr('User project properties'), + self.tr("User project properties"), UI.PixmapCache.getIcon("projectUserProps"), - self.tr('&User Properties...'), 0, 0, self, - 'project_user_properties') - self.userPropsAct.setStatusTip(self.tr( - 'Show the user specific project properties')) - self.userPropsAct.setWhatsThis(self.tr( - """<b>User Properties...</b>""" - """<p>This shows a dialog to edit the user specific project""" - """ properties.</p>""" - )) + self.tr("&User Properties..."), + 0, + 0, + self, + "project_user_properties", + ) + self.userPropsAct.setStatusTip( + self.tr("Show the user specific project properties") + ) + self.userPropsAct.setWhatsThis( + self.tr( + """<b>User Properties...</b>""" + """<p>This shows a dialog to edit the user specific project""" + """ properties.</p>""" + ) + ) self.userPropsAct.triggered.connect(self.__showUserProperties) self.actions.append(self.userPropsAct) self.filetypesAct = EricAction( - self.tr('Filetype Associations'), - self.tr('Filetype Associations...'), 0, 0, - self, 'project_filetype_associations') + self.tr("Filetype Associations"), + self.tr("Filetype Associations..."), + 0, + 0, + self, + "project_filetype_associations", + ) self.filetypesAct.setStatusTip( - self.tr('Show the project file type associations')) - self.filetypesAct.setWhatsThis(self.tr( - """<b>Filetype Associations...</b>""" - """<p>This shows a dialog to edit the file type associations of""" - """ the project. These associations determine the type""" - """ (source, form, interface, protocol or others) with a""" - """ filename pattern. They are used when adding a file to the""" - """ project and when performing a search for new files.</p>""" - )) - self.filetypesAct.triggered.connect( - self.__showFiletypeAssociations) + self.tr("Show the project file type associations") + ) + self.filetypesAct.setWhatsThis( + self.tr( + """<b>Filetype Associations...</b>""" + """<p>This shows a dialog to edit the file type associations of""" + """ the project. These associations determine the type""" + """ (source, form, interface, protocol or others) with a""" + """ filename pattern. They are used when adding a file to the""" + """ project and when performing a search for new files.</p>""" + ) + ) + self.filetypesAct.triggered.connect(self.__showFiletypeAssociations) self.actions.append(self.filetypesAct) self.lexersAct = EricAction( - self.tr('Lexer Associations'), - self.tr('Lexer Associations...'), 0, 0, - self, 'project_lexer_associations') - self.lexersAct.setStatusTip(self.tr( - 'Show the project lexer associations (overriding defaults)')) - self.lexersAct.setWhatsThis(self.tr( - """<b>Lexer Associations...</b>""" - """<p>This shows a dialog to edit the lexer associations of""" - """ the project. These associations override the global lexer""" - """ associations. Lexers are used to highlight the editor""" - """ text.</p>""" - )) + self.tr("Lexer Associations"), + self.tr("Lexer Associations..."), + 0, + 0, + self, + "project_lexer_associations", + ) + self.lexersAct.setStatusTip( + self.tr("Show the project lexer associations (overriding defaults)") + ) + self.lexersAct.setWhatsThis( + self.tr( + """<b>Lexer Associations...</b>""" + """<p>This shows a dialog to edit the lexer associations of""" + """ the project. These associations override the global lexer""" + """ associations. Lexers are used to highlight the editor""" + """ text.</p>""" + ) + ) self.lexersAct.triggered.connect(self.__showLexerAssociations) self.actions.append(self.lexersAct) - + ################################################################### ## Project debug actions ################################################################### - + self.dbgActGrp = createActionGroup(self) - + act = EricAction( - self.tr('Debugger Properties'), - self.tr('Debugger &Properties...'), 0, 0, - self.dbgActGrp, 'project_debugger_properties') - act.setStatusTip(self.tr('Show the debugger properties')) - act.setWhatsThis(self.tr( - """<b>Debugger Properties...</b>""" - """<p>This shows a dialog to edit project specific debugger""" - """ settings.</p>""" - )) + self.tr("Debugger Properties"), + self.tr("Debugger &Properties..."), + 0, + 0, + self.dbgActGrp, + "project_debugger_properties", + ) + act.setStatusTip(self.tr("Show the debugger properties")) + act.setWhatsThis( + self.tr( + """<b>Debugger Properties...</b>""" + """<p>This shows a dialog to edit project specific debugger""" + """ settings.</p>""" + ) + ) act.triggered.connect(self.__showDebugProperties) self.actions.append(act) - + act = EricAction( - self.tr('Load'), - self.tr('&Load'), 0, 0, - self.dbgActGrp, 'project_debugger_properties_load') - act.setStatusTip(self.tr('Load the debugger properties')) - act.setWhatsThis(self.tr( - """<b>Load Debugger Properties</b>""" - """<p>This loads the project specific debugger settings.</p>""" - )) + self.tr("Load"), + self.tr("&Load"), + 0, + 0, + self.dbgActGrp, + "project_debugger_properties_load", + ) + act.setStatusTip(self.tr("Load the debugger properties")) + act.setWhatsThis( + self.tr( + """<b>Load Debugger Properties</b>""" + """<p>This loads the project specific debugger settings.</p>""" + ) + ) act.triggered.connect(self.__readDebugProperties) self.actions.append(act) - + act = EricAction( - self.tr('Save'), - self.tr('&Save'), 0, 0, - self.dbgActGrp, 'project_debugger_properties_save') - act.setStatusTip(self.tr('Save the debugger properties')) - act.setWhatsThis(self.tr( - """<b>Save Debugger Properties</b>""" - """<p>This saves the project specific debugger settings.</p>""" - )) + self.tr("Save"), + self.tr("&Save"), + 0, + 0, + self.dbgActGrp, + "project_debugger_properties_save", + ) + act.setStatusTip(self.tr("Save the debugger properties")) + act.setWhatsThis( + self.tr( + """<b>Save Debugger Properties</b>""" + """<p>This saves the project specific debugger settings.</p>""" + ) + ) act.triggered.connect(self.__writeDebugProperties) self.actions.append(act) - + act = EricAction( - self.tr('Delete'), - self.tr('&Delete'), 0, 0, - self.dbgActGrp, 'project_debugger_properties_delete') - act.setStatusTip(self.tr('Delete the debugger properties')) - act.setWhatsThis(self.tr( - """<b>Delete Debugger Properties</b>""" - """<p>This deletes the file containing the project specific""" - """ debugger settings.</p>""" - )) + self.tr("Delete"), + self.tr("&Delete"), + 0, + 0, + self.dbgActGrp, + "project_debugger_properties_delete", + ) + act.setStatusTip(self.tr("Delete the debugger properties")) + act.setWhatsThis( + self.tr( + """<b>Delete Debugger Properties</b>""" + """<p>This deletes the file containing the project specific""" + """ debugger settings.</p>""" + ) + ) act.triggered.connect(self.__deleteDebugProperties) self.actions.append(act) - + act = EricAction( - self.tr('Reset'), - self.tr('&Reset'), 0, 0, - self.dbgActGrp, 'project_debugger_properties_resets') - act.setStatusTip(self.tr('Reset the debugger properties')) - act.setWhatsThis(self.tr( - """<b>Reset Debugger Properties</b>""" - """<p>This resets the project specific debugger settings.</p>""" - )) + self.tr("Reset"), + self.tr("&Reset"), + 0, + 0, + self.dbgActGrp, + "project_debugger_properties_resets", + ) + act.setStatusTip(self.tr("Reset the debugger properties")) + act.setWhatsThis( + self.tr( + """<b>Reset Debugger Properties</b>""" + """<p>This resets the project specific debugger settings.</p>""" + ) + ) act.triggered.connect(self.__initDebugProperties) self.actions.append(act) - + ################################################################### ## Project session actions ################################################################### - + self.sessActGrp = createActionGroup(self) - + act = EricAction( - self.tr('Load session'), - self.tr('Load session'), 0, 0, - self.sessActGrp, 'project_load_session') - act.setStatusTip(self.tr('Load the projects session file.')) - act.setWhatsThis(self.tr( - """<b>Load session</b>""" - """<p>This loads the projects session file. The session consists""" - """ of the following data.<br>""" - """- all open source files<br>""" - """- all breakpoint<br>""" - """- the commandline arguments<br>""" - """- the working directory<br>""" - """- the exception reporting flag</p>""" - )) + self.tr("Load session"), + self.tr("Load session"), + 0, + 0, + self.sessActGrp, + "project_load_session", + ) + act.setStatusTip(self.tr("Load the projects session file.")) + act.setWhatsThis( + self.tr( + """<b>Load session</b>""" + """<p>This loads the projects session file. The session consists""" + """ of the following data.<br>""" + """- all open source files<br>""" + """- all breakpoint<br>""" + """- the commandline arguments<br>""" + """- the working directory<br>""" + """- the exception reporting flag</p>""" + ) + ) act.triggered.connect(self.__readSession) self.actions.append(act) act = EricAction( - self.tr('Save session'), - self.tr('Save session'), 0, 0, - self.sessActGrp, 'project_save_session') - act.setStatusTip(self.tr('Save the projects session file.')) - act.setWhatsThis(self.tr( - """<b>Save session</b>""" - """<p>This saves the projects session file. The session consists""" - """ of the following data.<br>""" - """- all open source files<br>""" - """- all breakpoint<br>""" - """- the commandline arguments<br>""" - """- the working directory<br>""" - """- the exception reporting flag</p>""" - )) + self.tr("Save session"), + self.tr("Save session"), + 0, + 0, + self.sessActGrp, + "project_save_session", + ) + act.setStatusTip(self.tr("Save the projects session file.")) + act.setWhatsThis( + self.tr( + """<b>Save session</b>""" + """<p>This saves the projects session file. The session consists""" + """ of the following data.<br>""" + """- all open source files<br>""" + """- all breakpoint<br>""" + """- the commandline arguments<br>""" + """- the working directory<br>""" + """- the exception reporting flag</p>""" + ) + ) act.triggered.connect(self.__writeSession) self.actions.append(act) - + act = EricAction( - self.tr('Delete session'), - self.tr('Delete session'), 0, 0, - self.sessActGrp, 'project_delete_session') - act.setStatusTip(self.tr('Delete the projects session file.')) - act.setWhatsThis(self.tr( - """<b>Delete session</b>""" - """<p>This deletes the projects session file</p>""" - )) + self.tr("Delete session"), + self.tr("Delete session"), + 0, + 0, + self.sessActGrp, + "project_delete_session", + ) + act.setStatusTip(self.tr("Delete the projects session file.")) + act.setWhatsThis( + self.tr( + """<b>Delete session</b>""" + """<p>This deletes the projects session file</p>""" + ) + ) act.triggered.connect(self.__deleteSession) self.actions.append(act) - + ################################################################### ## Project Tools - check actions ################################################################### - + self.chkGrp = createActionGroup(self) self.codeMetricsAct = EricAction( - self.tr('Code Metrics'), - self.tr('&Code Metrics...'), 0, 0, - self.chkGrp, 'project_code_metrics') + self.tr("Code Metrics"), + self.tr("&Code Metrics..."), + 0, + 0, + self.chkGrp, + "project_code_metrics", + ) self.codeMetricsAct.setStatusTip( - self.tr('Show some code metrics for the project.')) - self.codeMetricsAct.setWhatsThis(self.tr( - """<b>Code Metrics...</b>""" - """<p>This shows some code metrics for all Python files in""" - """ the project.</p>""" - )) + self.tr("Show some code metrics for the project.") + ) + self.codeMetricsAct.setWhatsThis( + self.tr( + """<b>Code Metrics...</b>""" + """<p>This shows some code metrics for all Python files in""" + """ the project.</p>""" + ) + ) self.codeMetricsAct.triggered.connect(self.__showCodeMetrics) self.actions.append(self.codeMetricsAct) self.codeCoverageAct = EricAction( - self.tr('Python Code Coverage'), - self.tr('Code Co&verage...'), 0, 0, - self.chkGrp, 'project_code_coverage') + self.tr("Python Code Coverage"), + self.tr("Code Co&verage..."), + 0, + 0, + self.chkGrp, + "project_code_coverage", + ) self.codeCoverageAct.setStatusTip( - self.tr('Show code coverage information for the project.')) - self.codeCoverageAct.setWhatsThis(self.tr( - """<b>Code Coverage...</b>""" - """<p>This shows the code coverage information for all Python""" - """ files in the project.</p>""" - )) + self.tr("Show code coverage information for the project.") + ) + self.codeCoverageAct.setWhatsThis( + self.tr( + """<b>Code Coverage...</b>""" + """<p>This shows the code coverage information for all Python""" + """ files in the project.</p>""" + ) + ) self.codeCoverageAct.triggered.connect(self.__showCodeCoverage) self.actions.append(self.codeCoverageAct) self.codeProfileAct = EricAction( - self.tr('Profile Data'), - self.tr('&Profile Data...'), 0, 0, - self.chkGrp, 'project_profile_data') + self.tr("Profile Data"), + self.tr("&Profile Data..."), + 0, + 0, + self.chkGrp, + "project_profile_data", + ) self.codeProfileAct.setStatusTip( - self.tr('Show profiling data for the project.')) - self.codeProfileAct.setWhatsThis(self.tr( - """<b>Profile Data...</b>""" - """<p>This shows the profiling data for the project.</p>""" - )) + self.tr("Show profiling data for the project.") + ) + self.codeProfileAct.setWhatsThis( + self.tr( + """<b>Profile Data...</b>""" + """<p>This shows the profiling data for the project.</p>""" + ) + ) self.codeProfileAct.triggered.connect(self.__showProfileData) self.actions.append(self.codeProfileAct) - + ################################################################### ## Project Tools - graphics actions ################################################################### @@ -4322,90 +4627,119 @@ self.graphicsGrp = createActionGroup(self) self.applicationDiagramAct = EricAction( - self.tr('Application Diagram'), - self.tr('&Application Diagram...'), 0, 0, - self.graphicsGrp, 'project_application_diagram') + self.tr("Application Diagram"), + self.tr("&Application Diagram..."), + 0, + 0, + self.graphicsGrp, + "project_application_diagram", + ) self.applicationDiagramAct.setStatusTip( - self.tr('Show a diagram of the project.')) - self.applicationDiagramAct.setWhatsThis(self.tr( - """<b>Application Diagram...</b>""" - """<p>This shows a diagram of the project.</p>""" - )) - self.applicationDiagramAct.triggered.connect( - self.handleApplicationDiagram) + self.tr("Show a diagram of the project.") + ) + self.applicationDiagramAct.setWhatsThis( + self.tr( + """<b>Application Diagram...</b>""" + """<p>This shows a diagram of the project.</p>""" + ) + ) + self.applicationDiagramAct.triggered.connect(self.handleApplicationDiagram) self.actions.append(self.applicationDiagramAct) self.loadDiagramAct = EricAction( - self.tr('Load Diagram'), - self.tr('&Load Diagram...'), 0, 0, - self.graphicsGrp, 'project_load_diagram') - self.loadDiagramAct.setStatusTip( - self.tr('Load a diagram from file.')) - self.loadDiagramAct.setWhatsThis(self.tr( - """<b>Load Diagram...</b>""" - """<p>This loads a diagram from file.</p>""" - )) + self.tr("Load Diagram"), + self.tr("&Load Diagram..."), + 0, + 0, + self.graphicsGrp, + "project_load_diagram", + ) + self.loadDiagramAct.setStatusTip(self.tr("Load a diagram from file.")) + self.loadDiagramAct.setWhatsThis( + self.tr( + """<b>Load Diagram...</b>""" + """<p>This loads a diagram from file.</p>""" + ) + ) self.loadDiagramAct.triggered.connect(self.__loadDiagram) self.actions.append(self.loadDiagramAct) - + ################################################################### ## Project Tools - plugin packaging actions ################################################################### - + self.pluginGrp = createActionGroup(self) - + self.pluginPkgListAct = EricAction( - self.tr('Create Package List'), + self.tr("Create Package List"), UI.PixmapCache.getIcon("pluginArchiveList"), - self.tr('Create &Package List'), 0, 0, - self.pluginGrp, 'project_plugin_pkglist') + self.tr("Create &Package List"), + 0, + 0, + self.pluginGrp, + "project_plugin_pkglist", + ) self.pluginPkgListAct.setStatusTip( - self.tr('Create an initial PKGLIST file for an eric plugin.')) - self.pluginPkgListAct.setWhatsThis(self.tr( - """<b>Create Package List</b>""" - """<p>This creates an initial list of files to include in an""" - """ eric plugin archive. The list is created from the project""" - """ file.</p>""" - )) + self.tr("Create an initial PKGLIST file for an eric plugin.") + ) + self.pluginPkgListAct.setWhatsThis( + self.tr( + """<b>Create Package List</b>""" + """<p>This creates an initial list of files to include in an""" + """ eric plugin archive. The list is created from the project""" + """ file.</p>""" + ) + ) self.pluginPkgListAct.triggered.connect(self.__pluginCreatePkgList) self.actions.append(self.pluginPkgListAct) self.pluginArchiveAct = EricAction( - self.tr('Create Plugin Archives'), + self.tr("Create Plugin Archives"), UI.PixmapCache.getIcon("pluginArchive"), - self.tr('Create Plugin &Archives'), 0, 0, - self.pluginGrp, 'project_plugin_archive') - self.pluginArchiveAct.setStatusTip( - self.tr('Create eric plugin archive files.')) - self.pluginArchiveAct.setWhatsThis(self.tr( - """<b>Create Plugin Archives</b>""" - """<p>This creates eric plugin archive files using the list""" - """ of files given in a PKGLIST* file. The archive name is""" - """ built from the main script name if not designated in""" - """ the package list file.</p>""" - )) + self.tr("Create Plugin &Archives"), + 0, + 0, + self.pluginGrp, + "project_plugin_archive", + ) + self.pluginArchiveAct.setStatusTip(self.tr("Create eric plugin archive files.")) + self.pluginArchiveAct.setWhatsThis( + self.tr( + """<b>Create Plugin Archives</b>""" + """<p>This creates eric plugin archive files using the list""" + """ of files given in a PKGLIST* file. The archive name is""" + """ built from the main script name if not designated in""" + """ the package list file.</p>""" + ) + ) self.pluginArchiveAct.triggered.connect(self.__pluginCreateArchives) self.actions.append(self.pluginArchiveAct) - + self.pluginSArchiveAct = EricAction( - self.tr('Create Plugin Archives (Snapshot)'), + self.tr("Create Plugin Archives (Snapshot)"), UI.PixmapCache.getIcon("pluginArchiveSnapshot"), - self.tr('Create Plugin Archives (&Snapshot)'), 0, 0, - self.pluginGrp, 'project_plugin_sarchive') - self.pluginSArchiveAct.setStatusTip(self.tr( - 'Create eric plugin archive files (snapshot releases).')) - self.pluginSArchiveAct.setWhatsThis(self.tr( - """<b>Create Plugin Archives (Snapshot)</b>""" - """<p>This creates eric plugin archive files using the list""" - """ of files given in the PKGLIST* file. The archive name is""" - """ built from the main script name if not designated in""" - """ the package list file. The version entry of the main script""" - """ is modified to reflect a snapshot release.</p>""" - )) - self.pluginSArchiveAct.triggered.connect( - self.__pluginCreateSnapshotArchives) + self.tr("Create Plugin Archives (&Snapshot)"), + 0, + 0, + self.pluginGrp, + "project_plugin_sarchive", + ) + self.pluginSArchiveAct.setStatusTip( + self.tr("Create eric plugin archive files (snapshot releases).") + ) + self.pluginSArchiveAct.setWhatsThis( + self.tr( + """<b>Create Plugin Archives (Snapshot)</b>""" + """<p>This creates eric plugin archive files using the list""" + """ of files given in the PKGLIST* file. The archive name is""" + """ built from the main script name if not designated in""" + """ the package list file. The version entry of the main script""" + """ is modified to reflect a snapshot release.</p>""" + ) + ) + self.pluginSArchiveAct.triggered.connect(self.__pluginCreateSnapshotArchives) self.actions.append(self.pluginSArchiveAct) - + ################################################################### ## Project Tools - make actions ################################################################### @@ -4413,126 +4747,158 @@ self.makeGrp = createActionGroup(self) self.makeExecuteAct = EricAction( - self.tr('Execute Make'), - self.tr('&Execute Make'), 0, 0, - self.makeGrp, 'project_make_execute') - self.makeExecuteAct.setStatusTip( - self.tr("Perform a 'make' run.")) - self.makeExecuteAct.setWhatsThis(self.tr( - """<b>Execute Make</b>""" - """<p>This performs a 'make' run to rebuild the configured""" - """ target.</p>""" - )) + self.tr("Execute Make"), + self.tr("&Execute Make"), + 0, + 0, + self.makeGrp, + "project_make_execute", + ) + self.makeExecuteAct.setStatusTip(self.tr("Perform a 'make' run.")) + self.makeExecuteAct.setWhatsThis( + self.tr( + """<b>Execute Make</b>""" + """<p>This performs a 'make' run to rebuild the configured""" + """ target.</p>""" + ) + ) self.makeExecuteAct.triggered.connect(self.__executeMake) self.actions.append(self.makeExecuteAct) self.makeTestAct = EricAction( - self.tr('Test for Changes'), - self.tr('&Test for Changes'), 0, 0, - self.makeGrp, 'project_make_test') + self.tr("Test for Changes"), + self.tr("&Test for Changes"), + 0, + 0, + self.makeGrp, + "project_make_test", + ) self.makeTestAct.setStatusTip( - self.tr("Question 'make', if a rebuild is needed.")) - self.makeTestAct.setWhatsThis(self.tr( - """<b>Test for Changes</b>""" - """<p>This questions 'make', if a rebuild of the configured""" - """ target is necessary.</p>""" - )) + self.tr("Question 'make', if a rebuild is needed.") + ) + self.makeTestAct.setWhatsThis( + self.tr( + """<b>Test for Changes</b>""" + """<p>This questions 'make', if a rebuild of the configured""" + """ target is necessary.</p>""" + ) + ) self.makeTestAct.triggered.connect( - lambda: self.__executeMake(questionOnly=True)) + lambda: self.__executeMake(questionOnly=True) + ) self.actions.append(self.makeTestAct) - + ################################################################### ## Project Tools - other tools actions ################################################################### - + self.othersGrp = createActionGroup(self) - + self.createSBOMAct = EricAction( - self.tr('Create SBOM File'), - self.tr('Create &SBOM File'), 0, 0, - self.othersGrp, 'project_create_sbom') + self.tr("Create SBOM File"), + self.tr("Create &SBOM File"), + 0, + 0, + self.othersGrp, + "project_create_sbom", + ) self.createSBOMAct.setStatusTip( - self.tr("Create a SBOM file of the project dependencies.")) - self.createSBOMAct.setWhatsThis(self.tr( - """<b>Create SBOM File</b>""" - """<p>This allows the creation of a SBOM file of the project""" - """ dependencies. This may be based on various input sources""" - """ and will be saved as a CycloneDX SBOM file.</p>""" - )) + self.tr("Create a SBOM file of the project dependencies.") + ) + self.createSBOMAct.setWhatsThis( + self.tr( + """<b>Create SBOM File</b>""" + """<p>This allows the creation of a SBOM file of the project""" + """ dependencies. This may be based on various input sources""" + """ and will be saved as a CycloneDX SBOM file.</p>""" + ) + ) self.createSBOMAct.triggered.connect(self.__createSBOMFile) self.actions.append(self.createSBOMAct) - + ################################################################### ## Project Tools - code formatting actions ################################################################### - + self.blackFormattingGrp = createActionGroup(self) - + self.blackAboutAct = EricAction( self.tr("About Black"), self.tr("&Black"), - 0, 0, - self.blackFormattingGrp, "project_black_about" + 0, + 0, + self.blackFormattingGrp, + "project_black_about", ) - self.blackAboutAct.setStatusTip( - self.tr("Show some information about 'Black'.") + self.blackAboutAct.setStatusTip(self.tr("Show some information about 'Black'.")) + self.blackAboutAct.setWhatsThis( + self.tr( + "<b>Black</b>" + "<p>This shows some information about the installed 'Black' tool.</p>" + ) ) - self.blackAboutAct.setWhatsThis(self.tr( - "<b>Black</b>" - "<p>This shows some information about the installed 'Black' tool.</p>" - )) self.blackAboutAct.triggered.connect(self.__aboutBlack) self.actions.append(self.blackAboutAct) font = self.blackAboutAct.font() font.setBold(True) self.blackAboutAct.setFont(font) - + self.blackFormatAct = EricAction( self.tr("Format Code"), self.tr("&Format Code"), - 0, 0, - self.blackFormattingGrp, "project_black_format_code" + 0, + 0, + self.blackFormattingGrp, + "project_black_format_code", ) self.blackFormatAct.setStatusTip( self.tr("Format the project sources with 'Black'.") ) - self.blackFormatAct.setWhatsThis(self.tr( - "<b>Format Code</b>" - "<p>This shows a dialog to enter parameters for the formatting run and" - " reformats the project sources using 'Black'.</p>" - )) + self.blackFormatAct.setWhatsThis( + self.tr( + "<b>Format Code</b>" + "<p>This shows a dialog to enter parameters for the formatting run and" + " reformats the project sources using 'Black'.</p>" + ) + ) self.blackFormatAct.triggered.connect( lambda: self.__performFormatWithBlack(BlackFormattingAction.Format) ) self.actions.append(self.blackFormatAct) - + self.blackCheckFormattingAct = EricAction( self.tr("Check Code Formatting"), self.tr("&Check Code Formatting"), - 0, 0, - self.blackFormattingGrp, "project_black_check_code" + 0, + 0, + self.blackFormattingGrp, + "project_black_check_code", ) self.blackCheckFormattingAct.setStatusTip( self.tr( "Check, if the project sources need to be reformatted with 'Black'." ) ) - self.blackCheckFormattingAct.setWhatsThis(self.tr( - "<b>Check Code Formatting</b>" - "<p>This shows a dialog to enter parameters for the format check run and" - " performs a check, if the project sources need to be reformatted using" - " 'Black'.</p>" - )) + self.blackCheckFormattingAct.setWhatsThis( + self.tr( + "<b>Check Code Formatting</b>" + "<p>This shows a dialog to enter parameters for the format check run" + " and performs a check, if the project sources need to be reformatted" + " using Black'.</p>" + ) + ) self.blackCheckFormattingAct.triggered.connect( lambda: self.__performFormatWithBlack(BlackFormattingAction.Check) ) self.actions.append(self.blackCheckFormattingAct) - + self.blackDiffFormattingAct = EricAction( self.tr("Code Formatting Diff"), self.tr("Code Formatting &Diff"), - 0, 0, - self.blackFormattingGrp, "project_black_diff_code" + 0, + 0, + self.blackFormattingGrp, + "project_black_diff_code", ) self.blackDiffFormattingAct.setStatusTip( self.tr( @@ -4540,17 +4906,19 @@ " with 'Black'." ) ) - self.blackDiffFormattingAct.setWhatsThis(self.tr( - "<b>Diff Code Formatting</b>" - "<p>This shows a dialog to enter parameters for the format diff run and" - " generates a unified diff of potential project source reformatting using" - " 'Black'.</p>" - )) + self.blackDiffFormattingAct.setWhatsThis( + self.tr( + "<b>Diff Code Formatting</b>" + "<p>This shows a dialog to enter parameters for the format diff run and" + " generates a unified diff of potential project source reformatting" + " using Black'.</p>" + ) + ) self.blackDiffFormattingAct.triggered.connect( lambda: self.__performFormatWithBlack(BlackFormattingAction.Diff) ) self.actions.append(self.blackDiffFormattingAct) - + self.closeAct.setEnabled(False) self.saveAct.setEnabled(False) self.saveasAct.setEnabled(False) @@ -4562,36 +4930,36 @@ self.sessActGrp.setEnabled(False) self.dbgActGrp.setEnabled(False) self.pluginGrp.setEnabled(False) - + def initMenus(self): """ Public slot to initialize the project menus. - + @return tuple of generated menus @rtype tuple of (QMenu, QMenu) """ - menu = QMenu(self.tr('&Project'), self.parent()) - self.recentMenu = QMenu(self.tr('Open &Recent Projects'), menu) - self.sessionMenu = QMenu(self.tr('Session'), menu) - self.debuggerMenu = QMenu(self.tr('Debugger'), menu) - - toolsMenu = QMenu(self.tr('Project-T&ools'), self.parent()) - self.vcsMenu = QMenu(self.tr('&Version Control'), toolsMenu) + menu = QMenu(self.tr("&Project"), self.parent()) + self.recentMenu = QMenu(self.tr("Open &Recent Projects"), menu) + self.sessionMenu = QMenu(self.tr("Session"), menu) + self.debuggerMenu = QMenu(self.tr("Debugger"), menu) + + toolsMenu = QMenu(self.tr("Project-T&ools"), self.parent()) + self.vcsMenu = QMenu(self.tr("&Version Control"), toolsMenu) self.vcsMenu.setTearOffEnabled(True) self.vcsProjectHelper.initMenu(self.vcsMenu) self.vcsMenu.setEnabled(self.vcsSoftwareAvailable()) - self.checksMenu = QMenu(self.tr('Chec&k'), toolsMenu) + self.checksMenu = QMenu(self.tr("Chec&k"), toolsMenu) self.checksMenu.setTearOffEnabled(True) self.formattingMenu = QMenu(self.tr("Code &Formatting"), toolsMenu) self.formattingMenu.setTearOffEnabled(True) - self.menuShow = QMenu(self.tr('Sho&w'), toolsMenu) - self.graphicsMenu = QMenu(self.tr('&Diagrams'), toolsMenu) - self.packagersMenu = QMenu(self.tr('Pac&kagers'), toolsMenu) - self.apidocMenu = QMenu(self.tr('Source &Documentation'), toolsMenu) + self.menuShow = QMenu(self.tr("Sho&w"), toolsMenu) + self.graphicsMenu = QMenu(self.tr("&Diagrams"), toolsMenu) + self.packagersMenu = QMenu(self.tr("Pac&kagers"), toolsMenu) + self.apidocMenu = QMenu(self.tr("Source &Documentation"), toolsMenu) self.apidocMenu.setTearOffEnabled(True) - self.makeMenu = QMenu(self.tr('Make'), toolsMenu) - self.othersMenu = QMenu(self.tr('Other Tools'), toolsMenu) - + self.makeMenu = QMenu(self.tr("Make"), toolsMenu) + self.othersMenu = QMenu(self.tr("Other Tools"), toolsMenu) + self.__menus = { "Main": menu, "Recent": self.recentMenu, @@ -4607,7 +4975,7 @@ "OtherTools": self.othersMenu, "Formatting": self.formattingMenu, } - + # connect the aboutToShow signals self.recentMenu.aboutToShow.connect(self.__showContextMenuRecent) self.recentMenu.triggered.connect(self.__openRecent) @@ -4623,47 +4991,47 @@ self.othersMenu.aboutToShow.connect(self.__showContextMenuOthers) self.formattingMenu.aboutToShow.connect(self.__showContextMenuFormat) menu.aboutToShow.connect(self.__showMenu) - + # build the show menu self.menuShow.setTearOffEnabled(True) self.menuShow.addAction(self.codeMetricsAct) self.menuShow.addAction(self.codeCoverageAct) self.menuShow.addAction(self.codeProfileAct) - + # build the diagrams menu self.graphicsMenu.setTearOffEnabled(True) self.graphicsMenu.addAction(self.applicationDiagramAct) self.graphicsMenu.addSeparator() self.graphicsMenu.addAction(self.loadDiagramAct) - + # build the session menu self.sessionMenu.setTearOffEnabled(True) self.sessionMenu.addActions(self.sessActGrp.actions()) - + # build the debugger menu self.debuggerMenu.setTearOffEnabled(True) self.debuggerMenu.addActions(self.dbgActGrp.actions()) - + # build the packagers menu self.packagersMenu.setTearOffEnabled(True) self.packagersMenu.addActions(self.pluginGrp.actions()) self.packagersMenu.addSeparator() - + # build the make menu self.makeMenu.setTearOffEnabled(True) self.makeMenu.addActions(self.makeGrp.actions()) self.makeMenu.addSeparator() - + # build the 'Other Tools' menu self.othersMenu.setTearOffEnabled(True) self.othersMenu.addActions(self.othersGrp.actions()) self.othersMenu.addSeparator() - + # build the 'Code Formatting' menu self.formattingMenu.setTearOffEnabled(True) self.formattingMenu.addActions(self.blackFormattingGrp.actions()) self.formattingMenu.addSeparator() - + # build the project main menu menu.setTearOffEnabled(True) menu.addActions(self.actGrp1.actions()) @@ -4683,7 +5051,7 @@ menu.addSeparator() self.menuDebuggerAct = menu.addMenu(self.debuggerMenu) self.menuSessionAct = menu.addMenu(self.sessionMenu) - + # build the project tools menu toolsMenu.setTearOffEnabled(True) toolsMenu.addSeparator() @@ -4704,7 +5072,7 @@ self.menuPackagersAct = toolsMenu.addMenu(self.packagersMenu) toolsMenu.addSeparator() self.menuOtherToolsAct = toolsMenu.addMenu(self.othersMenu) - + self.menuCheckAct.setEnabled(False) self.menuShowAct.setEnabled(False) self.menuDiagramAct.setEnabled(False) @@ -4715,17 +5083,17 @@ self.menuMakeAct.setEnabled(False) self.menuOtherToolsAct.setEnabled(False) self.menuFormattingAct.setEnabled(False) - + self.__menu = menu self.__toolsMenu = toolsMenu - + return menu, toolsMenu - + def initToolbars(self, toolbarManager): """ Public slot to initialize the project toolbar and the basic VCS toolbar. - + @param toolbarManager reference to a toolbar manager object (EricToolBarManager) @return tuple of the generated toolbars (tuple of two QToolBar) @@ -4733,35 +5101,35 @@ tb = QToolBar(self.tr("Project"), self.ui) tb.setIconSize(UI.Config.ToolBarIconSize) tb.setObjectName("ProjectToolbar") - tb.setToolTip(self.tr('Project')) - + tb.setToolTip(self.tr("Project")) + tb.addActions(self.actGrp1.actions()) tb.addAction(self.closeAct) tb.addSeparator() tb.addAction(self.saveAct) tb.addAction(self.saveasAct) - + toolbarManager.addToolBar(tb, tb.windowTitle()) toolbarManager.addAction(self.addFilesAct, tb.windowTitle()) toolbarManager.addAction(self.addDirectoryAct, tb.windowTitle()) toolbarManager.addAction(self.addLanguageAct, tb.windowTitle()) toolbarManager.addAction(self.propsAct, tb.windowTitle()) toolbarManager.addAction(self.userPropsAct, tb.windowTitle()) - + import VCS - vcstb = VCS.getBasicHelper(self).initBasicToolbar( - self.ui, toolbarManager) - + + vcstb = VCS.getBasicHelper(self).initBasicToolbar(self.ui, toolbarManager) + return tb, vcstb - + def __showMenu(self): """ Private method to set up the project menu. """ self.menuRecentAct.setEnabled(len(self.recent) > 0) - + self.showMenu.emit("Main", self.__menus["Main"]) - + def __syncRecent(self): """ Private method to synchronize the list of recently opened projects @@ -4775,87 +5143,86 @@ if len(self.recent) > maxRecent: self.recent = self.recent[:maxRecent] self.__saveRecent() - + def __showContextMenuRecent(self): """ Private method to set up the recent projects menu. """ self.__loadRecent() - + self.recentMenu.clear() - + for idx, rp in enumerate(self.recent, start=1): - formatStr = '&{0:d}. {1}' if idx < 10 else '{0:d}. {1}' + formatStr = "&{0:d}. {1}" if idx < 10 else "{0:d}. {1}" act = self.recentMenu.addAction( formatStr.format( - idx, - Utilities.compactPath(rp, self.ui.maxMenuFilePathLen))) + idx, Utilities.compactPath(rp, self.ui.maxMenuFilePathLen) + ) + ) act.setData(rp) act.setEnabled(pathlib.Path(rp).exists()) - + self.recentMenu.addSeparator() - self.recentMenu.addAction(self.tr('&Clear'), self.clearRecent) - + self.recentMenu.addAction(self.tr("&Clear"), self.clearRecent) + def __openRecent(self, act): """ Private method to open a project from the list of rencently opened projects. - + @param act reference to the action that triggered (QAction) """ file = act.data() if file: self.openProject(file) - + def clearRecent(self): """ Public method to clear the recent projects menu. """ self.recent = [] self.__saveRecent() - + def clearHistories(self): """ Public method to clear the project related histories. """ self.clearRecent() - + for key in ["DebugClientsHistory", "DebuggerInterpreterHistory"]: Preferences.setProject(key, []) Preferences.syncPreferences() - + def __searchNewFiles(self): """ Private slot used to handle the search new files action. """ self.__doSearchNewFiles(False, True) - + def __searchProjectFile(self): """ Private slot to show the Find Project File dialog. """ 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) - self.__findProjectFileDialog.linguistFile.connect( - self.linguistFile) + self.__findProjectFileDialog.sourceFile.connect(self.sourceFile) + self.__findProjectFileDialog.designerFile.connect(self.designerFile) + self.__findProjectFileDialog.linguistFile.connect(self.linguistFile) self.__findProjectFileDialog.show() self.__findProjectFileDialog.raise_() self.__findProjectFileDialog.activateWindow() - + def __doSearchNewFiles(self, AI=True, onUserDemand=False): """ Private method to search for new files in the project directory. - + If new files were found, it shows a dialog listing these files and gives the user the opportunity to select the ones he wants to include. If 'Automatic Inclusion' is enabled, the new files are automatically added to the project. - + @param AI flag indicating whether the automatic inclusion should be honoured (boolean) @param onUserDemand flag indicating whether this method was @@ -4864,11 +5231,13 @@ autoInclude = Preferences.getProject("AutoIncludeNewFiles") recursiveSearch = Preferences.getProject("SearchNewFilesRecursively") newFiles = [] - - ignore_patterns = [pattern for pattern, filetype in - self.pdata["FILETYPES"].items() - if filetype == '__IGNORE__'] - + + ignore_patterns = [ + pattern + for pattern, filetype in self.pdata["FILETYPES"].items() + if filetype == "__IGNORE__" + ] + dirs = self.subdirs[:] for directory in dirs: skip = False @@ -4878,7 +5247,7 @@ break if skip: continue - + curpath = os.path.join(self.ppath, directory) try: newSources = os.listdir(curpath) @@ -4886,28 +5255,28 @@ newSources = [] pattern = ( self.pdata["TRANSLATIONPATTERN"].replace("%language%", "*") - if self.pdata["TRANSLATIONPATTERN"] else - "*.ts" + if self.pdata["TRANSLATIONPATTERN"] + else "*.ts" ) binpattern = self.__binaryTranslationFile(pattern) for ns in newSources: # ignore hidden files and directories - if ns.startswith('.'): + if ns.startswith("."): continue if ( - Utilities.isWindowsPlatform() and - os.path.isdir(os.path.join(curpath, ns)) and - ns.startswith('_') + Utilities.isWindowsPlatform() + and os.path.isdir(os.path.join(curpath, ns)) + and ns.startswith("_") ): # dot net hack continue - + # set fn to project relative name # then reset ns to fully qualified name for insertion, # possibly. fn = os.path.join(directory, ns) if directory else ns ns = os.path.abspath(os.path.join(curpath, ns)) - + # do not bother with dirs here... if os.path.isdir(ns): if recursiveSearch: @@ -4915,109 +5284,108 @@ if d not in dirs: dirs.append(d) continue - + 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] 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 == "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) + ) + ) ): if autoInclude and AI: self.appendFile(ns) else: newFiles.append(ns) - + # if autoInclude is set there is no more work left - if (autoInclude and AI): + if autoInclude and AI: return - + # if newfiles is empty, put up message box informing user nothing found if not newFiles: if onUserDemand: EricMessageBox.information( self.ui, self.tr("Search New Files"), - self.tr("There were no new files found to be added.")) + self.tr("There were no new files found to be added."), + ) return - + # autoInclude is not set, show a dialog from .AddFoundFilesDialog import AddFoundFilesDialog + dlg = AddFoundFilesDialog(newFiles, self.parent(), None) res = dlg.exec() - + # the 'Add All' button was pressed if res == 1: for file in newFiles: self.appendFile(file) - + # the 'Add Selected' button was pressed elif res == 2: files = dlg.getSelection() for file in files: self.appendFile(file) - + def othersAdded(self, fn, updateModel=True): """ Public slot to be called, if something was added to the OTHERS project data area. - + @param fn filename or directory name added (string) @param updateModel flag indicating an update of the model is requested (boolean) """ self.projectOthersAdded.emit(fn) updateModel and self.__model.addNewItem("OTHERS", fn) - + def getActions(self): """ Public method to get a list of all actions. - + @return list of all actions (list of EricAction) """ return self.actions[:] - + def addEricActions(self, actions): """ Public method to add actions to the list of actions. - + @param actions list of actions (list of EricAction) """ self.actions.extend(actions) - + def removeEricActions(self, actions): """ Public method to remove actions from the list of actions. - + @param actions list of actions (list of EricAction) """ for act in actions: with contextlib.suppress(ValueError): self.actions.remove(act) - + def getMenu(self, menuName): """ Public method to get a reference to the main menu or a submenu. - + @param menuName name of the menu (string) @return reference to the requested menu (QMenu) or None """ @@ -5025,30 +5393,30 @@ return self.__menus[menuName] except KeyError: return None - + def repopulateItem(self, fullname): """ Public slot to repopulate a named item. - + @param fullname full name of the item to repopulate (string) """ if not self.isOpen(): return - + with EricOverrideCursor(): name = self.getRelativePath(fullname) self.prepareRepopulateItem.emit(name) self.__model.repopulateItem(name) self.completeRepopulateItem.emit(name) - + ############################################################## ## Below is the VCS interface ############################################################## - + def initVCS(self, vcsSystem=None, nooverride=False): """ Public method used to instantiate a vcs system. - + @param vcsSystem type of VCS to be used (string) @param nooverride flag indicating to ignore an override request (boolean) @@ -5057,25 +5425,26 @@ vcs = None forProject = True override = False - + if vcsSystem is None: - if self.pdata["VCS"] and self.pdata["VCS"] != 'None': + 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.pudata["VCSOVERRIDE"] and - not nooverride + forProject + and self.pdata["VCS"] + and self.pdata["VCS"] != "None" + and self.pudata["VCSOVERRIDE"] + and not nooverride ): vcsSystem = self.pudata["VCSOVERRIDE"] override = True - + if vcsSystem is not None: import VCS + try: vcs = VCS.factory(vcsSystem) except ImportError: @@ -5083,7 +5452,7 @@ # override failed, revert to original self.pudata["VCSOVERRIDE"] = "" return self.initVCS(nooverride=True) - + if vcs: vcsExists, msg = vcs.vcsExists() if not vcsExists: @@ -5096,11 +5465,12 @@ self.tr( "<p>The selected VCS <b>{0}</b> could not be" " found. <br/>Reverting override.</p><p>{1}" - "</p>") - .format(vcsSystem, msg)) + "</p>" + ).format(vcsSystem, msg), + ) self.pudata["VCSOVERRIDE"] = "" return self.initVCS(nooverride=True) - + with EricOverridenCursor(): EricMessageBox.critical( self.ui, @@ -5108,14 +5478,16 @@ self.tr( "<p>The selected VCS <b>{0}</b> could not be" " found.<br/>Disabling version control.</p>" - "<p>{1}</p>").format(vcsSystem, msg)) + "<p>{1}</p>" + ).format(vcsSystem, msg), + ) vcs = None if forProject: - self.pdata["VCS"] = 'None' + self.pdata["VCS"] = "None" self.setDirty(True) else: vcs.vcsInitConfig(self) - + if vcs and forProject: # set the vcs options if vcs.vcsSupportCommandOptions(): @@ -5126,10 +5498,11 @@ with contextlib.suppress(LookupError): vcsother = copy.deepcopy(self.pdata["VCSOTHERDATA"]) vcs.vcsSetOtherData(vcsother) - + if forProject: if vcs is None: import VCS + self.vcsProjectHelper = VCS.getBasicHelper(self) self.vcsBasicHelper = True else: @@ -5138,17 +5511,17 @@ if self.vcsMenu is not None: self.vcsProjectHelper.initMenu(self.vcsMenu) self.vcsMenu.setEnabled(self.vcsSoftwareAvailable()) - + return vcs - + def resetVCS(self): """ Public method to reset the VCS. """ - self.pdata["VCS"] = 'None' + self.pdata["VCS"] = "None" self.vcs = self.initVCS() ericApp().getObject("PluginManager").deactivateVcsPlugins() - + def __showContextMenuVCS(self): """ Private slot called before the vcs menu is shown. @@ -5156,88 +5529,87 @@ self.vcsProjectHelper.showMenu() if self.vcsBasicHelper: self.showMenu.emit("VCS", self.vcsMenu) - + def vcsSoftwareAvailable(self): """ Public method to check, if some supported VCS software is available to the IDE. - + @return flag indicating availability of VCS software (boolean) """ vcsSystemsDict = ( - ericApp().getObject("PluginManager") + ericApp() + .getObject("PluginManager") .getPluginDisplayStrings("version_control") ) return len(vcsSystemsDict) != 0 - + def __vcsStatusChanged(self): """ Private slot to handle a change of the overall VCS status. """ self.projectChanged.emit() - + def __vcsConnectStatusMonitor(self): """ Private method to start the VCS monitor and connect its signals. """ if self.vcs is not None: self.vcs.committed.connect(self.vcsCommitted) - + self.vcs.startStatusMonitor(self) - self.vcs.vcsStatusMonitorData.connect( - self.__model.changeVCSStates) - self.vcs.vcsStatusMonitorData.connect( - self.vcsStatusMonitorData) - self.vcs.vcsStatusMonitorAllData.connect( - self.vcsStatusMonitorAllData) - self.vcs.vcsStatusMonitorStatus.connect( - self.vcsStatusMonitorStatus) - self.vcs.vcsStatusMonitorInfo.connect( - self.vcsStatusMonitorInfo) - self.vcs.vcsStatusChanged.connect( - self.__vcsStatusChanged) - + self.vcs.vcsStatusMonitorData.connect(self.__model.changeVCSStates) + self.vcs.vcsStatusMonitorData.connect(self.vcsStatusMonitorData) + self.vcs.vcsStatusMonitorAllData.connect(self.vcsStatusMonitorAllData) + self.vcs.vcsStatusMonitorStatus.connect(self.vcsStatusMonitorStatus) + self.vcs.vcsStatusMonitorInfo.connect(self.vcsStatusMonitorInfo) + self.vcs.vcsStatusChanged.connect(self.__vcsStatusChanged) + ######################################################################### ## Below is the interface to the checker tools ######################################################################### - + def __showContextMenuChecks(self): """ Private slot called before the checks menu is shown. """ self.showMenu.emit("Checks", self.checksMenu) - + ######################################################################### ## Below is the interface to the packagers tools ######################################################################### - + def __showContextMenuPackagers(self): """ Private slot called before the packagers menu is shown. """ self.showMenu.emit("Packagers", self.packagersMenu) - + ######################################################################### ## Below is the interface to the apidoc tools ######################################################################### - + def __showContextMenuApiDoc(self): """ Private slot called before the apidoc menu is shown. """ self.showMenu.emit("Apidoc", self.apidocMenu) - + ######################################################################### ## Below is the interface to the show tools ######################################################################### - + def __showCodeMetrics(self): """ Private slot used to calculate some code metrics for the project files. """ - files = [os.path.join(self.ppath, file) - for file in self.pdata["SOURCES"] if file.endswith(".py")] + files = [ + os.path.join(self.ppath, file) + for file in self.pdata["SOURCES"] + if file.endswith(".py") + ] from DataViews.CodeMetricsDialog import CodeMetricsDialog + self.codemetrics = CodeMetricsDialog() self.codemetrics.show() self.codemetrics.prepare(files) @@ -5254,9 +5626,11 @@ self.tr("Coverage Data"), self.tr( "There is no main script defined for the" - " current project. Aborting")) + " current project. Aborting" + ), + ) return - + files = Utilities.getCoverageFileNames(fn) if files: if len(files) > 1: @@ -5265,18 +5639,23 @@ self.tr("Code Coverage"), self.tr("Please select a coverage file"), files, - 0, False) + 0, + False, + ) if not ok: return else: fn = files[0] else: return - - files = [os.path.join(self.ppath, file) - for file in self.pdata["SOURCES"] - if os.path.splitext(file)[1].startswith(".py")] + + files = [ + os.path.join(self.ppath, file) + for file in self.pdata["SOURCES"] + if os.path.splitext(file)[1].startswith(".py") + ] from DataViews.PyCoverageDialog import PyCoverageDialog + self.codecoverage = PyCoverageDialog() self.codecoverage.show() self.codecoverage.start(fn, files) @@ -5292,9 +5671,11 @@ self.tr("Profile Data"), self.tr( "There is no main script defined for the" - " current project. Aborting")) + " current project. Aborting" + ), + ) return - + files = Utilities.getProfileFileNames(fn) if files: if len(files) > 1: @@ -5303,19 +5684,22 @@ self.tr("Profile Data"), self.tr("Please select a profile file"), files, - 0, False) + 0, + False, + ) if not ok: return else: fn = files[0] else: return - + from DataViews.PyProfileDialog import PyProfileDialog + self.profiledata = PyProfileDialog() self.profiledata.show() self.profiledata.start(fn) - + def __showContextMenuShow(self): """ Private slot called before the show menu is shown. @@ -5323,28 +5707,26 @@ fn = self.getMainScript(True) if not fn: fn = self.getProjectPath() - + self.codeProfileAct.setEnabled( - self.isPy3Project() and - bool(Utilities.getProfileFileName(fn)) + self.isPy3Project() and bool(Utilities.getProfileFileName(fn)) ) self.codeCoverageAct.setEnabled( - self.isPy3Project() and - bool(Utilities.getCoverageFileNames(fn)) + self.isPy3Project() and bool(Utilities.getCoverageFileNames(fn)) ) - + self.showMenu.emit("Show", self.menuShow) - + ######################################################################### ## Below is the interface to the diagrams ######################################################################### - + def __showContextMenuGraphics(self): """ Private slot called before the graphics menu is shown. """ self.showMenu.emit("Graphics", self.graphicsMenu) - + def handleApplicationDiagram(self): """ Public method to handle the application diagram context menu action. @@ -5353,109 +5735,111 @@ self.ui, self.tr("Application Diagram"), self.tr("""Include module names?"""), - yesDefault=True) - + yesDefault=True, + ) + from Graphics.UMLDialog import UMLDialog, UMLDialogType + self.applicationDiagram = UMLDialog( - UMLDialogType.APPLICATION_DIAGRAM, self, self.parent(), - noModules=not res) + UMLDialogType.APPLICATION_DIAGRAM, self, self.parent(), noModules=not res + ) self.applicationDiagram.show() - + def __loadDiagram(self): """ Private slot to load a diagram from file. """ from Graphics.UMLDialog import UMLDialog, UMLDialogType + self.loadedDiagram = None - loadedDiagram = UMLDialog(UMLDialogType.NO_DIAGRAM, - self, parent=self.parent()) + loadedDiagram = UMLDialog(UMLDialogType.NO_DIAGRAM, self, parent=self.parent()) if loadedDiagram.load(): self.loadedDiagram = loadedDiagram self.loadedDiagram.show(fromFile=True) - + ######################################################################### ## Below is the interface to the VCS monitor thread ######################################################################### - + def setStatusMonitorInterval(self, interval): """ Public method to se the interval of the VCS status monitor thread. - + @param interval status monitor interval in seconds (integer) """ if self.vcs is not None: self.vcs.setStatusMonitorInterval(interval, self) - + def getStatusMonitorInterval(self): """ Public method to get the monitor interval. - + @return interval in seconds (integer) """ if self.vcs is not None: return self.vcs.getStatusMonitorInterval() else: return 0 - + def setStatusMonitorAutoUpdate(self, auto): """ Public method to enable the auto update function. - + @param auto status of the auto update function (boolean) """ if self.vcs is not None: self.vcs.setStatusMonitorAutoUpdate(auto) - + def getStatusMonitorAutoUpdate(self): """ Public method to retrieve the status of the auto update function. - + @return status of the auto update function (boolean) """ if self.vcs is not None: return self.vcs.getStatusMonitorAutoUpdate() else: return False - + def checkVCSStatus(self): """ Public method to wake up the VCS status monitor thread. """ if self.vcs is not None: self.vcs.checkVCSStatus() - + def clearStatusMonitorCachedState(self, name): """ Public method to clear the cached VCS state of a file/directory. - + @param name name of the entry to be cleared (string) """ if self.vcs is not None: self.vcs.clearStatusMonitorCachedState(name) - + def startStatusMonitor(self): """ Public method to start the VCS status monitor thread. """ if self.vcs is not None: self.vcs.startStatusMonitor(self) - + def stopStatusMonitor(self): """ Public method to stop the VCS status monitor thread. """ if self.vcs is not None: self.vcs.stopStatusMonitor() - + ######################################################################### ## Below are the plugin development related methods ######################################################################### - + def __pluginVersionToTuple(self, versionStr): """ Private method to convert a plug-in version string into a version tuple. - + @param versionStr version string to be converted @type str @return version info as a tuple @@ -5471,12 +5855,12 @@ vParts.append(int(part)) except ValueError: vParts.append(part) - + if additional: vParts.append(additional) - + return tuple(vParts) - + def __pluginCreatePkgList(self): """ Private slot to create a PKGLIST file needed for archive file creation. @@ -5488,29 +5872,41 @@ self.tr("Create Package List"), self.tr( "<p>The file <b>PKGLIST</b> already" - " exists.</p><p>Overwrite it?</p>"), - icon=EricMessageBox.Warning) + " exists.</p><p>Overwrite it?</p>" + ), + icon=EricMessageBox.Warning, + ) if not res: return # don't overwrite - + # build the list of entries lst_ = [] - for key in ["SOURCES", "FORMS", "RESOURCES", "TRANSLATIONS", - "INTERFACES", "PROTOCOLS", "OTHERS"]: + for key in [ + "SOURCES", + "FORMS", + "RESOURCES", + "TRANSLATIONS", + "INTERFACES", + "PROTOCOLS", + "OTHERS", + ]: lst_.extend(self.pdata[key]) lst = [] for entry in lst_: if os.path.isdir(self.getAbsolutePath(entry)): lst.extend( - [self.getRelativePath(p) for p in - Utilities.direntries(self.getAbsolutePath(entry), True)]) + [ + self.getRelativePath(p) + for p in Utilities.direntries(self.getAbsolutePath(entry), True) + ] + ) continue else: lst.append(entry) lst.sort() if "PKGLIST" in lst: lst.remove("PKGLIST") - + # build the header to indicate a freshly generated list header = [ ";", @@ -5518,16 +5914,15 @@ ";", " ", ] - + # write the file try: newline = None if self.pdata["EOL"] == 0 else self.getEolString() - with open(pkglist, "w", encoding="utf-8", - newline=newline) as pkglistFile: + with open(pkglist, "w", encoding="utf-8", newline=newline) as pkglistFile: pkglistFile.write("\n".join(header) + "\n") pkglistFile.write( - "\n".join([Utilities.fromNativeSeparators(f) - for f in lst])) + "\n".join([Utilities.fromNativeSeparators(f) for f in lst]) + ) pkglistFile.write("\n") # ensure the file ends with an empty line except OSError as why: @@ -5536,17 +5931,19 @@ self.tr("Create Package List"), self.tr( """<p>The file <b>PKGLIST</b> could not be created.</p>""" - """<p>Reason: {0}</p>""").format(str(why))) + """<p>Reason: {0}</p>""" + ).format(str(why)), + ) return - + if "PKGLIST" not in self.pdata["OTHERS"]: self.appendFile("PKGLIST") - + @pyqtSlot() def __pluginCreateArchives(self, snapshot=False): """ Private slot to create eric plugin archives. - + @param snapshot flag indicating snapshot archives (boolean) """ if not self.pdata["MAINSCRIPT"]: @@ -5555,36 +5952,50 @@ self.tr("Create Plugin Archive"), self.tr( """The project does not have a main script defined. """ - """Aborting...""")) + """Aborting...""" + ), + ) return - + selectedLists = [] - pkglists = [os.path.basename(f) for f in - glob.glob(os.path.join(self.ppath, "PKGLIST*"))] + pkglists = [ + os.path.basename(f) for f in glob.glob(os.path.join(self.ppath, "PKGLIST*")) + ] if len(pkglists) == 1: selectedLists = [os.path.join(self.ppath, pkglists[0])] elif len(pkglists) > 1: dlg = EricListSelectionDialog( - sorted(pkglists), title=self.tr("Create Plugin Archive"), + sorted(pkglists), + title=self.tr("Create Plugin Archive"), message=self.tr("Select package lists:"), - checkBoxSelection=True) + checkBoxSelection=True, + ) if dlg.exec() == QDialog.DialogCode.Accepted: - selectedLists = [os.path.join(self.ppath, s) - for s in dlg.getSelection()] + selectedLists = [ + os.path.join(self.ppath, s) for s in dlg.getSelection() + ] else: return - + if not selectedLists: EricMessageBox.critical( self.ui, self.tr("Create Plugin Archive"), - self.tr("""<p>No package list files (PKGLIST*) available or""" - """ selected. Aborting...</p>""")) + self.tr( + """<p>No package list files (PKGLIST*) available or""" + """ selected. Aborting...</p>""" + ), + ) return - + progress = EricProgressDialog( - self.tr("Creating plugin archives..."), self.tr("Abort"), - 0, len(selectedLists), self.tr("%v/%m Archives"), self.ui) + self.tr("Creating plugin archives..."), + self.tr("Abort"), + 0, + len(selectedLists), + self.tr("%v/%m Archives"), + self.ui, + ) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Create Plugin Archives")) errors = 0 @@ -5592,7 +6003,7 @@ progress.setValue(count) if progress.wasCanceled(): break - + try: with open(pkglist, "r", encoding="utf-8") as pkglistFile: names = pkglistFile.read() @@ -5602,11 +6013,12 @@ self.tr("Create Plugin Archive"), self.tr( """<p>The file <b>{0}</b> could not be read.</p>""" - """<p>Reason: {1}</p>""").format( - os.path.basename(pkglist), str(why))) + """<p>Reason: {1}</p>""" + ).format(os.path.basename(pkglist), str(why)), + ) errors += 1 continue - + lines = names.splitlines() archiveName = "" archiveVersion = "" @@ -5631,22 +6043,25 @@ """<p>The file <b>{0}</b> is not ready yet.""" """</p><p>Please rework it and delete the""" """'; initial_list' line of the header.""" - """</p>""").format(os.path.basename(pkglist))) + """</p>""" + ).format(os.path.basename(pkglist)), + ) errors += 1 listOK = False break elif line.strip(): names.append(line.strip()) - + if not listOK: continue - + names = sorted(names) archive = ( os.path.join(self.ppath, archiveName) - if archiveName else - os.path.join(self.ppath, - self.pdata["MAINSCRIPT"].replace(".py", ".zip")) + if archiveName + else os.path.join( + self.ppath, self.pdata["MAINSCRIPT"].replace(".py", ".zip") + ) ) try: archiveFile = zipfile.ZipFile(archive, "w") @@ -5657,32 +6072,30 @@ self.tr( """<p>The eric plugin archive file <b>{0}</b>""" """ could not be created.</p>""" - """<p>Reason: {1}</p>""").format(archive, str(why))) + """<p>Reason: {1}</p>""" + ).format(archive, str(why)), + ) errors += 1 continue - + for name in names: if name: try: - self.__createZipDirEntries( - os.path.split(name)[0], archiveFile) + self.__createZipDirEntries(os.path.split(name)[0], archiveFile) if snapshot and name == self.pdata["MAINSCRIPT"]: - snapshotSource, version = ( - self.__createSnapshotSource( - os.path.join(self.ppath, - self.pdata["MAINSCRIPT"])) + snapshotSource, version = self.__createSnapshotSource( + os.path.join(self.ppath, self.pdata["MAINSCRIPT"]) ) archiveFile.writestr(name, snapshotSource) else: - archiveFile.write(os.path.join(self.ppath, name), - name) + archiveFile.write(os.path.join(self.ppath, name), name) 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) < - self.__pluginVersionToTuple(archiveVersion) + self.__pluginVersionToTuple(version) + < self.__pluginVersionToTuple(archiveVersion) ): version = archiveVersion except OSError as why: @@ -5692,62 +6105,68 @@ self.tr( """<p>The file <b>{0}</b> could not be""" """ stored in the archive. Ignoring it.</p>""" - """<p>Reason: {1}</p>""") - .format(os.path.join(self.ppath, name), str(why))) + """<p>Reason: {1}</p>""" + ).format(os.path.join(self.ppath, name), str(why)), + ) archiveFile.writestr("VERSION", version.encode("utf-8")) archiveFile.close() - + if archive not in self.pdata["OTHERS"]: self.appendFile(archive) - + progress.setValue(len(selectedLists)) - + if errors: self.ui.showNotification( UI.PixmapCache.getPixmap("pluginArchive48"), self.tr("Create Plugin Archive"), - self.tr("<p>The eric plugin archive files were " - "created with some errors.</p>"), + self.tr( + "<p>The eric plugin archive files were " + "created with some errors.</p>" + ), kind=NotificationTypes.CRITICAL, - timeout=0) + timeout=0, + ) else: self.ui.showNotification( UI.PixmapCache.getPixmap("pluginArchive48"), self.tr("Create Plugin Archive"), - self.tr("<p>The eric plugin archive files were " - "created successfully.</p>")) - + self.tr( + "<p>The eric plugin archive files were " "created successfully.</p>" + ), + ) + def __pluginCreateSnapshotArchives(self): """ Private slot to create eric plugin archive snapshot releases. """ self.__pluginCreateArchives(True) - + def __createZipDirEntries(self, path, zipFile): """ Private method to create dir entries in the zip file. - + @param path name of the directory entry to create (string) @param zipFile open ZipFile object (zipfile.ZipFile) """ if path in ("", "/", "\\"): return - + if not path.endswith("/") and not path.endswith("\\"): path = "{0}/".format(path) - + if path not in zipFile.namelist(): self.__createZipDirEntries(os.path.split(path[:-1])[0], zipFile) zipFile.writestr(path, b"") - + def __createSnapshotSource(self, filename): """ Private method to create a snapshot plugin version. - + The version entry in the plugin module is modified to signify a snapshot version. This method appends the string "-snapshot-" and date indicator to the version string. - + @param filename name of the plugin file to modify (string) @return modified source (bytes), snapshot version string (string) """ @@ -5758,36 +6177,37 @@ EricMessageBox.critical( self.ui, self.tr("Create Plugin Archive"), - self.tr("""<p>The plugin file <b>{0}</b> could """ - """not be read.</p>""" - """<p>Reason: {1}</p>""") - .format(filename, str(why))) + self.tr( + """<p>The plugin file <b>{0}</b> could """ + """not be read.</p>""" + """<p>Reason: {1}</p>""" + ).format(filename, str(why)), + ) return b"", "" - + lineno = 0 while lineno < len(sourcelines): if sourcelines[lineno].startswith("version = "): # found the line to modify datestr = time.strftime("%Y%m%d") - lineend = sourcelines[lineno].replace( - sourcelines[lineno].rstrip(), "") + lineend = sourcelines[lineno].replace(sourcelines[lineno].rstrip(), "") sversion = "{0}-snapshot-{1}".format( - sourcelines[lineno].replace("version = ", "") - .strip()[1:-1], - datestr) + sourcelines[lineno].replace("version = ", "").strip()[1:-1], datestr + ) sourcelines[lineno] = '{0} + "-snapshot-{1}"{2}'.format( - sourcelines[lineno].rstrip(), datestr, lineend) + sourcelines[lineno].rstrip(), datestr, lineend + ) break - + lineno += 1 - + source = Utilities.encode("".join(sourcelines), encoding)[0] return source, sversion - + def __pluginExtractVersion(self, filename): """ Private method to extract the version number entry. - + @param filename name of the plugin file (string) @return version string (string) """ @@ -5801,35 +6221,38 @@ self.tr("Create Plugin Archive"), self.tr( """<p>The plugin file <b>{0}</b> could """ - """not be read.</p> <p>Reason: {1}</p>""") - .format(filename, str(why))) + """not be read.</p> <p>Reason: {1}</p>""" + ).format(filename, str(why)), + ) return "" - + for sourceline in sourcelines: if sourceline.startswith("version = "): version = ( - sourceline.replace("version = ", "").strip() - .replace('"', "").replace("'", "") + sourceline.replace("version = ", "") + .strip() + .replace('"', "") + .replace("'", "") ) break - + return version - + ######################################################################### ## Below are methods implementing the 'make' support ######################################################################### - + def __showContextMenuMake(self): """ Private slot called before the make menu is shown. """ self.showMenu.emit("Make", self.makeMenu) - + def hasDefaultMakeParameters(self): """ Public method to test, if the project contains the default make parameters. - + @return flag indicating default parameter set @rtype bool """ @@ -5841,16 +6264,16 @@ "MakeParameters": "", "MakeTestOnly": True, } - + def isMakeEnabled(self): """ Public method to test, if make is enabled for the project. - + @return flag indicating enabled make support @rtype bool """ return self.pdata["MAKEPARAMS"]["MakeEnabled"] - + @pyqtSlot() def executeMake(self): """ @@ -5858,14 +6281,14 @@ (execute or question). """ self.__executeMake( - questionOnly=self.pdata["MAKEPARAMS"]["MakeTestOnly"], - interactive=False) - + questionOnly=self.pdata["MAKEPARAMS"]["MakeTestOnly"], interactive=False + ) + @pyqtSlot() def __executeMake(self, questionOnly=False, interactive=True): """ Private method to execute a project specific make run. - + @param questionOnly flag indicating to ask make for changes only @type bool @param interactive flag indicating an interactive invocation (i.e. @@ -5873,78 +6296,82 @@ @type bool """ if ( - not self.pdata["MAKEPARAMS"]["MakeEnabled"] or - self.__makeProcess is not None + not self.pdata["MAKEPARAMS"]["MakeEnabled"] + or self.__makeProcess is not None ): return - + prog = ( self.pdata["MAKEPARAMS"]["MakeExecutable"] - if self.pdata["MAKEPARAMS"]["MakeExecutable"] else - Project.DefaultMake + if self.pdata["MAKEPARAMS"]["MakeExecutable"] + else Project.DefaultMake ) - + args = [] if self.pdata["MAKEPARAMS"]["MakeParameters"]: - args.extend(Utilities.parseOptionString( - self.pdata["MAKEPARAMS"]["MakeParameters"])) - + args.extend( + Utilities.parseOptionString(self.pdata["MAKEPARAMS"]["MakeParameters"]) + ) + if self.pdata["MAKEPARAMS"]["MakeFile"]: - args.append("--makefile={0}".format( - 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"]) - + self.__makeProcess = QProcess(self) - self.__makeProcess.readyReadStandardOutput.connect( - self.__makeReadStdOut) - self.__makeProcess.readyReadStandardError.connect( - self.__makeReadStdErr) + self.__makeProcess.readyReadStandardOutput.connect(self.__makeReadStdOut) + self.__makeProcess.readyReadStandardError.connect(self.__makeReadStdErr) self.__makeProcess.finished.connect( lambda exitCode, exitStatus: self.__makeFinished( - exitCode, exitStatus, questionOnly, interactive)) + exitCode, exitStatus, questionOnly, interactive + ) + ) self.__makeProcess.setWorkingDirectory(self.getProjectPath()) self.__makeProcess.start(prog, args) - + if not self.__makeProcess.waitForStarted(): EricMessageBox.critical( self.ui, self.tr("Execute Make"), - self.tr("""The make process did not start.""")) - + self.tr("""The make process did not start."""), + ) + self.__cleanupMake() - + @pyqtSlot() def __makeReadStdOut(self): """ Private slot to process process output received via stdout. """ if self.__makeProcess is not None: - output = str(self.__makeProcess.readAllStandardOutput(), - Preferences.getSystem("IOEncoding"), - 'replace') + output = str( + self.__makeProcess.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) self.appendStdout.emit(output) - + @pyqtSlot() def __makeReadStdErr(self): """ Private slot to process process output received via stderr. """ if self.__makeProcess is not None: - error = str(self.__makeProcess.readAllStandardError(), - Preferences.getSystem("IOEncoding"), - 'replace') + error = str( + self.__makeProcess.readAllStandardError(), + Preferences.getSystem("IOEncoding"), + "replace", + ) self.appendStderr.emit(error) - - def __makeFinished(self, exitCode, exitStatus, questionOnly, - interactive=True): + + def __makeFinished(self, exitCode, exitStatus, questionOnly, interactive=True): """ Private slot handling the make process finished signal. - + @param exitCode exit code of the make process @type int @param exitStatus exit status of the make process @@ -5959,12 +6386,13 @@ EricMessageBox.critical( self.ui, self.tr("Execute Make"), - self.tr("""The make process crashed.""")) + self.tr("""The make process crashed."""), + ) else: if questionOnly and exitCode == 1: # a rebuild is needed title = self.tr("Test for Changes") - + if self.pdata["MAKEPARAMS"]["MakeTarget"]: message = self.tr( """<p>There are changes that require the configured""" @@ -5973,22 +6401,25 @@ else: message = self.tr( """<p>There are changes that require the default""" - """ make target to be rebuilt.</p>""") - + """ make target to be rebuilt.</p>""" + ) + self.ui.showNotification( UI.PixmapCache.getPixmap("makefile48"), title, message, kind=NotificationTypes.WARNING, - timeout=0) + timeout=0, + ) elif exitCode > 1: EricMessageBox.critical( self.ui, self.tr("Execute Make"), - self.tr("""The makefile contains errors.""")) - + self.tr("""The makefile contains errors."""), + ) + self.__cleanupMake() - + def __cleanupMake(self): """ Private method to clean up make related stuff. @@ -5998,16 +6429,16 @@ self.__makeProcess.finished.disconnect() self.__makeProcess.deleteLater() self.__makeProcess = None - + ######################################################################### ## Below are methods implementing some 'IDL' support functions ######################################################################### - + def hasDefaultIdlCompilerParameters(self): """ Public method to test, if the project contains the default IDL compiler parameters. - + @return flag indicating default parameter set @rtype bool """ @@ -6016,16 +6447,16 @@ "DefinedNames": [], "UndefinedNames": [], } - + ######################################################################### ## Below are methods implementing some 'UIC' support functions ######################################################################### - + def hasDefaultUicCompilerParameters(self): """ Public method to test, if the project contains the default uic compiler parameters. - + @return flag indicating default parameter set @rtype bool """ @@ -6034,11 +6465,11 @@ "RcSuffix": "", "PackagesRoot": "", } - + def getUicParameter(self, name): """ Public method to get a named uic related parameter. - + @param name name of the parameter @type str @return value of the given parameter @@ -6048,108 +6479,108 @@ return self.pdata["UICPARAMS"][name] else: return None - + ######################################################################### ## Below are methods implementing some 'RCC' support functions ######################################################################### - + def hasDefaultRccCompilerParameters(self): """ Public method to test, if the project contains the default rcc compiler parameters. - + @return flag indicating default parameter set @rtype bool """ - return ( - self.pdata["RCCPARAMS"] == self.getDefaultRccCompilerParameters() - ) - + return self.pdata["RCCPARAMS"] == self.getDefaultRccCompilerParameters() + def getDefaultRccCompilerParameters(self): """ Public method to get the default rcc compiler parameters. - + @return dictionary containing the default rcc compiler parameters @rtype dict """ return { - "CompressionThreshold": 70, # default value - "CompressLevel": 0, # use zlib default + "CompressionThreshold": 70, # default value + "CompressLevel": 0, # use zlib default "CompressionDisable": False, "PathPrefix": "", } - + ######################################################################### ## Below are methods implementing some 'docstring' support functions ######################################################################### - + def hasDefaultDocstringParameter(self): """ Public method to test, if the project contains the default docstring parameter. - + @return flag indicating default parameter @rtype bool """ return self.pdata["DOCSTRING"] == "" - + def getDocstringType(self): """ Public method to get the configured docstring style. - + @return configured docstring style @rtype str """ return self.pdata["DOCSTRING"] - + ######################################################################### ## Below are methods implementing the 'SBOM' support ######################################################################### - + def __showContextMenuOthers(self): """ Private slot called before the 'Other Tools' menu is shown. """ self.showMenu.emit("OtherTools", self.othersMenu) - + @pyqtSlot() def __createSBOMFile(self): """ Private slot to create a SBOM file of the project dependencies. """ import CycloneDXInterface - + CycloneDXInterface.createCycloneDXFile("<project>") - + ######################################################################### ## Below are methods implementing the 'Code Formatting' support ######################################################################### - + def __showContextMenuFormat(self): """ Private slot called before the 'Code Formatting' menu is shown. """ self.showMenu.emit("Formatting", self.othersMenu) - + @pyqtSlot() def __aboutBlack(self): """ Private slot to show some information about the installed 'Black' tool. """ import black - + EricMessageBox.information( None, self.tr("About Black"), - self.tr("""<p><b>Black Version {0}</b></p>""" - """<p><i>Black</i> is the uncompromising Python code""" - """ formatter.</p>""").format(black.__version__) + self.tr( + """<p><b>Black Version {0}</b></p>""" + """<p><i>Black</i> is the uncompromising Python code""" + """ formatter.</p>""" + ).format(black.__version__), ) - + def __performFormatWithBlack(self, action): """ Private method to format the project sources using the 'Black' tool. - + Following actions are supported. <ul> <li>BlackFormattingAction.Format - the code reformatting is performed</li> @@ -6158,25 +6589,26 @@ <li>BlackFormattingAction.Diff - a unified diff of potential code formatting changes is generated</li> </ul> - + @param action formatting operation to be performed @type BlackFormattingAction """ from CodeFormatting.BlackConfigurationDialog import BlackConfigurationDialog from CodeFormatting.BlackFormattingDialog import BlackFormattingDialog - + if ericApp().getObject("ViewManager").checkAllDirty(): dlg = BlackConfigurationDialog(withProject=True) if dlg.exec() == QDialog.DialogCode.Accepted: config = dlg.getConfiguration() - + formattingDialog = BlackFormattingDialog( config, self.getProjectFiles("SOURCES", normalized=True), project=self, - action=action + action=action, ) formattingDialog.exec() + # # eflag: noqa = M601