Simplify the class variables of the tree class
This commit is contained in:
+54
-61
@@ -65,13 +65,12 @@ class NWTree:
|
||||
|
||||
self._project = project
|
||||
|
||||
self._projTree: dict[str, NWItem] = {} # Holds all the items of the project
|
||||
self._treeOrder: list[str] = [] # The order of the tree items in the tree view
|
||||
self._treeRoots: dict[str, NWItem] = {} # The root items of the tree
|
||||
self._tree: dict[str, NWItem] = {} # Holds all the items of the project
|
||||
self._order: list[str] = [] # The order of the tree items in the tree view
|
||||
self._roots: dict[str, NWItem] = {} # The root items of the tree
|
||||
|
||||
self._trashRoot = None # The handle of the trash root folder
|
||||
self._archRoot = None # The handle of the archive root folder
|
||||
self._treeChanged = False # True if tree structure has changed
|
||||
self._trash = None # The handle of the trash root folder
|
||||
self._changed = False # True if tree structure has changed
|
||||
|
||||
return
|
||||
|
||||
@@ -81,17 +80,16 @@ class NWTree:
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear the item tree entirely."""
|
||||
self._projTree = {}
|
||||
self._treeOrder = []
|
||||
self._treeRoots = {}
|
||||
self._trashRoot = None
|
||||
self._archRoot = None
|
||||
self._treeChanged = False
|
||||
self._tree = {}
|
||||
self._order = []
|
||||
self._roots = {}
|
||||
self._trash = None
|
||||
self._changed = False
|
||||
return
|
||||
|
||||
def handles(self) -> list[str]:
|
||||
"""Returns a copy of the list of all the active handles."""
|
||||
return self._treeOrder.copy()
|
||||
return self._order.copy()
|
||||
|
||||
@overload # pragma: no cover
|
||||
def create(self, label: str, parent: None, itemType: Literal[nwItemType.ROOT],
|
||||
@@ -109,7 +107,7 @@ class NWTree:
|
||||
parent, None is returned. For root elements, this cannot occur.
|
||||
"""
|
||||
parent = None if itemType == nwItemType.ROOT else parent
|
||||
if parent is None or parent in self._treeOrder:
|
||||
if parent is None or parent in self._order:
|
||||
tHandle = self._makeHandle()
|
||||
newItem = NWItem(self._project, tHandle)
|
||||
newItem.setName(label)
|
||||
@@ -130,7 +128,7 @@ class NWTree:
|
||||
logger.warning("Invalid item handle '%s' detected, skipping", tHandle)
|
||||
return False
|
||||
|
||||
if tHandle in self._projTree:
|
||||
if tHandle in self._tree:
|
||||
logger.warning("Duplicate handle '%s' detected, skipping", tHandle)
|
||||
return False
|
||||
|
||||
@@ -138,20 +136,17 @@ class NWTree:
|
||||
|
||||
if nwItem.isRootType():
|
||||
logger.debug("Item '%s' is a root item", str(tHandle))
|
||||
self._treeRoots[tHandle] = nwItem
|
||||
if nwItem.itemClass == nwItemClass.ARCHIVE:
|
||||
logger.debug("Item '%s' is the archive folder", str(tHandle))
|
||||
self._archRoot = tHandle
|
||||
elif nwItem.itemClass == nwItemClass.TRASH:
|
||||
if self._trashRoot is None:
|
||||
self._roots[tHandle] = nwItem
|
||||
if nwItem.itemClass == nwItemClass.TRASH:
|
||||
if self._trash is None:
|
||||
logger.debug("Item '%s' is the trash folder", str(tHandle))
|
||||
self._trashRoot = tHandle
|
||||
self._trash = tHandle
|
||||
else:
|
||||
logger.error("Only one trash folder allowed")
|
||||
return False
|
||||
|
||||
self._projTree[tHandle] = nwItem
|
||||
self._treeOrder.append(tHandle)
|
||||
self._tree[tHandle] = nwItem
|
||||
self._order.append(tHandle)
|
||||
self._setTreeChanged(True)
|
||||
|
||||
return True
|
||||
@@ -171,7 +166,7 @@ class NWTree:
|
||||
items. In the order defined by the _treeOrder list.
|
||||
"""
|
||||
tree = []
|
||||
for tHandle in self._treeOrder:
|
||||
for tHandle in self._order:
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem:
|
||||
tree.append(tItem.pack())
|
||||
@@ -199,7 +194,7 @@ class NWTree:
|
||||
"""
|
||||
storage = self._project.storage
|
||||
files = set(storage.scanContent())
|
||||
for tHandle in self._treeOrder:
|
||||
for tHandle in self._order:
|
||||
if self.updateItemData(tHandle):
|
||||
logger.debug("Checking item '%s' ... OK", tHandle)
|
||||
files.discard(tHandle) # Remove it from the record
|
||||
@@ -220,7 +215,7 @@ class NWTree:
|
||||
oName, oParent, oClass, oLayout = aDoc.getMeta()
|
||||
|
||||
oName = oName or cHandle
|
||||
oParent = oParent if oParent in self._treeOrder else None
|
||||
oParent = oParent if oParent in self._order else None
|
||||
oClass = oClass or nwItemClass.NOVEL
|
||||
oLayout = oLayout or nwItemLayout.NOTE
|
||||
|
||||
@@ -258,7 +253,7 @@ class NWTree:
|
||||
|
||||
tocList = []
|
||||
tocLen = 0
|
||||
for tHandle in self._treeOrder:
|
||||
for tHandle in self._order:
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is None:
|
||||
continue
|
||||
@@ -300,7 +295,7 @@ class NWTree:
|
||||
"""Loop over all entries and add up the word counts."""
|
||||
noteWords = 0
|
||||
novelWords = 0
|
||||
for tHandle in self._treeOrder:
|
||||
for tHandle in self._order:
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is None:
|
||||
continue
|
||||
@@ -376,13 +371,13 @@ class NWTree:
|
||||
def rootClasses(self) -> set[nwItemClass]:
|
||||
"""Return a set of all root classes in use by the project."""
|
||||
rootClasses = set()
|
||||
for nwItem in self._treeRoots.values():
|
||||
for nwItem in self._roots.values():
|
||||
rootClasses.add(nwItem.itemClass)
|
||||
return rootClasses
|
||||
|
||||
def iterRoots(self, itemClass: nwItemClass | None) -> Iterator[tuple[str, NWItem]]:
|
||||
"""Iterate over all root items of a given class in order."""
|
||||
for tHandle in self._treeOrder:
|
||||
for tHandle in self._order:
|
||||
nwItem = self.__getitem__(tHandle)
|
||||
if isinstance(nwItem, NWItem) and nwItem.isRootType():
|
||||
if itemClass is None or nwItem.itemClass == itemClass:
|
||||
@@ -396,12 +391,12 @@ class NWTree:
|
||||
return True
|
||||
if tItem.itemClass == nwItemClass.TRASH:
|
||||
return True
|
||||
if self._trashRoot is not None:
|
||||
if tHandle == self._trashRoot:
|
||||
if self._trash is not None:
|
||||
if tHandle == self._trash:
|
||||
return True
|
||||
elif tItem.itemParent == self._trashRoot:
|
||||
elif tItem.itemParent == self._trash:
|
||||
return True
|
||||
elif tItem.itemRoot == self._trashRoot:
|
||||
elif tItem.itemRoot == self._trash:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -409,13 +404,13 @@ class NWTree:
|
||||
"""Returns the handle of the trash folder, or None if there
|
||||
isn't one.
|
||||
"""
|
||||
if self._trashRoot:
|
||||
return self._trashRoot
|
||||
if self._trash:
|
||||
return self._trash
|
||||
return None
|
||||
|
||||
def findRoot(self, itemClass: nwItemClass | None) -> str | None:
|
||||
"""Find the first root item for a given class."""
|
||||
for aRoot in self._treeRoots:
|
||||
for aRoot in self._roots:
|
||||
tItem = self.__getitem__(aRoot)
|
||||
if tItem is None:
|
||||
continue
|
||||
@@ -429,18 +424,18 @@ class NWTree:
|
||||
|
||||
def setOrder(self, newOrder: list[str]) -> None:
|
||||
"""Reorders the tree based on a list of items."""
|
||||
tmpOrder = [tHandle for tHandle in newOrder if tHandle in self._projTree]
|
||||
if not (len(tmpOrder) == len(newOrder) == len(self._treeOrder)):
|
||||
tmpOrder = [tHandle for tHandle in newOrder if tHandle in self._tree]
|
||||
if not (len(tmpOrder) == len(newOrder) == len(self._order)):
|
||||
# Something is wrong, so let's debug it
|
||||
for tHandle in newOrder:
|
||||
if tHandle not in self._projTree:
|
||||
if tHandle not in self._tree:
|
||||
logger.error("Handle '%s' in new tree order is not in old order", tHandle)
|
||||
for tHandle in self._treeOrder:
|
||||
for tHandle in self._order:
|
||||
if tHandle not in tmpOrder:
|
||||
logger.warning("Handle '%s' in old tree order is not in new order", tHandle)
|
||||
|
||||
# Save the temp list
|
||||
self._treeOrder = tmpOrder
|
||||
self._order = tmpOrder
|
||||
self._setTreeChanged(True)
|
||||
logger.debug("Project tree order updated")
|
||||
|
||||
@@ -452,36 +447,34 @@ class NWTree:
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""The number of items in the project."""
|
||||
return len(self._treeOrder)
|
||||
return len(self._order)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""True if there are any items in the project."""
|
||||
return bool(self._treeOrder)
|
||||
return bool(self._order)
|
||||
|
||||
def __getitem__(self, tHandle: str | None) -> NWItem | None:
|
||||
"""Return a project item based on its handle. Returns None if
|
||||
the handle doesn't exist in the project.
|
||||
"""
|
||||
if tHandle and tHandle in self._projTree:
|
||||
return self._projTree[tHandle]
|
||||
if tHandle and tHandle in self._tree:
|
||||
return self._tree[tHandle]
|
||||
logger.error("No tree item with handle '%s'", str(tHandle))
|
||||
return None
|
||||
|
||||
def __delitem__(self, tHandle: str) -> None:
|
||||
"""Remove an item from the internal lists and dictionaries."""
|
||||
if tHandle in self._treeOrder and tHandle in self._projTree:
|
||||
self._treeOrder.remove(tHandle)
|
||||
del self._projTree[tHandle]
|
||||
if tHandle in self._order and tHandle in self._tree:
|
||||
self._order.remove(tHandle)
|
||||
del self._tree[tHandle]
|
||||
else:
|
||||
logger.warning("Failed to delete item '%s': item not found", tHandle)
|
||||
return
|
||||
|
||||
if tHandle in self._treeRoots:
|
||||
del self._treeRoots[tHandle]
|
||||
if tHandle == self._trashRoot:
|
||||
self._trashRoot = None
|
||||
if tHandle == self._archRoot:
|
||||
self._archRoot = None
|
||||
if tHandle in self._roots:
|
||||
del self._roots[tHandle]
|
||||
if tHandle == self._trash:
|
||||
self._trash = None
|
||||
|
||||
self._setTreeChanged(True)
|
||||
|
||||
@@ -489,12 +482,12 @@ class NWTree:
|
||||
|
||||
def __contains__(self, tHandle: str) -> bool:
|
||||
"""Checks if a handle exists in the tree."""
|
||||
return tHandle in self._treeOrder
|
||||
return tHandle in self._order
|
||||
|
||||
def __iter__(self) -> Iterator[NWItem]:
|
||||
"""Iterate through project items."""
|
||||
for tHandle in self._treeOrder:
|
||||
tItem = self._projTree.get(tHandle)
|
||||
for tHandle in self._order:
|
||||
tItem = self._tree.get(tHandle)
|
||||
if isinstance(tItem, NWItem):
|
||||
yield tItem
|
||||
return
|
||||
@@ -507,7 +500,7 @@ class NWTree:
|
||||
"""Set the changed flag to theState, and if being set to True,
|
||||
propagate that state change to the parent NWProject class.
|
||||
"""
|
||||
self._treeChanged = state
|
||||
self._changed = state
|
||||
if state:
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
@@ -518,7 +511,7 @@ class NWTree:
|
||||
"""
|
||||
logger.debug("Generating new handle")
|
||||
handle = f"{random.getrandbits(52):013x}"
|
||||
if handle in self._projTree:
|
||||
if handle in self._tree:
|
||||
logger.warning("Duplicate handle encountered! Retrying ...")
|
||||
handle = self._makeHandle()
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ def testCoreTools_DocDuplicator(mockGUI, fncPath, tstPaths, mockRnd):
|
||||
assert list(dup.duplicate([C.hSceneDoc])) == [
|
||||
("0000000000010", C.hSceneDoc), # The Scene
|
||||
]
|
||||
assert theProject.tree._treeOrder == [
|
||||
assert theProject.tree._order == [
|
||||
C.hNovelRoot, C.hPlotRoot, C.hCharRoot, C.hWorldRoot,
|
||||
C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
"0000000000010",
|
||||
@@ -311,7 +311,7 @@ def testCoreTools_DocDuplicator(mockGUI, fncPath, tstPaths, mockRnd):
|
||||
("0000000000012", None), # The Chapter
|
||||
("0000000000013", None), # The Scene
|
||||
]
|
||||
assert theProject.tree._treeOrder == [
|
||||
assert theProject.tree._order == [
|
||||
C.hNovelRoot, C.hPlotRoot, C.hCharRoot, C.hWorldRoot,
|
||||
C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
"0000000000010",
|
||||
@@ -342,7 +342,7 @@ def testCoreTools_DocDuplicator(mockGUI, fncPath, tstPaths, mockRnd):
|
||||
("0000000000017", None), # The Chapter
|
||||
("0000000000018", None), # The Scene
|
||||
]
|
||||
assert theProject.tree._treeOrder == [
|
||||
assert theProject.tree._order == [
|
||||
C.hNovelRoot, C.hPlotRoot, C.hCharRoot, C.hWorldRoot,
|
||||
C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
"0000000000010",
|
||||
|
||||
@@ -421,8 +421,8 @@ def testCoreDocBuild_Custom(mockGUI, fncPath: Path):
|
||||
|
||||
# Add an invalid item to the project
|
||||
nHandle = "0123456789def"
|
||||
project.tree._treeOrder.append(nHandle)
|
||||
project.tree._projTree[nHandle] = None # type: ignore
|
||||
project.tree._order.append(nHandle)
|
||||
project.tree._tree[nHandle] = None # type: ignore
|
||||
|
||||
docBuild.queueAll()
|
||||
assert len(docBuild) == 8
|
||||
|
||||
@@ -727,7 +727,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
]
|
||||
|
||||
# Add a fake handle to the tree and check that it's ignored
|
||||
theProject.tree._treeOrder.append("0000000000000")
|
||||
theProject.tree._order.append("0000000000000")
|
||||
assert [(h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False)] == [
|
||||
(C.hTitlePage, "T0001"),
|
||||
(C.hChapterDoc, "T0001"),
|
||||
@@ -738,7 +738,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
(sHandle, "T0001"),
|
||||
(tHandle, "T0001"),
|
||||
]
|
||||
theProject.tree._treeOrder.remove("0000000000000")
|
||||
theProject.tree._order.remove("0000000000000")
|
||||
|
||||
# Extract stats
|
||||
assert theIndex.getNovelWordCount(skipExcl=False) == 43
|
||||
@@ -1077,7 +1077,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd):
|
||||
assert nStruct[0][0] == uHandle
|
||||
|
||||
# Inject garbage into tree
|
||||
theProject.tree._treeOrder.append("stuff")
|
||||
theProject.tree._order.append("stuff")
|
||||
nStruct = list(itemIndex.iterNovelStructure())
|
||||
assert len(nStruct) == 4
|
||||
assert nStruct[0][0] == nHandle
|
||||
|
||||
@@ -328,7 +328,7 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
|
||||
assert theProject.tree.handles() == newOrder
|
||||
|
||||
# Add a non-existing item
|
||||
theProject.tree._treeOrder.append(C.hInvalid)
|
||||
theProject.tree._order.append(C.hInvalid)
|
||||
|
||||
# Add an item with a non-existent parent
|
||||
nHandle = theProject.newFile("Test File", C.hChapterDir)
|
||||
|
||||
@@ -127,7 +127,7 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
|
||||
assert theTree.append(nwItem) is True
|
||||
assert theTree.updateItemData(nwItem.itemHandle) is True
|
||||
|
||||
assert theTree._treeChanged is True
|
||||
assert theTree._changed is True
|
||||
|
||||
# Check that tree is not empty (calls __bool__)
|
||||
assert bool(theTree) is True
|
||||
@@ -413,9 +413,9 @@ def testCoreTree_Methods(mockGUI, mockItems):
|
||||
assert roots[3][0] == "a000000000004"
|
||||
|
||||
# Add a fake item to root and check that it can handle it
|
||||
theTree._treeRoots["0000000000000"] = NWItem(theProject, "0000000000000")
|
||||
theTree._roots["0000000000000"] = NWItem(theProject, "0000000000000")
|
||||
assert theTree.findRoot(nwItemClass.WORLD) is None
|
||||
del theTree._treeRoots["0000000000000"]
|
||||
del theTree._roots["0000000000000"]
|
||||
|
||||
# Get item path
|
||||
assert theTree.getItemPath("stuff") == []
|
||||
@@ -456,13 +456,13 @@ def testCoreTree_MakeHandles(mockGUI):
|
||||
random.seed(42)
|
||||
tHandle = theTree._makeHandle()
|
||||
assert tHandle == handles[0]
|
||||
theTree._projTree[handles[0]] = None # type: ignore
|
||||
theTree._tree[handles[0]] = None # type: ignore
|
||||
|
||||
# Add the next in line to the project to force duplicate
|
||||
theTree._projTree[handles[1]] = None # type: ignore
|
||||
theTree._tree[handles[1]] = None # type: ignore
|
||||
tHandle = theTree._makeHandle()
|
||||
assert tHandle == handles[2]
|
||||
theTree._projTree[handles[2]] = None # type: ignore
|
||||
theTree._tree[handles[2]] = None # type: ignore
|
||||
|
||||
# Reset the seed to force collissions, which should still end up
|
||||
# returning the next handle in the sequence
|
||||
@@ -483,7 +483,7 @@ def testCoreTree_Stats(mockGUI, mockItems):
|
||||
theTree.append(nwItem)
|
||||
|
||||
assert len(theTree) == len(mockItems)
|
||||
theTree._treeOrder.append("stuff")
|
||||
theTree._order.append("stuff")
|
||||
|
||||
# Count Words
|
||||
novelWords, noteWords = theTree.sumWords()
|
||||
@@ -520,7 +520,7 @@ def testCoreTree_Reorder(caplog, mockGUI, mockItems):
|
||||
assert "Handle 'stuff' in new tree order is not in old order" in caplog.text
|
||||
|
||||
caplog.clear()
|
||||
theTree._treeOrder.append("stuff")
|
||||
theTree._order.append("stuff")
|
||||
theTree.setOrder(bHandle)
|
||||
assert theTree.handles() == bHandle
|
||||
assert "Handle 'stuff' in old tree order is not in new order" in caplog.text
|
||||
@@ -539,7 +539,7 @@ def testCoreTree_ToCFile(monkeypatch, fncPath, mockGUI, mockItems):
|
||||
theTree.updateItemData(nwItem.itemHandle)
|
||||
|
||||
assert len(theTree) == len(mockItems)
|
||||
theTree._treeOrder.append("stuff")
|
||||
theTree._order.append("stuff")
|
||||
|
||||
def mockIsFile(fileName):
|
||||
"""Return True for items that are files in novelWriter and
|
||||
|
||||
@@ -203,8 +203,8 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
assert nwGUI.closeProject()
|
||||
|
||||
assert len(SHARED.project.tree) == 0
|
||||
assert len(SHARED.project.tree._treeOrder) == 0
|
||||
assert len(SHARED.project.tree._treeRoots) == 0
|
||||
assert len(SHARED.project.tree._order) == 0
|
||||
assert len(SHARED.project.tree._roots) == 0
|
||||
assert SHARED.project.tree.trashRoot() is None
|
||||
assert SHARED.project.data.name == ""
|
||||
assert SHARED.project.data.title == ""
|
||||
@@ -223,8 +223,8 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
|
||||
# Check that we loaded the data
|
||||
assert len(SHARED.project.tree) == 8
|
||||
assert len(SHARED.project.tree._treeOrder) == 8
|
||||
assert len(SHARED.project.tree._treeRoots) == 4
|
||||
assert len(SHARED.project.tree._order) == 8
|
||||
assert len(SHARED.project.tree._roots) == 4
|
||||
assert SHARED.project.tree.trashRoot() is None
|
||||
assert SHARED.project.data.name == "New Project"
|
||||
assert SHARED.project.data.title == "New Novel"
|
||||
|
||||
@@ -260,19 +260,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
# ===========
|
||||
|
||||
projView.setSelectedHandle(C.hNovelRoot)
|
||||
assert SHARED.project.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||
assert SHARED.project.tree._order.index(C.hNovelRoot) == 0
|
||||
|
||||
# Move novel folder up
|
||||
assert projTree.moveTreeItem(-1) is False
|
||||
assert SHARED.project.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||
assert SHARED.project.tree._order.index(C.hNovelRoot) == 0
|
||||
|
||||
# Move novel folder down
|
||||
assert projTree.moveTreeItem(1) is True
|
||||
assert SHARED.project.tree._treeOrder.index(C.hNovelRoot) == 1
|
||||
assert SHARED.project.tree._order.index(C.hNovelRoot) == 1
|
||||
|
||||
# Move novel folder up again
|
||||
assert projTree.moveTreeItem(-1) is True
|
||||
assert SHARED.project.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||
assert SHARED.project.tree._order.index(C.hNovelRoot) == 0
|
||||
|
||||
# Clean up
|
||||
# qtbot.stop()
|
||||
@@ -881,7 +881,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
|
||||
assert len(SHARED.project.tree) == 21
|
||||
|
||||
# Check tree order that all items are next to eachother
|
||||
assert SHARED.project.tree._treeOrder == [
|
||||
assert SHARED.project.tree._order == [
|
||||
C.hNovelRoot, C.hTitlePage, "0000000000010", C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
"0000000000011", "0000000000012", "0000000000013", "0000000000014", "0000000000015",
|
||||
"0000000000016", "0000000000017", "0000000000018", "0000000000019", "000000000001a",
|
||||
|
||||
Reference in New Issue
Block a user