Complete build initial tree and save tree logic
This commit is contained in:
@@ -256,6 +256,8 @@ class NWItem:
|
|||||||
self._paraCount = 0
|
self._paraCount = 0
|
||||||
self._cursorPos = 0
|
self._cursorPos = 0
|
||||||
|
|
||||||
|
self._initCount = self._wordCount
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -60,13 +60,17 @@ class ProjectNode:
|
|||||||
|
|
||||||
def __init__(self, item: NWItem) -> None:
|
def __init__(self, item: NWItem) -> None:
|
||||||
self._item = item
|
self._item = item
|
||||||
self._children = []
|
self._children: list[ProjectNode] = []
|
||||||
self._parent: ProjectNode | None = None
|
self._parent: ProjectNode | None = None
|
||||||
self._row = 0
|
self._row = 0
|
||||||
self._cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
|
self._cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
|
||||||
self.refresh()
|
self.refresh()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@property
|
||||||
|
def item(self) -> NWItem:
|
||||||
|
return self._item
|
||||||
|
|
||||||
def refresh(self) -> None:
|
def refresh(self) -> None:
|
||||||
cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
|
cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
|
||||||
|
|
||||||
@@ -132,17 +136,38 @@ class ProjectNode:
|
|||||||
self.refresh()
|
self.refresh()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def allChildren(self) -> list[ProjectNode]:
|
||||||
|
nodes: list[ProjectNode] = []
|
||||||
|
self._recursiveAppendChildren(nodes)
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _recursiveAppendChildren(self, children: list[ProjectNode]) -> None:
|
||||||
|
for node in self._children:
|
||||||
|
children.append(node)
|
||||||
|
node._recursiveAppendChildren(children)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class ProjectModel(QAbstractItemModel):
|
class ProjectModel(QAbstractItemModel):
|
||||||
|
|
||||||
|
__slots__ = ("_root", "_map")
|
||||||
|
|
||||||
def __init__(self, tree: NWTree) -> None:
|
def __init__(self, tree: NWTree) -> None:
|
||||||
super().__init__(None)
|
super().__init__(None)
|
||||||
self._root = ProjectNode(NWItem(tree._project, ""))
|
self._root = ProjectNode(NWItem(tree._project, ""))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setRoot(self, root: ProjectNode) -> None:
|
@property
|
||||||
self._root = root
|
def root(self) -> ProjectNode:
|
||||||
return
|
return self._root
|
||||||
|
|
||||||
|
##
|
||||||
|
# Model Access
|
||||||
|
##
|
||||||
|
|
||||||
def rowCount(self, index: QModelIndex) -> int:
|
def rowCount(self, index: QModelIndex) -> int:
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
@@ -159,7 +184,7 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
return QModelIndex()
|
return QModelIndex()
|
||||||
|
|
||||||
def index(self, row: int, column: int, parent: QModelIndex = QModelIndex()) -> QModelIndex:
|
def index(self, row: int, column: int, parent: QModelIndex = QModelIndex()) -> QModelIndex:
|
||||||
if parent and parent.isValid():
|
if parent.isValid():
|
||||||
item = parent.internalPointer()
|
item = parent.internalPointer()
|
||||||
else:
|
else:
|
||||||
item = self._root
|
item = self._root
|
||||||
@@ -178,10 +203,22 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
return node.data(index.column(), role)
|
return node.data(index.column(), role)
|
||||||
|
|
||||||
def addChild(self, node: ProjectNode, parent: QModelIndex) -> None:
|
# def addChild(self, node: ProjectNode, parent: QModelIndex) -> None:
|
||||||
if parent and parent.isValid():
|
# if parent.isValid():
|
||||||
item = parent.internalPointer()
|
# item = parent.internalPointer()
|
||||||
else:
|
# else:
|
||||||
item = self._root
|
# item = self._root
|
||||||
item.addChild(node)
|
# item.addChild(node)
|
||||||
return
|
# return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def allExpanded(self) -> list[QModelIndex]:
|
||||||
|
"""Return a list of all expanded items."""
|
||||||
|
expanded = []
|
||||||
|
for node in self._root.allChildren():
|
||||||
|
if node._item.isExpanded:
|
||||||
|
expanded.append(self.createIndex(node.row(), 0, node))
|
||||||
|
return expanded
|
||||||
|
|||||||
+58
-21
@@ -63,7 +63,10 @@ class NWTree:
|
|||||||
also used for file names.
|
also used for file names.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("_project", "_tree", "_order", "_roots", "_model", "_nodes", "_trash", "_changed")
|
__slots__ = (
|
||||||
|
"_project", "_tree", "_order", "_roots",
|
||||||
|
"_model", "_items", "_nodes", "_trash", "_changed",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, project: NWProject) -> None:
|
def __init__(self, project: NWProject) -> None:
|
||||||
|
|
||||||
@@ -74,6 +77,7 @@ class NWTree:
|
|||||||
self._roots: dict[str, NWItem] = {} # The root items of the tree
|
self._roots: dict[str, NWItem] = {} # The root items of the tree
|
||||||
|
|
||||||
self._model = ProjectModel(self)
|
self._model = ProjectModel(self)
|
||||||
|
self._items: dict[str, NWItem] = {}
|
||||||
self._nodes: dict[str, ProjectNode] = {}
|
self._nodes: dict[str, ProjectNode] = {}
|
||||||
|
|
||||||
self._trash = None # The handle of the trash root folder
|
self._trash = None # The handle of the trash root folder
|
||||||
@@ -103,6 +107,11 @@ class NWTree:
|
|||||||
self._tree = {}
|
self._tree = {}
|
||||||
self._order = []
|
self._order = []
|
||||||
self._roots = {}
|
self._roots = {}
|
||||||
|
|
||||||
|
self._model = ProjectModel(self)
|
||||||
|
self._items = {}
|
||||||
|
self._nodes = {}
|
||||||
|
|
||||||
self._trash = None
|
self._trash = None
|
||||||
self._changed = False
|
self._changed = False
|
||||||
return
|
return
|
||||||
@@ -185,12 +194,20 @@ class NWTree:
|
|||||||
"""Pack the content of the tree into a list of dictionaries of
|
"""Pack the content of the tree into a list of dictionaries of
|
||||||
items. In the order defined by the _treeOrder list.
|
items. In the order defined by the _treeOrder list.
|
||||||
"""
|
"""
|
||||||
tree = []
|
nodes = self._model.root.allChildren()
|
||||||
for tHandle in self._order:
|
if len(nodes) != len(self._nodes):
|
||||||
tItem = self.__getitem__(tHandle)
|
logger.warning(
|
||||||
if tItem:
|
"Model tree is inconsitent with nodes map, %d != %d",
|
||||||
tree.append(tItem.pack())
|
len(nodes), len(self._nodes)
|
||||||
return tree
|
)
|
||||||
|
|
||||||
|
# tree = []
|
||||||
|
# for tHandle in self._order:
|
||||||
|
# tItem = self.__getitem__(tHandle)
|
||||||
|
# if tItem:
|
||||||
|
# tree.append(tItem.pack())
|
||||||
|
|
||||||
|
return [node.item.pack() for node in nodes]
|
||||||
|
|
||||||
def unpack(self, data: list[dict]) -> None:
|
def unpack(self, data: list[dict]) -> 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
|
||||||
@@ -200,31 +217,51 @@ class NWTree:
|
|||||||
for item in data:
|
for item in data:
|
||||||
nwItem = NWItem(self._project, "") # Handle is set by unpack()
|
nwItem = NWItem(self._project, "") # Handle is set by unpack()
|
||||||
if nwItem.unpack(item):
|
if nwItem.unpack(item):
|
||||||
|
self._items[nwItem.itemHandle] = nwItem
|
||||||
self.append(nwItem)
|
self.append(nwItem)
|
||||||
nwItem.saveInitialCount()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def buildModel(self) -> None:
|
def buildModel(self) -> None:
|
||||||
""""""
|
""""""
|
||||||
root = ProjectNode(NWItem(self._project, ""))
|
|
||||||
for item in self._tree.values():
|
|
||||||
node = ProjectNode(item)
|
|
||||||
self._nodes[item.itemHandle] = node
|
|
||||||
if pHandle := item.itemParent:
|
|
||||||
if parent := self._nodes.get(pHandle):
|
|
||||||
parent.addChild(node)
|
|
||||||
else:
|
|
||||||
logger.error("Could not add item '%s'", item.itemHandle)
|
|
||||||
else:
|
|
||||||
root.addChild(node)
|
|
||||||
|
|
||||||
self._model.beginInsertRows(self._model.index(0, 0), 0, 0)
|
self._model.beginInsertRows(self._model.index(0, 0), 0, 0)
|
||||||
self._model.setRoot(root)
|
later: dict[str, NWItem] = self._items.copy()
|
||||||
|
for _ in range(999):
|
||||||
|
later = self._buildTree(later)
|
||||||
|
if len(later) == 0:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logger.error("Not all items could be added to project tree")
|
||||||
|
for item in later.values():
|
||||||
|
item.setParent(None)
|
||||||
|
|
||||||
self._model.endInsertRows()
|
self._model.endInsertRows()
|
||||||
self._model.layoutChanged.emit()
|
self._model.layoutChanged.emit()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _buildTree(self, items: dict[str, NWItem]) -> dict[str, NWItem]:
|
||||||
|
""""""
|
||||||
|
remains: dict[str, NWItem] = {}
|
||||||
|
for handle, item in items.items():
|
||||||
|
if pHandle := item.itemParent:
|
||||||
|
if parent := self._nodes.get(pHandle):
|
||||||
|
node = ProjectNode(item)
|
||||||
|
parent.addChild(node)
|
||||||
|
self._nodes[handle] = node
|
||||||
|
elif pHandle in items:
|
||||||
|
remains[handle] = item
|
||||||
|
logger.warning("Item '%s' found before its parent", handle)
|
||||||
|
else:
|
||||||
|
item.setParent(None)
|
||||||
|
logger.error("Item '%s' has no parent in current tree", handle)
|
||||||
|
elif item.isRootType():
|
||||||
|
node = ProjectNode(item)
|
||||||
|
self._model.root.addChild(node)
|
||||||
|
self._nodes[handle] = node
|
||||||
|
|
||||||
|
return remains
|
||||||
|
|
||||||
def checkConsistency(self, prefix: str) -> tuple[int, int]:
|
def checkConsistency(self, prefix: str) -> tuple[int, int]:
|
||||||
"""Check the project tree consistency. Also check the content
|
"""Check the project tree consistency. Also check the content
|
||||||
folder and add back files that were discovered but were not
|
folder and add back files that were discovered but were not
|
||||||
|
|||||||
@@ -535,6 +535,11 @@ class GuiProjectTree2(QTreeView):
|
|||||||
treeHeader.resizeSection(self.C_ACTIVE, iPx + cMg)
|
treeHeader.resizeSection(self.C_ACTIVE, iPx + cMg)
|
||||||
treeHeader.resizeSection(self.C_STATUS, iPx + cMg)
|
treeHeader.resizeSection(self.C_STATUS, iPx + cMg)
|
||||||
|
|
||||||
|
self.blockSignals(True)
|
||||||
|
for index in SHARED.project.tree.model.allExpanded():
|
||||||
|
self.setExpanded(index, True)
|
||||||
|
self.blockSignals(False)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from PyQt5.QtGui import QPixmap
|
from PyQt5.QtGui import QIcon, QPixmap
|
||||||
from PyQt5.QtWidgets import QWidget
|
from PyQt5.QtWidgets import QWidget
|
||||||
|
|
||||||
|
|
||||||
@@ -73,6 +73,12 @@ class MockTheme:
|
|||||||
def getPixmap(self, *a):
|
def getPixmap(self, *a):
|
||||||
return QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
|
def getIcon(self, *a) -> QIcon:
|
||||||
|
return QIcon()
|
||||||
|
|
||||||
|
def getItemIcon(self, *a, **k) -> QIcon:
|
||||||
|
return QIcon()
|
||||||
|
|
||||||
|
|
||||||
class MockApp:
|
class MockApp:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user