From af089e8a6a94a40c3cee4e55f8e3fc404248c623 Mon Sep 17 00:00:00 2001 From: Olivier Keshavjee Date: Sat, 5 Mar 2016 00:35:14 +0100 Subject: [PATCH] Plain text file format in progress --- manuskript/load_save/version_1.py | 172 +++++++++++++++++++++++++----- manuskript/mainWindow.py | 2 +- manuskript/settings.py | 22 +++- 3 files changed, 162 insertions(+), 34 deletions(-) diff --git a/manuskript/load_save/version_1.py b/manuskript/load_save/version_1.py index 2760ea8..ca6c986 100644 --- a/manuskript/load_save/version_1.py +++ b/manuskript/load_save/version_1.py @@ -8,8 +8,11 @@ import os import zipfile +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor + from manuskript import settings -from manuskript.functions import mainWindow +from manuskript.functions import mainWindow, iconColor try: import zlib # Used with zipfile for compression @@ -19,6 +22,8 @@ except: compression = zipfile.ZIP_STORED +cache = {} + def saveProject(zip=None): """ Saves the project. If zip is False, the project is saved as a multitude of plain-text files for the most parts @@ -30,46 +35,153 @@ def saveProject(zip=None): @return: Nothing """ if zip is None: - zip = False + zip = True # Fixme files = [] mw = mainWindow() - # files.append((saveStandardItemModelXML(mw.mdlFlatData), - # "flatModel.xml")) - # # files.append((saveStandardItemModelXML(self.mdlCharacter), - # # "perso.xml")) - # files.append((saveStandardItemModelXML(mw.mdlWorld), - # "world.xml")) - # files.append((saveStandardItemModelXML(mw.mdlLabels), - # "labels.xml")) - # files.append((saveStandardItemModelXML(mw.mdlStatus), - # "status.xml")) - # files.append((saveStandardItemModelXML(mw.mdlPlots), - # "plots.xml")) - # files.append((mw.mdlOutline.saveToXML(), - # "outline.xml")) - # files.append((settings.save(), - # "settings.pickle")) + # General infos (book and author) + # Saved in plain text, in infos.txt - files.append(("blabla", "test/machin.txt")) - files.append(("youpi", "encore/truc.txt")) + path = "infos.txt" + content = "" + for name, col in [ + ("Title", 0), + ("Subtitle", 1), + ("Serie", 2), + ("Volume", 3), + ("Genre", 4), + ("License", 5), + ("Author", 6), + ("Email", 7), + ]: + val = mw.mdlFlatData.item(0, col).text().strip() + if val: + content += "{name}:{spaces}{value}\n".format( + name=name, + spaces=" " * (15 - len(name)), + value=val + ) + files.append((path, content)) + + # Summary + # In plain text, in summary.txt + + path = "summary.txt" + content = "" + for name, col in [ + ("Situation", 0), + ("Sentence", 1), + ("Paragraph", 2), + ("Page", 3), + ("Full", 4), + ]: + val = mw.mdlFlatData.item(1, col).text().strip() + val = "\n".join([" " * 13 + l for l in val.split("\n")])[13:] + if val: + content += "{name}:{spaces}{value}\n".format( + name=name, + spaces=" " * (12 - len(name)), + value=val + ) + files.append((path, content)) + + # Label & Status + # In plain text + + for mdl, path in [ + (mw.mdlStatus, "status.txt"), + (mw.mdlLabels, "labels.txt") + ]: + + content = "" + + # We skip the first row, which is empty and transparent + for i in range(1, mdl.rowCount()): + color = "" + if mdl.data(mdl.index(i, 0), Qt.DecorationRole) is not None: + color = iconColor(mdl.data(mdl.index(i, 0), Qt.DecorationRole)).name(QColor.HexRgb) + color = color if color != "#ff000000" else "#00000000" + + text = mdl.data(mdl.index(i, 0)) + + if text: + content += "{name}{color}\n".format( + name=text, + color= "" if color == "" else ":" + " " * (20 - len(text)) + color + ) + + files.append((path, content)) + + # Characters (self.mdlCharacter) + # In a character folder + + # TODO + + # Texts + # In an outline folder + + # TODO + + # World (mw.mdlWorld) + # Either in an XML file, or in lots of plain texts? + # More probably text, since there might be writing done in third-party. + + # TODO + + # Plots (mw.mdlPlots) + # Either in XML or lots of plain texts? + # More probably XML since there is not really a lot if writing to do (third-party) + + # TODO + + # Settings + # Saved in readable text (json) for easier versionning. But they mustn't be shared, it seems. + # Maybe include them only if zipped? + # Well, for now, we keep them here... + files.append(("settings.txt", settings.save(protocol=0))) project = mw.currentProject - project = os.path.join( - os.path.dirname(project), - "_" + os.path.basename(project) - ) + # Save to zip + if zip: + project = os.path.join( + os.path.dirname(project), + "_" + os.path.basename(project) + ) - zf = zipfile.ZipFile(project, mode="w") + zf = zipfile.ZipFile(project, mode="w") - for content, filename in files: - zf.writestr(filename, content, compress_type=compression) + for filename, content in files: + zf.writestr(filename, content, compress_type=compression) - zf.close() + zf.close() + + # Save to plain text + else: + dir = os.path.dirname(project) + folder = os.path.splitext(os.path.basename(project))[0] + print("Saving to folder", folder) + + for path, content in files: + filename = os.path.join(dir, folder, path) + os.makedirs(os.path.dirname(filename), exist_ok=True) + print("* Saving file", filename) + + # TODO: the first time it saves, it will overwrite everything, since it's not yet in cache. + # Or we have to cache while loading. + + if not path in cache or cache[path] != content: + print(" Not in cache or changed: we write") + mode = "w"+ ("b" if type(content) == bytes else "") + with open(filename, mode) as f: + f.write(content) + cache[path] = content + + else: + print(" In cache, and identical. Do nothing.") def loadProject(project): @@ -78,4 +190,8 @@ def loadProject(project): @param project: the filename of the project to open. @return: an array of errors, empty if None. """ + + # Don't forget to cache everything that is loaded + # In order to save only what has changed. + pass \ No newline at end of file diff --git a/manuskript/mainWindow.py b/manuskript/mainWindow.py index b7fea96..fb1ca49 100644 --- a/manuskript/mainWindow.py +++ b/manuskript/mainWindow.py @@ -460,7 +460,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.currentProject = projectName QSettings().setValue("lastProject", projectName) - saveProject(version=0) + saveProject() # version=0 # Giving some feedback print(self.tr("Project {} saved.").format(self.currentProject)) diff --git a/manuskript/settings.py b/manuskript/settings.py index 4f342f3..d03f6de 100644 --- a/manuskript/settings.py +++ b/manuskript/settings.py @@ -1,12 +1,16 @@ # -*- coding: utf-8 -*- import collections +import json import pickle from PyQt5.QtWidgets import qApp from manuskript.enums import Outline +# TODO: move some/all of those settings to application settings and not project settings +# in order to allow a shared project between several writers + viewSettings = { "Tree": { "Icon": "Nothing", @@ -28,7 +32,8 @@ viewSettings = { "Background": "Nothing", }, } - + +# Application spellcheck = False dict = None corkSizeFactor = 100 @@ -81,7 +86,7 @@ frequencyAnalyzer = { "phraseMax": 5 } -def save(filename=None): +def save(filename=None, protocol=None): global spellcheck, dict, corkSliderFactor, viewSettings, corkSizeFactor, folderView, lastTab, openIndexes, \ autoSave, autoSaveDelay, saveOnQuit, autoSaveNoChanges, autoSaveNoChangesDelay, outlineViewColumns, \ @@ -107,7 +112,7 @@ def save(filename=None): "textEditor":textEditor, "revisions":revisions, "frequencyAnalyzer": frequencyAnalyzer - } + } #pp=pprint.PrettyPrinter(indent=4, compact=False) #print("Saving:") @@ -117,8 +122,15 @@ def save(filename=None): f = open(filename, "wb") pickle.dump(allSettings, f) else: - return pickle.dumps(allSettings) - + if protocol == 0: + # This looks stupid + # But a simple json.dumps with sort_keys will throw a TypeError + # because of unorderable types. + return json.dumps(json.loads(json.dumps(allSettings)), indent=4, sort_keys=True) + else: + return pickle.dumps(allSettings) + + def load(string, fromString=False): """Load settings from 'string'. 'string' is the filename of the pickle dump. If fromString=True, string is the data of the pickle dumps."""