Re-implement word counters

This commit is contained in:
Veronica Berglyd Olsen
2024-11-21 00:14:54 +01:00
parent 2f71ec68d8
commit 55eed706fb
6 changed files with 50 additions and 76 deletions
+15 -4
View File
@@ -79,6 +79,7 @@ class ProjectNode:
self._cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {} self._cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
self._flags = NODE_FLAGS self._flags = NODE_FLAGS
self.refresh() self.refresh()
self.updateCount()
return return
def __repr__(self) -> str: def __repr__(self) -> str:
@@ -95,12 +96,19 @@ class ProjectNode:
@property @property
def item(self) -> NWItem: def item(self) -> NWItem:
"""The project item of the node."""
return self._item return self._item
@property @property
def children(self) -> list[ProjectNode]: def children(self) -> list[ProjectNode]:
"""All children of the node."""
return self._children return self._children
@property
def count(self) -> int:
"""The count of the node."""
return self._count
## ##
# Data Maintenance # Data Maintenance
## ##
@@ -127,11 +135,10 @@ class ProjectNode:
self._cache[C_STATUS_TIP] = sText self._cache[C_STATUS_TIP] = sText
self._cache[C_STATUS_ICON] = sIcon self._cache[C_STATUS_ICON] = sIcon
self.updateCount()
return return
def updateCount(self, propagate: bool = True) -> None: def updateCount(self, propagate: bool = True) -> None:
"""Update counts, and propagate upwards in the tree."""
self._count = self._item.wordCount + sum(c._count for c in self._children) self._count = self._item.wordCount + sum(c._count for c in self._children)
self._cache[C_COUNT_TEXT] = f"{self._count:n}" self._cache[C_COUNT_TEXT] = f"{self._count:n}"
if propagate and (parent := self._parent): if propagate and (parent := self._parent):
@@ -143,9 +150,11 @@ class ProjectNode:
## ##
def row(self) -> int: def row(self) -> int:
"""Return the node's row number."""
return self._row return self._row
def childCount(self) -> int: def childCount(self) -> int:
"""Return the number of children of the node."""
return len(self._children) return len(self._children)
def data(self, column: int, role: Qt.ItemDataRole) -> T_NodeData: def data(self, column: int, role: Qt.ItemDataRole) -> T_NodeData:
@@ -157,9 +166,11 @@ class ProjectNode:
return self._flags return self._flags
def parent(self) -> ProjectNode | None: def parent(self) -> ProjectNode | None:
"""Return the parent of the node."""
return self._parent return self._parent
def child(self, row: int) -> ProjectNode | None: def child(self, row: int) -> ProjectNode | None:
"""Return a child ofg the node."""
if 0 <= row < len(self._children): if 0 <= row < len(self._children):
return self._children[row] return self._children[row]
return None return None
@@ -366,9 +377,9 @@ class ProjectModel(QAbstractItemModel):
return self.createIndex(node.row(), 0, node) return self.createIndex(node.row(), 0, node)
return QModelIndex() return QModelIndex()
def indexFromNode(self, node: ProjectNode) -> QModelIndex: def indexFromNode(self, node: ProjectNode, column: int = 0) -> QModelIndex:
"""Get the index representing a node in the model.""" """Get the index representing a node in the model."""
return self.createIndex(node.row(), 0, node) return self.createIndex(node.row(), column, node)
def rootIndex(self) -> QModelIndex: def rootIndex(self) -> QModelIndex:
"""Get the index representing the root.""" """Get the index representing the root."""
+5
View File
@@ -145,6 +145,11 @@ class NWProject:
"""Return total edit time, including the current session.""" """Return total edit time, including the current session."""
return self._data.editTime + round(time() - self._session.start) return self._data.editTime + round(time() - self._session.start)
@property
def currentTotalCount(self) -> int:
"""Return the current total word count from the tree."""
return self._tree.model.root.count
## ##
# Item Methods # Item Methods
## ##
+9 -9
View File
@@ -230,22 +230,22 @@ class NWTree:
return return
def refreshItems(self, items: list[str], isRange: bool = False) -> None: def refreshItems(self, items: list[str]) -> None:
"""Refresh these items on the GUI. If they are an ordered range, """Refresh these items on the GUI. If they are an ordered range,
also set the isRange flag to True. also set the isRange flag to True.
""" """
indices = [] change = False
for tHandle in items: for tHandle in items:
if node := self._nodes.get(tHandle): if node := self._nodes.get(tHandle):
node.refresh() node.refresh()
node.updateCount()
SHARED.projectSignalProxy({"event": "projectItem", "handle": tHandle}) SHARED.projectSignalProxy({"event": "projectItem", "handle": tHandle})
indices.append(self._model.indexFromNode(node)) indexS = self._model.indexFromNode(node, 0)
if isRange and len(indices) >= 2: indexE = self._model.indexFromNode(node, 3)
self._model.dataChanged.emit(indices[0], indices[-1]) self._model.dataChanged.emit(indexS, indexE)
else: change = True
for index in indices: if change:
self._model.dataChanged.emit(index, index) self._project.setProjectChanged(True)
self._project.setProjectChanged(len(indices) > 0)
return return
def checkConsistency(self, prefix: str) -> tuple[int, int]: def checkConsistency(self, prefix: str) -> tuple[int, int]:
+1 -2
View File
@@ -110,7 +110,6 @@ class GuiDocEditor(QPlainTextEdit):
# Custom Signals # Custom Signals
closeEditorRequest = pyqtSignal() closeEditorRequest = pyqtSignal()
docCountsChanged = pyqtSignal(str, int, int, int)
docTextChanged = pyqtSignal(str, float) docTextChanged = pyqtSignal(str, float)
editedStatusChanged = pyqtSignal(bool) editedStatusChanged = pyqtSignal(bool)
itemHandleChanged = pyqtSignal(str) itemHandleChanged = pyqtSignal(str)
@@ -1281,7 +1280,7 @@ class GuiDocEditor(QPlainTextEdit):
self._nwItem.setCharCount(cCount) self._nwItem.setCharCount(cCount)
self._nwItem.setWordCount(wCount) self._nwItem.setWordCount(wCount)
self._nwItem.setParaCount(pCount) self._nwItem.setParaCount(pCount)
self.docCountsChanged.emit(self._docHandle, cCount, wCount, pCount) self._nwItem.notifyToRefresh()
self.docFooter.updateWordCount(wCount, False) self.docFooter.updateWordCount(wCount, False)
return return
-47
View File
@@ -67,7 +67,6 @@ class GuiProjectView(QWidget):
# Signals triggered when the meta data values of items change # Signals triggered when the meta data values of items change
rootFolderChanged = pyqtSignal(str) rootFolderChanged = pyqtSignal(str)
wordCountsChanged = pyqtSignal()
# Signals for user interaction with the project tree # Signals for user interaction with the project tree
selectedItemChanged = pyqtSignal(str) selectedItemChanged = pyqtSignal(str)
@@ -241,13 +240,6 @@ class GuiProjectView(QWidget):
self.projTree.newTreeItem(nwItemType.FILE, copyDoc=tHandle) self.projTree.newTreeItem(nwItemType.FILE, copyDoc=tHandle)
return return
@pyqtSlot(str, int, int, int)
def updateCounts(self, tHandle: str, cCount: int, wCount: int, pCount: int) -> None:
"""Slot for updating the word count of a specific item."""
self.projTree.propagateCount(tHandle, wCount, countChildren=True)
self.wordCountsChanged.emit()
return
@pyqtSlot(str) @pyqtSlot(str)
def updateRootItem(self, tHandle: str) -> None: def updateRootItem(self, tHandle: str) -> None:
"""Process root item changes.""" """Process root item changes."""
@@ -1067,45 +1059,6 @@ class GuiProjectTree(QTreeView):
# self.setTreeItemValues(nwItem) # self.setTreeItemValues(nwItem)
return return
def propagateCount(self, tHandle: str, newCount: int, countChildren: bool = False) -> None:
"""Recursive function setting the word count for a given item,
and propagating that count upwards in the tree until reaching a
root item. This function is more efficient than recalculating
everything each time the word count is updated, but is also
prone to diverging from the true values if the counts are not
properly reported to the function.
"""
# tItem = self._getTreeItem(tHandle)
# if tItem is None:
# return
# if countChildren:
# for i in range(tItem.childCount()):
# newCount += int(tItem.child(i).data(self.C_DATA, self.D_WORDS))
# tItem.setText(self.C_COUNT, f"{newCount:n}")
# tItem.setData(self.C_DATA, self.D_WORDS, int(newCount))
# pItem = tItem.parent()
# if pItem is None:
# return
# pCount = 0
# pHandle = None
# for i in range(pItem.childCount()):
# pCount += int(pItem.child(i).data(self.C_DATA, self.D_WORDS))
# pHandle = pItem.data(self.C_DATA, self.D_HANDLE)
# if pHandle:
# if SHARED.project.tree.checkType(pHandle, nwItemType.FILE):
# # A file has an internal word count we need to account
# # for, but a folder always has 0 words on its own.
# pCount += SHARED.project.index.getCounts(pHandle)[1]
# self.propagateCount(pHandle, pCount, countChildren=False)
return
class _UpdatableMenu(QMenu): class _UpdatableMenu(QMenu):
+20 -14
View File
@@ -242,7 +242,6 @@ class GuiMain(QMainWindow):
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem) self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
self.projView.rootFolderChanged.connect(self.projView.updateRootItem) self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox) self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
self.novelView.openDocumentRequest.connect(self._openDocument) self.novelView.openDocumentRequest.connect(self._openDocument)
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox) self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
@@ -251,8 +250,6 @@ class GuiMain(QMainWindow):
self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox) self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox)
self.docEditor.closeEditorRequest.connect(self.closeDocEditor) self.docEditor.closeEditorRequest.connect(self.closeDocEditor)
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
self.docEditor.docTextChanged.connect(self.projSearch.textChanged) self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus) self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle) self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
@@ -315,6 +312,9 @@ class GuiMain(QMainWindow):
self.keyEscape.setKey("Esc") self.keyEscape.setKey("Esc")
self.keyEscape.activated.connect(self._keyPressEscape) self.keyEscape.activated.connect(self._keyPressEscape)
# Internal Variables
self._lastTotalCount = 0
# Initialise Main GUI # Initialise Main GUI
self.initMain() self.initMain()
self.asProjTimer.start() self.asProjTimer.start()
@@ -1226,8 +1226,10 @@ class GuiMain(QMainWindow):
self.mainStatus.setUserIdle(editIdle or userIdle) self.mainStatus.setUserIdle(editIdle or userIdle)
SHARED.updateIdleTime(currTime, editIdle or userIdle) SHARED.updateIdleTime(currTime, editIdle or userIdle)
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime) self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
if CONFIG.memInfo and int(currTime) % 5 == 0: # pragma: no cover if int(currTime) % 5 == 0:
self.mainStatus.memInfo() self._updateStatusWordCount()
if CONFIG.memInfo: # pragma: no cover
self.mainStatus.memInfo()
return return
@pyqtSlot() @pyqtSlot()
@@ -1255,15 +1257,19 @@ class GuiMain(QMainWindow):
if not SHARED.hasProject: if not SHARED.hasProject:
self.mainStatus.setProjectStats(0, 0) self.mainStatus.setProjectStats(0, 0)
SHARED.project.updateWordCounts() currentTotalCount = SHARED.project.currentTotalCount
if CONFIG.incNotesWCount: if self._lastTotalCount != currentTotalCount:
iTotal = sum(SHARED.project.data.initCounts) self._lastTotalCount = currentTotalCount
cTotal = sum(SHARED.project.data.currCounts)
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal) SHARED.project.updateWordCounts()
else: if CONFIG.incNotesWCount:
iNovel, _ = SHARED.project.data.initCounts iTotal = sum(SHARED.project.data.initCounts)
cNovel, _ = SHARED.project.data.currCounts cTotal = sum(SHARED.project.data.currCounts)
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel) self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
else:
iNovel, _ = SHARED.project.data.initCounts
cNovel, _ = SHARED.project.data.currCounts
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
return return