def updateLabelColor(self, index): # px = QPixmap(64, 64) # px.fill(iconColor(self.mw.mdlLabels.item(index.row()).icon())) # self.btnLabelColor.setIcon(QIcon(px)) self.btnLabelColor.setStyleSheet( "background:{};".format(iconColor(self.mw.mdlLabels.item(index.row()).icon()).name())) self.btnLabelColor.setEnabled(True)
def updateLabelColor(self, index): # px = QPixmap(64, 64) # px.fill(iconColor(self.mw.mdlLabels.item(index.row()).icon())) # self.btnLabelColor.setIcon(QIcon(px)) self.btnLabelColor.setStyleSheet("background:{};".format( iconColor(self.mw.mdlLabels.item(index.row()).icon()).name())) self.btnLabelColor.setEnabled(True)
def setLabelColor(self): index = self.lstLabels.currentIndex() color = iconColor(self.mw.mdlLabels.item(index.row()).icon()) self.colorDialog = QColorDialog(color, self) color = self.colorDialog.getColor(color) if color.isValid(): px = QPixmap(32, 32) px.fill(color) self.mw.mdlLabels.item(index.row()).setIcon(QIcon(px)) self.updateLabelColor(index)
def chosePersoColor(self): idx = self.currentPersoIndex() item = self.item(idx.row(), Perso.name.value) if item: color = iconColor(item.icon()) else: color = Qt.white self.colorDialog = QColorDialog(color, self.mw) color = self.colorDialog.getColor(color) if color.isValid(): self.setPersoColor(item, color) self.updatePersoColor(idx)
def choseCharacterColor(self): ID = self.currentCharacterID() c = self._model.getCharacterByID(ID) if c: color = iconColor(c.icon) else: color = Qt.white self.colorDialog = QColorDialog(color, mainWindow()) color = self.colorDialog.getColor(color) if color.isValid(): c.setColor(color) mainWindow().updateCharacterColor(ID)
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))
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))
def test_several(): from PyQt5.QtGui import QPainter, QPixmap, QIcon, QColor from PyQt5.QtCore import QRect # drawProgress px = QPixmap(10, 10) F.drawProgress(QPainter(px), QRect(0, 0, 100, 100), 0.5) # colorFromProgress a = F.colorFromProgress(0.1) b = F.colorFromProgress(0.5) c = F.colorFromProgress(1.0) d = F.colorFromProgress(1.5) assert a != b != c != d # iconColor & iconFromColor & iconFromColorString icon = F.iconFromColorString("#ff0000") assert F.iconColor(icon).name().lower() == "#ff0000" # themeIcon assert F.themeIcon("text") != None assert F.themeIcon("nonexistingname") != None # randomColor c1 = F.randomColor() c2 = F.randomColor(c1) assert c1.name() != c2.name() # mixColors c1 = QColor("#FFF") c2 = QColor("#000") assert F.mixColors(c1, c2).name() == "#7f7f7f" # colorifyPixmap assert F.colorifyPixmap(px, c1) != None
def test_several(): from PyQt5.QtGui import QPainter, QPixmap, QIcon, QColor from PyQt5.QtCore import QRect # drawProgress px = QPixmap(10, 10) F.drawProgress(QPainter(px), QRect(0, 0, 100, 100), 0.5) # colorFromProgress a = F.colorFromProgress(0.1) b = F.colorFromProgress(0.5) c = F.colorFromProgress(1.0) d = F.colorFromProgress(1.5) assert a != b != c != d # iconColor & iconFromColor & iconFromColorString icon = F.iconFromColorString("#ff0000") assert F.iconColor(icon).name().lower() == "#ff0000" # themeIcon assert F.themeIcon("text") is not None assert F.themeIcon("nonexistingname") is not None # randomColor c1 = F.randomColor() c2 = F.randomColor(c1) assert c1.name() != c2.name() # mixColors c1 = QColor("#FFF") c2 = QColor("#000") assert F.mixColors(c1, c2).name() == "#7f7f7f" # colorifyPixmap assert F.colorifyPixmap(px, c1) != None
def getPersoColorName(self, index): icon = self.item(index.row()).icon() return iconColor(icon).name() if icon else ""
def color(self): """ Returns character's color in QColor @return: QColor """ return iconColor(self.icon)
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 and some XML or zip? for settings and stuff. If zip is True, everything is saved as a single zipped file. Easier to carry around, but does not allow collaborative work, versionning, or third-party editing. @param zip: if True, saves as a single file. If False, saves as plain-text. If None, tries to determine based on settings. @return: True if successful, False otherwise. """ if zip is None: zip = settings.saveToZip log("\n\nSaving to:", "zip" if zip else "folder") # List of files to be written files = [] # List of files to be removed removes = [] # List of files to be moved moves = [] mw = mainWindow() # File format version files.append(("MANUSKRIPT", "1")) # General infos (book and author) # Saved in plain text, in infos.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), ]: item = mw.mdlFlatData.item(0, col) if item: val = item.text().strip() else: val = "" 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), ]: item = mw.mdlFlatData.item(1, col) if item: val = item.text().strip() else: val = "" if val: content += formatMetaData(name, val, 12) 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 # In a character folder path = os.path.join("characters", "{name}.txt") mdl = mw.mdlCharacter # Review characters for c in mdl.characters: # Generates file's content content = "" for m in characterMap: val = mdl.data(c.index(m.value)).strip() if val: content += formatMetaData(characterMap[m], val, 20) # Character's color: content += formatMetaData("Color", c.color().name(QColor.HexRgb), 20) # Character's infos for info in c.infos: content += formatMetaData(info.description, info.value, 20) # generate file's path cpath = path.format(name="{ID}-{slugName}".format( ID=c.ID(), slugName=slugify(c.name()) )) # Has the character been renamed? if c.lastPath and cpath != c.lastPath: moves.append((c.lastPath, cpath)) # Update character's path c.lastPath = cpath files.append((cpath, content)) #################################################################################################################### # Texts # In an outline folder mdl = mw.mdlOutline # Go through the tree f, m, r = exportOutlineItem(mdl.rootItem) files += f moves += m removes += r # Writes revisions (if asked for) if settings.revisions["keep"]: files.append(("revisions.xml", mdl.saveToXML())) #################################################################################################################### # World # Either in an XML file, or in lots of plain texts? # More probably text, since there might be writing done in third-party. path = "world.opml" mdl = mw.mdlWorld root = ET.Element("opml") root.attrib["version"] = "1.0" body = ET.SubElement(root, "body") addWorldItem(body, mdl) content = ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True) files.append((path, content)) #################################################################################################################### # 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) path = "plots.xml" mdl = mw.mdlPlots root = ET.Element("root") addPlotItem(root, mdl) content = ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True) files.append((path, content)) #################################################################################################################### # 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 # We check if the file exist and we have write access. If the file does # not exists, we check the parent folder, because it might be a new project. if os.path.exists(project) and not os.access(project, os.W_OK) or \ not os.path.exists(project) and not os.access(os.path.dirname(project), os.W_OK): print("Error: you don't have write access to save this project there.") return False #################################################################################################################### # Save to zip if zip: # project = os.path.join( # os.path.dirname(project), # "_" + os.path.basename(project) # ) zf = zipfile.ZipFile(project, mode="w") for filename, content in files: zf.writestr(filename, content, compress_type=compression) zf.close() return True #################################################################################################################### # Save to plain text else: global cache # Project path dir = os.path.dirname(project) # Folder containing file: name of the project file (without .msk extension) folder = os.path.splitext(os.path.basename(project))[0] # Debug log("\nSaving to folder", folder) # If cache is empty (meaning we haven't loaded from disk), we wipe folder, just to be sure. if not cache: if os.path.exists(os.path.join(dir, folder)): shutil.rmtree(os.path.join(dir, folder)) # Moving files that have been renamed for old, new in moves: # Get full path oldPath = os.path.join(dir, folder, old) newPath = os.path.join(dir, folder, new) # Move the old file to the new place try: os.replace(oldPath, newPath) log("* Renaming/moving {} to {}".format(old, new)) except FileNotFoundError: # Maybe parent folder has been renamed pass # Update cache cache2 = {} for f in cache: f2 = f.replace(old, new) if f2 != f: log(" * Updating cache:", f, f2) cache2[f2] = cache[f] cache = cache2 # Writing files for path, content in files: filename = os.path.join(dir, folder, path) os.makedirs(os.path.dirname(filename), exist_ok=True) # Check if content is in cache, and write if necessary if path not in cache or cache[path] != content: log("* Writing file {} ({})".format(path, "not in cache" if path not in cache else "different")) # mode = "w" + ("b" if type(content) == bytes else "") if type(content) == bytes: with open(filename, "wb") as f: f.write(content) else: with open(filename, "w", encoding='utf8') as f: f.write(content) cache[path] = content # Removing phantoms for path in [p for p in cache if p not in [p for p, c in files]]: filename = os.path.join(dir, folder, path) log("* Removing", path) if os.path.isdir(filename): shutil.rmtree(filename) else: # elif os.path.exists(filename) os.remove(filename) # Clear cache cache.pop(path, 0) # Removing empty directories for root, dirs, files in os.walk(os.path.join(dir, folder, "outline")): for dir in dirs: newDir = os.path.join(root, dir) try: os.removedirs(newDir) log("* Removing empty directory:", newDir) except: # Directory not empty, we don't remove. pass # Write the project file's content with open(project, "w", encoding='utf8') as f: f.write("1") # Format number return True
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 and some XML or zip? for settings and stuff. If zip is True, everything is saved as a single zipped file. Easier to carry around, but does not allow collaborative work, versionning, or third-party editing. @param zip: if True, saves as a single file. If False, saves as plain-text. If None, tries to determine based on settings. @return: Nothing """ if zip is None: zip = settings.saveToZip log("\n\nSaving to:", "zip" if zip else "folder") # List of files to be written files = [] # List of files to be removed removes = [] # List of files to be moved moves = [] mw = mainWindow() # File format version files.append(("MANUSKRIPT", "1")) # General infos (book and author) # Saved in plain text, in infos.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), ]: item = mw.mdlFlatData.item(0, col) if item: val = item.text().strip() else: val = "" 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), ]: item = mw.mdlFlatData.item(1, col) if item: val = item.text().strip() else: val = "" if val: content += formatMetaData(name, val, 12) 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 # In a character folder path = os.path.join("characters", "{name}.txt") mdl = mw.mdlCharacter # Review characters for c in mdl.characters: # Generates file's content content = "" for m in characterMap: val = mdl.data(c.index(m.value)).strip() if val: content += formatMetaData(characterMap[m], val, 20) # Character's color: content += formatMetaData("Color", c.color().name(QColor.HexRgb), 20) # Character's infos for info in c.infos: content += formatMetaData(info.description, info.value, 20) # generate file's path cpath = path.format(name="{ID}-{slugName}".format( ID=c.ID(), slugName=slugify(c.name()) )) # Has the character been renamed? if c.lastPath and cpath != c.lastPath: moves.append((c.lastPath, cpath)) # Update character's path c.lastPath = cpath files.append((cpath, content)) #################################################################################################################### # Texts # In an outline folder mdl = mw.mdlOutline # Go through the tree f, m, r = exportOutlineItem(mdl.rootItem) files += f moves += m removes += r # Writes revisions (if asked for) if settings.revisions["keep"]: files.append(("revisions.xml", mdl.saveToXML())) #################################################################################################################### # World # Either in an XML file, or in lots of plain texts? # More probably text, since there might be writing done in third-party. path = "world.opml" mdl = mw.mdlWorld root = ET.Element("opml") root.attrib["version"] = "1.0" body = ET.SubElement(root, "body") addWorldItem(body, mdl) content = ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True) files.append((path, content)) #################################################################################################################### # 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) path = "plots.xml" mdl = mw.mdlPlots root = ET.Element("root") addPlotItem(root, mdl) content = ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True) files.append((path, content)) #################################################################################################################### # 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 #################################################################################################################### # Save to zip if zip: # project = os.path.join( # os.path.dirname(project), # "_" + os.path.basename(project) # ) zf = zipfile.ZipFile(project, mode="w") for filename, content in files: zf.writestr(filename, content, compress_type=compression) zf.close() #################################################################################################################### # Save to plain text else: global cache # Project path dir = os.path.dirname(project) # Folder containing file: name of the project file (without .msk extension) folder = os.path.splitext(os.path.basename(project))[0] # Debug log("\nSaving to folder", folder) # If cache is empty (meaning we haven't loaded from disk), we wipe folder, just to be sure. if not cache: if os.path.exists(os.path.join(dir, folder)): shutil.rmtree(os.path.join(dir, folder)) # Moving files that have been renamed for old, new in moves: # Get full path oldPath = os.path.join(dir, folder, old) newPath = os.path.join(dir, folder, new) # Move the old file to the new place try: os.replace(oldPath, newPath) log("* Renaming/moving {} to {}".format(old, new)) except FileNotFoundError: # Maybe parent folder has been renamed pass # Update cache cache2 = {} for f in cache: f2 = f.replace(old, new) if f2 != f: log(" * Updating cache:", f, f2) cache2[f2] = cache[f] cache = cache2 # Writing files for path, content in files: filename = os.path.join(dir, folder, path) os.makedirs(os.path.dirname(filename), exist_ok=True) # Check if content is in cache, and write if necessary if path not in cache or cache[path] != content: log("* Writing file {} ({})".format(path, "not in cache" if path not in cache else "different")) # mode = "w" + ("b" if type(content) == bytes else "") if type(content) == bytes: with open(filename, "wb") as f: f.write(content) else: with open(filename, "w", encoding='utf8') as f: f.write(content) cache[path] = content # Removing phantoms for path in [p for p in cache if p not in [p for p, c in files]]: filename = os.path.join(dir, folder, path) log("* Removing", path) if os.path.isdir(filename): shutil.rmtree(filename) else: # elif os.path.exists(filename) os.remove(filename) # Clear cache cache.pop(path, 0) # Removing empty directories for root, dirs, files in os.walk(os.path.join(dir, folder, "outline")): for dir in dirs: newDir = os.path.join(root, dir) try: os.removedirs(newDir) log("* Removing empty directory:", newDir) except: # Directory not empty, we don't remove. pass # Write the project file's content with open(project, "w", encoding='utf8') as f: f.write("1") # Format number