def _populateList(self): """Get the item selected in the tree, check that it is a folder, and try to find all files associated with it. The valid files are then added to the list view in order. The list itself can be reordered by the user. """ self.listBox.clear() if self.sourceItem is None: self.sourceItem = self.mainGui.projView.getSelectedHandle() if self.sourceItem is None: return False nwItem = self.theProject.tree[self.sourceItem] if nwItem is None: return False if nwItem.itemType is not nwItemType.FILE: self.mainGui.makeAlert( self.tr( "Element selected in the project tree must be a file."), nwAlert.ERROR) return False inDoc = NWDoc(self.theProject, self.sourceItem) theText = inDoc.readDocument() if theText is None: theText = "" return False spLevel = self.splitLevel.currentData() self.theProject.options.setValue("GuiDocSplit", "spLevel", spLevel) logger.debug("Scanning document '%s' for headings level <= %d", self.sourceItem, spLevel) self.sourceText = theText.splitlines() for lineNo, aLine in enumerate(self.sourceText): onLine = -1 if aLine.startswith("# ") and spLevel >= 1: onLine = lineNo elif aLine.startswith("## ") and spLevel >= 2: onLine = lineNo elif aLine.startswith("### ") and spLevel >= 3: onLine = lineNo elif aLine.startswith("#### ") and spLevel >= 4: onLine = lineNo if onLine >= 0: newItem = QListWidgetItem() newItem.setText(aLine.strip()) newItem.setData(Qt.UserRole, onLine) self.listBox.addItem(newItem) return True
def _doMerge(self): """Perform the merge of the files in the selected folder, and 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 = [] for i in range(self.listBox.count()): finalOrder.append(self.listBox.item(i).data(Qt.UserRole)) if len(finalOrder) == 0: self.theParent.makeAlert( self.tr("No source documents found. Nothing to do."), nwAlert.ERROR) return False theText = "" for tHandle in finalOrder: inDoc = NWDoc(self.theProject, tHandle) docText = inDoc.readDocument() docErr = inDoc.getError() if docText is None and docErr: self.theParent.makeAlert( [self.tr("Failed to open document file."), docErr], nwAlert.ERROR) if docText: theText += docText.rstrip("\n") + "\n\n" if self.sourceItem is None: self.theParent.makeAlert( self.tr("No source folder selected. Nothing to do."), nwAlert.ERROR) return False srcItem = self.theProject.projTree[self.sourceItem] if srcItem is None: self.theParent.makeAlert(self.tr("Internal error."), nwAlert.ERROR) return False nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent) newItem = self.theProject.projTree[nHandle] newItem.setStatus(srcItem.itemStatus) newItem.setImport(srcItem.itemImport) outDoc = NWDoc(self.theProject, nHandle) if not outDoc.writeDocument(theText): self.theParent.makeAlert( [self.tr("Could not save document."), outDoc.getError()], nwAlert.ERROR) return False self.theParent.treeView.revealNewTreeItem(nHandle) self.theParent.openDocument(nHandle, doScroll=True) self._doClose() return True
def _deleteTreeItem(self, tHandle): """Permanently delete a tree item from the project and the map. """ if self.theProject.tree.checkType(tHandle, nwItemType.FILE): delDoc = NWDoc(self.theProject, tHandle) if not delDoc.deleteDocument(): self.mainGui.makeAlert([ self.tr("Could not delete document file."), delDoc.getError() ], nwAlert.ERROR) return False self.theProject.index.deleteHandle(tHandle) del self.theProject.tree[tHandle] self._treeMap.pop(tHandle, None) return True
def testCoreDocument_Methods(mockGUI, nwMinimal): """Test other methods of the NWDoc class. """ theProject = NWProject(mockGUI) assert theProject.openProject(nwMinimal) assert theProject.projPath == nwMinimal sHandle = "8c659a11cd429" theDoc = NWDoc(theProject, sHandle) docPath = os.path.join(nwMinimal, "content", sHandle + ".nwd") assert theDoc.readDocument() == "### New Scene\n\n" # Check location assert theDoc.getFileLocation() == docPath # Check the item assert theDoc.getCurrentItem() is not None assert theDoc.getCurrentItem().itemHandle == sHandle # Check the meta theName, theParent, theClass, theLayout = theDoc.getMeta() assert theName == "New Scene" assert theParent == "a6d311a93600a" assert theClass == nwItemClass.NOVEL assert theLayout == nwItemLayout.DOCUMENT # Add meta data garbage assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n") assert readFile(docPath) == ("%%~name: New Scene\n" f"%%~path: a6d311a93600a/{sHandle}\n" "%%~kind: NOVEL/DOCUMENT\n" "%%~ stuff\n" "### Test File\n\n" "Text ...\n\n") assert theDoc.readDocument() == "### Test File\n\nText ...\n\n"
def _doSplit(self): """Perform the split of the file, create a new folder in the same parent folder, and multiple files depending on split level settings. The old file is not removed in the split process, and must be deleted manually. """ logger.verbose("GuiDocSplit split button clicked") if self.sourceItem is None: self.mainGui.makeAlert( self.tr("No source document selected. Nothing to do."), nwAlert.ERROR) return False srcItem = self.theProject.tree[self.sourceItem] if srcItem is None: self.mainGui.makeAlert(self.tr("Could not parse source document."), nwAlert.ERROR) return False inDoc = NWDoc(self.theProject, self.sourceItem) theText = inDoc.readDocument() docErr = inDoc.getError() if theText is None and docErr: self.mainGui.makeAlert( [self.tr("Failed to open document file."), docErr], nwAlert.ERROR) if theText is None: theText = "" nLines = len(self.sourceText) logger.debug("Splitting document %s with %d lines", self.sourceItem, nLines) finalOrder = [] for i in range(self.listBox.count()): listItem = self.listBox.item(i) wTitle = listItem.text() lineNo = listItem.data(Qt.UserRole) finalOrder.append([wTitle, lineNo, nLines]) if i > 0: finalOrder[i - 1][2] = lineNo nFiles = len(finalOrder) if nFiles == 0: self.mainGui.makeAlert(self.tr("No headers found. Nothing to do."), nwAlert.ERROR) return False msgYes = self.mainGui.askQuestion( self.tr("Split Document"), "{0}<br><br>{1}".format( self. tr("The document will be split into {0} file(s) in a new folder. " "The original document will remain intact.").format(nFiles), self.tr("Continue with the splitting process?"))) if not msgYes: return False # Create the folder fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemParent) self.mainGui.projView.revealNewTreeItem(fHandle) logger.verbose("Creating folder '%s'", fHandle) # Loop through, and create the files for wTitle, iStart, iEnd in finalOrder: wTitle = wTitle.lstrip("#").strip() nHandle = self.theProject.newFile(wTitle, fHandle) newItem = self.theProject.tree[nHandle] newItem.setStatus(srcItem.itemStatus) newItem.setImport(srcItem.itemImport) logger.verbose( "Creating new document '%s' with text from line %d to %d", nHandle, iStart + 1, iEnd) theText = "\n".join(self.sourceText[iStart:iEnd]) theText = theText.rstrip("\n") + "\n\n" outDoc = NWDoc(self.theProject, nHandle) if not outDoc.writeDocument(theText): self.mainGui.makeAlert( [self.tr("Could not save document."), outDoc.getError()], nwAlert.ERROR) return False self.mainGui.projView.revealNewTreeItem(nHandle) self._doClose() return True
def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI): """Test handling files and text in the Tokenizer class. """ theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) theProject.projLang = "en" theProject._loadProjectLocalisation() theToken = BareTokenizer(theProject) theToken.setKeepMarkdown(True) assert theProject.openProject(nwMinimal) sHandle = "8c659a11cd429" # Set some content to work with docText = ( "### Scene Six\n\n" "This is text with _italic text_, some **bold text**, some ~~deleted text~~, " "and some **_mixed text_** and **some _nested_ text**.\n\n" "#### Replace\n\n" "Also, replace <A> and <B>.\n\n") docTextR = docText.replace("<A>", "this").replace("<B>", "that") nDoc = NWDoc(theProject, sHandle) assert nDoc.writeDocument(docText) theProject.setAutoReplace({"A": "this", "B": "that"}) assert theProject.saveProject() # Root Heading assert theToken.addRootHeading("stuff") is False assert theToken.addRootHeading(sHandle) is False # First Page assert theToken.addRootHeading("7695ce551d265") is True assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n" assert theToken._theTokens[-1] == (Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE) # Not First Page assert theToken.addRootHeading("7695ce551d265") is True assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n" assert theToken._theTokens[-1] == (Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB) # Set Text assert theToken.setText("stuff") is False assert theToken.setText(sHandle) is True assert theToken._theText == docText with monkeypatch.context() as mp: mp.setattr("novelwriter.constants.nwConst.MAX_DOCSIZE", 100) assert theToken.setText(sHandle, docText) is True assert theToken._theText == ( "# ERROR\n\n" "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n") assert theToken.setText(sHandle, docText) is True assert theToken._theText == docText assert theToken._isNone is False assert theToken._isNovel is True assert theToken._isNote is False # Pre Processing theToken.doPreProcessing() assert theToken._theText == docTextR # Post Processing theToken._theResult = r"This is text with escapes: \** \~~ \__" theToken.doPostProcessing() assert theToken.theResult == "This is text with escapes: ** ~~ __" # Save File savePath = os.path.join(nwMinimal, "dump.nwd") theToken.saveRawMarkdown(savePath) assert readFile(savePath) == ("# Notes: Plot\n\n" "# Notes: Plot\n\n") # Ckeck abstract method with pytest.raises(NotImplementedError): theToken.doConvert()
def deleteItem(self, tHandle=None, alreadyAsked=False, bulkAction=False): """Delete an item from the project tree. As a first step, files are moved to the Trash folder. Permanent deletion is a second step. This second step also deletes the item from the project object as well as delete the files on disk. Folders are deleted if they're empty only, and the deletion is always permanent. """ if not self.theParent.hasProject: logger.error("No project open") return False if not self.hasFocus() and not bulkAction: logger.info("Delete action blocked due to no widget focus") return False if tHandle is None: tHandle = self.getSelectedHandle() if tHandle is None: logger.error("There is no item to delete") return False trItemS = self._getTreeItem(tHandle) nwItemS = self.theProject.projTree[tHandle] if trItemS is None or nwItemS is None: logger.error("Could not find tree item for deletion") return False wCount = int(trItemS.data(self.C_COUNT, Qt.UserRole)) if nwItemS.itemType == nwItemType.FILE: logger.debug("User requested file '%s' deleted", tHandle) trItemP = trItemS.parent() trItemT = self._addTrashRoot() if trItemP is None or trItemT is None: logger.error("Could not delete item") return False 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. doPermanent = False if not alreadyAsked: msgYes = self.theParent.askQuestion( self.tr("Delete File"), self.tr("Permanently delete file '{0}'?").format( nwItemS.itemName)) if msgYes: doPermanent = True else: doPermanent = True if doPermanent: logger.debug("Permanently deleting file with handle '%s'", tHandle) self.propagateCount(tHandle, 0) tIndex = trItemP.indexOfChild(trItemS) trItemC = trItemP.takeChild(tIndex) if self.theParent.docEditor.docHandle() == tHandle: self.theParent.closeDocument() delDoc = NWDoc(self.theProject, tHandle) if not delDoc.deleteDocument(): self.theParent.makeAlert([ self.tr("Could not delete document file."), delDoc.getError() ], nwAlert.ERROR) return False self.theIndex.deleteHandle(tHandle) self._deleteTreeItem(tHandle) self._setTreeChanged(True) self.wordCountsChanged.emit() else: # The file is not already in the trash folder, so we # move it there. msgYes = self.theParent.askQuestion( self.tr("Delete File"), self.tr("Move file '{0}' to Trash?").format( nwItemS.itemName), ) if msgYes: if pHandle is None: logger.warning("File has no parent item") logger.debug("Moving file '%s' to trash", tHandle) self.propagateCount(tHandle, 0) tIndex = trItemP.indexOfChild(trItemS) trItemC = trItemP.takeChild(tIndex) trItemT.addChild(trItemC) self._updateItemParent(tHandle) self.propagateCount(tHandle, wCount) self.theIndex.deleteHandle(tHandle) self._recordLastMove(trItemS, trItemP, tIndex) self._setTreeChanged(True) elif nwItemS.itemType == nwItemType.FOLDER: logger.debug("User requested folder '%s' deleted", tHandle) trItemP = trItemS.parent() if trItemP is None: logger.error("Could not delete folder") return False tIndex = trItemP.indexOfChild(trItemS) if trItemS.childCount() == 0: trItemP.takeChild(tIndex) self._deleteTreeItem(tHandle) self._setTreeChanged(True) else: self.theParent.makeAlert( self.tr("Cannot delete folder. It is not empty. " "Recursive deletion is not supported. " "Please delete the content first."), nwAlert.ERROR) return False elif nwItemS.itemType == nwItemType.ROOT: logger.debug("User requested root folder '%s' deleted", tHandle) tIndex = self.indexOfTopLevelItem(trItemS) if trItemS.childCount() == 0: self.takeTopLevelItem(tIndex) self._deleteTreeItem(tHandle) self.theParent.mainMenu.setAvailableRoot() self._setTreeChanged(True) else: self.theParent.makeAlert( self.tr("Cannot delete root folder. It is not empty. " "Recursive deletion is not supported. " "Please delete the content first."), nwAlert.ERROR) return False return True
def newTreeItem(self, itemType, itemClass): """Add new item to the tree, with a given itemType and itemClass, and attach it to the selected handle. Also make sure the item is added in a place it can be added, and that other meta data is set correctly to ensure a valid project tree. """ pHandle = self.getSelectedHandle() nHandle = None if not self.theParent.hasProject: logger.error("No project open") return False if not isinstance(itemType, nwItemType): # This would indicate an internal bug logger.error("No itemType provided") return False # The item needs to be assigned an item class, so one must be # provided, or it must be possible to extract it from the parent # item of the new item. if itemClass is None and pHandle is not None: pItem = self.theProject.projTree[pHandle] if pItem is not None: itemClass = pItem.itemClass # If class is still not set, alert the user and exit if itemClass is None: if itemType == nwItemType.FILE: self.theParent.makeAlert( self. tr("Please select a valid location in the tree to add the document." ), nwAlert.ERROR) else: self.theParent.makeAlert( self. tr("Please select a valid location in the tree to add the folder." ), nwAlert.ERROR) return False # Everything is fine, we have what we need, so we proceed logger.verbose( "Adding new item of type '%s' and class '%s' to handle '%s'", itemType.name, itemClass.name, str(pHandle)) if itemType == nwItemType.ROOT: tHandle = self.theProject.newRoot( trConst(nwLabels.CLASS_NAME[itemClass]), itemClass) if tHandle is None: logger.error("No root item added") return False else: # If no parent has been selected, make the new file under # the root NOVEL item. if pHandle is None: pHandle = self.theProject.projTree.findRoot(nwItemClass.NOVEL) # If still nothing, give up if pHandle is None: self.theParent.makeAlert( self.tr( "Did not find anywhere to add the file or folder!"), nwAlert.ERROR) return False # Now check if the selected item is a file, in which case # the new file will be a sibling pItem = self.theProject.projTree[pHandle] if pItem.itemType == nwItemType.FILE: nHandle = pHandle pHandle = pItem.itemParent # If we again have no home, give up if pHandle is None: self.theParent.makeAlert( self.tr( "Did not find anywhere to add the file or folder!"), nwAlert.ERROR) return False if self.theProject.projTree.isTrashRoot(pHandle): self.theParent.makeAlert( self. tr("Cannot add new files or folders to the Trash folder."), nwAlert.ERROR) return False parTree = self.theProject.projTree.getItemPath(pHandle) # If we're still here, add the file or folder if itemType == nwItemType.FILE: tHandle = self.theProject.newFile(self.tr("New File"), itemClass, pHandle) elif itemType == nwItemType.FOLDER: if len(parTree) >= nwConst.MAX_DEPTH - 1: # Folders cannot be deeper than MAX_DEPTH - 1, leaving room # for one more level of files. self.theParent.makeAlert( self.tr("Cannot add new folder to this item. " "Maximum folder depth has been reached."), nwAlert.ERROR) return False tHandle = self.theProject.newFolder(self.tr("New Folder"), itemClass, pHandle) else: logger.error("Failed to add new item") return False # If there is no handle set, return here if tHandle is None: return True # Add the new item to the tree self.revealNewTreeItem(tHandle, nHandle) self.theParent.editItem(tHandle) nwItem = self.theProject.projTree[tHandle] # If this is a folder, return here if nwItem.itemType != nwItemType.FILE: return True # This is a new files, so let's add some content newDoc = NWDoc(self.theProject, tHandle) curTxt = newDoc.readDocument() if curTxt is None: curTxt = "" if curTxt == "": if nwItem.itemLayout == nwItemLayout.DOCUMENT: newText = f"### {nwItem.itemName}\n\n" else: newText = f"# {nwItem.itemName}\n\n" # Save the text and index it newDoc.writeDocument(newText) self.theIndex.scanText(tHandle, newText) # Get Word Counts cC, wC, pC = self.theIndex.getCounts(tHandle) nwItem.setCharCount(cC) nwItem.setWordCount(wC) nwItem.setParaCount(pC) self.propagateCount(tHandle, wC) self.wordCountsChanged.emit() return True
def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal): """Test loading and saving a document with the NWDoc class. """ theProject = NWProject(mockGUI) assert theProject.openProject(nwMinimal) is True assert theProject.projPath == nwMinimal sHandle = "8c659a11cd429" # Read Document # ============= # Not a valid handle theDoc = NWDoc(theProject, "stuff") assert bool(theDoc) is False assert theDoc.readDocument() is None # Non-existent handle theDoc = NWDoc(theProject, "0000000000000") assert theDoc.readDocument() is None assert theDoc._currHash is None # Cause open() to fail while loading with monkeypatch.context() as mp: mp.setattr("builtins.open", causeOSError) theDoc = NWDoc(theProject, sHandle) assert theDoc.readDocument() is None assert theDoc.getError() == "OSError: Mock OSError" # Load the text theDoc = NWDoc(theProject, sHandle) assert theDoc.readDocument() == "### New Scene\n\n" # Try to open a new (non-existent) file nHandle = theProject.projTree.findRoot(nwItemClass.NOVEL) assert nHandle is not None xHandle = theProject.newFile("New File", nwItemClass.NOVEL, nHandle) theDoc = NWDoc(theProject, xHandle) assert bool(theDoc) is True assert repr(theDoc) == f"<NWDoc handle={xHandle}>" assert theDoc.readDocument() == "" # Write Document # ============== # Set handle and save again theText = "### Test File\n\nText ...\n\n" theDoc = NWDoc(theProject, xHandle) assert theDoc.readDocument(xHandle) == "" assert theDoc.writeDocument(theText) is True # Save again to ensure temp file and previous file is handled assert theDoc.writeDocument(theText) # Check file content docPath = os.path.join(nwMinimal, "content", xHandle + ".nwd") assert readFile(docPath) == ("%%~name: New File\n" f"%%~path: a508bb932959c/{xHandle}\n" "%%~kind: NOVEL/DOCUMENT\n" "### Test File\n\n" "Text ...\n\n") # Alter the document on disk and save again writeFile(docPath, "blablabla") assert theDoc.writeDocument(theText) is False # Force the overwrite assert theDoc.writeDocument(theText, forceWrite=True) is True # Force no meta data theDoc._theItem = None assert theDoc.writeDocument(theText) is True assert readFile(docPath) == theText # Cause open() to fail while saving with monkeypatch.context() as mp: mp.setattr("builtins.open", causeOSError) assert theDoc.writeDocument(theText) is False assert theDoc.getError() == "OSError: Mock OSError" theDoc._docError = "" assert theDoc.getError() == "" # Cause os.replace() to fail while saving with monkeypatch.context() as mp: mp.setattr("os.replace", causeOSError) assert theDoc.writeDocument(theText) is False assert theDoc.getError() == "OSError: Mock OSError" theDoc._docError = "" assert theDoc.getError() == "" # Saving with no handle theDoc._docHandle = None assert theDoc.writeDocument(theText) is False # Delete Document # =============== # Delete the last document theDoc = NWDoc(theProject, "stuff") assert theDoc.deleteDocument() is False assert os.path.isfile(docPath) # Cause the delete to fail with monkeypatch.context() as mp: mp.setattr("os.unlink", causeOSError) theDoc = NWDoc(theProject, xHandle) assert theDoc.deleteDocument() is False assert theDoc.getError() == "OSError: Mock OSError" # Make the delete pass theDoc = NWDoc(theProject, xHandle) assert theDoc.deleteDocument() is True assert not os.path.isfile(docPath)
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj): """Test the the various features of the status bar. """ monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) nwGUI.theProject.projTree.setSeed(42) assert nwGUI.newProject({"projPath": fncProj}) is True cHandle = nwGUI.theProject.newFile("A Note", nwItemClass.CHARACTER, "71ee45a3c0db9") newDoc = NWDoc(nwGUI.theProject, cHandle) newDoc.writeDocument("# A Note\n\n") nwGUI.treeView.revealNewTreeItem(cHandle) nwGUI.rebuildIndex(beQuiet=True) # Reference Time refTime = time.time() nwGUI.statusBar.setRefTime(refTime) assert nwGUI.statusBar.refTime == refTime # Project Status nwGUI.statusBar.setProjectStatus(nwState.NONE) assert nwGUI.statusBar.projIcon._theCol == nwGUI.statusBar.projIcon._colNone nwGUI.statusBar.setProjectStatus(nwState.BAD) assert nwGUI.statusBar.projIcon._theCol == nwGUI.statusBar.projIcon._colBad nwGUI.statusBar.setProjectStatus(nwState.GOOD) assert nwGUI.statusBar.projIcon._theCol == nwGUI.statusBar.projIcon._colGood # Document Status nwGUI.statusBar.setDocumentStatus(nwState.NONE) assert nwGUI.statusBar.docIcon._theCol == nwGUI.statusBar.docIcon._colNone nwGUI.statusBar.setDocumentStatus(nwState.BAD) assert nwGUI.statusBar.docIcon._theCol == nwGUI.statusBar.docIcon._colBad nwGUI.statusBar.setDocumentStatus(nwState.GOOD) assert nwGUI.statusBar.docIcon._theCol == nwGUI.statusBar.docIcon._colGood # Idle Status nwGUI.statusBar.mainConf.stopWhenIdle = False nwGUI.statusBar.setUserIdle(True) nwGUI.statusBar.updateTime() assert nwGUI.statusBar.userIdle is False assert nwGUI.statusBar.timeText.text() == "00:00:00" nwGUI.statusBar.mainConf.stopWhenIdle = True nwGUI.statusBar.setUserIdle(True) nwGUI.statusBar.updateTime(5) assert nwGUI.statusBar.userIdle is True assert nwGUI.statusBar.timeText.text() != "00:00:00" nwGUI.statusBar.setUserIdle(False) nwGUI.statusBar.updateTime(5) assert nwGUI.statusBar.userIdle is False assert nwGUI.statusBar.timeText.text() != "00:00:00" # Language nwGUI.statusBar.setLanguage("None", "None") assert nwGUI.statusBar.langText.text() == "None" nwGUI.statusBar.setLanguage("en", "None") assert nwGUI.statusBar.langText.text() == "American English" # Project Stats nwGUI.statusBar.mainConf.incNotesWCount = False nwGUI._updateStatusWordCount() assert nwGUI.statusBar.statsText.text() == "Words: 6 (+6)" nwGUI.statusBar.mainConf.incNotesWCount = True nwGUI._updateStatusWordCount() assert nwGUI.statusBar.statsText.text() == "Words: 8 (+8)"
def buildTestProject(theObject, projPath): """Build a standard test project in projPath using theProject object as the parent. """ from novelwriter.enum import nwItemClass from novelwriter.core import NWProject, NWDoc if isinstance(theObject, NWProject): theGUI = None theProject = theObject else: theGUI = theObject theProject = theObject.theProject theProject.clearProject() theProject.setProjectPath(projPath, newProject=True) theProject.setProjectName("New Project") theProject.setBookTitle("New Novel") theProject.setBookAuthors("Jane Doe") # Creating a minimal project with a few root folders and a # single chapter folder with a single file. xHandle = {} xHandle[1] = theProject.newRoot(nwItemClass.NOVEL, "Novel") xHandle[2] = theProject.newRoot(nwItemClass.PLOT, "Plot") xHandle[3] = theProject.newRoot(nwItemClass.CHARACTER, "Characters") xHandle[4] = theProject.newRoot(nwItemClass.WORLD, "World") xHandle[5] = theProject.newFile("Title Page", xHandle[1]) xHandle[6] = theProject.newFolder("New Chapter", xHandle[1]) xHandle[7] = theProject.newFile("New Chapter", xHandle[6]) xHandle[8] = theProject.newFile("New Scene", xHandle[6]) aDoc = NWDoc(theProject, xHandle[5]) aDoc.writeDocument("#! New Novel\n\n>> By Jane DOe <<\n") aDoc = NWDoc(theProject, xHandle[7]) aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter")) aDoc = NWDoc(theProject, xHandle[8]) aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene")) theProject.projOpened = time.time() theProject.setProjectChanged(True) theProject.saveProject(autoSave=True) if theGUI is not None: theGUI.hasProject = True theGUI.rebuildTrees() theGUI.rebuildIndex(beQuiet=True) return