Added timestamp to project tree, and made some class variables internal in various classes
This commit is contained in:
+32
-19
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -761,9 +761,7 @@ class GuiMain(QMainWindow):
|
||||
"""
|
||||
self._makeStatusIcons()
|
||||
self._makeImportIcons()
|
||||
self.treeView.clearTree()
|
||||
self.treeView.buildTree()
|
||||
self.novelView.clearTree()
|
||||
self.novelView.refreshTree()
|
||||
return
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user