Merge pull request #15 from vkbo/themes

Theme Support
This commit is contained in:
Veronica K. Berglyd Olsen
2019-05-18 20:59:10 +02:00
committed by GitHub
11 changed files with 200 additions and 40 deletions
+20 -2
View File
@@ -48,7 +48,8 @@ class Config:
self.guiTheme = "default"
self.winGeometry = [1100, 650]
self.treeColWidth = [120, 30, 50]
self.mainPanePos = [300, 400, 400]
self.mainPanePos = [300, 800]
self.docPanePos = [400, 400]
## Project
self.autoSaveProj = 60
@@ -125,6 +126,12 @@ class Config:
# Get options
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
if confParser.has_option(cnfSec,"theme"):
self.guiTheme = confParser.get(cnfSec,"theme")
## Sizes
cnfSec = "Sizes"
if confParser.has_section(cnfSec):
@@ -138,7 +145,11 @@ class Config:
)
if confParser.has_option(cnfSec,"mainpane"):
self.mainPanePos = self.unpackList(
confParser.get(cnfSec,"mainpane"), 3, self.mainPanePos
confParser.get(cnfSec,"mainpane"), 2, self.mainPanePos
)
if confParser.has_option(cnfSec,"docpane"):
self.docPanePos = self.unpackList(
confParser.get(cnfSec,"docpane"), 2, self.docPanePos
)
## Project
@@ -199,6 +210,7 @@ class Config:
cnfSec = "Main"
confParser.add_section(cnfSec)
confParser.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
confParser.set(cnfSec,"theme", str(self.guiTheme))
## Sizes
cnfSec = "Sizes"
@@ -206,6 +218,7 @@ class Config:
confParser.set(cnfSec,"geometry", self.packList(self.winGeometry))
confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth))
confParser.set(cnfSec,"mainpane", self.packList(self.mainPanePos))
confParser.set(cnfSec,"docpane", self.packList(self.docPanePos))
## Project
cnfSec = "Project"
@@ -298,4 +311,9 @@ class Config:
self.confChanged = True
return True
def setDocPanePos(self, panePos):
self.docPanePos = panePos
self.confChanged = True
return True
# End Class Config
+1 -1
View File
@@ -56,7 +56,7 @@ class GuiDocEditor(QTextEdit):
# Core Elements
self.theDoc = self.document()
self.theDict = enchant.Dict(self.mainConf.spellLanguage)
self.hLight = GuiDocHighlighter(self.theDoc)
self.hLight = GuiDocHighlighter(self.theDoc, self.theParent.theTheme)
self.hLight.setDict(self.theDict)
# Context Menu
+12 -12
View File
@@ -20,27 +20,27 @@ logger = logging.getLogger(__name__)
class GuiDocHighlighter(QSyntaxHighlighter):
def __init__(self, theDoc):
def __init__(self, theDoc, theTheme):
QSyntaxHighlighter.__init__(self, theDoc)
logger.debug("Initialising DocHighlighter ...")
self.mainConf = nw.CONFIG
self.theDoc = theDoc
self.theTheme = theTheme
self.theDict = None
self.spellCheck = False
self.hRules = []
self.colHead = QColor( 0,155,200)
self.colHeadH = QColor( 0,105,135)
self.colEmph = QColor(200,120, 0)
self.colDialN = QColor(200, 46, 0)
self.colDialD = QColor(184,200, 0)
self.colDialS = QColor(136,200, 0)
self.colComm = QColor(150,150,150)
self.colKey = QColor(200, 46, 0)
self.colVal = QColor(184,200, 0)
self.colSpell = QColor(200, 46, 0)
self.colHead = QColor(*self.theTheme.colHead)
self.colHeadH = QColor(*self.theTheme.colHeadH)
self.colEmph = QColor(*self.theTheme.colEmph)
self.colDialN = QColor(*self.theTheme.colDialN)
self.colDialD = QColor(*self.theTheme.colDialD)
self.colDialS = QColor(*self.theTheme.colDialS)
self.colComm = QColor(*self.theTheme.colComm)
self.colKey = QColor(*self.theTheme.colKey)
self.colVal = QColor(*self.theTheme.colVal)
self.colSpell = QColor(*self.theTheme.colSpell)
self.hStyles = {
"header1" : self._makeFormat(self.colHead, "bold",1.8),
+8 -1
View File
@@ -27,9 +27,16 @@ class GuiDocViewer(QTextBrowser):
# Class Variables
self.mainConf = nw.CONFIG
self.theParent = theParent
self.theTheme = theParent.theTheme
self.theDoc = self.document()
self.theDoc.setDefaultStyleSheet((
"h1, h2, h3, h4 {{"
" color: rgb({0},{1},{2});"
"}}"
).format(
*self.theTheme.colHead
))
self.theDoc.setDocumentMargin(self.mainConf.textMargin[0])
self.setMinimumWidth(300)
+20 -19
View File
@@ -18,6 +18,7 @@ from PyQt5.QtWidgets import QWidget, QMainWindow, QVBoxLayout, QFrame, QSpl
from PyQt5.QtGui import QIcon, QPixmap, QColor
from PyQt5.QtCore import Qt, QTimer
from nw.theme import Theme
from nw.gui.doctree import GuiDocTree
from nw.gui.doceditor import GuiDocEditor
from nw.gui.docviewer import GuiDocViewer
@@ -42,12 +43,14 @@ class GuiMain(QMainWindow):
logger.debug("Initialising GUI ...")
self.mainConf = nw.CONFIG
self.theTheme = Theme()
self.theProject = NWProject(self)
self.theDocument = NWDoc(self.theProject, self)
self.resize(*self.mainConf.winGeometry)
self._setWindowTitle()
self.setWindowIcon(QIcon(path.join(self.mainConf.appPath,"..","novelWriter.svg")))
self.theTheme.loadTheme()
# Main GUI Elements
self.docEditor = GuiDocEditor(self)
@@ -70,27 +73,29 @@ class GuiMain(QMainWindow):
self.treeBox.addWidget(self.docDetails)
self.treePane.setLayout(self.treeBox)
self.splitView = QSplitter(Qt.Horizontal)
self.splitView.addWidget(self.docEditor)
self.splitView.addWidget(self.docViewer)
self.splitMain = QSplitter(Qt.Horizontal)
self.splitMain.addWidget(self.treePane)
self.splitMain.addWidget(self.docEditor)
self.splitMain.addWidget(self.docViewer)
self.splitMain.addWidget(self.splitView)
self.splitMain.setSizes(self.mainConf.mainPanePos)
self.splitMain.splitterMoved.connect(self._splitMainMove)
self.setCentralWidget(self.splitMain)
self.idxTree = self.splitMain.indexOf(self.treePane)
self.idxEditor = self.splitMain.indexOf(self.docEditor)
self.idxViewer = self.splitMain.indexOf(self.docViewer)
self.idxMain = self.splitMain.indexOf(self.splitView)
self.idxEditor = self.splitView.indexOf(self.docEditor)
self.idxViewer = self.splitView.indexOf(self.docViewer)
self.splitMain.setCollapsible(self.idxTree, False)
self.splitMain.setCollapsible(self.idxEditor, False)
self.splitMain.setCollapsible(self.idxViewer, True)
self.splitMain.setCollapsible(self.idxMain, False)
self.splitView.setCollapsible(self.idxEditor, False)
self.splitView.setCollapsible(self.idxViewer, True)
self.docViewer.setVisible(False)
pPos = self.mainConf.mainPanePos
tPos = [pPos[0], pPos[1]+pPos[2]]
self.splitMain.setSizes(tPos)
# Build GUI Elements
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
@@ -105,11 +110,7 @@ class GuiMain(QMainWindow):
self.statusBar.showMessage("Ready")
# Load Theme StyleSheet
cssFile = path.join(self.mainConf.themePath,self.mainConf.guiTheme+".css")
if path.isfile(cssFile):
with open(cssFile,mode="r") as inFile:
theCss = inFile.read()
self.setStyleSheet(theCss)
self.setStyleSheet(self.theTheme.cssData)
self.asProjTimer = QTimer()
self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000))
@@ -260,11 +261,10 @@ class GuiMain(QMainWindow):
bPos = self.splitMain.sizes()
self.docViewer.setVisible(True)
if bPos[2] == 0:
bWidth = bPos[1]+bPos[2]
bPos[1] = int(bWidth/2)
bPos[2] = bWidth-bPos[1]
self.splitMain.setSizes(bPos)
vPos = [0,0]
vPos[0] = int(bPos[1]/2)
vPos[1] = bPos[1]-vPos[0]
self.splitView.setSizes(vPos)
self.docEditor.changeWidth()
return True
@@ -346,6 +346,7 @@ class GuiMain(QMainWindow):
self.mainConf.setWinSize(self.width(), self.height())
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
self.mainConf.setMainPanePos(self.splitMain.sizes())
self.mainConf.setDocPanePos(self.splitView.sizes())
self.mainConf.saveConfig()
return
+120
View File
@@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
"""novelWriter Theme Class
novelWriter Theme Class
===========================
This class reads and store the main theme
File History:
Created: 2019-05-18 [0.1.3]
"""
import logging
import configparser
import nw
from os import path
logger = logging.getLogger(__name__)
class Theme:
def __init__(self):
self.mainConf = nw.CONFIG
self.cssName = "styles.css"
self.confName = "theme.conf"
# Loaded Theme Settings
self.colHead = [0,0,0]
self.colHeadH = [0,0,0]
self.colEmph = [0,0,0]
self.colDialN = [0,0,0]
self.colDialD = [0,0,0]
self.colDialS = [0,0,0]
self.colComm = [0,0,0]
self.colKey = [0,0,0]
self.colVal = [0,0,0]
self.colSpell = [0,0,0]
# Changeable Settings
self.guiTheme = None
self.themePath = None
self.confFile = None
self.cssFile = None
self.cssData = ""
self.updateTheme()
return
##
# Actions
##
def updateTheme(self):
self.guiTheme = self.mainConf.guiTheme
self.themePath = path.join(self.mainConf.themePath, self.guiTheme)
self.confFile = path.join(self.themePath,self.confName)
self.cssFile = path.join(self.themePath,self.cssName)
return True
def loadTheme(self):
logger.debug("Loading theme files")
# CSS File
try:
if path.isfile(self.cssFile):
with open(self.cssFile,mode="r") as inFile:
self.cssData = inFile.read()
except Exception as e:
logger.error("Could not load theme css file")
return False
# Color Config
confParser = configparser.ConfigParser()
try:
confParser.read_file(open(self.confFile))
except Exception as e:
logger.error("Could not load theme config file")
return False
## Syntax
cnfSec = "Syntax"
if confParser.has_section(cnfSec):
self.colHead = self._loadColour(confParser,cnfSec,"headertext")
self.colHeadH = self._loadColour(confParser,cnfSec,"headertag")
self.colEmph = self._loadColour(confParser,cnfSec,"emphasis")
self.colDialN = self._loadColour(confParser,cnfSec,"straightquotes")
self.colDialD = self._loadColour(confParser,cnfSec,"doublequotes")
self.colDialS = self._loadColour(confParser,cnfSec,"singlequotes")
self.colComm = self._loadColour(confParser,cnfSec,"hidden")
self.colKey = self._loadColour(confParser,cnfSec,"keyword")
self.colVal = self._loadColour(confParser,cnfSec,"value")
self.colSpell = self._loadColour(confParser,cnfSec,"spellcheckline")
return True
##
# Internal Functions
##
def _loadColour(self, confParser, cnfSec, cnfName):
if confParser.has_option(cnfSec,cnfName):
inData = confParser.get(cnfSec,cnfName).split(",")
outData = []
try:
outData.append(int(inData[0]))
outData.append(int(inData[1]))
outData.append(int(inData[2]))
except:
logger.error("Could not load theme colours for '%s' from config file" % cnfName)
outData = [0,0,0]
else:
logger.warning("Could not find theme colours for '%s' in config file" % cnfName)
outData = [0,0,0]
return outData
# End Class Theme
+11
View File
@@ -0,0 +1,11 @@
[Syntax]
headertext = 0, 155, 200
headertag = 0, 105, 135
emphasis = 200, 120, 0
straightquotes = 200, 46, 0
doublequotes = 184, 200, 0
singlequotes = 136, 200, 0
hidden = 150, 150, 150
keyword = 200, 46, 0
value = 184, 200, 0
spellcheckline = 200, 46, 0
+3 -3
View File
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-18 18:12:49">
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-18 20:51:40">
<project>
<name>Sample Project</name>
<title>Sample Project</title>
@@ -8,8 +8,8 @@
</project>
<settings>
<spellCheck>False</spellCheck>
<lastEdited>96b68994dfa3d</lastEdited>
<lastViewed>96b68994dfa3d</lastViewed>
<lastEdited>636b6aa9b697b</lastEdited>
<lastViewed>636b6aa9b697b</lastViewed>
</settings>
<content count="15">
<item handle="7031beac91f75" order="0" parent="None">
+3 -1
View File
@@ -1,10 +1,12 @@
[Main]
timestamp = 2019-05-18 15:06:44
theme = default
[Sizes]
geometry = 1100, 650
treecols = 120, 30, 50
mainpane = 300, 400, 400
mainpane = 300, 800
docpane = 400, 400
[Project]
autosaveproject = 60
+2 -1
View File
@@ -70,7 +70,8 @@ def testConfigSetMainPanePos(nwTemp,nwRef):
refConf = path.join(nwRef, "novelwriter.conf")
assert theConf.setMainPanePos([0, 0])
assert theConf.confChanged
assert theConf.setMainPanePos([300, 400, 400])
assert theConf.setMainPanePos([300, 800])
assert theConf.setDocPanePos([400, 400])
assert theConf.saveConfig()
assert cmpFiles(tmpConf, refConf, [2])
assert not theConf.confChanged