From 56b2cbee10f62d65e46571883e1587feff3aadf9 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 21:54:32 +0200
Subject: [PATCH] Code cleanup and added comments
---
nw/common.py | 12 +++--
nw/config.py | 14 +++--
nw/core/document.py | 9 ++--
nw/core/spellcheck.py | 1 -
nw/core/tokenizer.py | 3 --
nw/error.py | 2 +-
nw/gui/docmerge.py | 2 -
nw/gui/docsplit.py | 4 +-
nw/gui/itemeditor.py | 3 +-
nw/gui/mainmenu.py | 27 ++++++----
nw/gui/outline.py | 4 ++
nw/gui/preferences.py | 23 +++++---
nw/gui/projload.py | 3 ++
nw/gui/projsettings.py | 53 +++++++++++++++----
nw/gui/projtree.py | 28 +++++++++-
nw/gui/statusbar.py | 2 +-
nw/gui/theme.py | 109 +++++++++++++++++++-------------------
nw/gui/writingstats.py | 2 +-
nw/guimain.py | 116 +++++++++++++++++++++++++----------------
19 files changed, 270 insertions(+), 147 deletions(-)
diff --git a/nw/common.py b/nw/common.py
index 872365df..b15e148a 100644
--- a/nw/common.py
+++ b/nw/common.py
@@ -35,6 +35,8 @@ from nw.constants import nwConst
logger = logging.getLogger(__name__)
def checkString(checkValue, defaultValue, allowNone=False):
+ """Check if a variable is a string or a none.
+ """
if allowNone:
if checkValue == None:
return None
@@ -45,6 +47,8 @@ def checkString(checkValue, defaultValue, allowNone=False):
return defaultValue
def checkInt(checkValue, defaultValue, allowNone=False):
+ """Check if a variable is an integer or a none.
+ """
if allowNone:
if checkValue == None:
return None
@@ -56,6 +60,8 @@ def checkInt(checkValue, defaultValue, allowNone=False):
return defaultValue
def checkBool(checkValue, defaultValue, allowNone=False):
+ """Check if a variable is a boolean or a none.
+ """
if allowNone:
if checkValue == None:
return None
@@ -92,7 +98,8 @@ def isHandle(theString):
return not invalidChar
def colRange(rgbStart, rgbEnd, nStep):
-
+ """Generate a range of colours from one RGB value to another.
+ """
if len(rgbStart) != 3 and len(rgbEnd) != 3 and nStep < 1:
logger.error("Cannot create colour range from given parameters")
return None
@@ -124,7 +131,7 @@ def colRange(rgbStart, rgbEnd, nStep):
def formatInt(theInt):
"""Formats an integer with k, M, G etc.
"""
- postFix = ["k","M","G","T","P","E"]
+ postFix = ["k", "M", "G", "T", "P", "E"]
theVal = float(theInt)
if theVal > 1000.0:
@@ -153,7 +160,6 @@ def splitVersionNumber(vString):
""" Splits a version string on the form aa.bb.cc into major, minor
and patch, and computes an integer value aabbcc.
"""
-
vMajor = 0
vMinor = 0
vPatch = 0
diff --git a/nw/config.py b/nw/config.py
index 619125fa..97e78c90 100644
--- a/nw/config.py
+++ b/nw/config.py
@@ -88,7 +88,7 @@ class Config:
self.guiSyntax = "default_light"
self.guiIcons = "typicons_colour_light"
self.guiDark = False
- self.guiLang = "en" # Hardcoded for now
+ self.guiLang = "en" # Hardcoded for now since the GUI is only in English
self.guiFont = ""
self.guiFontSize = 11
self.guiScale = 1.0 # Set automatically by Theme class
@@ -286,7 +286,7 @@ class Config:
# Check if config file exists
if self.confPath is not None:
- if path.isfile(path.join(self.confPath,self.confFile)):
+ if path.isfile(path.join(self.confPath, self.confFile)):
# If it exists, load it
self.loadConfig()
else:
@@ -855,7 +855,9 @@ class Config:
##
def _unpackList(self, inStr, listLen, listDefault, castTo=int):
- inData = inStr.split(",")
+ """Unpack a comma separated string of items into a list.
+ """
+ inData = inStr.split(",")
outData = []
for i in range(listLen):
try:
@@ -865,9 +867,13 @@ class Config:
return outData
def _packList(self, inData):
+ """Pack a list of items into a comma separated string.
+ """
return ", ".join(str(inVal) for inVal in inData)
def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault):
+ """Parse a line and return the correct datatype.
+ """
if cnfParse.has_section(cnfSec):
if cnfParse.has_option(cnfSec, cnfName):
if cnfType == self.CNF_STR:
@@ -883,6 +889,8 @@ class Config:
return cnfDefault
def _checkNone(self, checkVal):
+ """Convert a string to a none type.
+ """
if checkVal is None:
return None
if isinstance(checkVal, str):
diff --git a/nw/core/document.py b/nw/core/document.py
index 8c8b523f..0c29f995 100644
--- a/nw/core/document.py
+++ b/nw/core/document.py
@@ -44,10 +44,11 @@ class NWDoc():
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
- self.theItem = None
- self.docHandle = None
- self.fileLoc = None
- self.docMeta = ""
+
+ self.theItem = None
+ self.docHandle = None
+ self.fileLoc = None
+ self.docMeta = ""
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py
index 28e25e40..d4740b56 100644
--- a/nw/core/spellcheck.py
+++ b/nw/core/spellcheck.py
@@ -195,7 +195,6 @@ class NWSpellEnchant(NWSpellCheck):
class NWSpellEnchantDummy:
"""Fallback for when Enchant is selected, but not installed.
"""
-
def __init__(self):
return
diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py
index 3050cfc0..2ea8f330 100644
--- a/nw/core/tokenizer.py
+++ b/nw/core/tokenizer.py
@@ -227,7 +227,6 @@ class Tokenizer():
"""Set the text for the tokenizer from a handle. If theText is
not set, load it from the file.
"""
-
self.theHandle = theHandle
self.theItem = self.theProject.projTree[theHandle]
if self.theItem is None:
@@ -308,7 +307,6 @@ class Tokenizer():
4: The internal formatting map of the text, self.FMT_*
5: The style of the block, self.A_*
"""
-
# RegExes for adding formatting tags within text lines
rxFormats = [
(QRegularExpression(nwRegEx.FMT_I), [None, self.FMT_I_B, None, self.FMT_I_E]),
@@ -457,7 +455,6 @@ class Tokenizer():
"""Apply formatting to the text headers according to document
layout and user settings.
"""
-
# No special header formatting for notes and no-layout files
if self.isNone or self.isNote:
return
diff --git a/nw/error.py b/nw/error.py
index e8466657..4c3fef17 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -46,7 +46,7 @@ def formatHtmlErrMsg(exType, exValue, exTrace):
"
Please report this error by submitting an issue report on "
"GitHub, providing a description and this error message. "
"URL: <{issueUrl}>.
"
- "Environment
Version: {nwVersion}, OS: {osType} ({osKernel}),"
+ "
Environment
Version: {nwVersion}, OS: {osType} ({osKernel}), "
"Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}
"
"Error Type
{exType}: {exMessage}
"
"Traceback
{exTrace}
"
diff --git a/nw/gui/docmerge.py b/nw/gui/docmerge.py
index f6a565d1..547cbd44 100644
--- a/nw/gui/docmerge.py
+++ b/nw/gui/docmerge.py
@@ -95,7 +95,6 @@ class GuiDocMerge(QDialog):
create a new file in the same parent folder. The old files are
not removed in the merge process, and must be deleted manually.
"""
-
logger.verbose("GuiDocMerge merge button clicked")
finalOrder = []
@@ -142,7 +141,6 @@ class GuiDocMerge(QDialog):
are then added to the list view in order. The list itself can be
reordered by the user.
"""
-
tHandle = self.theParent.treeView.getSelectedHandle()
self.sourceItem = tHandle
if tHandle is None:
diff --git a/nw/gui/docsplit.py b/nw/gui/docsplit.py
index 60b4069a..218b4855 100644
--- a/nw/gui/docsplit.py
+++ b/nw/gui/docsplit.py
@@ -110,7 +110,6 @@ class GuiDocSplit(QDialog):
settings. The old file is not removed in the merge process, and
must be deleted manually.
"""
-
logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None:
@@ -132,7 +131,7 @@ class GuiDocSplit(QDialog):
nLines = len(theLines)
theLines.insert(0, "%Split Doc")
logger.debug(
- "Splitting document %s with %d lines" % (self.sourceItem,nLines)
+ "Splitting document %s with %d lines" % (self.sourceItem, nLines)
)
finalOrder = []
@@ -210,7 +209,6 @@ class GuiDocSplit(QDialog):
are then added to the list view in order. The list itself can be
reordered by the user.
"""
-
if self.sourceItem is None:
self.sourceItem = self.theParent.treeView.getSelectedHandle()
diff --git a/nw/gui/itemeditor.py b/nw/gui/itemeditor.py
index b1c2421a..1a149a09 100644
--- a/nw/gui/itemeditor.py
+++ b/nw/gui/itemeditor.py
@@ -150,7 +150,6 @@ class GuiItemEditor(QDialog):
def _doSave(self):
"""Save the setting to the item.
"""
-
logger.verbose("ItemEditor save button clicked")
itemName = self.editName.text()
@@ -171,6 +170,8 @@ class GuiItemEditor(QDialog):
return
def _doClose(self):
+ """Close the dialog without saving the settings.
+ """
logger.verbose("ItemEditor close button clicked")
self.close()
return
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 2ac66e33..b81bac82 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -192,7 +192,8 @@ class GuiMainMenu(QMenuBar):
##
def _buildProjectMenu(self):
-
+ """Assemble the Project menu.
+ """
# Project
self.projMenu = self.addMenu("&Project")
@@ -295,7 +296,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildDocumentMenu(self):
-
+ """Assemble the Document menu.
+ """
# Document
self.docuMenu = self.addMenu("&Document")
@@ -377,7 +379,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildEditMenu(self):
-
+ """Assemble the Edit menu.
+ """
# Edit
self.editMenu = self.addMenu("&Edit")
@@ -439,7 +442,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildViewMenu(self):
-
+ """Assemble the View menu.
+ """
# View
self.viewMenu = self.addMenu("&View")
@@ -486,7 +490,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildInsertMenu(self):
-
+ """Assemble the Insert menu.
+ """
# Insert
self.insertMenu = self.addMenu("&Insert")
@@ -576,7 +581,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildSearchMenu(self):
-
+ """Assemble the Search menu.
+ """
# Search
self.srcMenu = self.addMenu("&Search")
@@ -627,7 +633,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildFormatMenu(self):
-
+ """Assemble the Format menu.
+ """
# Format
self.fmtMenu = self.addMenu("&Format")
@@ -732,7 +739,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildToolsMenu(self):
-
+ """Assemble the Tools menu.
+ """
# Tools
self.toolsMenu = self.addMenu("&Tools")
@@ -828,7 +836,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildHelpMenu(self):
-
+ """Assemble the Help menu.
+ """
# Help
self.helpMenu = self.addMenu("&Help")
diff --git a/nw/gui/outline.py b/nw/gui/outline.py
index cd4ff718..c1986b98 100644
--- a/nw/gui/outline.py
+++ b/nw/gui/outline.py
@@ -195,8 +195,10 @@ class GuiOutline(QTreeWidget):
tLine = int(tItem.text(self.colIndex[nwOutline.LINE]))
except:
tLine = 1
+
logger.verbose("User selected entry with handle %s on line %s" % (tHandle, tLine))
self.theParent.openDocument(tHandle, tLine=tLine-1, doScroll=True)
+
return
def _itemSelected(self):
@@ -208,6 +210,7 @@ class GuiOutline(QTreeWidget):
tHandle = selItems[0].data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
sTitle = selItems[0].data(self.colIndex[nwOutline.LINE], Qt.UserRole)
self.theParent.projMeta.showItem(tHandle, sTitle)
+
return
def _headerRightClick(self, clickPos):
@@ -232,6 +235,7 @@ class GuiOutline(QTreeWidget):
if theItem in self.colIndex:
self.setColumnHidden(self.colIndex[theItem], not isChecked)
self._saveHeaderState()
+
return
##
diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py
index 31986546..8b103194 100644
--- a/nw/gui/preferences.py
+++ b/nw/gui/preferences.py
@@ -80,7 +80,9 @@ class GuiPreferences(PagedDialog):
##
def _doSave(self):
-
+ """Trigger all the save functions in the tabs, and collect the
+ status of the saves.
+ """
logger.verbose("ConfigEditor save button clicked")
validEntries = True
@@ -115,6 +117,8 @@ class GuiPreferences(PagedDialog):
return
def _doClose(self):
+ """Close the preferences without saving the changes.
+ """
logger.verbose("ConfigEditor close button clicked")
self.close()
return
@@ -284,7 +288,8 @@ class GuiConfigEditGeneralTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
@@ -329,7 +334,6 @@ class GuiConfigEditGeneralTab(QWidget):
def _backupFolder(self):
"""Open a dialog to select the backup folder.
"""
-
currDir = self.backupPath
if not path.isdir(currDir):
currDir = ""
@@ -515,7 +519,8 @@ class GuiConfigEditLayoutTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
@@ -681,7 +686,8 @@ class GuiConfigEditEditingTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
@@ -712,6 +718,8 @@ class GuiConfigEditEditingTab(QWidget):
##
def _disableComboItem(self, theList, theValue):
+ """Disable a list item in the combo box.
+ """
theIdx = theList.findData(theValue)
theModel = theList.model()
anItem = theModel.item(1)
@@ -719,6 +727,8 @@ class GuiConfigEditEditingTab(QWidget):
return theModel
def _doUpdateSpellTool(self, currIdx):
+ """Update the list of dictionaries based on spell tool selected.
+ """
spellTool = self.spellToolList.currentData()
self._updateLanguageList(spellTool)
return
@@ -903,7 +913,8 @@ class GuiConfigEditAutoReplaceTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
diff --git a/nw/gui/projload.py b/nw/gui/projload.py
index 738a493c..eddbbb33 100644
--- a/nw/gui/projload.py
+++ b/nw/gui/projload.py
@@ -152,6 +152,7 @@ class GuiProjectLoad(QDialog):
"""
logger.verbose("GuiProjectLoad open button clicked")
self._saveDialogState()
+
selItems = self.listBox.selectedItems()
if selItems:
self.openPath = selItems[0].data(self.C_NAME, Qt.UserRole)
@@ -160,6 +161,7 @@ class GuiProjectLoad(QDialog):
else:
self.openPath = None
self.openState = self.NONE_STATE
+
return
def _doSelectRecent(self):
@@ -189,6 +191,7 @@ class GuiProjectLoad(QDialog):
self.openPath = thePath
self.openState = self.OPEN_STATE
self.accept()
+
return
def _doClose(self):
diff --git a/nw/gui/projsettings.py b/nw/gui/projsettings.py
index b8b47a90..86ea3ead 100644
--- a/nw/gui/projsettings.py
+++ b/nw/gui/projsettings.py
@@ -97,6 +97,7 @@ class GuiProjectSettings(PagedDialog):
bookTitle = self.tabMain.editTitle.text()
bookAuthors = self.tabMain.editAuthors.toPlainText()
doBackup = not self.tabMain.doBackup.isChecked()
+
self.theProject.setProjectName(projName)
self.theProject.setBookTitle(bookTitle)
self.theProject.setBookAuthors(bookAuthors)
@@ -105,11 +106,14 @@ class GuiProjectSettings(PagedDialog):
if self.tabStatus.colChanged:
statusCol = self.tabStatus.getNewList()
self.theProject.setStatusColours(statusCol)
+
if self.tabImport.colChanged:
importCol = self.tabImport.getNewList()
self.theProject.setImportColours(importCol)
+
if self.tabStatus.colChanged or self.tabImport.colChanged:
self.theParent.rebuildTree()
+
if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList()
self.theProject.setAutoReplace(newList)
@@ -119,7 +123,7 @@ class GuiProjectSettings(PagedDialog):
return
def _doClose(self):
- """Close the dialog.
+ """Save settings and close the dialog.
"""
winWidth = self.mainConf.rpxInt(self.width())
winHeight = self.mainConf.rpxInt(self.height())
@@ -372,6 +376,8 @@ class GuiProjectEditStatus(QWidget):
##
def _selectColour(self):
+ """Open a dialog to select the status icon colour.
+ """
logger.verbose("Item colour button clicked")
if self.selColour is not None:
newCol = QColorDialog.getColor(
@@ -386,6 +392,8 @@ class GuiProjectEditStatus(QWidget):
return
def _newItem(self):
+ """Create a new status item.
+ """
logger.verbose("New item button clicked")
newItem = self._addItem("New Item", (0, 0, 0), None, 0)
newItem.setBackground(QBrush(QColor(0, 255, 0, 80)))
@@ -393,6 +401,8 @@ class GuiProjectEditStatus(QWidget):
return
def _delItem(self):
+ """Delete a status item.
+ """
logger.verbose("Delete item button clicked")
selItem = self._getSelectedItem()
if selItem is not None:
@@ -408,6 +418,8 @@ class GuiProjectEditStatus(QWidget):
return
def _saveItem(self):
+ """Save changes made to a status item.
+ """
logger.verbose("Save item button clicked")
selItem = self._getSelectedItem()
iRow = self.listBox.row(selItem)
@@ -427,6 +439,8 @@ class GuiProjectEditStatus(QWidget):
return
def _addItem(self, iName, iCol, oName, nUse):
+ """Add a status item to the list.
+ """
newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(QColor(*iCol))
newItem = QListWidgetItem()
@@ -439,11 +453,14 @@ class GuiProjectEditStatus(QWidget):
return newItem
def _selectedItem(self):
+ """Extract the info of a selected item and populate the settings
+ boxes and button.
+ """
logger.verbose("Item selected")
selItem = self._getSelectedItem()
if selItem is not None:
- selIdx = selItem.data(Qt.UserRole)
- selVal = self.colData[selIdx]
+ selIdx = selItem.data(Qt.UserRole)
+ selVal = self.colData[selIdx]
self.selColour = QColor(selVal[1], selVal[2], selVal[3])
newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(self.selColour)
@@ -459,6 +476,8 @@ class GuiProjectEditStatus(QWidget):
##
def _getSelectedItem(self):
+ """Get the currently selected item.
+ """
selItem = self.listBox.selectedItems()
if len(selItem) == 0:
return None
@@ -467,6 +486,8 @@ class GuiProjectEditStatus(QWidget):
return None
def _rowsMoved(self):
+ """A row has been moved, so sett the changed flag.
+ """
logger.verbose("A drag move event occurred")
self.colChanged = True
return
@@ -506,9 +527,9 @@ class GuiProjectEditReplace(QWidget):
self.editKey = QLineEdit()
self.editValue = QLineEdit()
- self.saveButton = QPushButton(self.theTheme.getIcon("done"),"")
- self.addButton = QPushButton(self.theTheme.getIcon("add"),"")
- self.delButton = QPushButton(self.theTheme.getIcon("remove"),"")
+ self.saveButton = QPushButton(self.theTheme.getIcon("done"), "")
+ self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
+ self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
self.saveButton.setToolTip("Save entry")
self.addButton.setToolTip("Add new entry")
self.delButton.setToolTip("Delete selected entry")
@@ -536,11 +557,13 @@ class GuiProjectEditReplace(QWidget):
return
def getNewList(self):
+ """Extract the list from the widget.
+ """
newList = {}
for n in range(self.listBox.topLevelItemCount()):
tItem = self.listBox.topLevelItem(n)
- aKey = self._stripNotAllowed(tItem.text(0))
- aVal = tItem.text(1)
+ aKey = self._stripNotAllowed(tItem.text(0))
+ aVal = tItem.text(1)
if len(aKey) > 0:
newList[aKey] = aVal
return newList
@@ -550,6 +573,9 @@ class GuiProjectEditReplace(QWidget):
##
def _selectedItem(self):
+ """Extract the details from the selected item and populate the
+ edit form.
+ """
selItem = self._getSelectedItem()
if selItem is None:
return False
@@ -564,7 +590,8 @@ class GuiProjectEditReplace(QWidget):
return True
def _saveEntry(self):
-
+ """Save the form data into the list widget.
+ """
selItem = self._getSelectedItem()
if selItem is None:
return False
@@ -586,6 +613,8 @@ class GuiProjectEditReplace(QWidget):
return
def _addEntry(self):
+ """Add a new list entry.
+ """
saveKey = "" % (self.listBox.topLevelItemCount() + 1)
newVal = ""
newItem = QTreeWidgetItem([saveKey, newVal])
@@ -593,6 +622,8 @@ class GuiProjectEditReplace(QWidget):
return True
def _delEntry(self):
+ """Delete the selected entry.
+ """
selItem = self._getSelectedItem()
if selItem is None:
return False
@@ -601,12 +632,16 @@ class GuiProjectEditReplace(QWidget):
return True
def _getSelectedItem(self):
+ """Extract the currently selected item.
+ """
selItem = self.listBox.selectedItems()
if len(selItem) == 0:
return None
return selItem[0]
def _stripNotAllowed(self, theKey):
+ """Clean up the replace key string.
+ """
retKey = ""
for c in theKey:
if c.isalnum():
diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py
index 3baf5257..124e0a59 100644
--- a/nw/gui/projtree.py
+++ b/nw/gui/projtree.py
@@ -90,7 +90,7 @@ class GuiProjectTree(QTreeWidget):
# for some fonts like the Ubuntu font.
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
- treeHeader.setMinimumSectionSize(iPx+6)
+ treeHeader.setMinimumSectionSize(iPx + 6)
# Allow Move by Drag & Drop
self.setDragEnabled(True)
@@ -237,6 +237,7 @@ class GuiProjectTree(QTreeWidget):
has focus. This also applies when the menu is used.
"""
if qApp.focusWidget() == self and self.theParent.hasProject:
+
tHandle = self.getSelectedHandle()
tItem = self._getTreeItem(tHandle)
pItem = tItem.parent()
@@ -248,6 +249,7 @@ class GuiProjectTree(QTreeWidget):
return False
cItem = self.takeTopLevelItem(tIndex)
self.insertTopLevelItem(nIndex, cItem)
+
else:
tIndex = pItem.indexOfChild(tItem)
nChild = pItem.childCount()
@@ -256,11 +258,14 @@ class GuiProjectTree(QTreeWidget):
return False
cItem = pItem.takeChild(tIndex)
pItem.insertChild(nIndex, cItem)
+
self.clearSelection()
cItem.setSelected(True)
self._setTreeChanged(True)
+
else:
return False
+
return True
def saveTreeOrder(self):
@@ -516,8 +521,10 @@ class GuiProjectTree(QTreeWidget):
for i in range(pItem.childCount()):
pCount += int(pItem.child(i).text(self.C_COUNT))
pHandle = pItem.data(self.C_NAME, Qt.UserRole)
+
if not nDepth > 200 and pHandle != "":
self.propagateCount(pHandle, pCount, nDepth+1)
+
return
def projectWordCount(self):
@@ -533,9 +540,11 @@ class GuiProjectTree(QTreeWidget):
if tItem == self.orphRoot:
continue
nWords += int(tItem.text(self.C_COUNT))
+
self.theProject.setProjectWordCount(nWords)
sWords = self.theProject.getSessionWordCount()
self.theParent.statusBar.setStats(nWords,sWords)
+
return
def buildTree(self):
@@ -547,9 +556,11 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Building project tree ...")
self.clear()
iCount = 0
+
for nwItem in self.theProject.getProjectItems():
iCount += 1
self._addTreeItem(nwItem)
+
logger.debug("%d items added to project tree" % iCount)
return True
@@ -558,10 +569,13 @@ class GuiProjectTree(QTreeWidget):
selected, return the first.
"""
selItem = self.selectedItems()
+
if len(selItem) == 0:
return None
+
if isinstance(selItem[0], QTreeWidgetItem):
return selItem[0].data(self.C_NAME, Qt.UserRole)
+
return None
def getSelectedHandles(self):
@@ -572,6 +586,7 @@ class GuiProjectTree(QTreeWidget):
for n in range(len(selItems)):
if isinstance(selItems[n], QTreeWidgetItem):
selHandles.append(selItems[n].data(self.C_NAME, Qt.UserRole))
+
return selHandles
def setSelectedHandle(self, tHandle, doScroll=False):
@@ -580,12 +595,14 @@ class GuiProjectTree(QTreeWidget):
if tHandle in self.theMap:
self.clearSelection()
self.theMap[tHandle].setSelected(True)
+
selItems = self.selectedIndexes()
if selItems and doScroll:
self.scrollTo(
selItems[0], QAbstractItemView.PositionAtCenter
)
return True
+
return False
##
@@ -601,9 +618,11 @@ class GuiProjectTree(QTreeWidget):
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
tItem = self.theProject.projTree[tHandle]
self.setSelectedHandle(tHandle) # Just to be safe
+
if self.ctxMenu.filterActions(tItem):
# Only open menu if any actions remain after filter
self.ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
+
return
##
@@ -615,9 +634,11 @@ class GuiProjectTree(QTreeWidget):
mouse in a blank area of the tree view.
"""
QTreeWidget.mousePressEvent(self, theEvent)
+
selItem = self.indexAt(theEvent.pos())
if not selItem.isValid():
self.clearSelection()
+
return
def dropEvent(self, theEvent):
@@ -766,6 +787,7 @@ class GuiProjectTree(QTreeWidget):
trashHandle = self.theProject.trashFolder()
if trashHandle is None:
return None
+
trItem = self._getTreeItem(trashHandle)
if trItem is None:
trItem = self._addTreeItem(
@@ -773,6 +795,7 @@ class GuiProjectTree(QTreeWidget):
)
trItem.setExpanded(True)
self._setTreeChanged(True)
+
return trItem
def _addOrphanedRoot(self):
@@ -790,6 +813,7 @@ class GuiProjectTree(QTreeWidget):
newItem.setExpanded(True)
newItem.setData(self.C_NAME, Qt.UserRole, "")
newItem.setIcon(self.C_NAME, self.theTheme.getIcon("proj_orphan"))
+
return
def _cleanOrphanedRoot(self):
@@ -834,10 +858,12 @@ class GuiProjectTree(QTreeWidget):
if trItemP is None:
logger.error("Failed to find new parent item of %s" % tHandle)
return
+
pHandle = trItemP.data(self.C_NAME, Qt.UserRole)
nwItemS.setParent(pHandle)
self.setTreeItemValues(tHandle)
self._setTreeChanged(True)
+
return
def _setTreeChanged(self, theState):
diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py
index 395fc5cc..330d7de9 100644
--- a/nw/gui/statusbar.py
+++ b/nw/gui/statusbar.py
@@ -264,7 +264,7 @@ class StatusLED(QAbstractButton):
qPaint.setPen(qPalette.dark().color())
qPaint.setBrush(self._theCol)
qPaint.setOpacity(1.0)
- qPaint.drawEllipse(1, 1, self.width()-2, self.height()-2)
+ qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
return
# END Class StatusLED
diff --git a/nw/gui/theme.py b/nw/gui/theme.py
index 0265df62..134d91dd 100644
--- a/nw/gui/theme.py
+++ b/nw/gui/theme.py
@@ -77,11 +77,11 @@ class GuiTheme:
self.themeLicenseUrl = ""
## GUI
- self.treeWCount = [ 0, 0, 0]
- self.statNone = [120,120,120]
- self.statUnsaved = [120,120, 40]
- self.statSaved = [ 40,120, 0]
- self.helpText = [ 0, 0, 0]
+ self.treeWCount = [ 0, 0, 0]
+ self.statNone = [120, 120, 120]
+ self.statUnsaved = [120, 120, 40]
+ self.statSaved = [ 40, 120, 0]
+ self.helpText = [ 0, 0, 0]
# Loaded Syntax Settings
@@ -95,22 +95,22 @@ class GuiTheme:
self.syntaxLicenseUrl = ""
## Colours
- self.colBack = [255,255,255]
- self.colText = [ 0, 0, 0]
- self.colLink = [ 0, 0, 0]
- 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]
- self.colTagErr = [ 0, 0, 0]
- self.colRepTag = [ 0, 0, 0]
- self.colMod = [ 0, 0, 0]
+ self.colBack = [255, 255, 255]
+ self.colText = [ 0, 0, 0]
+ self.colLink = [ 0, 0, 0]
+ 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]
+ self.colTagErr = [ 0, 0, 0]
+ self.colRepTag = [ 0, 0, 0]
+ self.colMod = [ 0, 0, 0]
# Changeable Settings
self.guiTheme = None
@@ -144,9 +144,9 @@ class GuiTheme:
qMetric = QFontMetrics(self.guiFont)
self.fontPointSize = self.guiFont.pointSizeF()
self.fontPixelSize = int(round(qMetric.height()))
- self.baseIconSize = int(round(qMetric.ascent()))
- self.textNHeight = qMetric.boundingRect("N").height()
- self.textNWidth = qMetric.boundingRect("N").width()
+ self.baseIconSize = int(round(qMetric.ascent()))
+ self.textNHeight = qMetric.boundingRect("N").height()
+ self.textNWidth= qMetric.boundingRect("N").width()
logger.verbose("GUI Font Family: %s" % self.guiFont.family())
logger.verbose("GUI Font Point Size: %.2f" % self.fontPointSize)
@@ -223,10 +223,10 @@ class GuiTheme:
self.guiTheme = self.mainConf.guiTheme
self.guiSyntax = self.mainConf.guiSyntax
self.themeRoot = self.mainConf.themeRoot
- self.themePath = path.join(self.mainConf.themeRoot,self.guiPath,self.guiTheme)
- self.syntaxFile = path.join(self.themeRoot,self.syntaxPath,self.guiSyntax+".conf")
- self.confFile = path.join(self.themePath,self.confName)
- self.cssFile = path.join(self.themePath,self.cssName)
+ self.themePath = path.join(self.mainConf.themeRoot, self.guiPath, self.guiTheme)
+ self.syntaxFile = path.join(self.themeRoot, self.syntaxPath, self.guiSyntax+".conf")
+ self.confFile = path.join(self.themePath, self.confName)
+ self.cssFile = path.join(self.themePath, self.cssName)
self.loadTheme()
self.loadSyntax()
@@ -256,7 +256,7 @@ class GuiTheme:
cssData = ""
try:
if path.isfile(self.cssFile):
- with open(self.cssFile,mode="r",encoding="utf8") as inFile:
+ with open(self.cssFile, mode="r", encoding="utf8") as inFile:
cssData = inFile.read()
except Exception as e:
logger.error("Could not load theme css file")
@@ -329,13 +329,13 @@ class GuiTheme:
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
- self.syntaxName = self._parseLine( confParser, cnfSec, "name", "")
- self.syntaxDescription = self._parseLine( confParser, cnfSec, "description", "")
- self.syntaxAuthor = self._parseLine( confParser, cnfSec, "author", "")
- self.syntaxCredit = self._parseLine( confParser, cnfSec, "credit", "")
- self.syntaxUrl = self._parseLine( confParser, cnfSec, "url", "")
- self.syntaxLicense = self._parseLine( confParser, cnfSec, "license", "")
- self.syntaxLicenseUrl = self._parseLine( confParser, cnfSec, "licenseurl", "")
+ self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
+ self.syntaxDescription = self._parseLine(confParser, cnfSec, "description", "")
+ self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author", "")
+ self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit", "")
+ self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
+ self.syntaxLicense = self._parseLine(confParser, cnfSec, "license", "")
+ self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## Syntax
cnfSec = "Syntax"
@@ -376,7 +376,7 @@ class GuiTheme:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
- ["Could not load theme config file.",str(e)], nwAlert.ERROR
+ ["Could not load theme config file.", str(e)], nwAlert.ERROR
)
continue
themeName = ""
@@ -409,7 +409,7 @@ class GuiTheme:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
- ["Could not load syntax file.",str(e)], nwAlert.ERROR
+ ["Could not load syntax file.", str(e)], nwAlert.ERROR
)
return []
syntaxName = ""
@@ -429,8 +429,10 @@ class GuiTheme:
##
def _loadColour(self, confParser, cnfSec, cnfName):
+ """Load a colour value from a config string.
+ """
if confParser.has_option(cnfSec,cnfName):
- inData = confParser.get(cnfSec,cnfName).split(",")
+ inData = confParser.get(cnfSec,cnfName).split(",")
outData = []
try:
outData.append(int(inData[0]))
@@ -438,16 +440,18 @@ class GuiTheme:
outData.append(int(inData[2]))
except:
logger.error("Could not load theme colours for '%s' from config file" % cnfName)
- outData = [0,0,0]
+ outData = [0, 0, 0]
else:
logger.warning("Could not find theme colours for '%s' in config file" % cnfName)
- outData = [0,0,0]
+ outData = [0, 0, 0]
return outData
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
+ """Set a palette colour value from a config string.
+ """
readCol = []
if confParser.has_option(cnfSec,cnfName):
- inData = confParser.get(cnfSec,cnfName).split(",")
+ inData = confParser.get(cnfSec,cnfName).split(",")
try:
readCol.append(int(inData[0]))
readCol.append(int(inData[1]))
@@ -547,8 +551,8 @@ class GuiIcons:
"reference" : (None, None),
## Switches
- "sticky-on" : (None, None),
- "sticky-off" : (None, None),
+ "sticky-on" : (None, None),
+ "sticky-off" : (None, None),
}
DECO_MAP = {
@@ -615,13 +619,13 @@ class GuiIcons:
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
- self.themeName = self._parseLine( confParser, cnfSec, "name", "")
- self.themeDescription = self._parseLine( confParser, cnfSec, "description", "")
- self.themeAuthor = self._parseLine( confParser, cnfSec, "author", "")
- self.themeCredit = self._parseLine( confParser, cnfSec, "credit", "")
- self.themeUrl = self._parseLine( confParser, cnfSec, "url", "")
- self.themeLicense = self._parseLine( confParser, cnfSec, "license", "")
- self.themeLicenseUrl = self._parseLine( confParser, cnfSec, "licenseurl", "")
+ self.themeName = self._parseLine(confParser, cnfSec, "name", "")
+ self.themeDescription = self._parseLine(confParser, cnfSec, "description", "")
+ self.themeAuthor = self._parseLine(confParser, cnfSec, "author", "")
+ self.themeCredit = self._parseLine(confParser, cnfSec, "credit", "")
+ self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
+ self.themeLicense = self._parseLine(confParser, cnfSec, "license", "")
+ self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## Palette
cnfSec = "Map"
@@ -705,7 +709,7 @@ class GuiIcons:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
- ["Could not load theme config file.",str(e)], nwAlert.ERROR
+ ["Could not load theme config file.", str(e)], nwAlert.ERROR
)
continue
themeName = ""
@@ -730,7 +734,6 @@ class GuiIcons:
an icon exists. Prefer svg files over png files. Always returns
a QIcon.
"""
-
if iconKey not in self.ICON_MAP:
logger.error("Requested unknown icon name '%s'" % iconKey)
return QIcon()
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index 6e4a4df8..955376a7 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -90,7 +90,7 @@ class GuiWritingStats(QDialog):
)
self.listBox = QTreeWidget()
- self.listBox.setHeaderLabels(["Session Start","Length","Words","Histogram"])
+ self.listBox.setHeaderLabels(["Session Start", "Length", "Words", "Histogram"])
self.listBox.setIndentation(0)
self.listBox.setColumnWidth(self.C_TIME, wCol0)
self.listBox.setColumnWidth(self.C_LENGTH, wCol1)
diff --git a/nw/guimain.py b/nw/guimain.py
index 46db9d1f..8c760531 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -128,7 +128,7 @@ class GuiMain(QMainWindow):
self.tabWidget = QTabWidget()
self.tabWidget.setTabPosition(QTabWidget.East)
self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}")
- self.tabWidget.addTab(self.splitDocs, "Editor")
+ self.tabWidget.addTab(self.splitDocs, "Editor")
self.tabWidget.addTab(self.splitOutline, "Outline")
self.tabWidget.currentChanged.connect(self._mainTabChanged)
@@ -139,8 +139,6 @@ class GuiMain(QMainWindow):
self.splitMain.addWidget(self.tabWidget)
self.splitMain.setSizes(self.mainConf.getMainPanePos())
- self.setCentralWidget(self.splitMain)
-
self.idxTree = self.splitMain.indexOf(self.treePane)
self.idxMain = self.splitMain.indexOf(self.tabWidget)
self.idxEditor = self.splitDocs.indexOf(self.docEditor)
@@ -167,8 +165,8 @@ class GuiMain(QMainWindow):
# Set Main Window Elements
self.setMenuBar(self.mainMenu)
+ self.setCentralWidget(self.splitMain)
self.setStatusBar(self.statusBar)
- self.statusBar.setStatus("Ready")
# Finalise Initialisation
##########################
@@ -222,6 +220,7 @@ class GuiMain(QMainWindow):
self.manageProjects()
logger.debug("novelWriter is ready ...")
+ self.statusBar.setStatus("novelWriter is ready ...")
return
@@ -348,9 +347,7 @@ class GuiMain(QMainWindow):
return saveOK
def openProject(self, projFile):
- """Open a project. The parameter projFile is passed from the
- open recent projects menu, and must be set to be forwarded to
- the project class. Otherwise, we just return.
+ """Open a project from a projFile path.
"""
if projFile is None:
return False
@@ -365,43 +362,47 @@ class GuiMain(QMainWindow):
# Try to open the project
if not self.theProject.openProject(projFile):
- if self.theProject.lockedBy is not None:
- if self.mainConf.showGUI:
- try:
- lockDetails = (
- "
The project was locked by the computer "
- "'%s' (%s %s), last active on %s"
- ) % (
- self.theProject.lockedBy[0],
- self.theProject.lockedBy[1],
- self.theProject.lockedBy[2],
- datetime.fromtimestamp(
- int(self.theProject.lockedBy[3])
- ).strftime("%x %X")
- )
- except:
- lockDetails = ""
+ # The project open failed.
- msgBox = QMessageBox()
- msgRes = msgBox.warning(
- self, "Project Locked", (
- "The project is already open by another instance of novelWriter, and "
- "is therefore locked. Override lock and continue anyway?
"
- "Note: If the program or the computer previously crashed, the lock "
- "can safely be overridden. If, however, another instance of "
- "novelWriter has the project open, overriding the lock may corrupt "
- "the project, and is not recommended.%s"
- ) % lockDetails,
- QMessageBox.Yes | QMessageBox.No, QMessageBox.No
- )
- if msgRes == QMessageBox.Yes:
- if not self.theProject.openProject(projFile, overrideLock=True):
- return False
- else:
- return False
- else:
+ if self.theProject.lockedBy is None:
+ # The project is not locked, so failed for some other
+ # reason handled by the project class.
return False
+ if self.mainConf.showGUI:
+ try:
+ lockDetails = (
+ "
The project was locked by the computer "
+ "'%s' (%s %s), last active on %s"
+ ) % (
+ self.theProject.lockedBy[0],
+ self.theProject.lockedBy[1],
+ self.theProject.lockedBy[2],
+ datetime.fromtimestamp(
+ int(self.theProject.lockedBy[3])
+ ).strftime("%x %X")
+ )
+ except:
+ lockDetails = ""
+
+ msgBox = QMessageBox()
+ msgRes = msgBox.warning(
+ self, "Project Locked", (
+ "The project is already open by another instance of novelWriter, and "
+ "is therefore locked. Override lock and continue anyway?
"
+ "Note: If the program or the computer previously crashed, the lock "
+ "can safely be overridden. If, however, another instance of "
+ "novelWriter has the project open, overriding the lock may corrupt "
+ "the project, and is not recommended.%s"
+ ) % lockDetails,
+ QMessageBox.Yes | QMessageBox.No, QMessageBox.No
+ )
+ if msgRes == QMessageBox.Yes:
+ if not self.theProject.openProject(projFile, overrideLock=True):
+ return False
+ else:
+ return False
+
# Project is loaded
self.hasProject = True
@@ -570,7 +571,7 @@ class GuiMain(QMainWindow):
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
inPath = QFileDialog.getOpenFileName(
- self,"Import File",lastPath,options=dlgOpt,filter=";;".join(extFilter)
+ self, "Import File", lastPath, options=dlgOpt, filter=";;".join(extFilter)
)
if inPath:
loadFile = inPath[0]
@@ -582,7 +583,7 @@ class GuiMain(QMainWindow):
theText = None
try:
- with open(loadFile,mode="rt",encoding="utf8") as inFile:
+ with open(loadFile, mode="rt", encoding="utf8") as inFile:
theText = inFile.read()
self.mainConf.setLastPath(loadFile)
except Exception as e:
@@ -602,7 +603,7 @@ class GuiMain(QMainWindow):
if not self.docEditor.isEmpty():
if self.mainConf.showGUI:
msgBox = QMessageBox()
- msgRes = msgBox.question(self, "Import Document",(
+ msgRes = msgBox.question(self, "Import Document", (
"Importing the file will overwrite the current content of the document. "
"Do you want to proceed?"
))
@@ -917,6 +918,8 @@ class GuiMain(QMainWindow):
return True
def setFocus(self, paneNo):
+ """Switch focus to one of the three main gUi panes.
+ """
if paneNo == 1:
self.treeView.setFocus()
elif paneNo == 2:
@@ -926,6 +929,8 @@ class GuiMain(QMainWindow):
return
def closeDocEditor(self):
+ """Close the document edit panel. This does not hide the editor.
+ """
self.closeDocument()
self.theProject.setLastEdited(None)
return
@@ -1059,6 +1064,8 @@ class GuiMain(QMainWindow):
return True
def _setWindowTitle(self, projName=None):
+ """Set the window title and add the project's working title.
+ """
winTitle = self.mainConf.appName
if projName is not None:
winTitle += " - %s" % projName
@@ -1066,19 +1073,30 @@ class GuiMain(QMainWindow):
return True
def _autoSaveProject(self):
- if (self.hasProject and self.theProject.projChanged and
- self.theProject.projPath is not None):
+ """Triggered by the auto-save project timer to save the project.
+ """
+ doSave = self.hasProject
+ doSave &= self.theProject.projChanged
+ doSave &= self.theProject.projPath is not None
+
+ if doSave:
logger.debug("Autosaving project")
self.saveProject(autoSave=True)
+
return
def _autoSaveDocument(self):
+ """Triggered by the auto-save document timer to save the
+ document.
+ """
if self.hasProject and self.docEditor.docChanged:
logger.debug("Autosaving document")
self.saveDocument()
return
def _makeStatusIcons(self):
+ """Generate all the item status icons based on project settings.
+ """
self.statusIcons = {}
iPx = self.mainConf.pxInt(32)
for sLabel, sCol, _ in self.theProject.statusItems:
@@ -1088,6 +1106,9 @@ class GuiMain(QMainWindow):
return
def _makeImportIcons(self):
+ """Generate all the item importance icons based on project
+ settings.
+ """
self.importIcons = {}
iPx = self.mainConf.pxInt(32)
for sLabel, sCol, _ in self.theProject.importItems:
@@ -1101,6 +1122,9 @@ class GuiMain(QMainWindow):
##
def closeEvent(self, theEvent):
+ """Capture the closing event of the GUI and call the close
+ function to handle all the close process steps.
+ """
if self.closeMain():
theEvent.accept()
else: