Re-implement word counters
This commit is contained in:
@@ -79,6 +79,7 @@ class ProjectNode:
|
||||
self._cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
|
||||
self._flags = NODE_FLAGS
|
||||
self.refresh()
|
||||
self.updateCount()
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
@@ -95,12 +96,19 @@ class ProjectNode:
|
||||
|
||||
@property
|
||||
def item(self) -> NWItem:
|
||||
"""The project item of the node."""
|
||||
return self._item
|
||||
|
||||
@property
|
||||
def children(self) -> list[ProjectNode]:
|
||||
"""All children of the node."""
|
||||
return self._children
|
||||
|
||||
@property
|
||||
def count(self) -> int:
|
||||
"""The count of the node."""
|
||||
return self._count
|
||||
|
||||
##
|
||||
# Data Maintenance
|
||||
##
|
||||
@@ -127,11 +135,10 @@ class ProjectNode:
|
||||
self._cache[C_STATUS_TIP] = sText
|
||||
self._cache[C_STATUS_ICON] = sIcon
|
||||
|
||||
self.updateCount()
|
||||
|
||||
return
|
||||
|
||||
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._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
||||
if propagate and (parent := self._parent):
|
||||
@@ -143,9 +150,11 @@ class ProjectNode:
|
||||
##
|
||||
|
||||
def row(self) -> int:
|
||||
"""Return the node's row number."""
|
||||
return self._row
|
||||
|
||||
def childCount(self) -> int:
|
||||
"""Return the number of children of the node."""
|
||||
return len(self._children)
|
||||
|
||||
def data(self, column: int, role: Qt.ItemDataRole) -> T_NodeData:
|
||||
@@ -157,9 +166,11 @@ class ProjectNode:
|
||||
return self._flags
|
||||
|
||||
def parent(self) -> ProjectNode | None:
|
||||
"""Return the parent of the node."""
|
||||
return self._parent
|
||||
|
||||
def child(self, row: int) -> ProjectNode | None:
|
||||
"""Return a child ofg the node."""
|
||||
if 0 <= row < len(self._children):
|
||||
return self._children[row]
|
||||
return None
|
||||
@@ -366,9 +377,9 @@ class ProjectModel(QAbstractItemModel):
|
||||
return self.createIndex(node.row(), 0, node)
|
||||
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."""
|
||||
return self.createIndex(node.row(), 0, node)
|
||||
return self.createIndex(node.row(), column, node)
|
||||
|
||||
def rootIndex(self) -> QModelIndex:
|
||||
"""Get the index representing the root."""
|
||||
|
||||
@@ -145,6 +145,11 @@ class NWProject:
|
||||
"""Return total edit time, including the current session."""
|
||||
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
|
||||
##
|
||||
|
||||
@@ -230,22 +230,22 @@ class NWTree:
|
||||
|
||||
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,
|
||||
also set the isRange flag to True.
|
||||
"""
|
||||
indices = []
|
||||
change = False
|
||||
for tHandle in items:
|
||||
if node := self._nodes.get(tHandle):
|
||||
node.refresh()
|
||||
node.updateCount()
|
||||
SHARED.projectSignalProxy({"event": "projectItem", "handle": tHandle})
|
||||
indices.append(self._model.indexFromNode(node))
|
||||
if isRange and len(indices) >= 2:
|
||||
self._model.dataChanged.emit(indices[0], indices[-1])
|
||||
else:
|
||||
for index in indices:
|
||||
self._model.dataChanged.emit(index, index)
|
||||
self._project.setProjectChanged(len(indices) > 0)
|
||||
indexS = self._model.indexFromNode(node, 0)
|
||||
indexE = self._model.indexFromNode(node, 3)
|
||||
self._model.dataChanged.emit(indexS, indexE)
|
||||
change = True
|
||||
if change:
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def checkConsistency(self, prefix: str) -> tuple[int, int]:
|
||||
|
||||
@@ -110,7 +110,6 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
|
||||
# Custom Signals
|
||||
closeEditorRequest = pyqtSignal()
|
||||
docCountsChanged = pyqtSignal(str, int, int, int)
|
||||
docTextChanged = pyqtSignal(str, float)
|
||||
editedStatusChanged = pyqtSignal(bool)
|
||||
itemHandleChanged = pyqtSignal(str)
|
||||
@@ -1281,7 +1280,7 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
self._nwItem.setCharCount(cCount)
|
||||
self._nwItem.setWordCount(wCount)
|
||||
self._nwItem.setParaCount(pCount)
|
||||
self.docCountsChanged.emit(self._docHandle, cCount, wCount, pCount)
|
||||
self._nwItem.notifyToRefresh()
|
||||
self.docFooter.updateWordCount(wCount, False)
|
||||
return
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@ class GuiProjectView(QWidget):
|
||||
|
||||
# Signals triggered when the meta data values of items change
|
||||
rootFolderChanged = pyqtSignal(str)
|
||||
wordCountsChanged = pyqtSignal()
|
||||
|
||||
# Signals for user interaction with the project tree
|
||||
selectedItemChanged = pyqtSignal(str)
|
||||
@@ -241,13 +240,6 @@ class GuiProjectView(QWidget):
|
||||
self.projTree.newTreeItem(nwItemType.FILE, copyDoc=tHandle)
|
||||
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)
|
||||
def updateRootItem(self, tHandle: str) -> None:
|
||||
"""Process root item changes."""
|
||||
@@ -1067,45 +1059,6 @@ class GuiProjectTree(QTreeView):
|
||||
# self.setTreeItemValues(nwItem)
|
||||
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):
|
||||
|
||||
|
||||
+20
-14
@@ -242,7 +242,6 @@ class GuiMain(QMainWindow):
|
||||
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
||||
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
||||
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
|
||||
|
||||
self.novelView.openDocumentRequest.connect(self._openDocument)
|
||||
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||
@@ -251,8 +250,6 @@ class GuiMain(QMainWindow):
|
||||
self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||
|
||||
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.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
|
||||
self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
|
||||
@@ -315,6 +312,9 @@ class GuiMain(QMainWindow):
|
||||
self.keyEscape.setKey("Esc")
|
||||
self.keyEscape.activated.connect(self._keyPressEscape)
|
||||
|
||||
# Internal Variables
|
||||
self._lastTotalCount = 0
|
||||
|
||||
# Initialise Main GUI
|
||||
self.initMain()
|
||||
self.asProjTimer.start()
|
||||
@@ -1226,8 +1226,10 @@ class GuiMain(QMainWindow):
|
||||
self.mainStatus.setUserIdle(editIdle or userIdle)
|
||||
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
||||
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
||||
if CONFIG.memInfo and int(currTime) % 5 == 0: # pragma: no cover
|
||||
self.mainStatus.memInfo()
|
||||
if int(currTime) % 5 == 0:
|
||||
self._updateStatusWordCount()
|
||||
if CONFIG.memInfo: # pragma: no cover
|
||||
self.mainStatus.memInfo()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -1255,15 +1257,19 @@ class GuiMain(QMainWindow):
|
||||
if not SHARED.hasProject:
|
||||
self.mainStatus.setProjectStats(0, 0)
|
||||
|
||||
SHARED.project.updateWordCounts()
|
||||
if CONFIG.incNotesWCount:
|
||||
iTotal = sum(SHARED.project.data.initCounts)
|
||||
cTotal = sum(SHARED.project.data.currCounts)
|
||||
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||
else:
|
||||
iNovel, _ = SHARED.project.data.initCounts
|
||||
cNovel, _ = SHARED.project.data.currCounts
|
||||
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
||||
currentTotalCount = SHARED.project.currentTotalCount
|
||||
if self._lastTotalCount != currentTotalCount:
|
||||
self._lastTotalCount = currentTotalCount
|
||||
|
||||
SHARED.project.updateWordCounts()
|
||||
if CONFIG.incNotesWCount:
|
||||
iTotal = sum(SHARED.project.data.initCounts)
|
||||
cTotal = sum(SHARED.project.data.currCounts)
|
||||
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||
else:
|
||||
iNovel, _ = SHARED.project.data.initCounts
|
||||
cNovel, _ = SHARED.project.data.currCounts
|
||||
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
||||
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user