--- a/eric6/Project/Project.py Mon Jan 25 14:10:07 2021 +0100 +++ b/eric6/Project/Project.py Mon Jan 25 20:07:51 2021 +0100 @@ -41,6 +41,10 @@ import Preferences import Utilities +from .ProjectFile import ProjectFile +from .UserProjectFile import UserProjectFile +from .DebuggerPropertiesFile import DebuggerPropertiesFile + class Project(QObject): """ @@ -184,6 +188,10 @@ self.__initData() + self.__projectFile = ProjectFile(self) + self.__userProjectFile = UserProjectFile(self) + self.__debuggerPropertiesFile = DebuggerPropertiesFile(self) + self.recent = [] self.__loadRecent() @@ -580,6 +588,7 @@ "README": "OTHERS", "README.*": "OTHERS", "*.e4p": "OTHERS", + "*.epj": "OTHERS", "GNUmakefile": "OTHERS", "makefile": "OTHERS", "Makefile": "OTHERS", @@ -785,34 +794,50 @@ def __readProject(self, fn): """ - Private method to read in a project (.e4p) file. + 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 """ - f = QFile(fn) - if f.open(QIODevice.ReadOnly): - from E5XML.ProjectReader import ProjectReader - reader = ProjectReader(f, self) - reader.readXML() - res = not reader.hasError() - f.close() + if os.path.splitext(fn)[1] == ".epj": + # new JSON based format + with E5OverrideCursor(): + res = self.__projectFile.readFile(fn) else: - E5MessageBox.critical( - self.ui, - self.tr("Read project file"), - self.tr( - "<p>The project file <b>{0}</b> could not be read.</p>") - .format(fn)) - return False - - 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() + # old XML based format + f = QFile(fn) + if f.open(QIODevice.ReadOnly): + from E5XML.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.Sha1).toHex(), + encoding="utf-8") + self.pdata["HASH"] = hashStr + self.setDirty(True) + else: + E5MessageBox.critical( + self.ui, + self.tr("Read Project File"), + self.tr( + "<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] @@ -857,15 +882,6 @@ if dn not in self.otherssubdirs: self.otherssubdirs.append(dn) - # 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.Sha1).toHex(), - encoding="utf-8") - self.pdata["HASH"] = hashStr - self.setDirty(True) - return res def __writeProject(self, fn=None): @@ -894,20 +910,26 @@ if fn is None: fn = self.pfile - f = QFile(fn) - if f.open(QIODevice.WriteOnly): - from E5XML.ProjectWriter import ProjectWriter - ProjectWriter(f, os.path.splitext( - os.path.basename(fn))[0]).writeXML() - res = True + if os.path.splitext(fn)[1] == ".epj": + # new JSON based format + with E5OverrideCursor(): + res = self.__projectFile.writeFile(fn) else: - E5MessageBox.critical( - self.ui, - self.tr("Save project file"), - self.tr( - "<p>The project file <b>{0}</b> could not be" - " written.</p>").format(fn)) - res = False + # old XML based format + f = QFile(fn) + if f.open(QIODevice.WriteOnly): + from E5XML.ProjectWriter import ProjectWriter + ProjectWriter(f, os.path.splitext( + os.path.basename(fn))[0]).writeXML() + res = True + else: + E5MessageBox.critical( + self.ui, + self.tr("Save Project File"), + self.tr( + "<p>The project file <b>{0}</b> could not be" + " written.</p>").format(fn)) + res = False if res: self.pfile = os.path.abspath(fn) @@ -922,52 +944,49 @@ def __readUserProperties(self): """ - Private method to read in the user specific project file (.e4q). + Private method to read in the user specific project file (.eqj or + .e4q). """ if self.pfile is None: return fn, ext = os.path.splitext(os.path.basename(self.pfile)) - fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn)) + fn = os.path.join(self.getProjectManagementDir(), '{0}.eqj'.format(fn)) if os.path.exists(fn): - f = QFile(fn) - if f.open(QIODevice.ReadOnly): - from E5XML.UserProjectReader import UserProjectReader - reader = UserProjectReader(f, self) - reader.readXML() - f.close() - else: - E5MessageBox.critical( - self.ui, - 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)) + # 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(fn)) + if os.path.exists(fn): + f = QFile(fn) + if f.open(QIODevice.ReadOnly): + from E5XML.UserProjectReader import UserProjectReader + reader = UserProjectReader(f, self) + reader.readXML() + f.close() + else: + E5MessageBox.critical( + self.ui, + 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)) def __writeUserProperties(self): """ - Private method to write the project data to an XML file. + 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}.e4q'.format(fn)) - - f = QFile(fn) - if f.open(QIODevice.WriteOnly): - from E5XML.UserProjectWriter import UserProjectWriter - UserProjectWriter( - f, os.path.splitext(os.path.basename(fn))[0]).writeXML() - f.close() - else: - E5MessageBox.critical( - self.ui, - self.tr("Save user project properties"), - self.tr( - "<p>The user specific project properties file <b>{0}</b>" - " could not be written.</p>").format(fn)) - + fn = os.path.join(self.getProjectManagementDir(), '{0}.eqj'.format(fn)) + + with E5OverrideCursor(): + self.__userProjectFile.writeFile(fn) + def __showContextMenuSession(self): """ Private slot called before the Session menu is shown. @@ -996,6 +1015,7 @@ If this flag is true, no errors are reported. @param indicator indicator string (string) """ + # TODO: read project session if self.pfile is None: if not quiet: E5MessageBox.critical( @@ -1035,6 +1055,7 @@ If this flag is true, no errors are reported. @param indicator indicator string (string) """ + # TODO: write project session if self.pfile is None: if not quiet: E5MessageBox.critical( @@ -1069,24 +1090,22 @@ if self.pfile is None: E5MessageBox.critical( self.ui, - self.tr("Delete project session"), + self.tr("Delete Project Session"), self.tr("Please save the project first.")) return fname, ext = os.path.splitext(os.path.basename(self.pfile)) - for fn in [ - os.path.join( - self.getProjectManagementDir(), "{0}.e5s".format(fname)), - os.path.join( - self.getProjectManagementDir(), "{0}.e4s".format(fname))]: + for ext in (".esj", ".e5s", ".e4s"): + fn = os.path.join( + self.getProjectManagementDir(), "{0}{1}".format(fname, ext)), if os.path.exists(fn): try: os.remove(fn) except OSError: E5MessageBox.critical( self.ui, - self.tr("Delete project session"), + self.tr("Delete Project Session"), self.tr( "<p>The project session file <b>{0}</b> could" " not be deleted.</p>").format(fn)) @@ -1098,10 +1117,11 @@ if self.pfile is None: E5MessageBox.critical( self.ui, - self.tr("Read tasks"), + self.tr("Read Tasks"), self.tr("Please save the project first.")) return - + + # TODO: read project tasks base, ext = os.path.splitext(os.path.basename(self.pfile)) fn = os.path.join(self.getProjectManagementDir(), '{0}.e6t'.format(base)) @@ -1120,7 +1140,7 @@ else: E5MessageBox.critical( self.ui, - self.tr("Read tasks"), + self.tr("Read Tasks"), self.tr( "<p>The tasks file <b>{0}</b> could not be read.</p>") .format(fn)) @@ -1134,13 +1154,14 @@ fn, ext = os.path.splitext(os.path.basename(self.pfile)) + # TODO: write project tasks fn = os.path.join(self.getProjectManagementDir(), '{0}.e6t'.format(fn)) f = QFile(fn) ok = f.open(QIODevice.WriteOnly) if not ok: E5MessageBox.critical( self.ui, - self.tr("Save tasks"), + self.tr("Save Tasks"), self.tr( "<p>The tasks file <b>{0}</b> could not be written.</p>") .format(fn)) @@ -1171,43 +1192,54 @@ @pyqtSlot() def __readDebugProperties(self, quiet=False): """ - Private method to read in the project debugger properties file (.e4d). + 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. + If this flag is true, no errors are reported. """ if self.pfile is None: if not quiet: E5MessageBox.critical( self.ui, - self.tr("Read debugger properties"), + self.tr("Read Debugger Properties"), 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}.e4d'.format(fn)) - - f = QFile(fn) - if f.open(QIODevice.ReadOnly): - from E5XML.DebuggerPropertiesReader import DebuggerPropertiesReader - reader = DebuggerPropertiesReader(f, self) - reader.readXML(quiet=quiet) - f.close() - self.debugPropertiesLoaded = True - self.debugPropertiesChanged = False + fn = os.path.join(self.getProjectManagementDir(), '{0}.edj'.format(fn)) + if os.path.exists(fn): + # try the new JSON based format first + self.__debuggerPropertiesFile.readFile(fn) else: - if not quiet: - E5MessageBox.critical( - self.ui, - self.tr("Read debugger properties"), - self.tr( - "<p>The project debugger properties file <b>{0}</b>" - " could not be read.</p>").format(fn)) - + # try the old XML based format second + fn, ext = os.path.splitext(os.path.basename(self.pfile)) + fn = os.path.join(self.getProjectManagementDir(), + '{0}.e4d'.format(fn)) + + f = QFile(fn) + if f.open(QIODevice.ReadOnly): + from E5XML.DebuggerPropertiesReader import ( + DebuggerPropertiesReader + ) + reader = DebuggerPropertiesReader(f, self) + reader.readXML(quiet=quiet) + f.close() + self.debugPropertiesLoaded = True + self.debugPropertiesChanged = False + else: + if not quiet: + E5MessageBox.critical( + self.ui, + self.tr("Read Debugger Properties"), + self.tr( + "<p>The project debugger properties file" + " <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 (.e4d). + 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. @@ -1216,51 +1248,40 @@ if not quiet: E5MessageBox.critical( self.ui, - self.tr("Save debugger properties"), + self.tr("Save Debugger Properties"), 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}.e4d'.format(fn)) - - f = QFile(fn) - if f.open(QIODevice.WriteOnly): - from E5XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter - DebuggerPropertiesWriter( - f, os.path.splitext(os.path.basename(fn))[0]).writeXML() - f.close() - self.debugPropertiesChanged = False - else: - if not quiet: - E5MessageBox.critical( - self.ui, - self.tr("Save debugger properties"), - self.tr( - "<p>The project debugger properties file <b>{0}</b>" - " could not be written.</p>").format(fn)) - + fn = os.path.join(self.getProjectManagementDir(), '{0}.edj'.format(fn)) + + with E5OverrideCursor(): + self.__debuggerPropertiesFile.writeFile(fn) + def __deleteDebugProperties(self): """ - Private method to delete the project debugger properties file (.e4d). + Private method to delete the project debugger properties file + (.edj or .e4d). """ if self.pfile is None: E5MessageBox.critical( self.ui, - self.tr("Delete debugger properties"), + self.tr("Delete Debugger Properties"), self.tr("Please save the project first.")) return fname, ext = os.path.splitext(os.path.basename(self.pfile)) - for fn in [os.path.join(self.getProjectManagementDir(), - "{0}.e4d".format(fname))]: + for ext in (".edj", ".e4d"): + fn = os.path.join(self.getProjectManagementDir(), + "{0}{1}".format(fname, ext)) if os.path.exists(fn): try: os.remove(fn) except OSError: E5MessageBox.critical( self.ui, - self.tr("Delete debugger properties"), + self.tr("Delete Debugger Properties"), self.tr( "<p>The project debugger properties file" " <b>{0}</b> could not be deleted.</p>") @@ -2927,7 +2948,7 @@ self.tr("Open project"), Preferences.getMultiProject("Workspace") or Utilities.getHomeDir(), - self.tr("Project Files (*.e4p)")) + self.tr("Project Files (*.epj);;XML Project Files (*.e4p)")) QApplication.processEvents() @@ -3097,6 +3118,9 @@ """ if self.isDirty(): if len(self.pfile) > 0: + if self.pfile.endswith(".e4p"): + self.pfile = self.pfile.replace(".e4p", ".epj") + self.__syncRecent() ok = self.__writeProject() else: ok = self.saveProjectAs() @@ -3112,7 +3136,7 @@ @return flag indicating success (boolean) """ - defaultFilter = self.tr("Project Files (*.e4p)") + defaultFilter = self.tr("Project Files (*.epj)") if self.ppath: defaultPath = self.ppath else: @@ -3124,7 +3148,8 @@ self.parent(), self.tr("Save project as"), defaultPath, - self.tr("Project Files (*.e4p)"), + self.tr("Project Files (*.epj);;" + "XML Project Files (*.e4p)"), defaultFilter, E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))