#!/usr/bin/env python # --!-- coding: utf8 --!-- # Lots of stuff from here comes from the excellet focuswriter. import os import re from PyQt5.QtCore import QSettings, QRect, QSize, Qt, QPoint, QFile, QIODevice, QTextStream from PyQt5.QtGui import QPixmap, QPainter, QColor, QBrush, QImage, QTextBlockFormat, QTextCharFormat, QFont, qGray from PyQt5.QtWidgets import qApp, QFrame from manuskript.functions import allPaths, appPath from manuskript.ui.views.textEditView import textEditView def loadThemeDatas(themeFile): settings = QSettings(themeFile, QSettings.IniFormat) _themeData = {} # Theme name _themeData["Name"] = getThemeName(themeFile) # Window Background loadThemeSetting(_themeData, settings, "Background/Color", "#000000") loadThemeSetting(_themeData, settings, "Background/ImageFile", "") loadThemeSetting(_themeData, settings, "Background/Type", 0) # Text Background loadThemeSetting(_themeData, settings, "Foreground/Color", "#ffffff") loadThemeSetting(_themeData, settings, "Foreground/Opacity", 50) loadThemeSetting(_themeData, settings, "Foreground/Margin", 40) loadThemeSetting(_themeData, settings, "Foreground/Padding", 10) loadThemeSetting(_themeData, settings, "Foreground/Position", 1) loadThemeSetting(_themeData, settings, "Foreground/Rounding", 5) loadThemeSetting(_themeData, settings, "Foreground/Width", 700) # Text Options loadThemeSetting(_themeData, settings, "Text/Color", "#ffffff") loadThemeSetting(_themeData, settings, "Text/Font", qApp.font().toString()) loadThemeSetting(_themeData, settings, "Text/Misspelled", "#ff0000") # Paragraph Options loadThemeSetting(_themeData, settings, "Spacings/IndendFirstLine", False) loadThemeSetting(_themeData, settings, "Spacings/LineSpacing", 100) loadThemeSetting(_themeData, settings, "Spacings/ParagraphAbove", 0) loadThemeSetting(_themeData, settings, "Spacings/ParagraphBelow", 0) loadThemeSetting(_themeData, settings, "Spacings/TabWidth", 48) return _themeData def loadThemeSetting(datas, settings, key, default): if settings.contains(key): datas[key] = type(default)(settings.value(key)) else: datas[key] = default def getThemeName(theme): settings = QSettings(theme, QSettings.IniFormat) if settings.contains("Name"): return settings.value("Name") else: return os.path.splitext(os.path.split(theme)[1])[0] def themeTextRect(themeDatas, screenRect): margin = themeDatas["Foreground/Margin"] x = 0 y = margin width = min(themeDatas["Foreground/Width"], screenRect.width() - 2 * margin) height = screenRect.height() - 2 * margin if themeDatas["Foreground/Position"] == 0: # Left x = margin elif themeDatas["Foreground/Position"] == 1: # Center x = (screenRect.width() - width) / 2 elif themeDatas["Foreground/Position"] == 2: # Right x = screenRect.width() - margin - width elif themeDatas["Foreground/Position"] == 3: # Stretched x = margin width = screenRect.width() - 2 * margin return QRect(x, y, width, height) def createThemePreview(theme, screenRect, size=QSize(200, 120)): if type(theme) == str and os.path.exists(theme): # Theme is the path to an ini file themeDatas = loadThemeDatas(theme) else: themeDatas = theme pixmap = generateTheme(themeDatas, screenRect) addThemePreviewText(pixmap, themeDatas, screenRect) px = QPixmap(pixmap).scaled(size, Qt.KeepAspectRatio) w = px.width() / 10 h = px.height() / 10 r = themeTextRect(themeDatas, screenRect) painter = QPainter(px) painter.drawPixmap(QRect(w, h, w * 4, h * 5), pixmap, QRect(r.topLeft() - QPoint(w / 3, h / 3), QSize(w * 4, h * 5))) painter.setPen(Qt.white) painter.drawRect(QRect(w, h, w * 4, h * 5)) painter.end() return px def findThemePath(themeName): p = findFirstFile(re.escape("{}.theme".format(themeName)), "resources/themes") if not p: return findFirstFile(".*\.theme", "resources/themes") else: return p def findBackground(filename): return findFirstFile(re.escape(filename), "resources/backgrounds") def findFirstFile(regex, path="resources"): paths = allPaths(path) for p in paths: lst = os.listdir(p) for l in lst: if re.match(regex, l): return os.path.join(p, l) def generateTheme(themeDatas, screenRect): # Window Background px = QPixmap(screenRect.size()) px.fill(QColor(themeDatas["Background/Color"])) painter = QPainter(px) if themeDatas["Background/ImageFile"]: path = findBackground(themeDatas["Background/ImageFile"]) _type = themeDatas["Background/Type"] if path and _type > 0: if _type == 1: # Tiled painter.fillRect(screenRect, QBrush(QImage(path))) else: img = QImage(path) scaled = img.size() if _type == 3: # Stretched scaled.scale(screenRect.size(), Qt.IgnoreAspectRatio) elif _type == 4: # Scaled scaled.scale(screenRect.size(), Qt.KeepAspectRatio) elif _type == 5: # Zoomed scaled.scale(screenRect.size(), Qt.KeepAspectRatioByExpanding) painter.drawImage((screenRect.width() - scaled.width()) / 2, (screenRect.height() - scaled.height()) / 2, img.scaled(scaled)) # Text Background textRect = themeTextRect(themeDatas, screenRect) painter.save() color = QColor(themeDatas["Foreground/Color"]) color.setAlpha(themeDatas["Foreground/Opacity"] * 255 / 100) painter.setBrush(color) painter.setPen(Qt.NoPen) r = themeDatas["Foreground/Rounding"] painter.drawRoundedRect(textRect, r, r) painter.restore() painter.end() return px def themeEditorGeometry(themeDatas, textRect): padding = themeDatas["Foreground/Padding"] x = textRect.x() + padding y = textRect.y() + padding + themeDatas["Spacings/ParagraphAbove"] width = textRect.width() - 2 * padding height = textRect.height() - 2 * padding - themeDatas["Spacings/ParagraphAbove"] return x, y, width, height def getThemeBlockFormat(themeDatas): bf = QTextBlockFormat() bf.setLineHeight(themeDatas["Spacings/LineSpacing"], QTextBlockFormat.ProportionalHeight) bf.setTextIndent(themeDatas["Spacings/TabWidth"] * 1 if themeDatas["Spacings/IndendFirstLine"] else 0) bf.setTopMargin(themeDatas["Spacings/ParagraphAbove"]) bf.setBottomMargin(themeDatas["Spacings/ParagraphBelow"]) return bf def setThemeEditorDatas(editor, themeDatas, pixmap, screenRect): textRect = themeTextRect(themeDatas, screenRect) x, y, width, height = themeEditorGeometry(themeDatas, textRect) editor.setGeometry(x, y, width, height) # p = editor.palette() ##p.setBrush(QPalette.Base, QBrush(pixmap.copy(x, y, width, height))) # p.setBrush(QPalette.Base, QColor(Qt.transparent)) # p.setColor(QPalette.Text, QColor(themeDatas["Text/Color"])) # p.setColor(QPalette.Highlight, QColor(themeDatas["Text/Color"])) # p.setColor(QPalette.HighlightedText, Qt.black if qGray(QColor(themeDatas["Text/Color"]).rgb()) > 127 else Qt.white) # editor.setPalette(p) editor.setAttribute(Qt.WA_NoSystemBackground, True) bf = getThemeBlockFormat(themeDatas) editor.setDefaultBlockFormat(bf) # b = editor.document().firstBlock() # cursor = editor.textCursor() # cursor.setBlockFormat(bf) # while b.isValid(): # bf2 = b.blockFormat() # bf2.merge(bf) # cursor.setPosition(b.position()) ##cursor.setPosition(b.position(), QTextCursor.KeepAnchor) # cursor.setBlockFormat(bf2) # b = b.next() editor.setTabStopWidth(themeDatas["Spacings/TabWidth"]) editor.document().setIndentWidth(themeDatas["Spacings/TabWidth"]) editor.highlighter.setMisspelledColor(QColor(themeDatas["Text/Misspelled"])) cf = QTextCharFormat() # f = QFont() # f.fromString(themeDatas["Text/Font"]) # cf.setFont(f) editor.highlighter.setDefaultCharFormat(cf) f = QFont() f.fromString(themeDatas["Text/Font"]) # editor.setFont(f) editor.setStyleSheet(""" background: transparent; color: {foreground}; font-family: {ff}; font-size: {fs}; selection-color: {sc}; selection-background-color: {sbc}; """.format( foreground=themeDatas["Text/Color"], ff=f.family(), fs="{}pt".format(str(f.pointSize())), sc="black" if qGray(QColor(themeDatas["Text/Color"]).rgb()) > 127 else "white", sbc=themeDatas["Text/Color"], ) ) editor._fromTheme = True def addThemePreviewText(pixmap, themeDatas, screenRect): # Text previewText = textEditView(highlighting=True) previewText.setFrameStyle(QFrame.NoFrame) previewText.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) previewText.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) f = QFile(appPath("resources/themes/preview.txt")) f.open(QIODevice.ReadOnly) previewText.setPlainText(QTextStream(f).readAll()) setThemeEditorDatas(previewText, themeDatas, pixmap, screenRect) previewText.render(pixmap, previewText.pos()) ## Text Background ##themeDatas["Foreground/Color"] ##themeDatas["Foreground/Opacity"] ##themeDatas["Foreground/Margin"] ##themeDatas["Foreground/Padding"] ##themeDatas["Foreground/Position"] ##themeDatas["Foreground/Rounding"] ##themeDatas["Foreground/Width"] ## Text Options ##themeDatas["Text/Color"] ##themeDatas["Text/Font"] # themeDatas["Text/Misspelled"] ## Paragraph Options ##themeDatas["Spacings/IndendFirstLine"] ##themeDatas["Spacings/LineSpacing"] ##themeDatas["Spacings/ParagraphAbove"] ##themeDatas["Spacings/ParagraphBelow"] ##themeDatas["Spacings/TabWidth"]