eric6/Project/Project.py

branch
jsonfiles
changeset 8006
c4110b8b5931
parent 8000
47b15df088e4
child 8009
29818ac4853c
--- 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))
         

eric ide

mercurial