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