From 3cbea6eff21cf56dd8be98ecc79c714cbcf1a906 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 22:01:59 +0200 Subject: [PATCH 01/17] SOme cleanup of code in config, and added tab width option --- nw/config.py | 194 +++++++++++++++++++++----------------------- nw/gui/doceditor.py | 6 +- 2 files changed, 97 insertions(+), 103 deletions(-) diff --git a/nw/config.py b/nw/config.py index a12aaa9a..f6a170de 100644 --- a/nw/config.py +++ b/nw/config.py @@ -25,6 +25,11 @@ class Config: WIN_WIDTH = 0 WIN_HEIGHT = 1 + CNF_STR = 0 + CNF_INT = 1 + CNF_BOOL = 2 + CNF_LIST = 3 + def __init__(self): # Set Application Variables @@ -60,6 +65,7 @@ class Config: self.textWidth = 600 self.textMargin = [40, 40] self.textSize = 13 + self.tabWidth = 40 self.doJustify = True self.autoSelect = True self.doReplace = True @@ -68,7 +74,7 @@ class Config: self.doReplaceDash = True self.doReplaceDots = True self.wordCountTimer = 5.0 - + self.fmtDoubleQuotes = ["“","”"] self.fmtSingleQuotes = ["‘","’"] self.fmtApostrophe = "’" @@ -117,140 +123,105 @@ class Config: def loadConfig(self): logger.debug("Loading config file") - confParser = configparser.ConfigParser() + cnfParse = configparser.ConfigParser() try: - confParser.read_file(open(path.join(self.confPath,self.confFile))) + cnfParse.read_file(open(path.join(self.confPath,self.confFile))) except Exception as e: logger.error("Could not load config file") return False - # Get options - ## Main cnfSec = "Main" - if confParser.has_section(cnfSec): - if confParser.has_option(cnfSec,"theme"): - self.guiTheme = confParser.get(cnfSec,"theme") + self.guiTheme = self._parseLine(cnfParse, cnfSec, "theme", self.CNF_STR) ## Sizes cnfSec = "Sizes" - if confParser.has_section(cnfSec): - if confParser.has_option(cnfSec,"geometry"): - self.winGeometry = self.unpackList( - confParser.get(cnfSec,"geometry"), 2, self.winGeometry - ) - if confParser.has_option(cnfSec,"treecols"): - self.treeColWidth = self.unpackList( - confParser.get(cnfSec,"treecols"), 3, self.treeColWidth - ) - if confParser.has_option(cnfSec,"mainpane"): - self.mainPanePos = self.unpackList( - confParser.get(cnfSec,"mainpane"), 2, self.mainPanePos - ) - if confParser.has_option(cnfSec,"docpane"): - self.docPanePos = self.unpackList( - confParser.get(cnfSec,"docpane"), 2, self.docPanePos - ) + self.winGeometry = self._parseLine(cnfParse, cnfSec, "geometry", self.CNF_LIST, self.winGeometry) + self.treeColWidth = self._parseLine(cnfParse, cnfSec, "treecols", self.CNF_LIST, self.treeColWidth) + self.mainPanePos = self._parseLine(cnfParse, cnfSec, "mainpane", self.CNF_LIST, self.mainPanePos) + self.docPanePos = self._parseLine(cnfParse, cnfSec, "docpane", self.CNF_LIST, self.docPanePos) ## Project cnfSec = "Project" - if confParser.has_section(cnfSec): - if confParser.has_option(cnfSec,"autosaveproject"): - self.autoSaveProj = confParser.getint(cnfSec,"autosaveproject") - if confParser.has_option(cnfSec,"autosavedoc"): - self.autoSaveDoc = confParser.getint(cnfSec,"autosavedoc") + self.autoSaveProj = self._parseLine(cnfParse, cnfSec, "autosaveproject", self.CNF_INT) + self.autoSaveDoc = self._parseLine(cnfParse, cnfSec, "autosavedoc", self.CNF_INT) ## Editor cnfSec = "Editor" - if confParser.has_section(cnfSec): - if confParser.has_option(cnfSec,"fixedwidth"): - self.textFixedW = confParser.getboolean(cnfSec,"fixedwidth") - if confParser.has_option(cnfSec,"width"): - self.textWidth = confParser.getint(cnfSec,"width") - if confParser.has_option(cnfSec,"margins"): - self.textMargin = self.unpackList( - confParser.get(cnfSec,"margins"), 2, self.textMargin - ) - if confParser.has_option(cnfSec,"textsize"): - self.textSize = confParser.getint(cnfSec,"textsize") - if confParser.has_option(cnfSec,"justify"): - self.doJustify = confParser.getboolean(cnfSec,"justify") - if confParser.has_option(cnfSec,"autoselect"): - self.autoSelect = confParser.getboolean(cnfSec,"autoselect") - if confParser.has_option(cnfSec,"autoreplace"): - self.doReplace = confParser.getboolean(cnfSec,"autoreplace") - if confParser.has_option(cnfSec,"repsquotes"): - self.doReplaceSQuote = confParser.getboolean(cnfSec,"repsquotes") - if confParser.has_option(cnfSec,"repdquotes"): - self.doReplaceDQuote = confParser.getboolean(cnfSec,"repdquotes") - if confParser.has_option(cnfSec,"repdash"): - self.doReplaceDash = confParser.getboolean(cnfSec,"repdash") - if confParser.has_option(cnfSec,"repdots"): - self.doReplaceDots = confParser.getboolean(cnfSec,"repdots") - if confParser.has_option(cnfSec,"spellcheck"): - self.spellLanguage = confParser.get(cnfSec,"spellcheck") + self.textFixedW = self._parseLine(cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL) + self.textWidth = self._parseLine(cnfParse, cnfSec, "width", self.CNF_INT) + self.textMargin = self._parseLine(cnfParse, cnfSec, "margins", self.CNF_LIST, self.textMargin) + self.textSize = self._parseLine(cnfParse, cnfSec, "textsize", self.CNF_INT) + self.tabWidth = self._parseLine(cnfParse, cnfSec, "tabwidth", self.CNF_INT) + self.doJustify = self._parseLine(cnfParse, cnfSec, "justify", self.CNF_BOOL) + self.autoSelect = self._parseLine(cnfParse, cnfSec, "autoselect", self.CNF_BOOL) + self.doReplace = self._parseLine(cnfParse, cnfSec, "autoreplace", self.CNF_BOOL) + self.doReplaceSQuote = self._parseLine(cnfParse, cnfSec, "repsquotes", self.CNF_BOOL) + self.doReplaceDQuote = self._parseLine(cnfParse, cnfSec, "repdquotes", self.CNF_BOOL) + self.doReplaceDash = self._parseLine(cnfParse, cnfSec, "repdash", self.CNF_BOOL) + self.doReplaceDots = self._parseLine(cnfParse, cnfSec, "repdots", self.CNF_BOOL) + self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR) ## Path cnfSec = "Path" - if confParser.has_section(cnfSec): - for i in range(10): - if confParser.has_option(cnfSec,"recent%d" % i): - self.recentList[i] = confParser.get(cnfSec,"recent%d" % i) + for i in range(10): + self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR) return True def saveConfig(self): logger.debug("Saving config file") - confParser = configparser.ConfigParser() + cnfParse = configparser.ConfigParser() # Set options ## Main 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)) + cnfParse.add_section(cnfSec) + cnfParse.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + cnfParse.set(cnfSec,"theme", str(self.guiTheme)) ## Sizes cnfSec = "Sizes" - confParser.add_section(cnfSec) - 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)) + cnfParse.add_section(cnfSec) + cnfParse.set(cnfSec,"geometry", self._packList(self.winGeometry)) + cnfParse.set(cnfSec,"treecols", self._packList(self.treeColWidth)) + cnfParse.set(cnfSec,"mainpane", self._packList(self.mainPanePos)) + cnfParse.set(cnfSec,"docpane", self._packList(self.docPanePos)) ## Project cnfSec = "Project" - confParser.add_section(cnfSec) - confParser.set(cnfSec,"autosaveproject", str(self.autoSaveProj)) - confParser.set(cnfSec,"autosavedoc", str(self.autoSaveDoc)) + cnfParse.add_section(cnfSec) + cnfParse.set(cnfSec,"autosaveproject", str(self.autoSaveProj)) + cnfParse.set(cnfSec,"autosavedoc", str(self.autoSaveDoc)) ## Editor cnfSec = "Editor" - confParser.add_section(cnfSec) - confParser.set(cnfSec,"fixedwidth", str(self.textFixedW)) - confParser.set(cnfSec,"width", str(self.textWidth)) - confParser.set(cnfSec,"margins", self.packList(self.textMargin)) - confParser.set(cnfSec,"textsize", str(self.textSize)) - confParser.set(cnfSec,"justify", str(self.doJustify)) - confParser.set(cnfSec,"autoselect", str(self.autoSelect)) - confParser.set(cnfSec,"autoreplace",str(self.doReplace)) - confParser.set(cnfSec,"repsquotes", str(self.doReplaceSQuote)) - confParser.set(cnfSec,"repdquotes", str(self.doReplaceDQuote)) - confParser.set(cnfSec,"repdash", str(self.doReplaceDash)) - confParser.set(cnfSec,"repdots", str(self.doReplaceDots)) - confParser.set(cnfSec,"spellcheck", str(self.spellLanguage)) + cnfParse.add_section(cnfSec) + cnfParse.set(cnfSec,"fixedwidth", str(self.textFixedW)) + cnfParse.set(cnfSec,"width", str(self.textWidth)) + cnfParse.set(cnfSec,"margins", self._packList(self.textMargin)) + cnfParse.set(cnfSec,"textsize", str(self.textSize)) + cnfParse.set(cnfSec,"tabwidth", str(self.tabWidth)) + cnfParse.set(cnfSec,"justify", str(self.doJustify)) + cnfParse.set(cnfSec,"autoselect", str(self.autoSelect)) + cnfParse.set(cnfSec,"autoreplace",str(self.doReplace)) + cnfParse.set(cnfSec,"repsquotes", str(self.doReplaceSQuote)) + cnfParse.set(cnfSec,"repdquotes", str(self.doReplaceDQuote)) + cnfParse.set(cnfSec,"repdash", str(self.doReplaceDash)) + cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots)) + cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage)) ## Path cnfSec = "Path" - confParser.add_section(cnfSec) + cnfParse.add_section(cnfSec) for i in range(10): - confParser.set(cnfSec,"recent%d" % i, str(self.recentList[i])) + cnfParse.set(cnfSec,"recent%d" % i, str(self.recentList[i])) # Write config file try: - confParser.write(open(path.join(self.confPath,self.confFile),"w")) + cnfParse.write(open(path.join(self.confPath,self.confFile),"w")) self.confChanged = False except Exception as e: logger.error("Could not save config file") @@ -258,19 +229,6 @@ class Config: return True - def unpackList(self, inStr, listLen, listDefault, castTo=int): - inData = inStr.split(",") - outData = [] - for i in range(listLen): - try: - outData.append(castTo(inData[i])) - except: - outData.append(listDefault[i]) - return outData - - def packList(self, inData): - return ", ".join(str(inVal) for inVal in inData) - ## # Setters ## @@ -316,4 +274,36 @@ class Config: self.confChanged = True return True + ## + # Internal Functions + ## + + def _unpackList(self, inStr, listLen, listDefault, castTo=int): + inData = inStr.split(",") + outData = [] + for i in range(listLen): + try: + outData.append(castTo(inData[i])) + except: + outData.append(listDefault[i]) + return outData + + def _packList(self, inData): + return ", ".join(str(inVal) for inVal in inData) + + def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault=[]): + if cnfParse.has_section(cnfSec): + if cnfParse.has_option(cnfSec, cnfName): + if cnfType == self.CNF_STR: + return cnfParse.get(cnfSec, cnfName) + elif cnfType == self.CNF_INT: + return cnfParse.getint(cnfSec, cnfName) + elif cnfType == self.CNF_BOOL: + return cnfParse.getboolean(cnfSec, cnfName) + elif cnfType == self.CNF_LIST: + return self._unpackList( + cnfParse.get(cnfSec, cnfName), len(cnfDefault), cnfDefault + ) + return None + # End Class Config diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 7ba063d7..0f749e7c 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -104,8 +104,12 @@ class GuiDocEditor(QTextEdit): mTB = self.mainConf.textMargin[0] mLR = self.mainConf.textMargin[1] self.setViewportMargins(mLR,mTB,mLR,mTB) + theOpt = QTextOption() + if self.mainConf.tabWidth >= 0: + theOpt.setTabStopDistance(self.mainConf.tabWidth) if self.mainConf.doJustify: - self.theDoc.setDefaultTextOption(QTextOption(Qt.AlignJustify)) + theOpt.setAlignment(Qt.AlignJustify) + self.theDoc.setDefaultTextOption(theOpt) return True ## From 2bb10bd0d3a54c678b936965a72e13bd6ed097f6 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 22:13:20 +0200 Subject: [PATCH 02/17] Fix setting of defaults in config --- nw/config.py | 36 ++++++++++++++++++------------------ nw/gui/doceditor.py | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/nw/config.py b/nw/config.py index f6a170de..34fe87a4 100644 --- a/nw/config.py +++ b/nw/config.py @@ -132,7 +132,7 @@ class Config: ## Main cnfSec = "Main" - self.guiTheme = self._parseLine(cnfParse, cnfSec, "theme", self.CNF_STR) + self.guiTheme = self._parseLine(cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme) ## Sizes cnfSec = "Sizes" @@ -143,29 +143,29 @@ class Config: ## Project cnfSec = "Project" - self.autoSaveProj = self._parseLine(cnfParse, cnfSec, "autosaveproject", self.CNF_INT) - self.autoSaveDoc = self._parseLine(cnfParse, cnfSec, "autosavedoc", self.CNF_INT) + self.autoSaveProj = self._parseLine(cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj) + self.autoSaveDoc = self._parseLine(cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc) ## Editor cnfSec = "Editor" - self.textFixedW = self._parseLine(cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL) - self.textWidth = self._parseLine(cnfParse, cnfSec, "width", self.CNF_INT) + self.textFixedW = self._parseLine(cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL, self.textFixedW) + self.textWidth = self._parseLine(cnfParse, cnfSec, "width", self.CNF_INT, self.textWidth) self.textMargin = self._parseLine(cnfParse, cnfSec, "margins", self.CNF_LIST, self.textMargin) - self.textSize = self._parseLine(cnfParse, cnfSec, "textsize", self.CNF_INT) - self.tabWidth = self._parseLine(cnfParse, cnfSec, "tabwidth", self.CNF_INT) - self.doJustify = self._parseLine(cnfParse, cnfSec, "justify", self.CNF_BOOL) - self.autoSelect = self._parseLine(cnfParse, cnfSec, "autoselect", self.CNF_BOOL) - self.doReplace = self._parseLine(cnfParse, cnfSec, "autoreplace", self.CNF_BOOL) - self.doReplaceSQuote = self._parseLine(cnfParse, cnfSec, "repsquotes", self.CNF_BOOL) - self.doReplaceDQuote = self._parseLine(cnfParse, cnfSec, "repdquotes", self.CNF_BOOL) - self.doReplaceDash = self._parseLine(cnfParse, cnfSec, "repdash", self.CNF_BOOL) - self.doReplaceDots = self._parseLine(cnfParse, cnfSec, "repdots", self.CNF_BOOL) - self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR) + self.textSize = self._parseLine(cnfParse, cnfSec, "textsize", self.CNF_INT, self.textSize) + self.tabWidth = self._parseLine(cnfParse, cnfSec, "tabwidth", self.CNF_INT, self.tabWidth) + self.doJustify = self._parseLine(cnfParse, cnfSec, "justify", self.CNF_BOOL, self.doJustify) + self.autoSelect = self._parseLine(cnfParse, cnfSec, "autoselect", self.CNF_BOOL, self.autoSelect) + self.doReplace = self._parseLine(cnfParse, cnfSec, "autoreplace", self.CNF_BOOL, self.doReplace) + self.doReplaceSQuote = self._parseLine(cnfParse, cnfSec, "repsquotes", self.CNF_BOOL, self.doReplaceSQuote) + self.doReplaceDQuote = self._parseLine(cnfParse, cnfSec, "repdquotes", self.CNF_BOOL, self.doReplaceDQuote) + self.doReplaceDash = self._parseLine(cnfParse, cnfSec, "repdash", self.CNF_BOOL, self.doReplaceDash) + self.doReplaceDots = self._parseLine(cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots) + self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage) ## Path cnfSec = "Path" for i in range(10): - self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR) + self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i]) return True @@ -291,7 +291,7 @@ class Config: def _packList(self, inData): return ", ".join(str(inVal) for inVal in inData) - def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault=[]): + def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault): if cnfParse.has_section(cnfSec): if cnfParse.has_option(cnfSec, cnfName): if cnfType == self.CNF_STR: @@ -304,6 +304,6 @@ class Config: return self._unpackList( cnfParse.get(cnfSec, cnfName), len(cnfDefault), cnfDefault ) - return None + return cnfDefault # End Class Config diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 0f749e7c..59f6a7c7 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -105,7 +105,7 @@ class GuiDocEditor(QTextEdit): mLR = self.mainConf.textMargin[1] self.setViewportMargins(mLR,mTB,mLR,mTB) theOpt = QTextOption() - if self.mainConf.tabWidth >= 0: + if self.mainConf.tabWidth is not None: theOpt.setTabStopDistance(self.mainConf.tabWidth) if self.mainConf.doJustify: theOpt.setAlignment(Qt.AlignJustify) From 14e44a72bd3e20a9037cfd8d21ef9e95c2f7d0b7 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:15:48 +0200 Subject: [PATCH 03/17] Changed the layout of the statusbar and added project/session stats --- nw/gui/statusbar.py | 58 +++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 3e68690f..818f6bc4 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -13,6 +13,7 @@ import logging import nw +from PyQt5.QtGui import QIcon, QColor, QPixmap from PyQt5.QtWidgets import QStatusBar, QLabel, QFrame logger = logging.getLogger(__name__) @@ -26,28 +27,45 @@ class GuiMainStatus(QStatusBar): self.mainConf = nw.CONFIG + self.iconGrey = QPixmap(16,16) + self.iconGrey.fill(QColor(120,120,120)) + self.iconYellow = QPixmap(16,16) + self.iconYellow.fill(QColor(120,120, 40)) + self.iconGreen = QPixmap(16,16) + self.iconGreen.fill(QColor( 40,120, 0)) + + + self.boxStats = QLabel() + self.boxStats.setToolTip("Project Word Count | Session Word Count") + self.boxCounts = QLabel() - self.boxCounts.setToolTip("Character, Word, Paragraph Count") - self.boxCounts.setFrameStyle(QFrame.Panel | QFrame.Sunken); - self.addPermanentWidget(self.boxCounts) + self.boxCounts.setToolTip("Document Character | Word | Paragraph Count") - self.projChanged = QLabel("P") + self.projChanged = QLabel("") + self.projChanged.setFixedHeight(16) + self.projChanged.setFixedWidth(16) self.projChanged.setToolTip("Project Changes Saved") - self.projChanged.setFrameStyle(QFrame.Panel | QFrame.Sunken); - self.addPermanentWidget(self.projChanged) - self.docChanged = QLabel("D") + self.docChanged = QLabel("") + self.docChanged.setFixedHeight(16) + self.docChanged.setFixedWidth(16) self.docChanged.setToolTip("Document Changes Saved") - self.docChanged.setFrameStyle(QFrame.Panel | QFrame.Sunken); - self.addPermanentWidget(self.docChanged) self.boxDocHandle = QLabel() self.boxDocHandle.setFrameStyle(QFrame.Panel | QFrame.Sunken); + + # Add Them + self.addPermanentWidget(self.docChanged) + self.addPermanentWidget(self.boxCounts) + self.addPermanentWidget(QLabel(" ")) + self.addPermanentWidget(self.projChanged) + self.addPermanentWidget(self.boxStats) if self.mainConf.debugGUI: self.addPermanentWidget(self.boxDocHandle) logger.debug("GuiMainStatus initialisation complete") + self.setStats(0,0) self.setCounts(0,0,0) self.setDocHandleCount(None) self.setProjectStatus(None) @@ -63,28 +81,32 @@ class GuiMainStatus(QStatusBar): def setProjectStatus(self, isChanged): if isChanged is None: - self.projChanged.setStyleSheet("QLabel {background-color: rgba(120,120,120,1.0);}") + self.projChanged.setPixmap(self.iconGrey) elif isChanged == True: - self.projChanged.setStyleSheet("QLabel {background-color: rgba(120,120,40,1.0);}") + self.projChanged.setPixmap(self.iconYellow) elif isChanged == False: - self.projChanged.setStyleSheet("QLabel {background-color: rgba(40,120,0,1.0);}") + self.projChanged.setPixmap(self.iconGreen) else: - self.projChanged.setStyleSheet("QLabel {background-color: rgba(120,120,120,1.0);}") + self.projChanged.setPixmap(self.iconGrey) return def setDocumentStatus(self, isChanged): if isChanged is None: - self.docChanged.setStyleSheet("QLabel {background-color: rgba(120,120,120,1.0);}") + self.docChanged.setPixmap(self.iconGrey) elif isChanged == True: - self.docChanged.setStyleSheet("QLabel {background-color: rgba(120,120,40,1.0);}") + self.docChanged.setPixmap(self.iconYellow) elif isChanged == False: - self.docChanged.setStyleSheet("QLabel {background-color: rgba(40,120,0,1.0);}") + self.docChanged.setPixmap(self.iconGreen) else: - self.docChanged.setStyleSheet("QLabel {background-color: rgba(120,120,120,1.0);}") + self.docChanged.setPixmap(self.iconGrey) + return + + def setStats(self, pWC, sWC): + self.boxStats.setText("Project: {:n} : {:n}".format(pWC,sWC)) return def setCounts(self, cC, wC, pC): - self.boxCounts.setText("C: {:n}  W: {:n}  P: {:n}".format(cC,wC,pC)) + self.boxCounts.setText("Document: {:n} : {:n} : {:n}".format(cC,wC,pC)) return def setDocHandleCount(self, theHandle): From 9fc59262a995de9ca718f7ce2eb035ac7a3819ac Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:16:27 +0200 Subject: [PATCH 04/17] Added project word count --- nw/gui/doceditor.py | 1 + nw/gui/doctree.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 59f6a7c7..c708536e 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -380,6 +380,7 @@ class GuiDocEditor(QTextEdit): self.paraCount = self.wCounter.paraCount self.theParent.statusBar.setCounts(self.charCount,self.wordCount,self.paraCount) self.theParent.treeView.propagateCount(tHandle, self.wordCount) + self.theParent.treeView.projectWordCount() return diff --git a/nw/gui/doctree.py b/nw/gui/doctree.py index da074375..3e4176d0 100644 --- a/nw/gui/doctree.py +++ b/nw/gui/doctree.py @@ -50,7 +50,7 @@ class GuiDocTree(QTreeWidget): self.setExpandsOnDoubleClick(True) self.setIndentation(13) self.setColumnCount(4) - self.setHeaderLabels(["Name","Count","Flags","Handle"]) + self.setHeaderLabels(["Name","Words","Flags","Handle"]) if not self.debugGUI: self.hideColumn(self.C_HANDLE) @@ -286,6 +286,16 @@ class GuiDocTree(QTreeWidget): self.propagateCount(pHandle, pCount, nDepth+1) return + def projectWordCount(self): + nWords = 0 + for n in range(self.topLevelItemCount()): + tItem = self.topLevelItem(n) + if tItem == self.orphRoot: + continue + nWords += int(tItem.text(self.C_COUNT)) + self.theParent.statusBar.setStats(nWords,0) + return + def buildTree(self): self.clear() for nwItem in self.theProject.getProjectItems(): From 49ea321d92c56c4d9e77bd3daef636a71f9b2ca4 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:24:30 +0200 Subject: [PATCH 05/17] Session word count now works --- nw/gui/doctree.py | 4 +++- nw/project/project.py | 25 +++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/nw/gui/doctree.py b/nw/gui/doctree.py index 3e4176d0..280fa09a 100644 --- a/nw/gui/doctree.py +++ b/nw/gui/doctree.py @@ -293,7 +293,9 @@ class GuiDocTree(QTreeWidget): if tItem == self.orphRoot: continue nWords += int(tItem.text(self.C_COUNT)) - self.theParent.statusBar.setStats(nWords,0) + self.theProject.setProjectWordCount(nWords) + sWords = self.theProject.getSessionWordCount() + self.theParent.statusBar.setStats(nWords,sWords) return def buildTree(self): diff --git a/nw/project/project.py b/nw/project/project.py index 3c2f68fa..dd21c3e4 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -20,7 +20,7 @@ from datetime import datetime from time import time from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert -from nw.common import checkString, checkBool +from nw.common import checkString, checkBool, checkInt from nw.project.item import NWItem from nw.project.status import NWStatus @@ -59,6 +59,8 @@ class NWProject(): self.importItems = None self.lastEdited = None self.lastViewed = None + self.lastWCount = 0 + self.currWCount = 0 # Set Defaults self.clearProject() @@ -156,6 +158,10 @@ class NWProject(): self.importItems.addEntry("Minor", (200, 50, 0)) self.importItems.addEntry("Major", (200,150, 0)) self.importItems.addEntry("Main", ( 50,200, 0)) + self.lastEdited = None + self.lastViewed = None + self.lastWCount = 0 + self.currWCount = 0 return @@ -215,6 +221,8 @@ class NWProject(): self.lastEdited = checkString(xItem.text,None,True) if xItem.tag == "lastViewed": self.lastViewed = checkString(xItem.text,None,True) + if xItem.tag == "lastWordCount": + self.lastWCount = checkInt(xItem.text,0,False) if xItem.tag == "status": self.statusItems.unpackEntries(xItem) if xItem.tag == "importance": @@ -276,9 +284,10 @@ class NWProject(): # Save Project Settings xSettings = etree.SubElement(nwXML,"settings") - self._saveProjectValue(xSettings,"spellCheck",self.spellCheck) - self._saveProjectValue(xSettings,"lastEdited",self.lastEdited) - self._saveProjectValue(xSettings,"lastViewed",self.lastViewed) + self._saveProjectValue(xSettings,"spellCheck", self.spellCheck) + self._saveProjectValue(xSettings,"lastEdited", self.lastEdited) + self._saveProjectValue(xSettings,"lastViewed", self.lastViewed) + self._saveProjectValue(xSettings,"lastWordCount",self.currWCount) xStatus = etree.SubElement(xSettings,"status") self.statusItems.packEntries(xStatus) @@ -365,6 +374,14 @@ class NWProject(): self.setProjectChanged(True) return True + def setProjectWordCount(self, theCount): + self.currWCount = theCount + self.setProjectChanged(True) + return True + + def getSessionWordCount(self): + return self.currWCount - self.lastWCount + def setStatusColours(self, newCols): replaceMap = self.statusItems.setNewEntries(newCols) if self.projTree is not None: From 6530a162fbcd64c21afd8d8e7dae56e2c17bf592 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:34:54 +0200 Subject: [PATCH 06/17] Fixed tests --- nw/gui/winmain.py | 4 ++-- sample/sampleNovel/nwProject.nwx | 7 ++++--- tests/reference/gui/0_nwProject.nwx | 3 ++- tests/reference/gui/1_nwProject.nwx | 3 ++- tests/reference/gui/2_nwProject.nwx | 3 ++- tests/reference/gui/3_nwProject.nwx | 3 ++- tests/reference/novelwriter.conf | 3 ++- tests/reference/proj/1_nwProject.nwx | 3 ++- tests/reference/proj/2_nwProject.nwx | 3 ++- tests/test_gui.py | 6 +++--- 10 files changed, 23 insertions(+), 15 deletions(-) diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 6eabeeb9..90e81481 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -431,9 +431,9 @@ class GuiMain(QMainWindow): # Main Window Actions ## - def closeMain(self, isYes=False): + def closeMain(self): - if not isYes: + if self.mainConf.showGUI: msgBox = QMessageBox() msgRes = msgBox.question( self, "Exit", diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index 0a81731f..a6fdb23c 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -9,7 +9,8 @@ True 636b6aa9b697b - 636b6aa9b697b + None + 523 New Notes @@ -75,7 +76,7 @@ 577 104 5 - 603 + 655 New File diff --git a/tests/reference/gui/0_nwProject.nwx b/tests/reference/gui/0_nwProject.nwx index a42179e9..e1685c0f 100644 --- a/tests/reference/gui/0_nwProject.nwx +++ b/tests/reference/gui/0_nwProject.nwx @@ -1,5 +1,5 @@ - + @@ -8,6 +8,7 @@ False None None + 0 New Note diff --git a/tests/reference/gui/1_nwProject.nwx b/tests/reference/gui/1_nwProject.nwx index 971954a6..ae897362 100644 --- a/tests/reference/gui/1_nwProject.nwx +++ b/tests/reference/gui/1_nwProject.nwx @@ -1,5 +1,5 @@ - + @@ -8,6 +8,7 @@ True 31489056e0916 31489056e0916 + 69 New Note diff --git a/tests/reference/gui/2_nwProject.nwx b/tests/reference/gui/2_nwProject.nwx index 02f3c384..8d2ab8f5 100644 --- a/tests/reference/gui/2_nwProject.nwx +++ b/tests/reference/gui/2_nwProject.nwx @@ -1,5 +1,5 @@ - + Project Name Project Title @@ -10,6 +10,7 @@ False None None + 0 New Note diff --git a/tests/reference/gui/3_nwProject.nwx b/tests/reference/gui/3_nwProject.nwx index 510192ff..d499a736 100644 --- a/tests/reference/gui/3_nwProject.nwx +++ b/tests/reference/gui/3_nwProject.nwx @@ -1,5 +1,5 @@ - + @@ -8,6 +8,7 @@ False None None + 0 New Note diff --git a/tests/reference/novelwriter.conf b/tests/reference/novelwriter.conf index 112f9fd8..cb999b5a 100644 --- a/tests/reference/novelwriter.conf +++ b/tests/reference/novelwriter.conf @@ -1,5 +1,5 @@ [Main] -timestamp = 2019-05-18 15:06:44 +timestamp = 2019-05-25 23:33:23 theme = default [Sizes] @@ -17,6 +17,7 @@ fixedwidth = True width = 600 margins = 40, 40 textsize = 13 +tabwidth = 40 justify = True autoselect = True autoreplace = True diff --git a/tests/reference/proj/1_nwProject.nwx b/tests/reference/proj/1_nwProject.nwx index e9ff11f5..40ec363c 100644 --- a/tests/reference/proj/1_nwProject.nwx +++ b/tests/reference/proj/1_nwProject.nwx @@ -1,5 +1,5 @@ - + @@ -8,6 +8,7 @@ False None None + 0 New Note diff --git a/tests/reference/proj/2_nwProject.nwx b/tests/reference/proj/2_nwProject.nwx index 8c68bc42..314a4f75 100644 --- a/tests/reference/proj/2_nwProject.nwx +++ b/tests/reference/proj/2_nwProject.nwx @@ -1,5 +1,5 @@ - + @@ -8,6 +8,7 @@ False None None + 0 New Note diff --git a/tests/test_gui.py b/tests/test_gui.py index 94ce7eb4..261f8ebf 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -155,7 +155,7 @@ def testMainWindows(qtbot, nwTempGUI, nwRef): sceneFile = path.join(nwTempGUI,"data_3","1489056e0916_main.nwd") assert cmpFiles(sceneFile, path.join(nwRef,"gui","1_1489056e0916_main.nwd")) - nwGUI.closeMain(True) + nwGUI.closeMain() # qtbot.stopForInteraction() @pytest.mark.gui @@ -217,7 +217,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef): projFile = path.join(nwTempGUI,"nwProject.nwx") assert cmpFiles(projFile, path.join(nwRef,"gui","2_nwProject.nwx"), [2]) - nwGUI.closeMain(True) + nwGUI.closeMain() # qtbot.stopForInteraction() @pytest.mark.gui @@ -263,5 +263,5 @@ def testItemEditor(qtbot, nwTempGUI, nwRef): projFile = path.join(nwTempGUI,"nwProject.nwx") assert cmpFiles(projFile, path.join(nwRef,"gui","3_nwProject.nwx"), [2]) - nwGUI.closeMain(True) + nwGUI.closeMain() # qtbot.stopForInteraction() From 7450604c1b76720537bd3511f93b304c00111840 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:40:17 +0200 Subject: [PATCH 07/17] Fixed a little annoying bit about dialogs and tests --- nw/gui/mainmenu.py | 2 +- nw/gui/winmain.py | 4 ++-- tests/test_gui.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 7cb5274d..8c731c5b 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -157,7 +157,7 @@ class GuiMainMenu(QMenuBar): menuItem = QAction(QIcon.fromTheme("document-revert"), "Close Project", self) menuItem.setStatusTip("Close Project") menuItem.setShortcut("Ctrl+Shift+W") - menuItem.triggered.connect(lambda : self.theParent.closeProject(False)) + menuItem.triggered.connect(self.theParent.closeProject) self.projMenu.addAction(menuItem) # Project > Recent Projects diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 90e81481..2203bf89 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -214,12 +214,12 @@ class GuiMain(QMainWindow): return True - def closeProject(self, isYes=False): + def closeProject(self): if not self.hasProject: return True - if not isYes: + if self.mainConf.showGUI: msgBox = QMessageBox() msgRes = msgBox.question( self, "Close Project", diff --git a/tests/test_gui.py b/tests/test_gui.py index 261f8ebf..7f141b47 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -27,7 +27,7 @@ def testMainWindows(qtbot, nwTempGUI, nwRef): nwGUI.theProject.handleSeed = 42 assert nwGUI.newProject(nwTempGUI, True) assert nwGUI.saveProject() - assert nwGUI.closeProject(True) + assert nwGUI.closeProject() assert len(nwGUI.theProject.projTree) == 0 assert len(nwGUI.theProject.treeOrder) == 0 From 1ded8425d01b6df84d5f5d7801a434cada14d4d8 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:43:09 +0200 Subject: [PATCH 08/17] Closing project should clear the status bar. --- nw/gui/statusbar.py | 12 ++++++++---- nw/gui/winmain.py | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 818f6bc4..66a027d3 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -63,17 +63,21 @@ class GuiMainStatus(QStatusBar): if self.mainConf.debugGUI: self.addPermanentWidget(self.boxDocHandle) + self.setSizeGripEnabled(True) + logger.debug("GuiMainStatus initialisation complete") + self.clearStatus() + + return + + def clearStatus(self): self.setStats(0,0) self.setCounts(0,0,0) self.setDocHandleCount(None) self.setProjectStatus(None) self.setDocumentStatus(None) - - self.setSizeGripEnabled(True) - - return + return True def setStatus(self, theMessage, timeOut=10.0): self.showMessage(theMessage, int(timeOut*1000)) diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 2203bf89..616f415c 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -176,6 +176,7 @@ class GuiMain(QMainWindow): self.treeView.clearTree() self.docEditor.clearEditor() self.closeDocViewer() + self.statusBar.clearStatus() return True ## From cfa9c8c9c85d93727637925a7241da49434cd781 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 25 May 2019 23:47:33 +0200 Subject: [PATCH 09/17] Try using d format instead in status bar --- nw/gui/statusbar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 66a027d3..d9feb745 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -106,11 +106,11 @@ class GuiMainStatus(QStatusBar): return def setStats(self, pWC, sWC): - self.boxStats.setText("Project: {:n} : {:n}".format(pWC,sWC)) + self.boxStats.setText("Project: {:d} : {:d}".format(pWC,sWC)) return def setCounts(self, cC, wC, pC): - self.boxCounts.setText("Document: {:n} : {:n} : {:n}".format(cC,wC,pC)) + self.boxCounts.setText("Document: {:d} : {:d} : {:d}".format(cC,wC,pC)) return def setDocHandleCount(self, theHandle): From 8e9f6ce4c7f13715e140ea502b34a2349c4e663e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 00:37:23 +0200 Subject: [PATCH 10/17] Don't spellcheck command lines, and uppercase words --- nw/gui/dochighlight.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index 4db6267a..697b2dea 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -194,13 +194,14 @@ class GuiDocHighlighter(QSyntaxHighlighter): self.setCurrentBlockState(0) - if self.theDict is None or not self.spellCheck: + if self.theDict is None or not self.spellCheck or theText.startswith("@"): return rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0) while rxSpell.hasNext(): rxMatch = rxSpell.next() if not self.theDict.check(rxMatch.captured(0)): + if rxMatch.captured(0) == rxMatch.captured(0).upper(): continue xPos = rxMatch.capturedStart(0) xLen = rxMatch.capturedLength(0) spFmt = self.format(xPos) From 601bd3dca636c4346bcfc18b6e8893a3e45608bd Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 14:06:48 +0200 Subject: [PATCH 11/17] Filenames are set nwFiles now, and deleted some unused file in nw/gui --- nw/config.py | 2 ++ nw/constants.py | 11 +++++-- nw/gui/aboutview.py | 77 ------------------------------------------- nw/gui/winmain.py | 9 +++-- nw/project/project.py | 9 ++--- 5 files changed, 22 insertions(+), 86 deletions(-) delete mode 100644 nw/gui/aboutview.py diff --git a/nw/config.py b/nw/config.py index 34fe87a4..1627270f 100644 --- a/nw/config.py +++ b/nw/config.py @@ -43,6 +43,7 @@ class Config: self.confFile = None self.homePath = None self.appPath = None + self.appRoot = None self.guiPath = None self.themePath = None @@ -101,6 +102,7 @@ class Config: self.confFile = self.appHandle+".conf" self.homePath = path.expanduser("~") self.appPath = path.dirname(__file__) + self.appRoot = path.join(self.appPath,path.pardir) self.guiPath = path.join(self.appPath,"gui") self.themePath = path.join(self.appPath,"themes") diff --git a/nw/constants.py b/nw/constants.py index 7dc39f05..f8a37ae7 100644 --- a/nw/constants.py +++ b/nw/constants.py @@ -12,6 +12,14 @@ from nw.enum import nwItemClass, nwItemLayout +class nwFiles(): + + APP_ICON = "novelWriter.svg" + PROJ_FILE = "nwProject.nwx" + PROJ_DICT = "wordlist.txt" + +# END Class nwFiles + class nwLabels(): CLASS_NAME = { @@ -59,5 +67,4 @@ class nwLabels(): nwItemLayout.NOTE : "Nt", } - -# END nwLabels +# END Class nwLabels diff --git a/nw/gui/aboutview.py b/nw/gui/aboutview.py deleted file mode 100644 index fa3e747c..00000000 --- a/nw/gui/aboutview.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -"""novelWriter GUI About View - - novelWriter – GUI About View -============================== - Class holding the tab viewing the about information - - File History: - Created: 2018-10-02 [0.0.1] - -""" - -import logging -import nw - -from os import path -from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QLabel -from PyQt5.QtSvg import QSvgWidget -from PyQt5.QtCore import Qt, QSize -from PyQt5.QtGui import QFont - -logger = logging.getLogger(__name__) - -class GuiAboutView(QWidget): - - def __init__(self): - QWidget.__init__(self) - - logger.debug("Initialising AboutView ...") - self.mainConf = nw.CONFIG - self.innerBox = QHBoxLayout() - self.outerBox = QVBoxLayout() - - logoPath = path.abspath(path.join(self.mainConf.appPath,"..","novelWriter.svg")) - logger.verbose("Loading image: %s" % logoPath) - nwLogo = QSvgWidget(logoPath) - nwLogo.setFixedSize(QSize(300,300)) - - nwName = QLabel() - nwName.setText(nw.__package__) - nwName.setAlignment(Qt.AlignCenter) - fnName = QFont() - fnName.setPointSize(22) - fnName.setBold(True) - nwName.setFont(fnName) - - nwVersion = QLabel() - nwVersion.setText("Version %s" % nw.__version__) - nwVersion.setAlignment(Qt.AlignCenter) - - nwCredits = QLabel() - nwCredits.setText("Created By: %s" % ",".join(nw.__credits__)) - nwCredits.setAlignment(Qt.AlignCenter) - - nwWebsite = QLabel() - nwWebsite.setText("%s" % (nw.__website__,nw.__website__)) - nwWebsite.setOpenExternalLinks(True) - nwWebsite.setAlignment(Qt.AlignCenter) - - self.outerBox.addStretch(1) - self.innerBox.addStretch(1) - self.innerBox.addWidget(nwLogo) - self.innerBox.addStretch(1) - self.outerBox.addLayout(self.innerBox) - self.outerBox.addWidget(nwName) - self.outerBox.addWidget(nwVersion) - self.outerBox.addWidget(nwCredits) - self.outerBox.addWidget(nwWebsite) - self.outerBox.addStretch(1) - - self.setLayout(self.outerBox) - - logger.debug("AboutView initialisation complete") - - return - -# END Class GuiAboutView diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 616f415c..3d1b4f26 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -36,6 +36,7 @@ from nw.project.item import NWItem from nw.convert.tokenizer import Tokenizer from nw.convert.tohtml import ToHtml from nw.enum import nwItemType, nwAlert +from nw.constants import nwFiles logger = logging.getLogger(__name__) @@ -53,7 +54,7 @@ class GuiMain(QMainWindow): self.resize(*self.mainConf.winGeometry) self._setWindowTitle() - self.setWindowIcon(QIcon(path.join(self.mainConf.appPath,"..","novelWriter.svg"))) + self.setWindowIcon(QIcon(path.join(self.mainConf.appRoot, nwFiles.APP_ICON))) self.theTheme.loadTheme() # Main GUI Elements @@ -255,7 +256,7 @@ class GuiMain(QMainWindow): self.theProject.openProject(projFile) self._setWindowTitle(self.theProject.projName) self.rebuildTree() - self.docEditor.setPwl(path.join(self.theProject.projMeta,"wordlist.txt")) + self.docEditor.setPwl(path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)) self.docEditor.setSpellCheck(self.theProject.spellCheck) self.mainMenu.updateMenu() @@ -394,7 +395,9 @@ class GuiMain(QMainWindow): dlgOpt = QFileDialog.Options() dlgOpt |= QFileDialog.DontUseNativeDialog projFile, _ = QFileDialog.getOpenFileName( - self,"Open novelWriter Project","","novelWriter Project File (nwProject.nwx);;All Files (*)",options=dlgOpt + self, "Open novelWriter Project", "", + "novelWriter Project File (%s);;All Files (*)" % nwFiles.PROJ_FILE, + options=dlgOpt ) if projFile: return projFile diff --git a/nw/project/project.py b/nw/project/project.py index dd21c3e4..d5c700e2 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -19,10 +19,11 @@ from hashlib import sha256 from datetime import datetime from time import time -from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert -from nw.common import checkString, checkBool, checkInt from nw.project.item import NWItem from nw.project.status import NWStatus +from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert +from nw.common import checkString, checkBool, checkInt +from nw.constants import nwFiles logger = logging.getLogger(__name__) @@ -143,7 +144,7 @@ class NWProject(): self.projPath = None self.projMeta = None self.projCache = None - self.projFile = "nwProject.nwx" + self.projFile = nwFiles.PROJ_FILE self.projName = "" self.bookTitle = "" self.bookAuthors = [] @@ -168,7 +169,7 @@ class NWProject(): def openProject(self, fileName): if not path.isfile(fileName): - fileName = path.join(fileName, "nwProject.nwx") + fileName = path.join(fileName, nwFiles.PROJ_FILE) if not path.isfile(fileName): self.makeAlert("File not found: %s" % fileName, nwAlert.ERROR) return False From 367d2c5014d83da3e9c79580643e687104ccb9b4 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 14:39:13 +0200 Subject: [PATCH 12/17] Added a log file that appends session stats. --- nw/constants.py | 1 + nw/gui/winmain.py | 3 ++- nw/project/project.py | 26 +++++++++++++++++++ .../sampleNovel/data_6/36b6aa9b697b_main.nwd | 6 ++++- sample/sampleNovel/meta/sessionInfo.log | 3 +++ sample/sampleNovel/nwProject.nwx | 10 +++---- 6 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 sample/sampleNovel/meta/sessionInfo.log diff --git a/nw/constants.py b/nw/constants.py index f8a37ae7..478a7f5f 100644 --- a/nw/constants.py +++ b/nw/constants.py @@ -17,6 +17,7 @@ class nwFiles(): APP_ICON = "novelWriter.svg" PROJ_FILE = "nwProject.nwx" PROJ_DICT = "wordlist.txt" + SESS_INFO = "sessionInfo.log" # END Class nwFiles diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 3d1b4f26..021024c7 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -237,7 +237,7 @@ class GuiMain(QMainWindow): saveOK = True if saveOK: - self.theProject.clearProject() + self.theProject.closeProject() self.clearGUI() self.hasProject = False @@ -451,6 +451,7 @@ class GuiMain(QMainWindow): self.saveDocument() if self._takeProjectAction(): self.saveProject() + self.theProject.closeProject() self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setTreeColWidths(self.treeView.getColumnSizes()) self.mainConf.setMainPanePos(self.splitMain.sizes()) diff --git a/nw/project/project.py b/nw/project/project.py index d5c700e2..6ddb992d 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -35,6 +35,7 @@ class NWProject(): self.theParent = theParent self.mainConf = self.theParent.mainConf self.projChanged = None + self.projOpened = None # Debug self.handleSeed = None @@ -135,6 +136,7 @@ class NWProject(): def clearProject(self): self.projChanged = None + self.projOpened = None # Project Settings self.projTree = {} @@ -251,6 +253,7 @@ class NWProject(): self._scanProjectFolder() self.setProjectChanged(False) + self.projOpened = time() return True @@ -321,6 +324,11 @@ class NWProject(): return True + def closeProject(self): + self._appendSessionStats() + self.clearProject() + return True + ## # Set Functions ## @@ -594,6 +602,24 @@ class NWProject(): return + def _appendSessionStats(self): + + if self.projMeta is None: + return False + + with open(path.join(self.projMeta, nwFiles.SESS_INFO), mode="a+") as outFile: + print(( + "Start: {opened:s} " + "End: {closed:s} " + "Words: {words:8d}" + ).format( + opened = datetime.fromtimestamp(self.projOpened).strftime("%Y-%m-%d %H:%M:%S"), + closed = datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + words = self.getSessionWordCount(), + ), file=outFile) + + return True + def _makeHandle(self, addSeed=""): if self.handleSeed is None: newSeed = str(time()) + addSeed diff --git a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd index 72d112c6..52aacd59 100644 --- a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd +++ b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd @@ -13,8 +13,12 @@ Some text here would look good as well, and maybe some "dialogue"? So, this is some __text__ that we’ve been adding to this document. It is utterly meaningless text, _but_ since this is just dummy text, that doesn’t really matter. The text is perfectly happy to live in this document regardless. -This paragraph is also meaningless. At least a bit. It’s also very short. +This paragraph is also meaningless. At least a bit. It’s also very short. But we could make it less meaningless if we wanted to … This one is a bit longer. “It also has some dialogue in it” she said, before she moved on to check if the spellchecker worked. It did. “Cool,” she concluded. +@ToDo: Stuff that will be done at some point + + + diff --git a/sample/sampleNovel/meta/sessionInfo.log b/sample/sampleNovel/meta/sessionInfo.log new file mode 100644 index 00000000..c4442696 --- /dev/null +++ b/sample/sampleNovel/meta/sessionInfo.log @@ -0,0 +1,3 @@ +Start: 2019-05-26 14:28:28 End: 2019-05-26 14:28:38 Words: -23 +Start: 2019-05-26 14:31:12 End: 2019-05-26 14:31:34 Words: 12 +Start: 2019-05-26 14:33:27 End: 2019-05-26 14:33:31 Words: 0 diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index a6fdb23c..a4d878b0 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -10,7 +10,7 @@ True 636b6aa9b697b None - 523 + 535 New Notes @@ -73,10 +73,10 @@ Notes False SCENE - 577 - 104 + 633 + 116 5 - 655 + 551 New File From 76cb03fad884c1bc18f422ecb2f31f06344d79fc Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 15:34:34 +0200 Subject: [PATCH 13/17] Some reorganisation of how stuff is opened and closed. --- nw/gui/mainmenu.py | 9 ++- nw/gui/winmain.py | 63 +++++++++---------- nw/project/project.py | 4 +- .../sampleNovel/data_6/36b6aa9b697b_main.nwd | 2 +- sample/sampleNovel/meta/sessionInfo.log | 7 +++ sample/sampleNovel/nwProject.nwx | 12 ++-- 6 files changed, 52 insertions(+), 45 deletions(-) diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 8c731c5b..04233cca 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -157,7 +157,7 @@ class GuiMainMenu(QMenuBar): menuItem = QAction(QIcon.fromTheme("document-revert"), "Close Project", self) menuItem.setStatusTip("Close Project") menuItem.setShortcut("Ctrl+Shift+W") - menuItem.triggered.connect(self.theParent.closeProject) + menuItem.triggered.connect(lambda : self.theParent.closeProject(False)) self.projMenu.addAction(menuItem) # Project > Recent Projects @@ -253,6 +253,13 @@ class GuiMainMenu(QMenuBar): menuItem.triggered.connect(self.theParent.saveDocument) self.docuMenu.addAction(menuItem) + # Document > Close + menuItem = QAction(QIcon.fromTheme("document-revert"), "Close Document", self) + menuItem.setStatusTip("Close Current Document") + menuItem.setShortcut("Ctrl+W") + menuItem.triggered.connect(self.theParent.closeDocument) + self.docuMenu.addAction(menuItem) + # Document > Separator self.docuMenu.addSeparator() diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 021024c7..585b9853 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -216,16 +216,18 @@ class GuiMain(QMainWindow): return True - def closeProject(self): - + def closeProject(self, isYes=False): + """Closes the project if one is open. + isYes is passed on from the close application event so the user doesn't get prompted twice. + """ if not self.hasProject: + # There is no project loaded, everything OK return True - if self.mainConf.showGUI: + if self.mainConf.showGUI and not isYes: msgBox = QMessageBox() msgRes = msgBox.question( - self, "Close Project", - "Close current project?
Unsaved changes will be saved." + self, "Close Project", "Save changes and close current project?" ) if msgRes != QMessageBox.Yes: return False @@ -244,15 +246,19 @@ class GuiMain(QMainWindow): return saveOK def openProject(self, projFile=None): - + """Open a project. + projFile is passed from the open recent projects menu, so can be set. If not, we pop the dialog. + """ if projFile is None: projFile = self.openProjectDialog() if projFile is None: return False + # Make sure any open project is cleared out first before we load another one if not self.closeProject(): return False + # Do the stuff self.theProject.openProject(projFile) self._setWindowTitle(self.theProject.projName) self.rebuildTree() @@ -260,6 +266,7 @@ class GuiMain(QMainWindow): self.docEditor.setSpellCheck(self.theProject.spellCheck) self.mainMenu.updateMenu() + # Restore previously open documents, if any if self.theProject.lastEdited is not None: self.openDocument(self.theProject.lastEdited) if self.theProject.lastViewed is not None: @@ -270,7 +277,9 @@ class GuiMain(QMainWindow): return True def saveProject(self): - + """Save the current project. + """ + # If the project is new, it may not have a path, so we need one if self.theProject.projPath is None: projPath = self.saveProjectDialog() self.theProject.setProjectPath(projPath) @@ -288,13 +297,15 @@ class GuiMain(QMainWindow): ## def closeDocument(self): - self.saveDocument() + if self.docEditor.docChanged: + self.saveDocument() self.theDocument.clearDocument() + self.docEditor.clearEditor() + self.theProject.setLastEdited(None) return True def openDocument(self, tHandle): - if self.docEditor.docChanged: - self.saveDocument() + self.closeDocument() self.docEditor.setText(self.theDocument.openDocument(tHandle)) self.docEditor.setReadOnly(False) self.docEditor.setCursorPosition(self.theDocument.theItem.cursorPos) @@ -305,13 +316,13 @@ class GuiMain(QMainWindow): def saveDocument(self): if self.theDocument.theItem is not None: - docHtml = self.docEditor.getText() + docText = self.docEditor.getText() cursPos = self.docEditor.getCursorPosition() self.theDocument.theItem.setCharCount(self.docEditor.charCount) self.theDocument.theItem.setWordCount(self.docEditor.wordCount) self.theDocument.theItem.setParaCount(self.docEditor.paraCount) self.theDocument.theItem.setCursorPos(cursPos) - self.theDocument.saveDocument(docHtml) + self.theDocument.saveDocument(docText) self.docEditor.setDocumentChanged(False) return True @@ -440,18 +451,14 @@ class GuiMain(QMainWindow): if self.mainConf.showGUI: msgBox = QMessageBox() msgRes = msgBox.question( - self, "Exit", - "Do you want to exit %s?" % nw.__package__ + self, "Exit", "Do you want to save changes and exit?" ) if msgRes != QMessageBox.Yes: return False logger.info("Exiting %s" % nw.__package__) - if self._takeDocumentAction(): - self.saveDocument() - if self._takeProjectAction(): - self.saveProject() - self.theProject.closeProject() + self.closeProject(True) + self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setTreeColWidths(self.treeView.getColumnSizes()) self.mainConf.setMainPanePos(self.splitMain.sizes()) @@ -493,31 +500,17 @@ class GuiMain(QMainWindow): return True def _autoSaveProject(self): - if self._takeProjectAction(): + if self.hasProject and self.theProject.projChanged and self.theProject.projPath is not None: logger.debug("Autosaving project") self.saveProject() return def _autoSaveDocument(self): - if self._takeDocumentAction(): + if self.hasProject and self.docEditor.docChanged and self.theDocument.theItem is not None: logger.debug("Autosaving document") self.saveDocument() return - def _takeProjectAction(self): - if self.theProject.projPath is None: - return False - if not self.theProject.projChanged: - return False - return True - - def _takeDocumentAction(self): - if self.theDocument.theItem is None: - return False - if not self.docEditor.docChanged: - return False - return True - def _makeStatusIcons(self): self.statusIcons = {} for sLabel, sCol, _ in self.theProject.statusItems: diff --git a/nw/project/project.py b/nw/project/project.py index 6ddb992d..dce45ef3 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -123,14 +123,14 @@ class NWProject(): ## def newProject(self): - hNovel = self.newRoot("Novel", nwItemClass.NOVEL) hChars = self.newRoot("Characters", nwItemClass.CHARACTER) hWorld = self.newRoot("Plot", nwItemClass.PLOT) hWorld = self.newRoot("World", nwItemClass.WORLD) hChapt = self.newFolder("New Chapter", nwItemClass.NOVEL, hNovel) hScene = self.newFile("New Scene", nwItemClass.NOVEL, hChapt) - + self.projOpened = time() + self.setProjectChanged(True) return True def clearProject(self): diff --git a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd index 52aacd59..99650f20 100644 --- a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd +++ b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd @@ -13,7 +13,7 @@ Some text here would look good as well, and maybe some "dialogue"? So, this is some __text__ that we’ve been adding to this document. It is utterly meaningless text, _but_ since this is just dummy text, that doesn’t really matter. The text is perfectly happy to live in this document regardless. -This paragraph is also meaningless. At least a bit. It’s also very short. But we could make it less meaningless if we wanted to … +This paragraph is also meaningless. At least a bit. It’s also very short. But we could make it less meaningless if we wanted to … but we won’t. This one is a bit longer. “It also has some dialogue in it” she said, before she moved on to check if the spellchecker worked. It did. “Cool,” she concluded. diff --git a/sample/sampleNovel/meta/sessionInfo.log b/sample/sampleNovel/meta/sessionInfo.log index c4442696..035ac8c3 100644 --- a/sample/sampleNovel/meta/sessionInfo.log +++ b/sample/sampleNovel/meta/sessionInfo.log @@ -1,3 +1,10 @@ Start: 2019-05-26 14:28:28 End: 2019-05-26 14:28:38 Words: -23 Start: 2019-05-26 14:31:12 End: 2019-05-26 14:31:34 Words: 12 Start: 2019-05-26 14:33:27 End: 2019-05-26 14:33:31 Words: 0 +Start: 2019-05-26 14:40:29 End: 2019-05-26 14:40:32 Words: 0 +Start: 2019-05-26 14:40:43 End: 2019-05-26 14:40:46 Words: 0 +Start: 2019-05-26 14:48:40 End: 2019-05-26 14:48:50 Words: 0 +Start: 2019-05-26 14:50:18 End: 2019-05-26 14:50:21 Words: 0 +Start: 2019-05-26 14:50:29 End: 2019-05-26 14:50:47 Words: 3 +Start: 2019-05-26 15:10:52 End: 2019-05-26 15:11:09 Words: 0 +Start: 2019-05-26 15:28:56 End: 2019-05-26 15:29:08 Words: 0 diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index a4d878b0..5b583ada 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -8,9 +8,9 @@ True - 636b6aa9b697b + None None - 535 + 538 New Notes @@ -73,10 +73,10 @@ Notes False SCENE - 633 - 116 + 647 + 119 5 - 551 + 565
New File From c485851df3534b63faede2ef3fce0a9c751ac706 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 15:55:10 +0200 Subject: [PATCH 14/17] Added closing of document feature --- nw/gui/mainmenu.py | 2 +- nw/gui/winmain.py | 13 ++++++++++--- sample/sampleNovel/data_6/36b6aa9b697b_main.nwd | 2 +- sample/sampleNovel/meta/sessionInfo.log | 5 +++++ sample/sampleNovel/nwProject.nwx | 12 ++++++------ 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 04233cca..981933ee 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -257,7 +257,7 @@ class GuiMainMenu(QMenuBar): menuItem = QAction(QIcon.fromTheme("document-revert"), "Close Document", self) menuItem.setStatusTip("Close Current Document") menuItem.setShortcut("Ctrl+W") - menuItem.triggered.connect(self.theParent.closeDocument) + menuItem.triggered.connect(self.theParent.closeDocEditor) self.docuMenu.addAction(menuItem) # Document > Separator diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 585b9853..07ea1b84 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -258,8 +258,11 @@ class GuiMain(QMainWindow): if not self.closeProject(): return False - # Do the stuff - self.theProject.openProject(projFile) + # Try to open the project + if not self.theProject.openProject(projFile): + return False + + # Update GUI self._setWindowTitle(self.theProject.projName) self.rebuildTree() self.docEditor.setPwl(path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)) @@ -301,7 +304,6 @@ class GuiMain(QMainWindow): self.saveDocument() self.theDocument.clearDocument() self.docEditor.clearEditor() - self.theProject.setLastEdited(None) return True def openDocument(self, tHandle): @@ -478,6 +480,11 @@ class GuiMain(QMainWindow): self.docViewer.setFocus() return + def closeDocEditor(self): + self.closeDocument() + self.theProject.setLastEdited(None) + return + def closeDocViewer(self): self.docViewer.clearViewer() self.theProject.setLastViewed(None) diff --git a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd index 99650f20..eb71a80b 100644 --- a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd +++ b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd @@ -13,7 +13,7 @@ Some text here would look good as well, and maybe some "dialogue"? So, this is some __text__ that we’ve been adding to this document. It is utterly meaningless text, _but_ since this is just dummy text, that doesn’t really matter. The text is perfectly happy to live in this document regardless. -This paragraph is also meaningless. At least a bit. It’s also very short. But we could make it less meaningless if we wanted to … but we won’t. +This paragraph is also meaningless. At least a bit. It’s also very short. But we could make it less meaningless if we wanted to … but we won’t, will we. This one is a bit longer. “It also has some dialogue in it” she said, before she moved on to check if the spellchecker worked. It did. “Cool,” she concluded. diff --git a/sample/sampleNovel/meta/sessionInfo.log b/sample/sampleNovel/meta/sessionInfo.log index 035ac8c3..79d3266e 100644 --- a/sample/sampleNovel/meta/sessionInfo.log +++ b/sample/sampleNovel/meta/sessionInfo.log @@ -8,3 +8,8 @@ Start: 2019-05-26 14:50:18 End: 2019-05-26 14:50:21 Words: 0 Start: 2019-05-26 14:50:29 End: 2019-05-26 14:50:47 Words: 3 Start: 2019-05-26 15:10:52 End: 2019-05-26 15:11:09 Words: 0 Start: 2019-05-26 15:28:56 End: 2019-05-26 15:29:08 Words: 0 +Start: 2019-05-26 15:37:34 End: 2019-05-26 15:37:43 Words: -538 +Start: 2019-05-26 15:37:47 End: 2019-05-26 15:38:07 Words: 538 +Start: 2019-05-26 15:38:11 End: 2019-05-26 15:43:07 Words: 2 +Start: 2019-05-26 15:49:18 End: 2019-05-26 15:49:47 Words: 0 +Start: 2019-05-26 15:52:39 End: 2019-05-26 15:52:56 Words: 0 diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index 5b583ada..448da65a 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -8,9 +8,9 @@ True - None + 636b6aa9b697b None - 538 + 540 New Notes @@ -73,10 +73,10 @@ Notes False SCENE - 647 - 119 + 656 + 121 5 - 565 + 573 New File From 61cbfe08c5eaf1dc51049470dc99e5401ba4c661 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 15:55:53 +0200 Subject: [PATCH 15/17] Moved makeAlert to dialog section in main GUI source --- nw/gui/winmain.py | 66 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 07ea1b84..36a9e0f1 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -140,39 +140,6 @@ class GuiMain(QMainWindow): return - def makeAlert(self, theMessage, theLevel=nwAlert.INFO): - """Alert both the user and the logger at the same time. Message can be either a string or an - array of strings. Severity level is 0 = info, 1 = warning, and 2 = error. - """ - - if isinstance(theMessage, list): - popMsg = "
".join(theMessage) - logMsg = theMessage - else: - popMsg = theMessage - logMsg = [theMessage] - - msgBox = QMessageBox() - if theLevel == nwAlert.INFO: - for msgLine in logMsg: - logger.info(msgLine) - msgBox.information(self, "Information", popMsg) - elif theLevel == nwAlert.WARN: - for msgLine in logMsg: - logger.warning(msgLine) - msgBox.warning(self, "Warning", popMsg) - elif theLevel == nwAlert.ERROR: - for msgLine in logMsg: - logger.error(msgLine) - msgBox.critical(self, "Error", popMsg) - elif theLevel == nwAlert.BUG: - for msgLine in logMsg: - logger.error(msgLine) - popMsg += "
This is a bug!" - msgBox.critical(self, "Internal Error", popMsg) - - return - def clearGUI(self): self.treeView.clearTree() self.docEditor.clearEditor() @@ -444,6 +411,39 @@ class GuiMain(QMainWindow): self._setWindowTitle(self.theProject.projName) return True + def makeAlert(self, theMessage, theLevel=nwAlert.INFO): + """Alert both the user and the logger at the same time. Message can be either a string or an + array of strings. Severity level is 0 = info, 1 = warning, and 2 = error. + """ + + if isinstance(theMessage, list): + popMsg = "
".join(theMessage) + logMsg = theMessage + else: + popMsg = theMessage + logMsg = [theMessage] + + msgBox = QMessageBox() + if theLevel == nwAlert.INFO: + for msgLine in logMsg: + logger.info(msgLine) + msgBox.information(self, "Information", popMsg) + elif theLevel == nwAlert.WARN: + for msgLine in logMsg: + logger.warning(msgLine) + msgBox.warning(self, "Warning", popMsg) + elif theLevel == nwAlert.ERROR: + for msgLine in logMsg: + logger.error(msgLine) + msgBox.critical(self, "Error", popMsg) + elif theLevel == nwAlert.BUG: + for msgLine in logMsg: + logger.error(msgLine) + popMsg += "
This is a bug!" + msgBox.critical(self, "Internal Error", popMsg) + + return + ## # Main Window Actions ## From 6c6d1ac325426e3a47f00b7d38e5397fb26ec83c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 17:02:32 +0200 Subject: [PATCH 16/17] Added a session timer to the status bar. --- nw/gui/statusbar.py | 41 ++++++++++++++++++++++++++++++++++++++++- nw/gui/winmain.py | 4 +++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index d9feb745..4cc437cc 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -13,6 +13,8 @@ import logging import nw +from time import time +from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QIcon, QColor, QPixmap from PyQt5.QtWidgets import QStatusBar, QLabel, QFrame @@ -26,6 +28,7 @@ class GuiMainStatus(QStatusBar): logger.debug("Initialising GuiMainStatus ...") self.mainConf = nw.CONFIG + self.refTime = None self.iconGrey = QPixmap(16,16) self.iconGrey.fill(QColor(120,120,120)) @@ -34,10 +37,14 @@ class GuiMainStatus(QStatusBar): self.iconGreen = QPixmap(16,16) self.iconGreen.fill(QColor( 40,120, 0)) - self.boxStats = QLabel() self.boxStats.setToolTip("Project Word Count | Session Word Count") + self.boxTime = QLabel("") + self.boxTime.setToolTip("Session Time") + self.boxTime.setAlignment(Qt.AlignRight) + self.boxTime.setMinimumWidth(80) + self.boxCounts = QLabel() self.boxCounts.setToolTip("Document Character | Word | Paragraph Count") @@ -60,11 +67,17 @@ class GuiMainStatus(QStatusBar): self.addPermanentWidget(QLabel(" ")) self.addPermanentWidget(self.projChanged) self.addPermanentWidget(self.boxStats) + self.addPermanentWidget(self.boxTime) if self.mainConf.debugGUI: self.addPermanentWidget(self.boxDocHandle) self.setSizeGripEnabled(True) + self.sessionTimer = QTimer() + self.sessionTimer.setInterval(1000) + self.sessionTimer.timeout.connect(self._updateTime) + self.sessionTimer.start() + logger.debug("GuiMainStatus initialisation complete") self.clearStatus() @@ -72,13 +85,19 @@ class GuiMainStatus(QStatusBar): return def clearStatus(self): + self.setRefTime(None) self.setStats(0,0) self.setCounts(0,0,0) self.setDocHandleCount(None) self.setProjectStatus(None) self.setDocumentStatus(None) + self._updateTime() return True + def setRefTime(self, theTime): + self.refTime = theTime + return + def setStatus(self, theMessage, timeOut=10.0): self.showMessage(theMessage, int(timeOut*1000)) return @@ -120,4 +139,24 @@ class GuiMainStatus(QStatusBar): self.boxDocHandle.setText("%13s" % theHandle) return + ## + # Internal Functions + ## + + def _updateTime(self): + sTime = time() + if self.refTime is None: + theTime = "00:00:00" + else: + # This is much faster than using datetime format + tS = int(time() - self.refTime) + tM = int(tS/60) + tH = int(tM/60) + tM = tM - tH*60 + tS = tS - tM*60 - tH*3600 + theTime = "%02d:%02d:%02d" % (tH,tM,tS) + self.boxTime.setText(theTime) + print((time()-sTime)*1e6) + return + # END Class GuiMainStatus diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 36a9e0f1..46948ae1 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -180,6 +180,7 @@ class GuiMain(QMainWindow): self.rebuildTree() self.saveProject() self.hasProject = True + self.statusBar.setRefTime(self.theProject.projOpened) return True @@ -209,7 +210,7 @@ class GuiMain(QMainWindow): self.theProject.closeProject() self.clearGUI() self.hasProject = False - + return saveOK def openProject(self, projFile=None): @@ -234,6 +235,7 @@ class GuiMain(QMainWindow): self.rebuildTree() self.docEditor.setPwl(path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)) self.docEditor.setSpellCheck(self.theProject.spellCheck) + self.statusBar.setRefTime(self.theProject.projOpened) self.mainMenu.updateMenu() # Restore previously open documents, if any From 5c20075b735bf694ad1c43a51ead1c1cced4df09 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sun, 26 May 2019 17:06:40 +0200 Subject: [PATCH 17/17] Fixed a bug in closing the app with the X button and selecting No on the question --- nw/gui/statusbar.py | 2 -- nw/gui/winmain.py | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 4cc437cc..484e9519 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -144,7 +144,6 @@ class GuiMainStatus(QStatusBar): ## def _updateTime(self): - sTime = time() if self.refTime is None: theTime = "00:00:00" else: @@ -156,7 +155,6 @@ class GuiMainStatus(QStatusBar): tS = tS - tM*60 - tH*3600 theTime = "%02d:%02d:%02d" % (tH,tM,tS) self.boxTime.setText(theTime) - print((time()-sTime)*1e6) return # END Class GuiMainStatus diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 46948ae1..0de1c27f 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -548,7 +548,10 @@ class GuiMain(QMainWindow): return def closeEvent(self, theEvent): - self.closeMain() + if self.closeMain(): + theEvent.accept() + else: + theEvent.ignore() return ##