diff --git a/nw/common.py b/nw/common.py index b148e7df..ada29987 100644 --- a/nw/common.py +++ b/nw/common.py @@ -83,8 +83,8 @@ def colRange(rgbStart, rgbEnd, nStep): return retCol def splitVersionNumber(vString): - """ Splits a version string on the form aa.bb.cc into major, minor and patch, and computes an - integer value aabbcc. + """ Splits a version string on the form aa.bb.cc into major, minor + and patch, and computes an integer value aabbcc. """ vMajor = 0 diff --git a/nw/convert/file/text.py b/nw/convert/file/text.py index 4d3cd346..0925515b 100644 --- a/nw/convert/file/text.py +++ b/nw/convert/file/text.py @@ -100,9 +100,9 @@ class TextFile(): self.fileName = path.basename(filePath) if path.isfile(filePath) and self.mainConf.showGUI: msgBox = QMessageBox() - msgRes = msgBox.question( - self.theParent, "Overwrite", ("File '%s' already exists.
Do you want to overwrite it?" % self.fileName) - ) + msgRes = msgBox.question(self.theParent, "Overwrite", ( + "File '%s' already exists.
Do you want to overwrite it?" % self.fileName + )) if msgRes != QMessageBox.Yes: return False @@ -137,11 +137,14 @@ class TextFile(): return True def checkInclude(self, tHandle): - """This function checks whether a file should be included in the export or not. For standard - note and novel files, this is controlled by the options selected by the user. For other - files classified as non-exportable, a few checks must be made, and the following are not: + """This function checks whether a file should be included in the + export or not. For standard note and novel files, this is + controlled by the options selected by the user. For other files + classified as non-exportable, a few checks must be made, and the + following are not: * Items that are not actual files. - * Items that have been orphaned which are tagged as NO_LAYOUT and NO_CLASS. + * Items that have been orphaned which are tagged as NO_LAYOUT + and NO_CLASS. * Items that appear in the TRASH folder """ @@ -168,8 +171,9 @@ class TextFile(): ## def _doOpenFile(self, filePath): - """This function does the actual opening of the file, and can be overloaded by a subclass - that uses a different file format that requires a different approach. + """This function does the actual opening of the file, and can be + overloaded by a subclass that uses a different file format that + requires a different approach. """ try: self.outFile = open(filePath,mode="wt+",encoding="utf8") @@ -180,8 +184,8 @@ class TextFile(): return True def _doCloseFile(self): - """This function closes the file, and is meant to be overloaded by the subclass for other - file formats. + """This function closes the file, and is meant to be overloaded + by the subclass for other file formats. """ if self.outFile is not None: self.outFile.close() diff --git a/nw/convert/text/tohtml.py b/nw/convert/text/tohtml.py index fae40df4..4abdeeb3 100644 --- a/nw/convert/text/tohtml.py +++ b/nw/convert/text/tohtml.py @@ -27,8 +27,9 @@ class ToHtml(Tokenizer): return def setPreview(self, forPreview, doComments): - """If we're using this class to generate markdown preview, we need to make a few changes to - formatting, which is selected by this flag. + """If we're using this class to generate markdown preview, we + need to make a few changes to formatting, which is selected by + this flag. """ self.forPreview = forPreview diff --git a/nw/convert/text/tolatex.py b/nw/convert/text/tolatex.py index 435fee84..e68cd3b2 100644 --- a/nw/convert/text/tolatex.py +++ b/nw/convert/text/tolatex.py @@ -28,7 +28,8 @@ class ToLaTeX(Tokenizer): return def doPostProcessing(self): - """The latexcodec misses dashes and non-breaking spaces, so we do those here. + """The latexcodec misses dashes and non-breaking spaces, so we + do those here. """ repDict = { @@ -62,8 +63,9 @@ class ToLaTeX(Tokenizer): begText = "\\begin{center}\n" endText = "\\end{center}\n\n" - # First check if we have a comment or plain text, as they need some - # extra replacing before we proceed to wrapping and final formatting. + # First check if we have a comment or plain text, as they + # need some extra replacing before we proceed to wrapping + # and final formatting. if tType == self.T_COMMENT: tText = "%% %s" % tText @@ -75,8 +77,9 @@ class ToLaTeX(Tokenizer): tLen = len(tText) - # Then the text can receive final formatting before we append it to the results. - # We also store text lines in a buffer and merge them only when we find an empty line + # Then the text can receive final formatting before we + # append it to the results. We also store text lines in a + # buffer and merge them only when we find an empty line # indicating a new paragraph. if tType == self.T_EMPTY: if len(thisPar) > 0: diff --git a/nw/convert/text/tomarkdown.py b/nw/convert/text/tomarkdown.py index 78098c5e..c9ea57b7 100644 --- a/nw/convert/text/tomarkdown.py +++ b/nw/convert/text/tomarkdown.py @@ -55,8 +55,9 @@ class ToMarkdown(Tokenizer): thisPar = [] for tType, tText, tFormat, tAlign in self.theTokens: - # First check if we have a comment or plain text, as they need some - # extra replacing before we proceed to wrapping and final formatting. + # First check if we have a comment or plain text, as they + # need some extra replacing before we proceed to wrapping + # and final formatting. if tType == self.T_COMMENT: tText = " %s" % tText @@ -68,7 +69,8 @@ class ToMarkdown(Tokenizer): tLen = len(tText) - # The text can now be word wrapped, if we have requested this and it's needed. + # The text can now be word wrapped, if we have requested + # this and it's needed. if self.wordWrap > 0 and tLen > self.wordWrap: if tType == self.T_COMMENT: tText = textwrap.fill( @@ -77,8 +79,9 @@ class ToMarkdown(Tokenizer): else: tText = tWrap.fill(tText) - # Then the text can receive final formatting before we append it to the results. - # We also store text lines in a buffer and merge them only when we find an empty line, + # Then the text can receive final formatting before we + # append it to the results. We also store text lines in a + # buffer and merge them only when we find an empty line, # indicating a new paragraph. if tType == self.T_EMPTY: if len(thisPar) > 0: diff --git a/nw/convert/text/totext.py b/nw/convert/text/totext.py index e6073012..e7fdb9d2 100644 --- a/nw/convert/text/totext.py +++ b/nw/convert/text/totext.py @@ -61,8 +61,9 @@ class ToText(Tokenizer): thisPar = [] for tType, tText, tFormat, tAlign in self.theTokens: - # First check if we have a comment or plain text, as they need some - # extra replacing before we proceed to wrapping and final formatting. + # First check if we have a comment or plain text, as they + # need some extra replacing before we proceed to wrapping + # and final formatting. if tType == self.T_COMMENT: tText = "[%s]" % tText @@ -74,7 +75,8 @@ class ToText(Tokenizer): tLen = len(tText) - # The text can now be word wrapped, if we have requested this and it's needed. + # The text can now be word wrapped, if we have requested + # this and it's needed. if tAlign == self.A_CENTRE: if self.wordWrap > 0: if tLen > self.wordWrap: @@ -88,8 +90,9 @@ class ToText(Tokenizer): if self.wordWrap > 0 and tLen > self.wordWrap: tText = tWrap.fill(tText) - # Then the text can receive final formatting before we append it to the results. - # We also store text lines in a buffer and merge them only when we find an empty line, + # Then the text can receive final formatting before we + # append it to the results. We also store text lines in a + # buffer and merge them only when we find an empty line, # indicating a new paragraph. if tType == self.T_EMPTY: if len(thisPar) > 0: diff --git a/nw/convert/tokenizer.py b/nw/convert/tokenizer.py index acc90b91..e391aab7 100644 --- a/nw/convert/tokenizer.py +++ b/nw/convert/tokenizer.py @@ -153,10 +153,11 @@ class Tokenizer(): return def tokenizeText(self): - """Scan the text for either lines starting with specific characters that indicate headers, - comments, commands etc, or just contains plain text. in the case of plain text, apply the - same RegExes that the syntax highlighter uses and save the locations of these formatting - tags into the token array. + """Scan the text for either lines starting with specific + characters that indicate headers, comments, commands etc, or + just contains plain text. in the case of plain text, apply the + same RegExes that the syntax highlighter uses and save the + locations of these formatting tags into the token array. """ # RegExes for adding formatting tags within text lines diff --git a/nw/gui/dialogs/export.py b/nw/gui/dialogs/export.py index d3be1260..ea2eb0bf 100644 --- a/nw/gui/dialogs/export.py +++ b/nw/gui/dialogs/export.py @@ -362,8 +362,8 @@ class GuiExportMain(QWidget): "Comments are exported as LaTeX comments." ), FMT_PDOC : ( - "Exports first to markdown or html5. The file is then passed on to Pandoc for a second " - "stage. Use the Pandoc tab for settings up the conversion." + "Exports first to markdown or html5. The file is then passed on to Pandoc for a " + "second stage. Use the Pandoc tab for settings up the conversion." ), } @@ -537,7 +537,8 @@ class GuiExportMain(QWidget): ## def _updateFormat(self, currIdx): - """Update help text under output format selection and file extension in file box + """Update help text under output format selection and file + extension in file box """ if currIdx == -1: self.outputHelp.setText("") diff --git a/nw/gui/dialogs/sessionlog.py b/nw/gui/dialogs/sessionlog.py index be2a1f94..0afd21c5 100644 --- a/nw/gui/dialogs/sessionlog.py +++ b/nw/gui/dialogs/sessionlog.py @@ -51,9 +51,15 @@ class GuiSessionLogView(QDialog): self.setMinimumWidth(420) self.setMinimumHeight(400) - widthCol0 = self.optState.validIntRange(self.optState.getSetting("widthCol0"), 30, 999, 180) - widthCol1 = self.optState.validIntRange(self.optState.getSetting("widthCol1"), 30, 999, 80) - widthCol2 = self.optState.validIntRange(self.optState.getSetting("widthCol2"), 30, 999, 80) + widthCol0 = self.optState.validIntRange( + self.optState.getSetting("widthCol0"), 30, 999, 180 + ) + widthCol1 = self.optState.validIntRange( + self.optState.getSetting("widthCol1"), 30, 999, 80 + ) + widthCol2 = self.optState.validIntRange( + self.optState.getSetting("widthCol2"), 30, 999, 80 + ) self.listBox = QTreeWidget() self.listBox.setHeaderLabels(["Session Start","Length","Words",""]) diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index e2214b03..4a21ddac 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -136,9 +136,9 @@ class GuiDocEditor(QTextEdit): return True def initEditor(self): - """Initialise or re-initialise the editor with the user's settings. - This function is both called when the editor is created, and when the user changes the - main editor preferences. + """Initialise or re-initialise the editor with the user's + settings. This function is both called when the editor is + created, and when the user changes the main editor preferences. """ # Reload dictionaries @@ -179,10 +179,12 @@ class GuiDocEditor(QTextEdit): # Initialise the syntax highlighter self.hLight.initHighlighter() - # If we have a document open, we should reload it in case the font changed, otherwise - # we just clear the editor entirely, which makes it read only. + # If we have a document open, we should reload it in case the + # font changed, otherwise we just clear the editor entirely, + # which makes it read only. if self.theHandle is not None: - # We must save the current handle as clearEditor() sets it to None + # We must save the current handle as clearEditor() sets it + # to None tHandle = self.theHandle self.clearEditor() self.loadText(tHandle) @@ -193,11 +195,13 @@ class GuiDocEditor(QTextEdit): return True def loadText(self, tHandle): - """Load text from a document into the editor. If we have an io error, we must handle this - and clear the editor so that we don't risk overwriting the file if it exists. This can for - instance happen of the file contains binary elements or an encoding that novelWriter does - not support. If load is successful, ot the document is new (empty string) we set up the - editor for editing the file. + """Load text from a document into the editor. If we have an io + error, we must handle this and clear the editor so that we don't + risk overwriting the file if it exists. This can for instance + happen of the file contains binary elements or an encoding that + novelWriter does not support. If load is successful, or the + document is new (empty string) we set up the editor for editing + the file. """ theDoc = self.nwDocument.openDocument(tHandle) @@ -251,8 +255,9 @@ class GuiDocEditor(QTextEdit): return self.docChanged def getText(self): - """Get the text content of the current document. This method uses QTextEdit->toPlainText for - Qt versions lower than 5.9, and the QDocument->toRawText for higher version. The latter + """Get the text content of the current document. This method + uses QTextEdit->toPlainText for Qt versions lower than 5.9, and + the QDocument->toRawText for higher version. The latter preserves non-breaking spaces, which the former does not. """ if self.mainConf.verQtValue >= 50900: @@ -296,8 +301,8 @@ class GuiDocEditor(QTextEdit): ## def changeWidth(self): - """Automatically adjust the margins so the text is centred, but only if Config.textFixedW is - set to True. + """Automatically adjust the margins so the text is centred, but + only if Config.textFixedW is set to True. """ if self.mainConf.textFixedW: vBar = self.verticalScrollBar() @@ -322,23 +327,40 @@ class GuiDocEditor(QTextEdit): if not self.theParent.hasProject: logger.error("No project open") return False - if theAction == nwDocAction.UNDO: self.undo() - elif theAction == nwDocAction.REDO: self.redo() - elif theAction == nwDocAction.CUT: self.cut() - elif theAction == nwDocAction.COPY: self.copy() - elif theAction == nwDocAction.PASTE: self.paste() - elif theAction == nwDocAction.BOLD: self._wrapSelection("**","**") - elif theAction == nwDocAction.ITALIC: self._wrapSelection("_","_") - elif theAction == nwDocAction.U_LINE: self._wrapSelection("__","__") - elif theAction == nwDocAction.S_QUOTE: self._wrapSelection(self.typSQOpen,self.typSQClose) - elif theAction == nwDocAction.D_QUOTE: self._wrapSelection(self.typDQOpen,self.typDQClose) - elif theAction == nwDocAction.SEL_ALL: self._makeSelection(QTextCursor.Document) - elif theAction == nwDocAction.SEL_PARA: self._makeSelection(QTextCursor.BlockUnderCursor) - elif theAction == nwDocAction.FIND: self._beginSearch() - elif theAction == nwDocAction.REPLACE: self._beginReplace() - elif theAction == nwDocAction.GO_NEXT: self._findNext() - elif theAction == nwDocAction.GO_PREV: self._findPrev() - elif theAction == nwDocAction.REPL_NEXT: self._replaceNext() + if theAction == nwDocAction.UNDO: + self.undo() + elif theAction == nwDocAction.REDO: + self.redo() + elif theAction == nwDocAction.CUT: + self.cut() + elif theAction == nwDocAction.COPY: + self.copy() + elif theAction == nwDocAction.PASTE: + self.paste() + elif theAction == nwDocAction.BOLD: + self._wrapSelection("**","**") + elif theAction == nwDocAction.ITALIC: + self._wrapSelection("_","_") + elif theAction == nwDocAction.U_LINE: + self._wrapSelection("__","__") + elif theAction == nwDocAction.S_QUOTE: + self._wrapSelection(self.typSQOpen,self.typSQClose) + elif theAction == nwDocAction.D_QUOTE: + self._wrapSelection(self.typDQOpen,self.typDQClose) + elif theAction == nwDocAction.SEL_ALL: + self._makeSelection(QTextCursor.Document) + elif theAction == nwDocAction.SEL_PARA: + self._makeSelection(QTextCursor.BlockUnderCursor) + elif theAction == nwDocAction.FIND: + self._beginSearch() + elif theAction == nwDocAction.REPLACE: + self._beginReplace() + elif theAction == nwDocAction.GO_NEXT: + self._findNext() + elif theAction == nwDocAction.GO_PREV: + self._findPrev() + elif theAction == nwDocAction.REPL_NEXT: + self._replaceNext() else: logger.error("Unknown or unsupported document action %s" % str(theAction)) return False @@ -366,13 +388,15 @@ class GuiDocEditor(QTextEdit): def keyPressEvent(self, keyEvent): """Intercept key press events. - We need to intercept key presses briefly to record the state of selection. This is in order - to know whether we had a selection prior to triggering the _docChange slot, as we do not - want to trigger autoreplace on selections. Autoreplace on selections messes with undo/redo - history. - We also need to intercept the Shift key modifier for certain key combinations that modifies - standard keys like enter and space. However, we don't want to spend a lot of time in this - function as it is triggered on every keypress when typing. + We need to intercept key presses briefly to record the state of + selection. This is in order to know whether we had a selection + prior to triggering the _docChange slot, as we do not want to + trigger autoreplace on selections. Autoreplace on selections + messes with undo/redo history. + We also need to intercept the Shift key modifier for certain key + combinations that modifies standard keys like enter and space. + However, we don't want to spend a lot of time in this function + as it is triggered on every keypress when typing. """ self.hasSelection = self.textCursor().hasSelection() @@ -393,8 +417,9 @@ class GuiDocEditor(QTextEdit): return def mouseReleaseEvent(self, mEvent): - """If the mouse button is released and the control key is pressed, check if we're clicking - on a tag, and trigger the follow tag function. + """If the mouse button is released and the control key is + pressed, check if we're clicking on a tag, and trigger the + follow tag function. """ if qApp.keyboardModifiers() == Qt.ControlModifier: theCursor = self.cursorForPosition(mEvent.pos()) @@ -407,9 +432,11 @@ class GuiDocEditor(QTextEdit): ## def _followTag(self, theCursor=None): - """Activated by Ctrl+Enter. Checks that we're in a block starting with '@'. We then find the - word under the cursor and check that it is after the ':'. If all this is fine, we have a tag - and can tell the document viewer to try and find and load the file where the tag is defined. + """Activated by Ctrl+Enter. Checks that we're in a block + starting with '@'. We then find the word under the cursor and + check that it is after the ':'. If all this is fine, we have a + tag and can tell the document viewer to try and find and load + the file where the tag is defined. """ if theCursor is None: @@ -574,7 +601,8 @@ class GuiDocEditor(QTextEdit): return def _runCounter(self): - """Decide whether to run the word counter, or stop the timer due to inactivity. + """Decide whether to run the word counter, or stop the timer due + to inactivity. """ sinceActive = time()-self.lastEdit if sinceActive > 5*self.wcInterval: @@ -603,9 +631,10 @@ class GuiDocEditor(QTextEdit): return def _wrapSelection(self, tBefore, tAfter): - """Wraps the selected text in whatever is in tBefore and tAfter. If there is no selection, - the autoSelect setting decides the action. AutoSelect will select the word under the cursor - before wrapping it. If this feature is disabled, nothing is done. + """Wraps the selected text in whatever is in tBefore and tAfter. + If there is no selection, the autoSelect setting decides the + action. AutoSelect will select the word under the cursor before + wrapping it. If this feature is disabled, nothing is done. """ theCursor = self.textCursor() if self.mainConf.autoSelect and not theCursor.hasSelection(): @@ -643,15 +672,16 @@ class GuiDocEditor(QTextEdit): return def _beginReplace(self): - """Opens the replace line of the search bar and sets the replace text. + """Opens the replace line of the search bar and sets the replace + text. """ self._beginSearch() self.theParent.searchBar.setReplaceText("") return def _findNext(self): - """Searches for the next occurrence of the search bar text in the document. - Wraps back to the top if not found. + """Searches for the next occurrence of the search bar text in + the document. Wraps back to the top if not found. """ searchFor = self.theParent.searchBar.getSearchText() wasFound = self.find(searchFor) @@ -662,8 +692,8 @@ class GuiDocEditor(QTextEdit): return def _findPrev(self): - """Searches for the previous occurrence of the search bar text in the document. - Wraps back to the end if not found. + """Searches for the previous occurrence of the search bar text + in the document. Wraps back to the end if not found. """ searchFor = self.theParent.searchBar.getSearchText() wasFound = self.find(searchFor, QTextDocument.FindBackward) @@ -674,8 +704,9 @@ class GuiDocEditor(QTextEdit): return def _replaceNext(self): - """Searches for the next occurrence of the search bar text in the document and replaces it - with the replace text. Wraps back to the top if not found. + """Searches for the next occurrence of the search bar text in + the document and replaces it with the replace text. Wraps back + to the top if not found. """ theCursor = self.textCursor() searchFor = self.theParent.searchBar.getSearchText() diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index 854ceac4..bafa5b48 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -129,7 +129,8 @@ class GuiDocTree(QTreeWidget): tHandle = self.theProject.newRoot(nwLabels.CLASS_NAME[itemClass], itemClass) else: - # If no parent has been selected, make the new file under the root NOVEL item. + # If no parent has been selected, make the new file under + # the root NOVEL item. if pHandle is None: pHandle = self.theProject.findRootItem(nwItemClass.NOVEL) @@ -138,7 +139,8 @@ class GuiDocTree(QTreeWidget): logger.error("Did not find anywhere to add the item!") return False - # Now check if the selected item is a file, in which case the new file will be a sibling + # Now check if the selected item is a file, in which case + # the new file will be a sibling pItem = self.theProject.getItem(pHandle) if pItem.itemType == nwItemType.FILE: pHandle = pItem.parHandle @@ -177,8 +179,8 @@ class GuiDocTree(QTreeWidget): return True def moveTreeItem(self, nStep): - """Move an item up or down in the tree, but only if the treeView has focus. This also - applies when the menu is used. + """Move an item up or down in the tree, but only if the treeView + has focus. This also applies when the menu is used. """ if QApplication.focusWidget() == self and self.theParent.hasProject: tHandle = self.getSelectedHandle() @@ -225,10 +227,11 @@ class GuiDocTree(QTreeWidget): return retVals def deleteItem(self, tHandle=None): - """Delete items from the tree. Note that this does not delete the item from the item tree in - the project object. However, since this is only meta data, there isn't really a need to do - that to save memory. Items not in the tree are not saved to the project file, so a loaded - project will be clean anyway. + """Delete items from the tree. Note that this does not delete + the item from the item tree in the project object. However, + since this is only meta data, there isn't really a need to do + that to save memory. Items not in the tree are not saved to the + project file, so a loaded project will be clean anyway. """ if tHandle is None: @@ -453,8 +456,9 @@ class GuiDocTree(QTreeWidget): return def _updateItemParent(self, tHandle): - """Update the parent handle of an item so that the information in the project is consistent - with the treeView. Also move the word count over to the new parent tree. + """Update the parent handle of an item so that the information + in the project is consistent with the treeView. Also move the + word count over to the new parent tree. """ trItemS = self._getTreeItem(tHandle) @@ -496,8 +500,8 @@ class GuiDocTree(QTreeWidget): ## def mousePressEvent(self, theEvent): - """Overload mousePressEvent to clear selection if clicking the mouse in a blank - area of the tree view. + """Overload mousePressEvent to clear selection if clicking the + mouse in a blank area of the tree view. """ QTreeWidget.mousePressEvent(self, theEvent) selItem = self.indexAt(theEvent.pos()) @@ -506,8 +510,8 @@ class GuiDocTree(QTreeWidget): return def dropEvent(self, theEvent): - """Overload the drop of dragged item event to check whether the drop is allowed - or not. Disallowed drops are cancelled. + """Overload the drop of dragged item event to check whether the + drop is allowed or not. Disallowed drops are cancelled. """ sHandle = self.getSelectedHandle() if sHandle is None: diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 49551b9b..ffa876c5 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -17,7 +17,7 @@ from PyQt5.QtCore import QUrl from PyQt5.QtGui import QIcon, QDesktopServices from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox -from nw.enum import nwItemType, nwItemClass, nwDocAction +from nw.enum import nwItemType, nwItemClass, nwDocAction logger = logging.getLogger(__name__) @@ -122,8 +122,9 @@ class GuiMainMenu(QMenuBar): aboutMsg = ( "

About {name:s}

" "

Version: {version:s}
Release Date: {date:s}

" - "

{name:s} is a markdown-like text editor designed for organising and writing novels. " - "It is written in Python 3 with a Qt5 GUI, using PyQt5

" + "

{name:s} is a markdown-like text editor designed for organising " + "and writing novels. It is written in Python 3 with a Qt5 GUI, " + "using PyQt5

" "

{name:s} is licensed under GPL v3.0

" "

{copyright:s}

" "

Website: {website:s}

" diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index a6bf6f01..8eedb626 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -244,7 +244,8 @@ class GuiMain(QMainWindow): 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. + 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 @@ -288,15 +289,17 @@ class GuiMain(QMainWindow): return saveOK def openProject(self, projFile=None): - """Open a project. The parameter projFile is passed from the open recent projects menu, so - can be set. If not, we pop the dialog. + """Open a project. The parameter 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 + # Make sure any open project is cleared out first before we load + # another one if not self.closeProject(): return False @@ -629,8 +632,9 @@ class GuiMain(QMainWindow): 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. + """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): @@ -724,7 +728,8 @@ class GuiMain(QMainWindow): return True def _autoSaveProject(self): - if self.hasProject and self.theProject.projChanged and self.theProject.projPath is not None: + if (self.hasProject and self.theProject.projChanged and + self.theProject.projPath is not None): logger.debug("Autosaving project") self.saveProject(isAuto=True) return @@ -756,8 +761,8 @@ class GuiMain(QMainWindow): ## def resizeEvent(self, theEvent): - """Extend QMainWindow.resizeEvent to signal dependent GUI elements that its pane may have - changed size. + """Extend QMainWindow.resizeEvent to signal dependent GUI + elements that its pane may have changed size. """ QMainWindow.resizeEvent(self,theEvent) self.docEditor.changeWidth() @@ -803,7 +808,8 @@ class GuiMain(QMainWindow): return def _keyPressEscape(self): - """When the escape key is pressed somewhere in the main window, do the following, in order. + """When the escape key is pressed somewhere in the main window, + do the following, in order. """ if self.searchBar.isVisible(): self.searchBar.setVisible(False) @@ -811,13 +817,15 @@ class GuiMain(QMainWindow): return def _splitMainMove(self, pWidth, pHeight): - """Alert dependent GUI elements that the main pane splitter has been moved. + """Alert dependent GUI elements that the main pane splitter has + been moved. """ self.docEditor.changeWidth() return def _splitViewMove(self, pWidth, pHeight): - """Alert dependent GUI elements that the main pane splitter has been moved. + """Alert dependent GUI elements that the main pane splitter has + been moved. """ self.docEditor.changeWidth() return diff --git a/nw/project/document.py b/nw/project/document.py index e54a72f0..3e807a2b 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -53,7 +53,8 @@ class NWDoc(): self.clearDocument() return None - # By default, the document is editable. Except for files in the trash folder. + # By default, the document is editable. + # Except for files in the trash folder. self.docEditable = True if self.theItem.parHandle == self.theProject.trashRoot: self.docEditable = False @@ -70,13 +71,14 @@ class NWDoc(): theDoc = inFile.read() except Exception as e: self.makeAlert(["Failed to open document file.",str(e)], nwAlert.ERROR) - # Note: Document must be cleared in case of an io error, or else the auto-save or - # save will try to overwrite it with an empty file. Return None to alert the caller. + # Note: Document must be cleared in case of an io error, + # or else the auto-save or save will try to overwrite it + # with an empty file. Return None to alert the caller. self.clearDocument() return None else: - # The document file does not exist, so we assume it's a new document and initialise an - # empty text string. + # The document file does not exist, so we assume it's a new + # document and initialise an empty text string. logger.debug("The requested document does not exist.") return "" diff --git a/nw/project/index.py b/nw/project/index.py index db7d21e1..d3b62613 100644 --- a/nw/project/index.py +++ b/nw/project/index.py @@ -130,7 +130,8 @@ class NWIndex(): return False def saveIndex(self): - """Save the current index as a json file in the project meta folder. + """Save the current index as a json file in the project meta + folder. """ indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE) @@ -155,7 +156,8 @@ class NWIndex(): return True def checkIndex(self): - """Check that the entries in the index are valid and contain the elements it should. + """Check that the entries in the index are valid and contain the + elements it should. """ self.indexBroken = False @@ -193,8 +195,9 @@ class NWIndex(): ## def scanText(self, tHandle, theText): - """Scan a piece of text associated with a handle. This will update the indices accordingly. - This function takes the handle and text as separate inputs as we want to primarily scan the + """Scan a piece of text associated with a handle. This will + update the indices accordingly. This function takes the handle + and text as separate inputs as we want to primarily scan the files before we save them, unless we're rebuilding the index. """ @@ -243,7 +246,8 @@ class NWIndex(): return True def indexTitle(self, tHandle, isNovel, aLine, nLine, itemLayout): - """Save information about the title and its location in the file. + """Save information about the title and its location in the + file. """ if aLine.startswith("# "): @@ -272,7 +276,8 @@ class NWIndex(): return True def indexNoteRef(self, tHandle, aLine, nLine, nTitle): - """Validate and save the information about a reference to a tag in another file. + """Validate and save the information about a reference to a tag + in another file. """ isValid, theBits, thePos = self.scanThis(aLine) @@ -303,8 +308,9 @@ class NWIndex(): ## def scanThis(self, aLine): - """Scan a line starting with @ to check that it's valid and to split up its elements into - an array and an array of positions. The latter is needed for the syntax highlighter. + """Scan a line starting with @ to check that it's valid and to + split up its elements into an array and an array of positions. + The latter is needed for the syntax highlighter. """ theBits = [] @@ -343,8 +349,8 @@ class NWIndex(): return True, theBits, thePos def checkThese(self, theBits, tItem): - """Check the tags against the index to see if they are valid tags. This is needed for syntax - highlighting. + """Check the tags against the index to see if they are valid + tags. This is needed for syntax highlighting. """ nBits = len(theBits) @@ -357,7 +363,8 @@ class NWIndex(): if not isGood[0] or nBits == 1: return isGood - # If we have a tag, only the first value is accepted, the rest is ignored + # If we have a tag, only the first value is accepted, the rest + # is ignored if theBits[0] == nwKeyWords.TAG_KEY and nBits > 1: isGood[0] = True if theBits[1] in self.tagIndex.keys(): @@ -396,7 +403,8 @@ class NWIndex(): return True def buildReferenceList(self, tHandle): - """Build a list of files referring back to our file, specified by tHandle. + """Build a list of files referring back to our file, specified + by tHandle. """ theRefs = {} @@ -429,8 +437,9 @@ class NWIndex(): return None, 0 def buildTagNovelMap(self, theTags, theFilters=None): - """Build a two-dimensional map of all titles of the novel and which tags they link to from - the various meta tags. This map is used to display the timeline view. + """Build a two-dimensional map of all titles of the novel and + which tags they link to from the various meta tags. This map is + used to display the timeline view. """ tagMap = {} diff --git a/nw/project/item.py b/nw/project/item.py index 55d532a1..9640265b 100644 --- a/nw/project/item.py +++ b/nw/project/item.py @@ -85,17 +85,28 @@ class NWItem(): def setFromTag(self, tagName, tagValue): logger.verbose("Setting tag '%s' to value '%s'" % (tagName, str(tagValue))) - if tagName == "name": self.setName(tagValue) - elif tagName == "order": self.setOrder(tagValue) - elif tagName == "type": self.setType(tagValue) - elif tagName == "class": self.setClass(tagValue) - elif tagName == "layout": self.setLayout(tagValue) - elif tagName == "status": self.setStatus(tagValue) - elif tagName == "expanded": self.setExpanded(tagValue) - elif tagName == "charCount": self.setCharCount(tagValue) - elif tagName == "wordCount": self.setWordCount(tagValue) - elif tagName == "paraCount": self.setParaCount(tagValue) - elif tagName == "cursorPos": self.setCursorPos(tagValue) + if tagName == "name": + self.setName(tagValue) + elif tagName == "order": + self.setOrder(tagValue) + elif tagName == "type": + self.setType(tagValue) + elif tagName == "class": + self.setClass(tagValue) + elif tagName == "layout": + self.setLayout(tagValue) + elif tagName == "status": + self.setStatus(tagValue) + elif tagName == "expanded": + self.setExpanded(tagValue) + elif tagName == "charCount": + self.setCharCount(tagValue) + elif tagName == "wordCount": + self.setWordCount(tagValue) + elif tagName == "paraCount": + self.setParaCount(tagValue) + elif tagName == "cursorPos": + self.setCursorPos(tagValue) else: logger.error("Unknown tag '%s'" % tagName) return diff --git a/nw/project/project.py b/nw/project/project.py index 9a0bfd03..a0208885 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -37,7 +37,7 @@ class NWProject(): self.mainConf = self.theParent.mainConf self.projOpened = None # The time stamp of when the project file was opened self.projChanged = None # The project has unsaved changes - self.projAltered = None # The project has been altered this session (used to trigger backup) + self.projAltered = None # The project has been altered this session # Debug self.handleSeed = None @@ -224,7 +224,8 @@ class NWProject(): if xChild.tag == "project": logger.debug("Found project meta") for xItem in xChild: - if xItem.text is None: continue + if xItem.text is None: + continue if xItem.tag == "name": logger.verbose("Working Title: '%s'" % xItem.text) self.projName = xItem.text @@ -239,7 +240,8 @@ class NWProject(): elif xChild.tag == "settings": logger.debug("Found project settings") for xItem in xChild: - if xItem.text is None: continue + if xItem.text is None: + continue if xItem.tag == "spellCheck": self.spellCheck = checkBool(xItem.text,False) elif xItem.tag == "lastEdited": @@ -493,8 +495,9 @@ class NWProject(): return None def getRootItem(self, tHandle): - """Iterate upwards in the tree until we find the item with parent None, the root item. - We do this with a for loop with a maximum depth of 200 to make infinite loops impossible. + """Iterate upwards in the tree until we find the item with + parent None, the root item. We do this with a for loop with a + maximum depth of 200 to make infinite loops impossible. """ tItem = self.getItem(tHandle) if tItem is not None: @@ -506,9 +509,10 @@ class NWProject(): return None def getProjectItems(self): - """This function is called from the tree view when building the tree. Each item in the - project is returned in the order saved in the project file, but first it checks that it has - a parent item already sent to the tree. + """This function is called from the tree view when building the + tree. Each item in the project is returned in the order saved in + the project file, but first it checks that it has a parent item + already sent to the tree. """ sentItems = [] iterItems = self.treeOrder.copy() @@ -521,10 +525,12 @@ class NWProject(): if n > 10000: return # Just in case if tItem is None: - # Technically a bug since treeOrder is built from the same data as projTree + # Technically a bug since treeOrder is built from the + # same data as projTree continue elif tItem.parHandle is None: - # Item is a root, or already been identified as an orphaned item + # Item is a root, or already been identified as an + # orphaned item sentItems.append(tHandle) yield tItem elif tItem.parHandle in sentItems: @@ -532,7 +538,8 @@ class NWProject(): sentItems.append(tHandle) yield tItem elif tItem.parHandle in iterItems: - # Item's parent exists, but hasn't been sent yet, so add it again to the end + # Item's parent exists, but hasn't been sent yet, so add + # it again to the end logger.warning("Item %s found before its parent" % tHandle) iterItems.append(tHandle) nMax = len(iterItems) @@ -547,7 +554,8 @@ class NWProject(): ## def deleteItem(self, tHandle): - """This only removes the item from the order list, but not from the project tree. + """This only removes the item from the order list, but not from + the project tree. """ self.treeOrder.remove(tHandle) self.setProjectChanged(True) @@ -560,8 +568,8 @@ class NWProject(): return None def checkRootUnique(self, theClass): - """Checks if there already is a root entry of class 'theClass' in the - root of the project tree. + """Checks if there already is a root entry of class 'theClass' + in the root of the project tree. """ if theClass == nwItemClass.CUSTOM: return True @@ -689,7 +697,9 @@ class NWProject(): if self.projMeta is None: return False - with open(path.join(self.projMeta, nwFiles.SESS_INFO),mode="a+",encoding="utf8") as outFile: + sessionFile = path.join(self.projMeta, nwFiles.SESS_INFO) + + with open(sessionFile,mode="a+",encoding="utf8") as outFile: print(( "Start: {opened:s} " "End: {closed:s} " @@ -717,9 +727,10 @@ class NWProject(): return itemHandle def _maintainPrevious(self): - """This function will take the current project file and copy it into the project cache - folder with an incremental file extension added. These serve as a backup in case the xml - file gets corrupted. + """This function will take the current project file and copy it + into the project cache folder with an incremental file extension + added. These serve as a backup in case the xml file gets + corrupted. """ countFile = path.join(self.projCache, nwFiles.PROJ_COUNT) diff --git a/nw/project/status.py b/nw/project/status.py index ea1a045a..d3bbb560 100644 --- a/nw/project/status.py +++ b/nw/project/status.py @@ -13,7 +13,7 @@ import logging import nw -from lxml import etree +from lxml import etree from nw.enum import nwItemClass from nw.common import checkInt diff --git a/nw/tools/analyse.py b/nw/tools/analyse.py index 37076063..bb847d74 100644 --- a/nw/tools/analyse.py +++ b/nw/tools/analyse.py @@ -58,7 +58,7 @@ class TextAnalysis(): return rScore, gLevel def getReadabilityText(self, rScore): - if rScore >= 90.0: + if rScore >= 90.0: return "Very Easy" elif rScore >= 80.0: return "Easy" @@ -78,13 +78,15 @@ class TextAnalysis(): # def _countWords(self): - """Counts the number of words in a text by simply splitting on all white spaces. + """Counts the number of words in a text by simply splitting on + all white spaces. """ return len(self.theText.strip().split()) def _countSentences(self): - """Counts the number of non-repeated sentence endings seen in the text. - Note: This will count filenames and urls as multiple sentences. + """Counts the number of non-repeated sentence endings seen in + the text. Note: This will count filenames and urls as multiple + sentences. """ nSent = 0 sawEnd = False @@ -98,7 +100,8 @@ class TextAnalysis(): return nSent def _countParagraphs(self, pThreshold=2): - """Counts the number of paragraphs by counting repeated line breaks. + """Counts the number of paragraphs by counting repeated line + breaks. """ nPara = 1 sawEnd = 0 @@ -114,9 +117,10 @@ class TextAnalysis(): return nPara def _countSyllablesEN(self): - """Attempt to count the syllables in a piece of English language text. - This function tends to slightly over-estimate the number of syllables as it doesn't handle - the complexity of silent vowels in endings very well. It will count them all. + """Attempt to count the syllables in a piece of English language + text. This function tends to slightly over-estimate the number + of syllables as it doesn't handle the complexity of silent + vowels in endings very well. It will count them all. """ cleanText = "" @@ -160,7 +164,6 @@ class TextAnalysis(): nSyll += 1 if nSyll < 1: nSyll = 1 - # print("%-15s: %d" % (inWord,nSyll)) allSylls += nSyll return allSylls/len(theWords) diff --git a/nw/tools/spellenchant.py b/nw/tools/spellenchant.py index b2faa472..cf4f1574 100644 --- a/nw/tools/spellenchant.py +++ b/nw/tools/spellenchant.py @@ -32,8 +32,9 @@ class NWSpellEnchant(NWSpellCheck): return def setLanguage(self, theLang, projectDict=None): - """Load a dictionary for the language specified in the config. If that fails, we load a - dummy dictionary so that lookups don't crash. + """Load a dictionary for the language specified in the config. + If that fails, we load a dummy dictionary so that lookups don't + crash. """ try: if projectDict is None: