From d973f28755c2a69893127ab8d83d659cf25f12a3 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 15 Oct 2020 18:39:15 +0200 Subject: [PATCH 1/6] Code and comment cleanup in main GUI --- nw/core/document.py | 4 +-- nw/core/project.py | 4 +-- nw/guimain.py | 67 ++++++++++++++++++++++++++++----------------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/nw/core/document.py b/nw/core/document.py index 1261c12d..00fd7146 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -120,7 +120,7 @@ class NWDoc(): logger.verbose("DocMeta: '%s'" % self._docMeta) if showStatus and not isOrphan: - self.theParent.statusBar.setStatus("Opened Document: %s" % self._theItem.itemName) + self.theParent.setStatus("Opened Document: %s" % self._theItem.itemName) return theText @@ -166,7 +166,7 @@ class NWDoc(): os.unlink(docPath) os.rename(docTemp, docPath) - self.theParent.statusBar.setStatus("Saved Document: %s" % self._theItem.itemName) + self.theParent.setStatus("Saved Document: %s" % self._theItem.itemName) return True diff --git a/nw/core/project.py b/nw/core/project.py index 46e37202..15fd7d27 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -782,7 +782,7 @@ class NWProject(): """Create a zip file of the entire project. """ logger.info("Backing up project") - self.theParent.statusBar.setStatus("Backing up project ...") + self.theParent.setStatus("Backing up project ...") if self.mainConf.backupPath is None or self.mainConf.backupPath == "": self.theParent.makeAlert(( @@ -847,7 +847,7 @@ class NWProject(): ) return False - self.theParent.statusBar.setStatus("Project backed up to '%s.zip'" % baseName) + self.theParent.setStatus("Project backed up to '%s.zip'" % baseName) return True diff --git a/nw/guimain.py b/nw/guimain.py index 18801727..6742a0d9 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -62,7 +62,9 @@ class GuiMain(QMainWindow): self.mainConf = nw.CONFIG self.threadPool = QThreadPool() - # Some runtime info useful for debugging + # System Info + # =========== + logger.info("OS: %s" % self.mainConf.osType) logger.info("Kernel: %s" % self.mainConf.kernelVer) logger.info("Host: %s" % self.mainConf.hostName) @@ -76,6 +78,9 @@ class GuiMain(QMainWindow): self.mainConf.verPyString, self.mainConf.verPyHexVal) ) + # Core Classes + # ============ + # Core Classes and settings self.theTheme = GuiTheme(self) self.theProject = NWProject(self) @@ -89,7 +94,7 @@ class GuiMain(QMainWindow): self.setWindowIcon(QIcon(self.mainConf.appIcon)) # Build the GUI - ################ + # ============= # Main GUI Elements self.statusBar = GuiMainStatus(self) @@ -106,7 +111,7 @@ class GuiMain(QMainWindow): self.statusIcons = [] self.importIcons = [] - # Assemble Main Window + # Project Tree View self.treePane = QWidget() self.treeBox = QVBoxLayout() self.treeBox.setContentsMargins(0, 0, 0, 0) @@ -114,20 +119,24 @@ class GuiMain(QMainWindow): self.treeBox.addWidget(self.treeMeta) self.treePane.setLayout(self.treeBox) + # Splitter : Document Viewer / Document Meta self.splitView = QSplitter(Qt.Vertical) self.splitView.addWidget(self.docViewer) self.splitView.addWidget(self.viewMeta) self.splitView.setSizes(self.mainConf.getViewPanePos()) + # Splitter : Document Editor / Document Viewer self.splitDocs = QSplitter(Qt.Horizontal) self.splitDocs.addWidget(self.docEditor) self.splitDocs.addWidget(self.splitView) + # Splitter : Project Outlie / Outline Details self.splitOutline = QSplitter(Qt.Vertical) self.splitOutline.addWidget(self.projView) self.splitOutline.addWidget(self.projMeta) self.splitOutline.setSizes(self.mainConf.getOutlinePanePos()) + # Main Tabs : Edirot / Outline self.tabWidget = QTabWidget() self.tabWidget.setTabPosition(QTabWidget.East) self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}") @@ -135,6 +144,7 @@ class GuiMain(QMainWindow): self.tabWidget.addTab(self.splitOutline, "Outline") self.tabWidget.currentChanged.connect(self._mainTabChanged) + # Splitter : Project Tree / Main Tabs xCM = self.mainConf.pxInt(4) self.splitMain = QSplitter(Qt.Horizontal) self.splitMain.setContentsMargins(xCM, xCM, xCM, xCM) @@ -142,6 +152,7 @@ class GuiMain(QMainWindow): self.splitMain.addWidget(self.tabWidget) self.splitMain.setSizes(self.mainConf.getMainPanePos()) + # Indices of All Splitter Widgets self.idxTree = self.splitMain.indexOf(self.treePane) self.idxMain = self.splitMain.indexOf(self.tabWidget) self.idxEditor = self.splitDocs.indexOf(self.docEditor) @@ -151,6 +162,7 @@ class GuiMain(QMainWindow): self.idxTabEdit = self.tabWidget.indexOf(self.splitDocs) self.idxTabProj = self.tabWidget.indexOf(self.splitOutline) + # Splitter Behaviour self.splitMain.setCollapsible(self.idxTree, False) self.splitMain.setCollapsible(self.idxMain, False) self.splitDocs.setCollapsible(self.idxEditor, False) @@ -158,10 +170,11 @@ class GuiMain(QMainWindow): self.splitView.setCollapsible(self.idxViewDoc, False) self.splitView.setCollapsible(self.idxViewMeta, False) + # Editor / Viewer Default State self.splitView.setVisible(False) self.docEditor.closeSearch() - # Build the Tree View + # Initialise the Project Tree self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) self.rebuildTree() @@ -172,13 +185,13 @@ class GuiMain(QMainWindow): self.setStatusBar(self.statusBar) # Finalise Initialisation - ########################## + # ======================= - # Set Up Autosaving Project Timer + # Set Up Auto-Save Project Timer self.asProjTimer = QTimer() self.asProjTimer.timeout.connect(self._autoSaveProject) - # Set Up Autosaving Document Timer + # Set Up Auto-Save Document Timer self.asDocTimer = QTimer() self.asDocTimer.timeout.connect(self._autoSaveDocument) @@ -203,11 +216,13 @@ class GuiMain(QMainWindow): # Check that config loaded fine self.reportConfErr() + # Initialise Main GUI self.initMain() self.asProjTimer.start() self.asDocTimer.start() self.statusBar.clearStatus() + # Handle Windows Mode self.showNormal() if self.mainConf.isFullScreen: self.toggleFullScreenMode() @@ -224,7 +239,7 @@ class GuiMain(QMainWindow): self.showProjectLoadDialog() logger.debug("novelWriter is ready ...") - self.statusBar.setStatus("novelWriter is ready ...") + self.setStatus("novelWriter is ready ...") return @@ -249,8 +264,7 @@ class GuiMain(QMainWindow): ## def newProject(self, projData=None): - """Create new project with a few default files and folders. - The variable forceNew is used for testing. + """Create new project via the new project wizard. """ if self.hasProject: self.makeAlert( @@ -293,7 +307,7 @@ 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. + twice to confirm. """ if not self.hasProject: # There is no project loaded, everything OK @@ -302,7 +316,7 @@ class GuiMain(QMainWindow): if not isYes: msgBox = QMessageBox() msgRes = msgBox.question( - self, "Close Project", "Save changes and close current project?" + self, "Close Project", "Save changes and close the current project?" ) if msgRes != QMessageBox.Yes: return False @@ -318,7 +332,7 @@ class GuiMain(QMainWindow): if self.mainConf.askBeforeBackup: msgBox = QMessageBox() msgRes = msgBox.question( - self, "Backup Project", "Backup current project?" + self, "Backup Project", "Backup the current project?" ) if msgRes != QMessageBox.Yes: doBackup = False @@ -433,6 +447,7 @@ class GuiMain(QMainWindow): if self.theProject.projPath is None: projPath = self.selectProjectPath() self.theProject.setProjectPath(projPath) + if self.theProject.projPath is None: return False @@ -454,6 +469,7 @@ class GuiMain(QMainWindow): if self.docEditor.docChanged: self.saveDocument() self.docEditor.clearEditor() + return True def openDocument(self, tHandle, tLine=None, changeFocus=True, doScroll=False): @@ -469,6 +485,7 @@ class GuiMain(QMainWindow): self.treeView.setSelectedHandle(tHandle, doScroll=doScroll) else: return False + return True def openNextDocument(self, tHandle, wrapAround=False): @@ -546,6 +563,7 @@ class GuiMain(QMainWindow): vPos[1] = bPos[1] - vPos[0] self.splitDocs.setSizes(vPos) self.viewMeta.setVisible(self.mainConf.showRefPanel) + self.docViewer.navigateTo(tAnchor) return True @@ -697,9 +715,9 @@ class GuiMain(QMainWindow): for nDone, tItem in enumerate(self.theProject.projTree): if tItem is not None: - self.statusBar.setStatus("Indexing: '%s'" % tItem.itemName) + self.setStatus("Indexing: '%s'" % tItem.itemName) else: - self.statusBar.setStatus("Indexing: Unknown item") + self.setStatus("Indexing: Unknown item") if tItem is not None and tItem.itemType == nwItemType.FILE: logger.verbose("Scanning: %s" % tItem.itemName) @@ -717,7 +735,7 @@ class GuiMain(QMainWindow): self.treeView.projectWordCount() tEnd = time() - self.statusBar.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0)) + self.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0)) self.docEditor.updateTagHighLighting() qApp.restoreOverrideCursor() @@ -754,7 +772,8 @@ class GuiMain(QMainWindow): def showProjectLoadDialog(self): """Opens the projects dialog for selecting either existing projects from a cache of recently opened projects, or provide a - browse button for projects not yet cached. + browse button for projects not yet cached. Selecting to create a + new project is forwarded to the new project wizard. """ dlgProj = GuiProjectLoad(self) dlgProj.exec_() @@ -767,7 +786,7 @@ class GuiMain(QMainWindow): return True def showNewProjectDialog(self): - """Open the wizard and assemble the project options dict. + """Open the wizard and assemble a project options dict. """ newProj = GuiProjectWizard(self) newProj.exec_() @@ -865,8 +884,7 @@ class GuiMain(QMainWindow): 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. + can be either a string or an array of strings. """ if isinstance(theMessage, list): popMsg = "
".join(theMessage) @@ -955,7 +973,7 @@ class GuiMain(QMainWindow): return True def setFocus(self, paneNo): - """Switch focus to one of the three main gUi panes. + """Switch focus to one of the three main GUI panes. """ if paneNo == 1: self.treeView.setFocus() @@ -1236,9 +1254,9 @@ class GuiMain(QMainWindow): return def _treeKeyPressReturn(self): - """The user pressed return an item in the tree. If it is a file, - we open it. Otherwise, we do nothing. Pressing return does not - change focus to the editor as double click does. + """The user pressed return on an item in the tree. If it is a + file, we open it. Otherwise, we do nothing. Pressing return does + not change focus to the editor as double click does. """ tHandle = self.treeView.getSelectedHandle() logger.verbose("User pressed return on tree item with handle %s" % tHandle) @@ -1257,7 +1275,6 @@ class GuiMain(QMainWindow): """ if self.docEditor.docSearch.isVisible(): self.docEditor.closeSearch() - return elif self.isFocusMode: self.toggleFocusMode() return From 1923a55443cc5e861718912c036777e8fafd4099 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 15 Oct 2020 19:08:35 +0200 Subject: [PATCH 2/6] Code cleanup in core classes and rename of parHandle to itemParent in NWItem class --- nw/core/document.py | 11 +++++++---- nw/core/index.py | 6 +++--- nw/core/item.py | 19 +++++++++++-------- nw/core/project.py | 6 +++--- nw/core/tree.py | 10 +++++----- nw/gui/build.py | 4 ++-- nw/gui/docmerge.py | 2 +- nw/gui/docsplit.py | 4 ++-- nw/gui/projtree.py | 12 ++++++------ tests/test_item.py | 10 +++++----- tests/test_project.py | 4 ++-- 11 files changed, 47 insertions(+), 41 deletions(-) diff --git a/nw/core/document.py b/nw/core/document.py index 00fd7146..1452e9dd 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -67,7 +67,9 @@ class NWDoc(): def openDocument(self, tHandle, showStatus=True, isOrphan=False): """Open a document from handle, capturing potential file system - errors and parse meta data. + errors and parse meta data. If the document doesn't exist on + disk, return an empty string. If something went wrong, return + None. """ if not isHandle(tHandle): return None @@ -125,8 +127,8 @@ class NWDoc(): return theText def saveDocument(self, docText): - """Save the document via temp file in case of save failure, and - in any case keep a backup of the file. + """Save the document. The file is saved via a temp file in case + of save failure. Returns True if successful, False if not. """ if self._docHandle is None: return False @@ -139,6 +141,7 @@ class NWDoc(): docPath = os.path.join(self.theProject.projContent, docFile) docTemp = os.path.join(self.theProject.projContent, docFile+"~") + # DocMeta line if self._theItem is None: docMeta = "" else: @@ -171,7 +174,7 @@ class NWDoc(): return True def deleteDocument(self, tHandle): - """Permanently delete a document source file and its backups + """Permanently delete a document source file and related files from the project data folder. """ if not isHandle(tHandle): diff --git a/nw/core/index.py b/nw/core/index.py index f715d835..c08562e1 100644 --- a/nw/core/index.py +++ b/nw/core/index.py @@ -279,7 +279,7 @@ class NWIndex(): if theItem.itemLayout == nwItemLayout.NO_LAYOUT: logger.info("Not indexing no-layout item %s" % tHandle) return False - if theItem.parHandle is None: + if theItem.itemParent is None: logger.info("Not indexing orphaned item %s" % tHandle) return False @@ -288,7 +288,7 @@ class NWIndex(): self.textCounts[tHandle] = [cC, wC, pC] # If the file is archived or trashed, we don't index the file itself - if self.theProject.projTree.isTrashRoot(theItem.parHandle): + if self.theProject.projTree.isTrashRoot(theItem.itemParent): logger.info("Not indexing trash item %s" % tHandle) return False if theRoot.itemClass == nwItemClass.ARCHIVE: @@ -583,7 +583,7 @@ class NWIndex(): def getCounts(self, tHandle, sTitle=None): """Returns the counts for a file, or a section of a file - starting at title nTitle. + starting at title sTitle if it is provided. """ cC = 0 wC = 0 diff --git a/nw/core/item.py b/nw/core/item.py index 702287dd..944b5f0c 100644 --- a/nw/core/item.py +++ b/nw/core/item.py @@ -42,7 +42,7 @@ class NWItem(): self.itemName = "" self.itemHandle = None - self.parHandle = None + self.itemParent = None self.itemOrder = None self.itemType = nwItemType.NO_TYPE self.itemClass = nwItemClass.NO_CLASS @@ -70,7 +70,7 @@ class NWItem(): xPack = etree.SubElement(xParent, "item", attrib={ "handle" : str(self.itemHandle), "order" : str(self.itemOrder), - "parent" : str(self.parHandle), + "parent" : str(self.itemParent), }) self._subPack(xPack, "name", text=str(self.itemName)) self._subPack(xPack, "type", text=str(self.itemType.name)) @@ -85,6 +85,7 @@ class NWItem(): self._subPack(xPack, "cursorPos", text=str(self.cursorPos), none=False) else: self._subPack(xPack, "expanded", text=str(self.isExpanded)) + return def unpackXML(self, xItem): @@ -101,7 +102,7 @@ class NWItem(): return False if "parent" in xItem.attrib: - self.parHandle = xItem.attrib["parent"] + self.itemParent = xItem.attrib["parent"] setMap = { "name" : self.setName, @@ -131,9 +132,11 @@ class NWItem(): """ if not none and (text is None or text == "None"): return None - xSub = etree.SubElement(xParent, name, attrib=attrib) + xAttr = {} if attrib is None else attrib + xSub = etree.SubElement(xParent, name, attrib=xAttr) if text is not None: xSub.text = text + return ## @@ -162,14 +165,14 @@ class NWItem(): """Set the parent handle, and ensure that it is valid. """ if theParent is None: - self.parHandle = None + self.itemParent = None elif isinstance(theParent, str): if len(theParent) == 13: - self.parHandle = theParent + self.itemParent = theParent else: - self.parHandle = None + self.itemParent = None else: - self.parHandle = None + self.itemParent = None return def setOrder(self, theOrder): diff --git a/nw/core/project.py b/nw/core/project.py index 15fd7d27..622bcc38 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -1139,16 +1139,16 @@ class NWProject(): # Technically a bug since treeOrder is built from the # same data as projTree continue - elif tItem.parHandle is None: + elif tItem.itemParent is None: # Item is a root, or already been identified as an # orphaned item sentItems.append(tHandle) yield tItem - elif tItem.parHandle in sentItems: + elif tItem.itemParent in sentItems: # Item's parent has been sent, so all is fine sentItems.append(tHandle) yield tItem - elif tItem.parHandle in iterItems: + elif tItem.itemParent in iterItems: # 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) diff --git a/nw/core/tree.py b/nw/core/tree.py index c51ae16c..b43230a9 100644 --- a/nw/core/tree.py +++ b/nw/core/tree.py @@ -134,7 +134,7 @@ class NWTree(): for xItem in xContent: nwItem = NWItem(self.theProject) if nwItem.unpackXML(xItem): - self.append(nwItem.itemHandle, nwItem.parHandle, nwItem) + self.append(nwItem.itemHandle, nwItem.itemParent, nwItem) nwItem.saveInitialCount() return True @@ -261,10 +261,10 @@ class NWTree(): tItem = self.__getitem__(tHandle) if tItem is not None: for i in range(nwConst.maxDepth + 1): - if tItem.parHandle is None: + if tItem.itemParent is None: return tItem else: - tHandle = tItem.parHandle + tHandle = tItem.itemParent tItem = self.__getitem__(tHandle) return None @@ -279,10 +279,10 @@ class NWTree(): if tItem is not None: tTree.append(tHandle) for i in range(nwConst.maxDepth + 1): - if tItem.parHandle is None: + if tItem.itemParent is None: return tTree else: - tHandle = tItem.parHandle + tHandle = tItem.itemParent tItem = self.__getitem__(tHandle) if tItem is None: return tTree diff --git a/nw/gui/build.py b/nw/gui/build.py index 1a69b62e..45a1a073 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -684,8 +684,8 @@ class GuiBuildNovel(QDialog): isNone |= theItem.itemLayout == nwItemLayout.NO_LAYOUT isNone |= theItem.itemClass == nwItemClass.NO_CLASS isNone |= theItem.itemClass == nwItemClass.TRASH - isNone |= theItem.parHandle == self.theProject.projTree.trashRoot() - isNone |= theItem.parHandle is None + isNone |= theItem.itemParent == self.theProject.projTree.trashRoot() + isNone |= theItem.itemParent is None isNote = theItem.itemLayout == nwItemLayout.NOTE isNovel = not isNone and not isNote diff --git a/nw/gui/docmerge.py b/nw/gui/docmerge.py index 2418a161..d7d128fe 100644 --- a/nw/gui/docmerge.py +++ b/nw/gui/docmerge.py @@ -127,7 +127,7 @@ class GuiDocMerge(QDialog): ), nwAlert.ERROR) return - nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.parHandle) + nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent) newItem = self.theProject.projTree[nHandle] newItem.setStatus(srcItem.itemStatus) diff --git a/nw/gui/docsplit.py b/nw/gui/docsplit.py index 3f497c18..1a5e9491 100644 --- a/nw/gui/docsplit.py +++ b/nw/gui/docsplit.py @@ -154,7 +154,7 @@ class GuiDocSplit(QDialog): return # Check that another folder can be created - parTree = self.theProject.projTree.getItemPath(srcItem.parHandle) + parTree = self.theProject.projTree.getItemPath(srcItem.itemParent) if len(parTree) >= nwConst.maxDepth - 1: self.theParent.makeAlert(( "Cannot add new folder for the document split. " @@ -176,7 +176,7 @@ class GuiDocSplit(QDialog): # Create the folder fHandle = self.theProject.newFolder( - srcItem.itemName, srcItem.itemClass, srcItem.parHandle + srcItem.itemName, srcItem.itemClass, srcItem.itemParent ) self.theParent.treeView.revealNewTreeItem(fHandle) logger.verbose("Creating folder %s" % fHandle) diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 9f4bb5e6..6b1ee7bd 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -219,7 +219,7 @@ class GuiProjectTree(QTreeWidget): pItem = self.theProject.projTree[pHandle] if pItem.itemType == nwItemType.FILE: nHandle = pHandle - pHandle = pItem.parHandle + pHandle = pItem.itemParent # If we again have no home, give up if pHandle is None: @@ -270,7 +270,7 @@ class GuiProjectTree(QTreeWidget): """ nwItem = self.theProject.projTree[tHandle] trItem = self._addTreeItem(nwItem, nHandle) - pHandle = nwItem.parHandle + pHandle = nwItem.itemParent if pHandle is not None and pHandle in self.theMap: self.theMap[pHandle].setExpanded(True) self.clearSelection() @@ -430,7 +430,7 @@ class GuiProjectTree(QTreeWidget): logger.error("Could not delete item") return False - pHandle = nwItemS.parHandle + pHandle = nwItemS.itemParent if self.theProject.projTree.isTrashRoot(pHandle): # If the file is in the trash folder already, as the # user if they want to permanently delete the file. @@ -815,7 +815,7 @@ class GuiProjectTree(QTreeWidget): project tree. """ tHandle = nwItem.itemHandle - pHandle = nwItem.parHandle + pHandle = nwItem.itemParent tClass = nwItem.itemClass newItem = QTreeWidgetItem([""]*4) @@ -1022,11 +1022,11 @@ class GuiProjectTreeMenu(QMenu): trashHandle = self.theTree.theProject.projTree.trashRoot() - inTrash = theItem.parHandle == trashHandle and trashHandle is not None + inTrash = theItem.itemParent == trashHandle and trashHandle is not None isTrash = theItem.itemHandle == trashHandle and trashHandle is not None isFile = theItem.itemType == nwItemType.FILE isArch = theRoot.itemClass == nwItemClass.ARCHIVE - isOrph = isFile and theItem.parHandle is None + isOrph = isFile and theItem.itemParent is None showOpen = isFile showView = isFile diff --git a/tests/test_item.py b/tests/test_item.py index d1097161..936ba185 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -31,13 +31,13 @@ def testItemSettersSimple(nwDummy): # Parent theItem.setParent(None) - assert theItem.parHandle is None + assert theItem.itemParent is None theItem.setParent(123) - assert theItem.parHandle is None + assert theItem.itemParent is None theItem.setParent("0123456789abcdef") - assert theItem.parHandle is None + assert theItem.itemParent is None theItem.setParent("0123456789abc") - assert theItem.parHandle == "0123456789abc" + assert theItem.itemParent == "0123456789abc" # Order theItem.setOrder(None) @@ -227,7 +227,7 @@ def testItemXMLPackUnpack(nwDummy): # Unpack assert theItem.unpackXML(xContent[0]) assert theItem.itemHandle == "0123456789abc" - assert theItem.parHandle == "0123456789abc" + assert theItem.itemParent == "0123456789abc" assert theItem.itemOrder == 1 assert theItem.isExpanded assert theItem.paraCount == 3 diff --git a/tests/test_project.py b/tests/test_project.py index 282acbc7..e8f1442b 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -521,7 +521,7 @@ def testProjectOrphanedFiles(nwDummy, nwLipsum): assert oItem is not None assert oItem.itemName == "Mars" assert oItem.itemHandle == "636b6aa9b697b" - assert oItem.parHandle is None + assert oItem.itemParent is None assert oItem.itemClass == nwItemClass.WORLD assert oItem.itemType == nwItemType.FILE assert oItem.itemLayout == nwItemLayout.NOTE @@ -531,7 +531,7 @@ def testProjectOrphanedFiles(nwDummy, nwLipsum): assert oItem is not None assert oItem.itemName == "Orphaned File 1" assert oItem.itemHandle == "736b6aa9b697b" - assert oItem.parHandle is None + assert oItem.itemParent is None assert oItem.itemClass == nwItemClass.NO_CLASS assert oItem.itemType == nwItemType.FILE assert oItem.itemLayout == nwItemLayout.NO_LAYOUT From bf7429f028419951ae33323f7aeb86dfe313ff7f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 15 Oct 2020 20:33:10 +0200 Subject: [PATCH 3/6] Update format statements and improve status bar clock --- .github/workflows/syntax.yml | 4 ++-- nw/gui/about.py | 1 - nw/gui/doceditor.py | 19 +++++++++---------- nw/gui/dochighlight.py | 10 +++++++--- nw/gui/outline.py | 10 +++++++--- nw/gui/outlinedetails.py | 10 +++++++--- nw/gui/projsettings.py | 7 ++++--- nw/gui/statusbar.py | 21 +++++++++------------ setup.cfg | 2 +- 9 files changed, 46 insertions(+), 38 deletions(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 038480f7..bf8ffec3 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -25,5 +25,5 @@ jobs: flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics - name: Coding Style Violations run: | - flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E241,E251,E261,E266,E302,E305 --show-source --statistics - flake8 tests --count --max-line-length=99 --ignore E203,E221,E226,E241,E251,E261,E266,E302,E305 --show-source --statistics + flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --show-source --statistics + flake8 tests --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --show-source --statistics diff --git a/nw/gui/about.py b/nw/gui/about.py index c6957bae..248cf7d1 100644 --- a/nw/gui/about.py +++ b/nw/gui/about.py @@ -222,7 +222,6 @@ class GuiAbout(QDialog): hColB = self.theParent.theTheme.colHead[2], ) self.pageAbout.document().setDefaultStyleSheet(styleSheet) - # self.pageCredit.document().setDefaultStyleSheet(styleSheet) self.pageLicense.document().setDefaultStyleSheet(styleSheet) return diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index d5e3b045..6d431319 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -807,16 +807,16 @@ class GuiDocEditor(QTextEdit): return self.docSearch.cycleFocus(toNext) return True - def mouseReleaseEvent(self, mEvent): + def mouseReleaseEvent(self, theEvent): """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()) + theCursor = self.cursorForPosition(theEvent.pos()) self._followTag(theCursor) - QTextEdit.mouseReleaseEvent(self, mEvent) + QTextEdit.mouseReleaseEvent(self, theEvent) self.docFooter.updateLineCount() return @@ -1203,20 +1203,19 @@ class GuiDocEditor(QTextEdit): """Check if document size crosses the big document limit set in config. If so, we will set the big document flag to True. """ - newState = theSize > self.mainConf.bigDocLimit*1000 + bigLim = self.mainConf.bigDocLimit*1000 + newState = theSize > bigLim if newState != self.bigDoc: if newState: logger.info( - "The document size is {:n} > {:n}, big doc mode has been enabled".format( - theSize, self.mainConf.bigDocLimit*1000 - ) + f"The document size is {theSize:n} > {bigLim:n}, " + f"big doc mode has been enabled" ) else: logger.info( - "The document size is {:n} <= {:n}, big doc mode has been disabled".format( - theSize, self.mainConf.bigDocLimit*1000 - ) + f"The document size is {theSize:n} <= {bigLim:n}, " + f"big doc mode has been disabled" ) self.bigDoc = newState diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index ba39b6e6..cc11ef86 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -149,18 +149,22 @@ class GuiDocHighlighter(QSyntaxHighlighter): # Quoted Strings if self.mainConf.highlightQuotes: + fmtDO = self.mainConf.fmtDoubleQuotes[0] + fmtDC = self.mainConf.fmtDoubleQuotes[1] + fmtSO = self.mainConf.fmtSingleQuotes[0] + fmtSC = self.mainConf.fmtSingleQuotes[1] self.hRules.append(( - "\\B{:s}(.*?){:s}\\B".format('"', '"'), { + "\\B\"(.*?)\"\\B", { 0 : self.hStyles["dialogue1"], } )) self.hRules.append(( - "\\B{:s}(.*?){:s}\\B".format(*self.mainConf.fmtDoubleQuotes), { + f"\\B{fmtDO:s}(.*?){fmtDC:s}\\B", { 0 : self.hStyles["dialogue2"], } )) self.hRules.append(( - "\\B{:s}(.*?){:s}\\B".format(*self.mainConf.fmtSingleQuotes), { + f"\\B{fmtSO:s}(.*?){fmtSC:s}\\B", { 0 : self.hStyles["dialogue3"], } )) diff --git a/nw/gui/outline.py b/nw/gui/outline.py index d3dc4815..4238cc44 100644 --- a/nw/gui/outline.py +++ b/nw/gui/outline.py @@ -439,6 +439,10 @@ class GuiOutline(QTreeWidget): newItem = QTreeWidgetItem() hIcon = "doc_%s" % tLevel.lower() + cC = int(novIdx["cCount"]) + wC = int(novIdx["wCount"]) + pC = int(novIdx["pCount"]) + newItem.setText(self.colIndex[nwOutline.TITLE], novIdx["title"]) newItem.setData(self.colIndex[nwOutline.TITLE], Qt.UserRole, tHandle) newItem.setIcon(self.colIndex[nwOutline.TITLE], self.theTheme.getIcon(hIcon)) @@ -448,9 +452,9 @@ class GuiOutline(QTreeWidget): newItem.setText(self.colIndex[nwOutline.LINE], sTitle[1:].lstrip("0")) newItem.setData(self.colIndex[nwOutline.LINE], Qt.UserRole, sTitle) newItem.setText(self.colIndex[nwOutline.SYNOP], novIdx["synopsis"]) - newItem.setText(self.colIndex[nwOutline.CCOUNT], "{:n}".format(novIdx["cCount"])) - newItem.setText(self.colIndex[nwOutline.WCOUNT], "{:n}".format(novIdx["wCount"])) - newItem.setText(self.colIndex[nwOutline.PCOUNT], "{:n}".format(novIdx["pCount"])) + newItem.setText(self.colIndex[nwOutline.CCOUNT], f"{cC:n}") + newItem.setText(self.colIndex[nwOutline.WCOUNT], f"{wC:n}") + newItem.setText(self.colIndex[nwOutline.PCOUNT], f"{pC:n}") newItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight) newItem.setTextAlignment(self.colIndex[nwOutline.WCOUNT], Qt.AlignRight) newItem.setTextAlignment(self.colIndex[nwOutline.PCOUNT], Qt.AlignRight) diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py index 49998dd9..4f6a56f5 100644 --- a/nw/gui/outlinedetails.py +++ b/nw/gui/outlinedetails.py @@ -266,9 +266,13 @@ class GuiOutlineDetails(QScrollArea): self.fileValue.setText(nwItem.itemName) self.itemValue.setText(nwItem.itemStatus) - self.cCValue.setText("{:n}".format(checkInt(novIdx["cCount"], 0))) - self.wCValue.setText("{:n}".format(checkInt(novIdx["wCount"], 0))) - self.pCValue.setText("{:n}".format(checkInt(novIdx["pCount"], 0))) + cC = checkInt(novIdx["cCount"], 0) + wC = checkInt(novIdx["wCount"], 0) + pC = checkInt(novIdx["pCount"], 0) + + self.cCValue.setText(f"{cC:n}") + self.wCValue.setText(f"{wC:n}") + self.pCValue.setText(f"{pC:n}") self.synopValue.setText(novIdx["synopsis"]) diff --git a/nw/gui/projsettings.py b/nw/gui/projsettings.py index 30d9ad54..d51d8006 100644 --- a/nw/gui/projsettings.py +++ b/nw/gui/projsettings.py @@ -270,11 +270,12 @@ class GuiProjectEditMeta(QWidget): self.revLabel = QLabel("Revision count:") self.revLabel.setIndent(xInd) - self.revValue = QLabel("{:n}".format(self.theProject.saveCount)) + self.revValue = QLabel(f"{self.theProject.saveCount:n}") + editHours = self.theProject.editTime/3600 self.editLabel = QLabel("Edit time:") self.editLabel.setIndent(xInd) - self.editValue = QLabel("{:.2f} hours".format(self.theProject.editTime/3600)) + self.editValue = QLabel(f"{editHours:.2f} hours") self.statsLabel = QLabel("Project Stats") @@ -294,7 +295,7 @@ class GuiProjectEditMeta(QWidget): self.wordsLabel = QLabel("Word count:") self.wordsLabel.setIndent(xInd) - self.wordsValue = QLabel("{:n}".format(self.theProject.currWCount)) + self.wordsValue = QLabel(f"{self.theProject.currWCount:n}") self.mainForm.addWidget(self.headLabel, 0, 0, 1, 2, Qt.AlignTop) self.mainForm.addWidget(self.nameLabel, 1, 0, 1, 1, Qt.AlignTop) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index c43c3f6f..91a86519 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -196,10 +196,7 @@ class GuiMainStatus(QStatusBar): "Project word count (session change)" ) self.statsText.setText(( - "Words: {pWC:n} ({sWC:+n})" - ).format( - pWC = self.projWords, - sWC = self.sessWords, + f"Words: {self.projWords:n} ({self.sessWords:+n})" )) return @@ -207,16 +204,16 @@ class GuiMainStatus(QStatusBar): """Update the session clock. """ if self.refTime is None: - theTime = "00:00:00" + self.timeText.setText("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.timeText.setText(theTime) + tM = tS//60 + tH = tM//60 + tM %= 60 + tS %= 60 + self.timeText.setText(f"{tH:02d}:{tM:02d}:{tS:02d}") + return # END Class GuiMainStatus @@ -237,7 +234,7 @@ class StatusLED(QAbstractButton): return ## - # Getters and Setters + # Setters ## def setState(self, theState): diff --git a/setup.cfg b/setup.cfg index a791e31e..ff0117c5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,6 +6,6 @@ version = attr: nw.__version__ universal = 0 [flake8] -ignore = E203,E221,E226,E241,E251,E261,E266,E302,E305 +ignore = E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 max-line-length = 99 exclude = docs/* From e0d15a882ea241832f143c6e4a9876690b5980fe Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 15 Oct 2020 20:56:09 +0200 Subject: [PATCH 4/6] Further improvements to the status bar clock --- nw/gui/statusbar.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 91a86519..da8a0427 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -206,14 +206,8 @@ class GuiMainStatus(QStatusBar): if self.refTime is None: self.timeText.setText("00:00:00") else: - # This is much faster than using datetime format tS = int(time() - self.refTime) - tM = tS//60 - tH = tM//60 - tM %= 60 - tS %= 60 - self.timeText.setText(f"{tH:02d}:{tM:02d}:{tS:02d}") - + self.timeText.setText(f"{tS//3600:02d}:{(tS//60)%60:02d}:{tS%60:02d}") return # END Class GuiMainStatus From 7deee064d08a04d618abecc28ace49c629d19fa6 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 15 Oct 2020 22:44:47 +0200 Subject: [PATCH 5/6] More improvements, mostly string formatting --- nw/core/tohtml.py | 1 + nw/core/tools.py | 17 ++++++++--------- nw/gui/statusbar.py | 4 +++- tests/test_tools.py | 1 + 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 770505db..df6c4a09 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -41,6 +41,7 @@ class ToHtml(Tokenizer): def __init__(self, theProject, theParent): Tokenizer.__init__(self, theProject, theParent) + self.genMode = self.M_EXPORT self.cssStyles = True diff --git a/nw/core/tools.py b/nw/core/tools.py index 59c141d9..8062d591 100644 --- a/nw/core/tools.py +++ b/nw/core/tools.py @@ -136,7 +136,6 @@ def numberToWord(numVal, theLanguage): def _numberToWordEN(numVal): """Convert numbers to English words. """ - numWord = "" oneWord = "" tenWord = "" hunWord = "" @@ -145,8 +144,8 @@ def _numberToWordEN(numVal): return "Zero" oneVal = numVal % 10 - tenVal = (numVal-oneVal) % 100 - hunVal = (numVal-tenVal-oneVal) % 1000 + tenVal = (numVal - oneVal) % 100 + hunVal = (numVal - tenVal - oneVal) % 1000 theHundreds = { 100: "One Hundred", 200: "Two Hundred", 300: "Three Hundred", @@ -167,18 +166,18 @@ def _numberToWordEN(numVal): } hunWord = theHundreds.get(hunVal, "") - tenWord = theTens.get(tenVal, "") if tenVal == 10: oneWord = theTeens.get(oneVal, "") - numWord = ("%s %s" % (hunWord, oneWord)).strip() + return f"{hunWord} {oneWord}".strip() else: oneWord = theOnes.get(oneVal, "") if tenVal == 0: - numWord = ("%s %s" % (hunWord, oneWord)).strip() + return f"{hunWord} {oneWord}".strip() else: + tenWord = theTens.get(tenVal, "") if oneVal == 0: - numWord = ("%s %s" % (hunWord, tenWord)).strip() + return f"{hunWord} {tenWord}".strip() else: - numWord = ("%s %s-%s" % (hunWord, tenWord, oneWord)).strip() + return f"{hunWord} {tenWord}-{oneWord}".strip() - return numWord + return "" diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index da8a0427..b0ab87ae 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -207,7 +207,9 @@ class GuiMainStatus(QStatusBar): self.timeText.setText("00:00:00") else: tS = int(time() - self.refTime) - self.timeText.setText(f"{tS//3600:02d}:{(tS//60)%60:02d}:{tS%60:02d}") + self.timeText.setText( + f"{tS//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}" + ) return # END Class GuiMainStatus diff --git a/tests/test_tools.py b/tests/test_tools.py index bfff8f3f..4832aadb 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -61,6 +61,7 @@ def testNumberWords(): assert numberToWord(21, "en") == "Twenty-One" assert numberToWord(29, "en") == "Twenty-Nine" assert numberToWord(42, "en") == "Forty-Two" + assert numberToWord(114, "en") == "One Hundred Fourteen" assert numberToWord(142, "en") == "One Hundred Forty-Two" assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine" From 487a1167959c9fc9414967173abd7f4dacec57f0 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 15 Oct 2020 22:51:30 +0200 Subject: [PATCH 6/6] Removed some redundant parantheses --- 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 b0ab87ae..0b95ef16 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -195,9 +195,9 @@ class GuiMainStatus(QStatusBar): self.statsText.setToolTip( "Project word count (session change)" ) - self.statsText.setText(( + self.statsText.setText( f"Words: {self.projWords:n} ({self.sessWords:+n})" - )) + ) return def _updateTime(self):