Added timestamp to project tree, and made some class variables internal in various classes

This commit is contained in:
Veronica K. B. Olsen
2021-01-02 16:57:12 +01:00
parent ad5f3f2450
commit 4d7f925c5a
8 changed files with 108 additions and 87 deletions
+32 -19
View File
@@ -51,18 +51,16 @@ class NWIndex():
self.indexBroken = False
# Indices
self.tagIndex = None
self.refIndex = None
self.novelIndex = None
self.noteIndex = None
self.textCounts = None
self.tagIndex = {}
self.refIndex = {}
self.novelIndex = {}
self.noteIndex = {}
self.textCounts = {}
# TimeStamps
self.timeNovel = 0
self.timeNote = 0
self.timeIndex = 0
self.clearIndex()
self._timeNovel = 0
self._timeNotes = 0
self._timeIndex = 0
return
@@ -78,9 +76,9 @@ class NWIndex():
self.novelIndex = {}
self.noteIndex = {}
self.textCounts = {}
self.timeNovel = 0
self.timeNote = 0
self.timeIndex = 0
self._timeNovel = 0
self._timeNotes = 0
self._timeIndex = 0
return
def deleteHandle(self, tHandle):
@@ -123,6 +121,21 @@ class NWIndex():
return True
def novelChangedSince(self, checkTime):
"""Check if the novel index has changed since a given time.
"""
return self._timeNovel > checkTime
def notesChangedSince(self, checkTime):
"""Check if the notes index has changed since a given time.
"""
return self._timeNotes > checkTime
def indexChangedSince(self, checkTime):
"""Check if the index has changed since a given time.
"""
return self._timeIndex > checkTime
##
# Load and Save Index to/from File
##
@@ -155,9 +168,9 @@ class NWIndex():
self.textCounts = theData["textCounts"]
nowTime = round(time())
self.timeNovel = nowTime
self.timeNote = nowTime
self.timeIndex = nowTime
self._timeNovel = nowTime
self._timeNotes = nowTime
self._timeIndex = nowTime
self.checkIndex()
@@ -334,11 +347,11 @@ class NWIndex():
# Update timestamps for index changes
nowTime = round(time())
self.timeIndex = nowTime
self._timeIndex = nowTime
if isNovel:
self.timeNovel = nowTime
self._timeNovel = nowTime
else:
self.timeNote = nowTime
self._timeNotes = nowTime
return True
+2 -2
View File
@@ -1025,7 +1025,7 @@ class NWProject():
by drag-and-drop. Forwarded to the NWTree class.
"""
if len(self.projTree) != len(newOrder):
logger.warning("Size of new and old tree order do not match")
logger.warning("Sizes of new and old tree order do not match")
self.projTree.setOrder(newOrder)
self.setProjectChanged(True)
return True
@@ -1341,7 +1341,7 @@ class NWProject():
if oLayout is None:
oLayout = nwItemLayout.NOTE
if oParent is None or not self.projTree.handleExists(oParent):
if oParent is None or oParent not in self.projTree:
oParent = self.projTree.findRoot(oClass)
if oParent is None:
oParent = self.projTree.findRoot(nwItemClass.NOVEL)
-5
View File
@@ -294,11 +294,6 @@ class NWTree():
tTree.append(tHandle)
return tTree
def handleExists(self, tHandle):
"""Check if a handle exists in the project.
"""
return tHandle in self._treeOrder
##
# Setters
##
+15 -11
View File
@@ -54,9 +54,9 @@ class GuiNovelTree(QTreeWidget):
self.theProject = theParent.theProject
self.theIndex = theParent.theIndex
# Tree State
self.lastBuild = 0
self.treeMap = {}
# Internal Variables
self._treeMap = {}
self._lastBuild = 0
# Build GUI
iPx = self.theTheme.baseIconSize
@@ -124,14 +124,17 @@ class GuiNovelTree(QTreeWidget):
"""Clear the GUI content and the related maps.
"""
self.clear()
self.treeMap = {}
self._treeMap = {}
self._lastBuild = 0
return
def refreshTree(self, overRide=False):
"""Called whenever the Novel tab is activated.
"""
if self.lastBuild >= self.theIndex.timeNovel:
logger.verbose("Novel tree more recent than the novel index: not updating")
treeChanged = self.theParent.treeView.changedSince(self._lastBuild)
indexChanged = self.theIndex.novelChangedSince(self._lastBuild)
if not (treeChanged or indexChanged):
logger.verbose("No changes made to the novel")
return
selItem = self.selectedItems()
@@ -139,10 +142,11 @@ class GuiNovelTree(QTreeWidget):
if selItem:
titleKey = selItem[0].data(self.C_TITLE, Qt.UserRole)[2]
self.theParent.treeView.flushTreeOrder()
self._populateTree()
if titleKey is not None and titleKey in self.treeMap:
self.treeMap[titleKey].setSelected(True)
if titleKey is not None and titleKey in self._treeMap:
self._treeMap[titleKey].setSelected(True)
return
@@ -233,7 +237,7 @@ class GuiNovelTree(QTreeWidget):
def _populateTree(self):
"""Build the tree based on the project index.
"""
self.clear()
self.clearTree()
for titleKey in self.theIndex.getNovelStructure(skipExcluded=True):
@@ -250,7 +254,7 @@ class GuiNovelTree(QTreeWidget):
tLevel = self.theIndex.novelIndex[tHandle][sTitle]["level"]
tItem = self._createTreeItem(tHandle, sTitle, tLevel, titleKey)
self.treeMap[titleKey] = tItem
self._treeMap[titleKey] = tItem
if tLevel == "H1":
currTitle = tItem
@@ -284,7 +288,7 @@ class GuiNovelTree(QTreeWidget):
tItem.setExpanded(True)
self.lastBuild = time()
self._lastBuild = time()
return
+2 -5
View File
@@ -179,11 +179,8 @@ class GuiOutline(QTreeWidget):
# If the novel index has changed since the tree was last built,
# we rebuild the tree from the updated index.
lastChange = self.theParent.theIndex.timeNovel
logger.verbose("Last outline build: %.3f" % self.lastBuild)
logger.verbose("Novel index change: %.3f" % lastChange)
doBuild = lastChange > self.lastBuild and self.theProject.autoOutline
idxChanged = self.theParent.theIndex.novelChangedSince(self.lastBuild)
doBuild = idxChanged and self.theProject.autoOutline
if doBuild or overRide:
logger.debug("Rebuilding Project Outline")
self._populateTree()
+49 -43
View File
@@ -29,6 +29,8 @@
import nw
import logging
from time import time
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
@@ -60,22 +62,27 @@ class GuiProjectTree(QTreeWidget):
self.theProject = theParent.theProject
self.theIndex = theParent.theIndex
# Tree Settings
self.theMap = {}
self.treeChanged = False
# Internal Variables
self._treeMap = {}
self._treeChanged = False
self._timeChanged = 0
##
# Build GUI
##
# Context Menu
self.ctxMenu = GuiProjectTreeMenu(self)
self.clearTree()
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._rightClickMenu)
# Build GUI
# Tree Settings
iPx = self.theTheme.baseIconSize
self.setIconSize(QSize(iPx, iPx))
self.setExpandsOnDoubleClick(True)
self.setIndentation(iPx)
self.setColumnCount(4)
self.setHeaderLabels(["Label", "Words", "Inc", "Flags"])
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._rightClickMenu)
treeHeadItem = self.headerItem()
treeHeadItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
@@ -102,7 +109,7 @@ class GuiProjectTree(QTreeWidget):
# Set Multiple Selection by CTRL
# Disabled for now, until the merge files option has been added
# self.setSelectionMode(QAbstractItemView.ExtendedSelection)
# self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
# Get user's column width preferences for NAME and COUNT
treeColWidth = self.mainConf.getTreeColWidths()
@@ -116,10 +123,11 @@ class GuiProjectTree(QTreeWidget):
# Set custom settings
self.initTree()
logger.debug("GuiProjectTree initialisation complete")
# Internal Function Mapping
self.makeAlert = self.theParent.makeAlert
self.askQuestion = self.theParent.askQuestion
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
logger.debug("GuiProjectTree initialisation complete")
return
@@ -147,8 +155,9 @@ class GuiProjectTree(QTreeWidget):
"""Clear the GUI content and the related map.
"""
self.clear()
self.theMap = {}
self.treeChanged = False
self._treeMap = {}
self._treeChanged = False
self._timeChanged = 0
return
def newTreeItem(self, itemType, itemClass):
@@ -274,8 +283,8 @@ class GuiProjectTree(QTreeWidget):
return False
pHandle = nwItem.itemParent
if pHandle is not None and pHandle in self.theMap:
self.theMap[pHandle].setExpanded(True)
if pHandle is not None and pHandle in self._treeMap:
self._treeMap[pHandle].setExpanded(True)
self.clearSelection()
trItem.setSelected(True)
return True
@@ -338,7 +347,7 @@ class GuiProjectTree(QTreeWidget):
"""Calls saveTreeOrder if there are unsaved changes, otherwise
does nothing.
"""
if self.treeChanged:
if self._treeChanged:
logger.verbose("Flushing project tree to project class")
self.saveTreeOrder()
self._setTreeChanged(False)
@@ -391,7 +400,7 @@ class GuiProjectTree(QTreeWidget):
self.makeAlert("The Trash folder is already empty.", nwAlert.INFO)
return False
msgYes = self.theParent.askQuestion(
msgYes = self.askQuestion(
"Empty Trash", "Permanently delete %d file(s) from Trash?" % nTrash
)
if not msgYes:
@@ -446,7 +455,7 @@ class GuiProjectTree(QTreeWidget):
# user if they want to permanently delete the file.
doPermanent = False
if not alreadyAsked:
msgYes = self.theParent.askQuestion(
msgYes = self.askQuestion(
"Delete File", "Permanently delete file '%s'?" % nwItemS.itemName
)
if msgYes:
@@ -474,7 +483,7 @@ class GuiProjectTree(QTreeWidget):
# move it there.
doTrash = False
if askForTrash:
msgYes = self.theParent.askQuestion(
msgYes = self.askQuestion(
"Delete File", "Move file '%s' to Trash?" % nwItemS.itemName
)
if msgYes:
@@ -533,7 +542,9 @@ class GuiProjectTree(QTreeWidget):
return True
def setTreeItemValues(self, tHandle):
"""Set the name and flag values for a tree item.
"""Set the name and flag values for a tree item from a handle in
the project tree. Does not trigger a tree change as the data is
already coming from the project tree.
"""
trItem = self._getTreeItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
@@ -622,9 +633,9 @@ class GuiProjectTree(QTreeWidget):
sent first.
"""
logger.debug("Building the project tree ...")
self.clear()
iCount = 0
self.clearTree()
iCount = 0
for nwItem in self.theProject.getProjectItems():
iCount += 1
self._addTreeItem(nwItem)
@@ -642,21 +653,10 @@ class GuiProjectTree(QTreeWidget):
return None
def getSelectedHandles(self):
"""Return a list of all currently selected item handles.
"""
selItems = self.selectedItems()
selHandles = []
for n in range(len(selItems)):
if isinstance(selItems[n], QTreeWidgetItem):
selHandles.append(selItems[n].data(self.C_NAME, Qt.UserRole))
return selHandles
def setSelectedHandle(self, tHandle, doScroll=False):
"""Set a specific handle as the selected item.
"""
if tHandle not in self.theMap:
if tHandle not in self._treeMap:
return False
tItem = self._getTreeItem(tHandle)
@@ -664,7 +664,7 @@ class GuiProjectTree(QTreeWidget):
return False
self.clearSelection()
self.theMap[tHandle].setSelected(True)
self._treeMap[tHandle].setSelected(True)
selItems = self.selectedIndexes()
if selItems and doScroll:
@@ -672,6 +672,11 @@ class GuiProjectTree(QTreeWidget):
return True
def changedSince(self, checkTime):
"""Check if the tree has changed since a given time.
"""
return self._timeChanged > checkTime
##
# Slots
##
@@ -797,7 +802,7 @@ class GuiProjectTree(QTreeWidget):
def _getTreeItem(self, tHandle):
"""Returns the QTreeWidgetItem of a given item handle.
"""
return self.theMap.get(tHandle, None)
return self._treeMap.get(tHandle, None)
def _scanChildren(self, theList, theItem, theIndex):
"""This is a recursive function returning all items in a tree
@@ -834,7 +839,7 @@ class GuiProjectTree(QTreeWidget):
newItem.setData(self.C_NAME, Qt.UserRole, tHandle)
newItem.setData(self.C_COUNT, Qt.UserRole, 0)
self.theMap[tHandle] = newItem
self._treeMap[tHandle] = newItem
if pHandle is None:
if nwItem.itemType == nwItemType.ROOT:
self.addTopLevelItem(newItem)
@@ -845,20 +850,20 @@ class GuiProjectTree(QTreeWidget):
self.makeAlert(
"There is nowhere to add item with name '%s'" % nwItem.itemName, nwAlert.ERROR
)
del self.theMap[tHandle]
del self._treeMap[tHandle]
return None
else:
byIndex = -1
if nHandle is not None and nHandle in self.theMap:
if nHandle is not None and nHandle in self._treeMap:
try:
byIndex = self.theMap[pHandle].indexOfChild(self.theMap[nHandle])
byIndex = self._treeMap[pHandle].indexOfChild(self._treeMap[nHandle])
except Exception:
logger.error("Failed to get index of item with handle %s" % nHandle)
if byIndex >= 0:
self.theMap[pHandle].insertChild(byIndex+1, newItem)
self._treeMap[pHandle].insertChild(byIndex+1, newItem)
else:
self.theMap[pHandle].addChild(newItem)
self._treeMap[pHandle].addChild(newItem)
self.propagateCount(tHandle, nwItem.wordCount)
self.setTreeItemValues(tHandle)
@@ -919,8 +924,9 @@ class GuiProjectTree(QTreeWidget):
def _setTreeChanged(self, theState):
"""Set the tree change flag, and propagate to the project.
"""
self.treeChanged = theState
self._treeChanged = theState
if theState:
self._timeChanged = time()
self.theProject.setProjectChanged(True)
return
-2
View File
@@ -761,9 +761,7 @@ class GuiMain(QMainWindow):
"""
self._makeStatusIcons()
self._makeImportIcons()
self.treeView.clearTree()
self.treeView.buildTree()
self.novelView.clearTree()
self.novelView.refreshTree()
return
+8
View File
@@ -205,6 +205,10 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
nItem = theProject.projTree[nHandle]
cItem = theProject.projTree[cHandle]
assert not theIndex.novelChangedSince(0)
assert not theIndex.notesChangedSince(0)
assert not theIndex.indexChangedSince(0)
assert theIndex.scanText(cHandle, (
"# Jane Smith\n"
"@tag: Jane"
@@ -216,6 +220,10 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
assert theIndex.tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!"
assert theIndex.novelChangedSince(0)
assert theIndex.notesChangedSince(0)
assert theIndex.indexChangedSince(0)
assert theIndex.checkThese([], cItem) == []
assert theIndex.checkThese(["@tag", "Jane"], cItem) == [True, True]
assert theIndex.checkThese(["@tag", "John"], cItem) == [True, True]