From fb81c7e8e0aaedcdcf3570b26133359420450e59 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 14 Jan 2021 20:09:18 +0100 Subject: [PATCH] Minimal docs update, and add test for Project Details --- docs/source/int_interface.rst | 1 + docs/source/write_projects.rst | 12 ++--- nw/gui/projdetails.py | 46 ++++++++-------- tests/test_gui_projdetails.py | 96 ++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 31 deletions(-) create mode 100644 tests/test_gui_projdetails.py diff --git a/docs/source/int_interface.rst b/docs/source/int_interface.rst index dcbf5fd9..d8f91047 100644 --- a/docs/source/int_interface.rst +++ b/docs/source/int_interface.rst @@ -406,6 +406,7 @@ Most features are available as keyboard shortcuts. These are as follows: ":kbd:`F11`", "Activate full screen mode." ":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser." ":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)" + ":kbd:`Shift`:kbd:`F6`", "Open the :guilabel:`Project Details` dialog." ":kbd:`Return`", "If in the project tree, open a document for editing." .. note:: diff --git a/docs/source/write_projects.rst b/docs/source/write_projects.rst index dbc19691..e00dd234 100644 --- a/docs/source/write_projects.rst +++ b/docs/source/write_projects.rst @@ -15,7 +15,9 @@ A list of recently opened projects is maintained, and displayed in the :guilabel dialog. A project can be removed from this list by selecting it and pressing the :kbd:`Del` key. Project-specific settings are available in :guilabel:`Project Settings` in the :guilabel:`Project` -menu. See further details below in the :ref:`a_proj_settings` section. +menu. See further details below in the :ref:`a_proj_settings` section. Details about the project, +including word counts, and a table of contents with word and page counts, is available through the +:guilabel:`Project Details` dialog. .. _a_proj_roots: @@ -249,14 +251,6 @@ override the default spell checking language here. You can also override the aut setting. -Details Tab ------------ - -This tab presents an overview of technical meta data for the project. It states where on your file -system the project is saved, how may times it has been saved, how many folders and documents it -contains, and how many words exist in the entire project. - - Status and Importance Tabs -------------------------- diff --git a/nw/gui/projdetails.py b/nw/gui/projdetails.py index ebf69d78..716177af 100644 --- a/nw/gui/projdetails.py +++ b/nw/gui/projdetails.py @@ -261,19 +261,19 @@ class GuiProjectDetailsContents(QWidget): # Contents Tree # ============= - self.chTree = QTreeWidget() - self.chTree.setIconSize(QSize(iPx, iPx)) - self.chTree.setIndentation(0) - self.chTree.setColumnCount(6) - self.chTree.setHeaderLabels(["Title", "Words", "Pages", "Page", "Progress", ""]) + self.tocTree = QTreeWidget() + self.tocTree.setIconSize(QSize(iPx, iPx)) + self.tocTree.setIndentation(0) + self.tocTree.setColumnCount(6) + self.tocTree.setHeaderLabels(["Title", "Words", "Pages", "Page", "Progress", ""]) - treeHeadItem = self.chTree.headerItem() + treeHeadItem = self.tocTree.headerItem() treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight) treeHeadItem.setTextAlignment(self.C_PAGES, Qt.AlignRight) treeHeadItem.setTextAlignment(self.C_PAGE, Qt.AlignRight) treeHeadItem.setTextAlignment(self.C_PROG, Qt.AlignRight) - treeHeader = self.chTree.header() + treeHeader = self.tocTree.header() treeHeader.setStretchLastSection(True) treeHeader.setMinimumSectionSize(hPx) @@ -283,12 +283,12 @@ class GuiProjectDetailsContents(QWidget): wCol3 = self.mainConf.pxInt(self.optState.getInt("GuiProjectDetails", "widthCol3", 60)) wCol4 = self.mainConf.pxInt(self.optState.getInt("GuiProjectDetails", "widthCol4", 90)) - self.chTree.setColumnWidth(0, wCol0) - self.chTree.setColumnWidth(1, wCol1) - self.chTree.setColumnWidth(2, wCol2) - self.chTree.setColumnWidth(3, wCol3) - self.chTree.setColumnWidth(4, wCol4) - self.chTree.setColumnWidth(5, hPx) + self.tocTree.setColumnWidth(0, wCol0) + self.tocTree.setColumnWidth(1, wCol1) + self.tocTree.setColumnWidth(2, wCol2) + self.tocTree.setColumnWidth(3, wCol3) + self.tocTree.setColumnWidth(4, wCol4) + self.tocTree.setColumnWidth(5, hPx) # Options # ======= @@ -334,7 +334,7 @@ class GuiProjectDetailsContents(QWidget): # ======== self.outerBox = QVBoxLayout() - self.outerBox.addWidget(self.chTree) + self.outerBox.addWidget(self.tocTree) self.outerBox.addLayout(self.optionsBox) self.setLayout(self.outerBox) @@ -348,11 +348,11 @@ class GuiProjectDetailsContents(QWidget): """Return the column widths for the tree columns. """ retVals = [ - self.chTree.columnWidth(0), - self.chTree.columnWidth(1), - self.chTree.columnWidth(2), - self.chTree.columnWidth(3), - self.chTree.columnWidth(4), + self.tocTree.columnWidth(0), + self.tocTree.columnWidth(1), + self.tocTree.columnWidth(2), + self.tocTree.columnWidth(3), + self.tocTree.columnWidth(4), ] return retVals @@ -389,19 +389,21 @@ class GuiProjectDetailsContents(QWidget): pTotal += pCount theList.append((tLevel, tTitle, wCount, pCount)) - self.chTree.clear() + self.tocTree.clear() for tLevel, tTitle, wCount, pCount in theList: newItem = QTreeWidgetItem() if tLevel == "H2": tTitle = nwUnicode.U_ENSP+tTitle + pgProg = 100.0*(tPages - 1)/pTotal if pTotal > 0 else 0.0 + newItem.setIcon(self.C_TITLE, self.theTheme.getIcon("doc_%s" % tLevel.lower())) newItem.setText(self.C_TITLE, tTitle) newItem.setText(self.C_WORDS, f"{wCount:n}") newItem.setText(self.C_PAGES, f"{pCount:n}") newItem.setText(self.C_PAGE, f"{tPages:n}") - newItem.setText(self.C_PROG, f"{100*(tPages - 1)/pTotal:.1f}{nwUnicode.U_THSP}%") + newItem.setText(self.C_PROG, f"{pgProg:.1f}{nwUnicode.U_THSP}%") newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight) newItem.setTextAlignment(self.C_PAGES, Qt.AlignRight) @@ -410,7 +412,7 @@ class GuiProjectDetailsContents(QWidget): tPages += pCount - self.chTree.addTopLevelItem(newItem) + self.tocTree.addTopLevelItem(newItem) return diff --git a/tests/test_gui_projdetails.py b/tests/test_gui_projdetails.py new file mode 100644 index 00000000..5af043a1 --- /dev/null +++ b/tests/test_gui_projdetails.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +"""novelWriter Writing Stats Dialog Class Tester +""" + +import pytest + +from tools import getGuiItem + +from PyQt5.QtWidgets import QAction, QMessageBox + +from nw.gui import GuiProjectDetails +from nw.constants import nwUnicode + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum): + """Test the full writing stats tool. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "warning", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) + + # Create a project to work on + assert nwGUI.openProject(nwLipsum) + assert nwGUI.rebuildIndex(beQuiet=True) + qtbot.wait(100) + + # Open the Writing Stats dialog + nwGUI.mainConf.lastPath = "" + nwGUI.mainMenu.aProjectDetails.activate(QAction.Trigger) + qtbot.waitUntil(lambda: getGuiItem("GuiProjectDetails") is not None, timeout=1000) + + projDet = getGuiItem("GuiProjectDetails") + assert isinstance(projDet, GuiProjectDetails) + qtbot.wait(stepDelay) + + # Overview Page + # ============= + + assert projDet.tabMain.bookTitle.text() == "Lorem Ipsum" + assert projDet.tabMain.projName.text()[-11:] == "Lorem Ipsum" + assert projDet.tabMain.bookAuthors.text()[-10:] == "lipsum.com" + + assert projDet.tabMain.wordCountVal.text() == f"{3000:n}" + assert projDet.tabMain.chapCountVal.text() == f"{3:n}" + assert projDet.tabMain.sceneCountVal.text() == f"{5:n}" + assert projDet.tabMain.revCountVal.text() == f"{nwGUI.theProject.saveCount:n}" + + assert projDet.tabMain.projPathVal.text() == nwLipsum + + # Contents Page + # ============= + + tocTab = projDet.tabContents + tocTree = tocTab.tocTree + assert tocTree.topLevelItemCount() == 7 + assert tocTree.topLevelItem(0).text(tocTab.C_TITLE) == "Lorem Ipsum" + assert tocTree.topLevelItem(2).text(tocTab.C_TITLE) == nwUnicode.U_ENSP+"Prologue" + assert tocTree.topLevelItem(3).text(tocTab.C_TITLE) == "Act One" + assert tocTree.topLevelItem(4).text(tocTab.C_TITLE) == nwUnicode.U_ENSP+"Chapter One" + assert tocTree.topLevelItem(5).text(tocTab.C_TITLE) == nwUnicode.U_ENSP+"Chapter Two" + assert tocTree.topLevelItem(6).text(tocTab.C_TITLE) == "END" + + # Count Pages + tocTab.wpValue.setValue(100) + tocTab.dblValue.setChecked(False) + tocTab._populateTree() + + thePages = [1, 2, 1, 1, 11, 17, 0] + thePage = [1, 2, 4, 5, 6, 17, 34] + for i in range(7): + assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == f"{thePages[i]:n}" + assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == f"{thePage[i]:n}" + + tocTab.dblValue.setChecked(True) + tocTab._populateTree() + + thePages = [2, 2, 2, 2, 12, 18, 0] + thePage = [1, 3, 5, 7, 9, 21, 39] + for i in range(7): + assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == f"{thePages[i]:n}" + assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == f"{thePage[i]:n}" + + # qtbot.stopForInteraction() + + # Clean Up + projDet._doClose() + nwGUI.closeMain() + monkeypatch.undo() + +# END Test testGuiProjDetails_Dialog