Add annotations to tree and item classes

This commit is contained in:
Veronica Berglyd Olsen
2023-07-20 16:00:16 +02:00
parent bba46ba042
commit fe7e44a3a6
4 changed files with 150 additions and 212 deletions
+86 -101
View File
@@ -22,15 +22,23 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING, Any
from PyQt5.QtGui import QIcon
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
from novelwriter.common import ( from novelwriter.common import (
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified, yesNo checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified, yesNo
) )
from novelwriter.constants import nwHeaders, nwLabels, trConst from novelwriter.constants import nwHeaders, nwLabels, trConst
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -43,7 +51,7 @@ class NWItem:
"_paraCount", "_cursorPos", "_initCount", "_paraCount", "_cursorPos", "_initCount",
) )
def __init__(self, project): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._name = "" self._name = ""
@@ -69,10 +77,10 @@ class NWItem:
return return
def __repr__(self): def __repr__(self) -> str:
return f"<NWItem handle={self._handle}, parent={self._parent}, name='{self._name}'>" return f"<NWItem handle={self._handle}, parent={self._parent}, name='{self._name}'>"
def __bool__(self): def __bool__(self) -> bool:
return self._handle is not None return self._handle is not None
## ##
@@ -80,87 +88,86 @@ class NWItem:
## ##
@property @property
def itemName(self): def itemName(self) -> str:
return self._name return self._name
@property @property
def itemHandle(self): def itemHandle(self) -> str | None:
return self._handle return self._handle
@property @property
def itemParent(self): def itemParent(self) -> str | None:
return self._parent return self._parent
@property @property
def itemRoot(self): def itemRoot(self) -> str | None:
return self._root return self._root
@property @property
def itemOrder(self): def itemOrder(self) -> int:
return self._order return self._order
@property @property
def itemType(self): def itemType(self) -> nwItemType:
return self._type return self._type
@property @property
def itemClass(self): def itemClass(self) -> nwItemClass:
return self._class return self._class
@property @property
def itemLayout(self): def itemLayout(self) -> nwItemLayout:
return self._layout return self._layout
@property @property
def itemStatus(self): def itemStatus(self) -> str | None:
return self._status return self._status
@property @property
def itemImport(self): def itemImport(self) -> str | None:
return self._import return self._import
@property @property
def isActive(self): def isActive(self) -> bool:
return self._active return self._active
@property @property
def isExpanded(self): def isExpanded(self) -> bool:
return self._expanded return self._expanded
@property @property
def mainHeading(self): def mainHeading(self) -> str:
return self._heading return self._heading
@property @property
def charCount(self): def charCount(self) -> int:
return self._charCount return self._charCount
@property @property
def wordCount(self): def wordCount(self) -> int:
return self._wordCount return self._wordCount
@property @property
def paraCount(self): def paraCount(self) -> int:
return self._paraCount return self._paraCount
@property @property
def initCount(self): def initCount(self) -> int:
return self._initCount return self._initCount
@property @property
def cursorPos(self): def cursorPos(self) -> int:
return self._cursorPos return self._cursorPos
## ##
# Pack/Unpack Data # Pack/Unpack Data
## ##
def pack(self): def pack(self) -> dict[str, dict[str, str]]:
"""Pack all the data in the class instance into a dictionary. """Pack all the data in the class instance into a dictionary."""
""" item: dict[str, str] = {}
item = {} meta: dict[str, str] = {}
meta = {} name: dict[str, str] = {}
name = {}
item["handle"] = str(self._handle) item["handle"] = str(self._handle)
item["parent"] = str(self._parent) item["parent"] = str(self._parent)
@@ -190,9 +197,8 @@ class NWItem:
return data return data
def unpack(self, data): def unpack(self, data: dict[str, dict[str, Any]]) -> bool:
"""Set the values from a data dictionary. """Set the values from a data dictionary."""
"""
item = data.get("itemAttr", {}) item = data.get("itemAttr", {})
meta = data.get("metaAttr", {}) meta = data.get("metaAttr", {})
name = data.get("nameAttr", {}) name = data.get("nameAttr", {})
@@ -243,9 +249,8 @@ class NWItem:
# Lookup Methods # Lookup Methods
## ##
def describeMe(self): def describeMe(self) -> str:
"""Return a string description of the item. """Return a string description of the item."""
"""
descKey = "none" descKey = "none"
if self._type == nwItemType.ROOT: if self._type == nwItemType.ROOT:
descKey = "root" descKey = "root"
@@ -268,7 +273,7 @@ class NWItem:
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, "")) return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
def getImportStatus(self, incIcon=True): def getImportStatus(self, incIcon: bool = True) -> tuple[str, QIcon | None]:
"""Return the relevant importance or status label and icon for """Return the relevant importance or status label and icon for
the current item based on its class. the current item based on its class.
""" """
@@ -284,51 +289,43 @@ class NWItem:
# Checker Methods # Checker Methods
## ##
def isNovelLike(self): def isNovelLike(self) -> bool:
"""Returns true if the item is of a novel-like class. """Check if the item is of a novel-like class."""
"""
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE) return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE)
def documentAllowed(self): def documentAllowed(self) -> bool:
"""Returns true if the item is allowed to be of document layout. """Check if the item is allowed to be of document layout."""
"""
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH) return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH)
def isInactiveClass(self): def isInactiveClass(self) -> bool:
"""Returns true if the item is in an inactive class. """Check if the item is in an inactive class."""
"""
return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH) return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH)
def isRootType(self): def isRootType(self) -> bool:
"""Check if item is a root item."""
return self._type == nwItemType.ROOT return self._type == nwItemType.ROOT
def isFolderType(self): def isFolderType(self) -> bool:
"""Check if item is a folder item."""
return self._type == nwItemType.FOLDER return self._type == nwItemType.FOLDER
def isFileType(self): def isFileType(self) -> bool:
"""Check if item is a file item."""
return self._type == nwItemType.FILE return self._type == nwItemType.FILE
def isNoteLayout(self): def isNoteLayout(self) -> bool:
"""Check if item is a project note."""
return self._layout == nwItemLayout.NOTE return self._layout == nwItemLayout.NOTE
def isDocumentLayout(self): def isDocumentLayout(self) -> bool:
"""Check if item is a novel document."""
return self._layout == nwItemLayout.DOCUMENT return self._layout == nwItemLayout.DOCUMENT
## ##
# Special Setters # Special Setters
## ##
def setImportStatus(self, value): def setClassDefaults(self, itemClass: nwItemClass) -> None:
"""Update the importance or status value based on class. This is
a wrapper setter for setStatus and setImport.
"""
if self.isNovelLike():
self.setStatus(value)
else:
self.setImport(value)
return
def setClassDefaults(self, itemClass):
"""Set the default values based on the item's class and the """Set the default values based on the item's class and the
project settings. project settings.
""" """
@@ -358,27 +355,24 @@ class NWItem:
# Set Item Values # Set Item Values
## ##
def setName(self, name): def setName(self, name: Any) -> None:
"""Set the item name. """Set the item name."""
"""
if isinstance(name, str): if isinstance(name, str):
self._name = simplified(name) self._name = simplified(name)
else: else:
self._name = "" self._name = ""
return return
def setHandle(self, handle): def setHandle(self, handle: Any) -> None:
"""Set the item handle, and ensure it is valid. """Set the item handle, and ensure it is valid."""
"""
if isHandle(handle): if isHandle(handle):
self._handle = handle self._handle = handle
else: else:
self._handle = None self._handle = None
return return
def setParent(self, handle): def setParent(self, handle: Any) -> None:
"""Set the parent handle, and ensure it is valid. """Set the parent handle, and ensure it is valid."""
"""
if handle is None: if handle is None:
self._parent = None self._parent = None
elif isHandle(handle): elif isHandle(handle):
@@ -387,9 +381,8 @@ class NWItem:
self._parent = None self._parent = None
return return
def setRoot(self, handle): def setRoot(self, handle: Any) -> None:
"""Set the root handle, and ensure it is valid. """Set the root handle, and ensure it is valid."""
"""
if handle is None: if handle is None:
self._root = None self._root = None
elif isHandle(handle): elif isHandle(handle):
@@ -398,7 +391,7 @@ class NWItem:
self._root = None self._root = None
return return
def setOrder(self, order): def setOrder(self, order: Any) -> None:
"""Set the item order, and ensure that it is valid. This value """Set the item order, and ensure that it is valid. This value
is purely a meta value, and not actually used by novelWriter at is purely a meta value, and not actually used by novelWriter at
the moment. the moment.
@@ -406,7 +399,7 @@ class NWItem:
self._order = checkInt(order, 0) self._order = checkInt(order, 0)
return return
def setType(self, value): def setType(self, value: Any) -> None:
"""Set the item type from either a proper nwItemType, or set it """Set the item type from either a proper nwItemType, or set it
from a string representing an nwItemType. from a string representing an nwItemType.
""" """
@@ -419,7 +412,7 @@ class NWItem:
self._type = nwItemType.NO_TYPE self._type = nwItemType.NO_TYPE
return return
def setClass(self, value): def setClass(self, value: Any) -> None:
"""Set the item class from either a proper nwItemClass, or set """Set the item class from either a proper nwItemClass, or set
it from a string representing an nwItemClass. it from a string representing an nwItemClass.
""" """
@@ -432,7 +425,7 @@ class NWItem:
self._class = nwItemClass.NO_CLASS self._class = nwItemClass.NO_CLASS
return return
def setLayout(self, value): def setLayout(self, value: Any) -> None:
"""Set the item layout from either a proper nwItemLayout, or set """Set the item layout from either a proper nwItemLayout, or set
it from a string representing an nwItemLayout. it from a string representing an nwItemLayout.
""" """
@@ -445,32 +438,30 @@ class NWItem:
self._layout = nwItemLayout.NO_LAYOUT self._layout = nwItemLayout.NO_LAYOUT
return return
def setStatus(self, value): def setStatus(self, value: Any) -> None:
"""Set the item status by looking it up in the valid status """Set the item status by looking it up in the valid status
items of the current project. items of the current project.
""" """
self._status = self._project.data.itemStatus.check(value) self._status = self._project.data.itemStatus.check(value)
return return
def setImport(self, value): def setImport(self, value: Any) -> None:
"""Set the item importance by looking it up in the valid import """Set the item importance by looking it up in the valid import
items of the current project. items of the current project.
""" """
self._import = self._project.data.itemImport.check(value) self._import = self._project.data.itemImport.check(value)
return return
def setActive(self, state): def setActive(self, state: Any) -> None:
"""Set the active flag. """Set the active flag."""
"""
if isinstance(state, bool): if isinstance(state, bool):
self._active = state self._active = state
else: else:
self._active = False self._active = False
return return
def setExpanded(self, state): def setExpanded(self, state: Any) -> None:
"""Set the expanded status of an item in the project tree. """Set the expanded status of an item in the project tree."""
"""
if isinstance(state, bool): if isinstance(state, bool):
self._expanded = state self._expanded = state
else: else:
@@ -481,52 +472,46 @@ class NWItem:
# Set Document Meta Data # Set Document Meta Data
## ##
def setMainHeading(self, value): def setMainHeading(self, value: str) -> None:
"""Set the main heading level. """Set the main heading level."""
"""
if value in nwHeaders.H_LEVEL: if value in nwHeaders.H_LEVEL:
self._heading = value self._heading = value
return return
def setCharCount(self, count): def setCharCount(self, count: Any) -> None:
"""Set the character count, and ensure that it is an integer. """Set the character count, and ensure that it is an integer."""
"""
if isinstance(count, int): if isinstance(count, int):
self._charCount = max(0, count) self._charCount = max(0, count)
else: else:
self._charCount = 0 self._charCount = 0
return return
def setWordCount(self, count): def setWordCount(self, count: Any) -> None:
"""Set the word count, and ensure that it is an integer. """Set the word count, and ensure that it is an integer."""
"""
if isinstance(count, int): if isinstance(count, int):
self._wordCount = max(0, count) self._wordCount = max(0, count)
else: else:
self._wordCount = 0 self._wordCount = 0
return return
def setParaCount(self, count): def setParaCount(self, count: Any) -> None:
"""Set the paragraph count, and ensure that it is an integer. """Set the paragraph count, and ensure that it is an integer."""
"""
if isinstance(count, int): if isinstance(count, int):
self._paraCount = max(0, count) self._paraCount = max(0, count)
else: else:
self._paraCount = 0 self._paraCount = 0
return return
def setCursorPos(self, position): def setCursorPos(self, position: Any) -> None:
"""Set the cursor position, and ensure that it is an integer. """Set the cursor position, and ensure that it is an integer."""
"""
if isinstance(position, int): if isinstance(position, int):
self._cursorPos = max(0, position) self._cursorPos = max(0, position)
else: else:
self._cursorPos = 0 self._cursorPos = 0
return return
def saveInitialCount(self): def saveInitialCount(self) -> None:
"""Save the initial word count. """Save the initial word count."""
"""
self._initCount = self._wordCount self._initCount = self._wordCount
return return
+63 -94
View File
@@ -22,18 +22,23 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import random import random
import logging import logging
from typing import TYPE_CHECKING, Any, Iterator
from pathlib import Path from pathlib import Path
from novelwriter.enum import nwItemClass, nwItemLayout from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.common import checkHandle from novelwriter.common import checkHandle
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -41,15 +46,16 @@ class NWTree:
MAX_DEPTH = 1000 # Cap of tree traversing for loops MAX_DEPTH = 1000 # Cap of tree traversing for loops
def __init__(self, theProject): def __init__(self, project: NWProject) -> None:
self.theProject = theProject self._project = project
self._projTree = {} # Holds all the items of the project self._projTree: dict[str, NWItem] = {} # Holds all the items of the project
self._treeOrder = [] # The order of the tree items on the tree view self._treeOrder: list[str] = [] # The order of the tree items on the tree view
self._treeRoots = {} # The root items of the tree self._treeRoots: 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._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._treeChanged = False # True if tree structure has changed
return return
@@ -58,9 +64,8 @@ class NWTree:
# Class Methods # Class Methods
## ##
def clear(self): def clear(self) -> None:
"""Clear the item tree entirely. """Clear the item tree entirely."""
"""
self._projTree = {} self._projTree = {}
self._treeOrder = [] self._treeOrder = []
self._treeRoots = {} self._treeRoots = {}
@@ -69,14 +74,12 @@ class NWTree:
self._treeChanged = False self._treeChanged = False
return return
def handles(self): def handles(self) -> list[str]:
"""Returns a copy of the list of all the active handles. """Returns a copy of the list of all the active handles."""
"""
return self._treeOrder.copy() return self._treeOrder.copy()
def append(self, tHandle, pHandle, nwItem): def append(self, tHandle: str | None, pHandle: str | None, nwItem: NWItem) -> bool:
"""Add a new item to the end of the tree. """Add a new item to the end of the tree."""
"""
tHandle = checkHandle(tHandle, None, True) tHandle = checkHandle(tHandle, None, True)
pHandle = checkHandle(pHandle, None, True) pHandle = checkHandle(pHandle, None, True)
if tHandle is None: if tHandle is None:
@@ -111,9 +114,9 @@ class NWTree:
return True return True
def pack(self): def pack(self) -> list[dict[str, dict[str, str]]]:
"""Pack the content of the tree into the provided XML object. In """Pack the content of the tree into a list of doctionaries of
the order defined by the _treeOrder list. items. In the order defined by the _treeOrder list.
""" """
tree = [] tree = []
for tHandle in self._treeOrder: for tHandle in self._treeOrder:
@@ -122,25 +125,24 @@ class NWTree:
tree.append(tItem.pack()) tree.append(tItem.pack())
return tree return tree
def unpack(self, data): def unpack(self, data: list[dict[str, dict[str, Any]]]) -> None:
"""Iterate through all items of a list and add them to the """Iterate through all items of a list and add them to the
project tree. project tree.
""" """
self.clear() self.clear()
for item in data: for item in data:
nwItem = NWItem(self.theProject) nwItem = NWItem(self._project)
if nwItem.unpack(item): if nwItem.unpack(item):
self.append(nwItem.itemHandle, nwItem.itemParent, nwItem) self.append(nwItem.itemHandle, nwItem.itemParent, nwItem)
nwItem.saveInitialCount() nwItem.saveInitialCount()
return
return True def writeToCFile(self) -> bool:
def writeToCFile(self):
"""Write the convenience table of contents file in the root of """Write the convenience table of contents file in the root of
the project directory. the project directory.
""" """
runtimePath = self.theProject.storage.runtimePath runtimePath = self._project.storage.runtimePath
contentPath = self.theProject.storage.contentPath contentPath = self._project.storage.contentPath
if not (isinstance(contentPath, Path) and isinstance(runtimePath, Path)): if not (isinstance(contentPath, Path) and isinstance(runtimePath, Path)):
return False return False
@@ -184,9 +186,8 @@ class NWTree:
return True return True
def sumWords(self): def sumWords(self) -> tuple[int, int]:
"""Loop over all entries and add up the word counts. """Loop over all entries and add up the word counts."""
"""
noteWords = 0 noteWords = 0
novelWords = 0 novelWords = 0
for tHandle in self._treeOrder: for tHandle in self._treeOrder:
@@ -205,7 +206,7 @@ class NWTree:
# Tree Item Methods # Tree Item Methods
## ##
def updateItemData(self, tHandle): def updateItemData(self, tHandle: str) -> bool:
"""Update the root item handle of a given item. Returns True if """Update the root item handle of a given item. Returns True if
a root was found and data updated, otherwise False. a root was found and data updated, otherwise False.
""" """
@@ -226,15 +227,14 @@ class NWTree:
else: else:
raise RecursionError("Critical internal error") raise RecursionError("Critical internal error")
def checkType(self, tHandle, itemType): def checkType(self, tHandle: str, itemType: nwItemType) -> bool:
"""Return true of item exists and is of the specified item type. """Check if item exists and is of the specified item type."""
"""
tItem = self.__getitem__(tHandle) tItem = self.__getitem__(tHandle)
if not tItem: if not tItem:
return False return False
return tItem.itemType == itemType return tItem.itemType == itemType
def getItemPath(self, tHandle): def getItemPath(self, tHandle: str) -> list[str]:
"""Iterate upwards in the tree until we find the item with """Iterate upwards in the tree until we find the item with
parent None, the root item, and return the list of handles. parent None, the root item, and return the list of handles.
We do this with a for loop with a maximum depth to make We do this with a for loop with a maximum depth to make
@@ -263,17 +263,15 @@ class NWTree:
# Tree Root Methods # Tree Root Methods
## ##
def rootClasses(self): def rootClasses(self) -> set[nwItemClass]:
"""Return a set of all root classes in use by the project. """Return a set of all root classes in use by the project."""
"""
rootClasses = set() rootClasses = set()
for nwItem in self._treeRoots.values(): for nwItem in self._treeRoots.values():
rootClasses.add(nwItem.itemClass) rootClasses.add(nwItem.itemClass)
return rootClasses return rootClasses
def iterRoots(self, itemClass): def iterRoots(self, itemClass: nwItemClass | None) -> Iterator[tuple[str, NWItem]]:
"""Iterate over all root items of a given class in order. """Iterate over all root items of a given class in order."""
"""
for tHandle in self._treeOrder: for tHandle in self._treeOrder:
nwItem = self.__getitem__(tHandle) nwItem = self.__getitem__(tHandle)
if isinstance(nwItem, NWItem) and nwItem.isRootType(): if isinstance(nwItem, NWItem) and nwItem.isRootType():
@@ -281,14 +279,8 @@ class NWTree:
yield tHandle, nwItem yield tHandle, nwItem
return return
def isRoot(self, tHandle): def isTrash(self, tHandle: str) -> bool:
"""Check if a handle is a root item. """Check if an item is in or is the trash folder."""
"""
return tHandle in self._treeRoots
def isTrash(self, tHandle):
"""Check if an item is in or is the trash folder.
"""
tItem = self.__getitem__(tHandle) tItem = self.__getitem__(tHandle)
if tItem is None: if tItem is None:
return True return True
@@ -303,7 +295,7 @@ class NWTree:
return True return True
return False return False
def trashRoot(self): def trashRoot(self) -> str | None:
"""Returns the handle of the trash folder, or None if there """Returns the handle of the trash folder, or None if there
isn't one. isn't one.
""" """
@@ -311,14 +303,13 @@ class NWTree:
return self._trashRoot return self._trashRoot
return None return None
def findRoot(self, theClass): def findRoot(self, itemClass: nwItemClass) -> str | None:
"""Find the first root item for a given class. """Find the first root item for a given class."""
"""
for aRoot in self._treeRoots: for aRoot in self._treeRoots:
tItem = self.__getitem__(aRoot) tItem = self.__getitem__(aRoot)
if tItem is None: if tItem is None:
continue continue
if theClass == tItem.itemClass: if itemClass == tItem.itemClass:
return tItem.itemHandle return tItem.itemHandle
return None return None
@@ -326,9 +317,8 @@ class NWTree:
# Setters # Setters
## ##
def setOrder(self, newOrder): def setOrder(self, newOrder: list[str]) -> None:
"""Reorders the tree based on a list of items. """Reorders the tree based on a list of items."""
"""
tmpOrder = [tHandle for tHandle in newOrder if tHandle in self._projTree] tmpOrder = [tHandle for tHandle in newOrder if tHandle in self._projTree]
if not (len(tmpOrder) == len(newOrder) == len(self._treeOrder)): if not (len(tmpOrder) == len(newOrder) == len(self._treeOrder)):
# Something is wrong, so let's debug it # Something is wrong, so let's debug it
@@ -346,37 +336,19 @@ class NWTree:
return return
def setFileItemLayout(self, tHandle, itemLayout):
"""Set the nwItemLayout for a specific file.
"""
tItem = self.__getitem__(tHandle)
if tItem is None:
return False
if not tItem.isFileType():
logger.error("Item '%s' is not a file", tHandle)
return False
if not isinstance(itemLayout, nwItemLayout):
return False
tItem.setLayout(itemLayout)
return True
## ##
# Special Methods # Special Methods
## ##
def __len__(self): def __len__(self) -> int:
"""The number of items in the project. """The number of items in the project."""
"""
return len(self._treeOrder) return len(self._treeOrder)
def __bool__(self): def __bool__(self) -> bool:
"""True if there are any items in the project. """True if there are any items in the project."""
"""
return bool(self._treeOrder) return bool(self._treeOrder)
def __getitem__(self, tHandle): def __getitem__(self, tHandle: str) -> NWItem | None:
"""Return a project item based on its handle. Returns None if """Return a project item based on its handle. Returns None if
the handle doesn't exist in the project. the handle doesn't exist in the project.
""" """
@@ -385,9 +357,8 @@ class NWTree:
logger.error("No tree item with handle '%s'", str(tHandle)) logger.error("No tree item with handle '%s'", str(tHandle))
return None return None
def __delitem__(self, tHandle): def __delitem__(self, tHandle: str) -> None:
"""Remove an item from the internal lists and dictionaries. """Remove an item from the internal lists and dictionaries."""
"""
if tHandle in self._treeOrder and tHandle in self._projTree: if tHandle in self._treeOrder and tHandle in self._projTree:
self._treeOrder.remove(tHandle) self._treeOrder.remove(tHandle)
del self._projTree[tHandle] del self._projTree[tHandle]
@@ -406,14 +377,12 @@ class NWTree:
return return
def __contains__(self, tHandle): def __contains__(self, tHandle: str) -> bool:
"""Checks if a handle exists in the tree. """Checks if a handle exists in the tree."""
"""
return tHandle in self._treeOrder return tHandle in self._treeOrder
def __iter__(self): def __iter__(self) -> Iterator[NWItem]:
"""Iterate through project items. """Iterate through project items."""
"""
for tHandle in self._treeOrder: for tHandle in self._treeOrder:
tItem = self._projTree.get(tHandle) tItem = self._projTree.get(tHandle)
if isinstance(tItem, NWItem): if isinstance(tItem, NWItem):
@@ -424,16 +393,16 @@ class NWTree:
# Internal Functions # Internal Functions
## ##
def _setTreeChanged(self, theState): def _setTreeChanged(self, state: bool) -> None:
"""Set the changed flag to theState, and if being set to True, """Set the changed flag to theState, and if being set to True,
propagate that state change to the parent NWProject class. propagate that state change to the parent NWProject class.
""" """
self._treeChanged = theState self._treeChanged = state
if theState: if state:
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def _makeHandle(self): def _makeHandle(self) -> str:
"""Generate a unique item handle. In the event that the key """Generate a unique item handle. In the event that the key
already exists, generate a new one. already exists, generate a new one.
""" """
-8
View File
@@ -262,19 +262,11 @@ def testCoreItem_Methods(mockGUI, mockRnd, fncPath):
assert stT == "Note" assert stT == "Note"
assert isinstance(stI, QIcon) assert isinstance(stI, QIcon)
theItem.setImportStatus(C.sDraft)
stT, stI = theItem.getImportStatus()
assert stT == "Draft"
theItem.setClass("CHARACTER") theItem.setClass("CHARACTER")
stT, stI = theItem.getImportStatus() stT, stI = theItem.getImportStatus()
assert stT == "Minor" assert stT == "Minor"
assert isinstance(stI, QIcon) assert isinstance(stI, QIcon)
theItem.setImportStatus(C.iMajor)
stT, stI = theItem.getImportStatus()
assert stT == "Major"
# Representation # Representation
# ============== # ==============
+1 -9
View File
@@ -149,7 +149,6 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
assert theTree.trashRoot() == "a000000000003" assert theTree.trashRoot() == "a000000000003"
assert theTree.findRoot(nwItemClass.ARCHIVE) == "a000000000002" assert theTree.findRoot(nwItemClass.ARCHIVE) == "a000000000002"
assert theTree.isTrash("a000000000003") is True assert theTree.isTrash("a000000000003") is True
assert theTree.isRoot("a000000000002") is True
# Check that we have the root classes # Check that we have the root classes
assert theTree.rootClasses() == { assert theTree.rootClasses() == {
@@ -255,7 +254,7 @@ def testCoreTree_PackUnpack(mockGUI, mockItems):
theTree.clear() theTree.clear()
assert len(theTree) == 0 assert len(theTree) == 0
assert theTree.handles() == [] assert theTree.handles() == []
assert theTree.unpack(tree) is True theTree.unpack(tree)
assert theTree.handles() == aHandles assert theTree.handles() == aHandles
# END Test testCoreTree_PackUnpack # END Test testCoreTree_PackUnpack
@@ -339,13 +338,6 @@ def testCoreTree_Methods(mockGUI, mockItems):
"c000000000001", "b000000000001", "a000000000001" "c000000000001", "b000000000001", "a000000000001"
] ]
# Change file layout
assert theTree.setFileItemLayout("stuff", nwItemLayout.DOCUMENT) is False
assert theTree.setFileItemLayout("b000000000001", nwItemLayout.DOCUMENT) is False
assert theTree.setFileItemLayout("c000000000001", "stuff") is False
assert theTree.setFileItemLayout("c000000000001", nwItemLayout.NOTE) is True
assert theTree["c000000000001"].itemLayout == nwItemLayout.NOTE
# END Test testCoreTree_Methods # END Test testCoreTree_Methods