#!/usr/bin/env python # --!-- coding: utf8 --!-- # Version 0 of file saving format. # Was used at the begining and up util version XXX when # it was superseded by Version 1, which is more open and flexible import zipfile from PyQt5.QtCore import QModelIndex, Qt from PyQt5.QtGui import QColor, QStandardItem from PyQt5.QtWidgets import qApp from lxml import etree as ET from manuskript import settings from manuskript.functions import iconColor, iconFromColorString, mainWindow from manuskript.models.characterModel import Character, CharacterInfo try: import zlib # Used with zipfile for compression compression = zipfile.ZIP_DEFLATED except: compression = zipfile.ZIP_STORED ########################################################################################### # SAVE ########################################################################################### def saveProject(): """ Saves the whole project. Call this function to save the project in Version 0 format. """ files = [] mw = mainWindow() files.append((saveStandardItemModelXML(mw.mdlFlatData), "flatModel.xml")) print("ERROR: file format 0 does not save characters !") # files.append((saveStandardItemModelXML(mw.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")) saveFilesToZip(files, mw.currentProject) def saveFilesToZip(files, zipname): """Saves given files to zipname. files is actually a list of (content, filename).""" zf = zipfile.ZipFile(zipname, mode="w") for content, filename in files: zf.writestr(filename, content, compress_type=compression) zf.close() def saveStandardItemModelXML(mdl, xml=None): """Saves the given QStandardItemModel to XML. If xml (filename) is given, saves to xml. Otherwise returns as string.""" root = ET.Element("model") root.attrib["version"] = qApp.applicationVersion() # Header header = ET.SubElement(root, "header") vHeader = ET.SubElement(header, "vertical") for x in range(mdl.rowCount()): vH = ET.SubElement(vHeader, "label") vH.attrib["row"] = str(x) vH.attrib["text"] = str(mdl.headerData(x, Qt.Vertical)) hHeader = ET.SubElement(header, "horizontal") for y in range(mdl.columnCount()): hH = ET.SubElement(hHeader, "label") hH.attrib["row"] = str(y) hH.attrib["text"] = str(mdl.headerData(y, Qt.Horizontal)) # Data data = ET.SubElement(root, "data") saveItem(data, mdl) # print(qApp.tr("Saving to {}.").format(xml)) if xml: ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True) else: return ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True) def saveItem(root, mdl, parent=QModelIndex()): for x in range(mdl.rowCount(parent)): row = ET.SubElement(root, "row") row.attrib["row"] = str(x) for y in range(mdl.columnCount(parent)): col = ET.SubElement(row, "col") col.attrib["col"] = str(y) if mdl.data(mdl.index(x, y, parent), Qt.DecorationRole) != None: color = iconColor(mdl.data(mdl.index(x, y, parent), Qt.DecorationRole)).name(QColor.HexArgb) col.attrib["color"] = color if color != "#ff000000" else "#00000000" if mdl.data(mdl.index(x, y, parent)) != "": col.text = mdl.data(mdl.index(x, y, parent)) if mdl.hasChildren(mdl.index(x, y, parent)): saveItem(col, mdl, mdl.index(x, y, parent)) ########################################################################################### # LOAD ########################################################################################### def loadProject(project): files = loadFilesFromZip(project) mw = mainWindow() errors = [] if "flatModel.xml" in files: loadStandardItemModelXML(mw.mdlFlatData, files["flatModel.xml"], fromString=True) else: errors.append("flatModel.xml") if "perso.xml" in files: loadStandardItemModelXMLForCharacters(mw.mdlCharacter, files["perso.xml"]) else: errors.append("perso.xml") if "world.xml" in files: loadStandardItemModelXML(mw.mdlWorld, files["world.xml"], fromString=True) else: errors.append("world.xml") if "labels.xml" in files: loadStandardItemModelXML(mw.mdlLabels, files["labels.xml"], fromString=True) else: errors.append("labels.xml") if "status.xml" in files: loadStandardItemModelXML(mw.mdlStatus, files["status.xml"], fromString=True) else: errors.append("status.xml") if "plots.xml" in files: loadStandardItemModelXML(mw.mdlPlots, files["plots.xml"], fromString=True) else: errors.append("plots.xml") if "outline.xml" in files: mw.mdlOutline.loadFromXML(files["outline.xml"], fromString=True) else: errors.append("outline.xml") if "settings.pickle" in files: settings.load(files["settings.pickle"], fromString=True) else: errors.append("settings.pickle") return errors def loadFilesFromZip(zipname): """Returns the content of zipfile as a dict of filename:content.""" zf = zipfile.ZipFile(zipname) files = {} for f in zf.namelist(): files[f] = zf.read(f) return files def loadStandardItemModelXML(mdl, xml, fromString=False): """Load data to a QStandardItemModel mdl from xml. By default xml is a filename. If fromString=True, xml is a string containg the data.""" # print(qApp.tr("Loading {}... ").format(xml), end="") if not fromString: try: tree = ET.parse(xml) except: print("Failed.") return else: root = ET.fromstring(xml) # root = tree.getroot() # Header hLabels = [] vLabels = [] for l in root.find("header").find("horizontal").findall("label"): hLabels.append(l.attrib["text"]) for l in root.find("header").find("vertical").findall("label"): vLabels.append(l.attrib["text"]) # print(root.find("header").find("vertical").text) # mdl.setVerticalHeaderLabels(vLabels) # mdl.setHorizontalHeaderLabels(hLabels) # Populates with empty items for i in enumerate(vLabels): row = [] for r in enumerate(hLabels): row.append(QStandardItem()) mdl.appendRow(row) # Data data = root.find("data") loadItem(data, mdl) return True def loadItem(root, mdl, parent=QModelIndex()): for row in root: r = int(row.attrib["row"]) for col in row: c = int(col.attrib["col"]) item = mdl.itemFromIndex(mdl.index(r, c, parent)) if not item: item = QStandardItem() mdl.itemFromIndex(parent).setChild(r, c, item) if col.text: # mdl.setData(mdl.index(r, c, parent), col.text) item.setText(col.text) if "color" in col.attrib: # mdl.itemFromIndex(mdl.index(r, c, parent)).setIcon(iconFromColorString(col.attrib["color"])) item.setIcon(iconFromColorString(col.attrib["color"])) if len(col) != 0: # loadItem(col, mdl, mdl.index(r, c, parent)) loadItem(col, mdl, mdl.indexFromItem(item)) def loadStandardItemModelXMLForCharacters(mdl, xml): """ Loads a standardItemModel saved to XML by version 0, but for the new characterModel. @param mdl: characterModel @param xml: the content of the xml @return: nothing """ mdl = mainWindow().mdlCharacter root = ET.fromstring(xml) data = root.find("data") for row in data: char = Character(mdl) for col in row: c = int(col.attrib["col"]) # Value if col.text: char._data[c] = col.text # Color if "color" in col.attrib: char.setColor(QColor(col.attrib["color"])) # Infos if len(col) != 0: for rrow in col: info = CharacterInfo(char) for ccol in rrow: cc = int(ccol.attrib["col"]) if cc == 11 and ccol.text: info.description = ccol.text if cc == 12 and ccol.text: info.value = ccol.text char.infos.append(info) mdl.characters.append(char)