Rewrite how project items are refreshed on the GUI
This commit is contained in:
+10
-21
@@ -55,7 +55,7 @@ class NWItem:
|
|||||||
"_project", "_name", "_handle", "_parent", "_root", "_order",
|
"_project", "_name", "_handle", "_parent", "_root", "_order",
|
||||||
"_type", "_class", "_layout", "_status", "_import", "_active",
|
"_type", "_class", "_layout", "_status", "_import", "_active",
|
||||||
"_expanded", "_heading", "_charCount", "_wordCount",
|
"_expanded", "_heading", "_charCount", "_wordCount",
|
||||||
"_paraCount", "_cursorPos", "_initCount", "_blocked",
|
"_paraCount", "_cursorPos", "_initCount",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, project: NWProject, handle: str) -> None:
|
def __init__(self, project: NWProject, handle: str) -> None:
|
||||||
@@ -82,8 +82,6 @@ class NWItem:
|
|||||||
self._cursorPos = 0 # Last cursor position
|
self._cursorPos = 0 # Last cursor position
|
||||||
self._initCount = 0 # Initial word count
|
self._initCount = 0 # Initial word count
|
||||||
|
|
||||||
self._blocked = True
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@@ -259,7 +257,6 @@ class NWItem:
|
|||||||
self._cursorPos = 0
|
self._cursorPos = 0
|
||||||
|
|
||||||
self._initCount = self._wordCount
|
self._initCount = self._wordCount
|
||||||
self._blocked = False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -284,9 +281,17 @@ class NWItem:
|
|||||||
cls._paraCount = source._paraCount
|
cls._paraCount = source._paraCount
|
||||||
cls._cursorPos = source._cursorPos
|
cls._cursorPos = source._cursorPos
|
||||||
cls._initCount = source._initCount
|
cls._initCount = source._initCount
|
||||||
cls._blocked = source._blocked
|
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
##
|
||||||
|
# Action Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def notifyToRefresh(self) -> None:
|
||||||
|
"""Notify GUI that item info needs to be refreshed."""
|
||||||
|
self._project.tree.refreshItems([self._handle])
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Lookup Methods
|
# Lookup Methods
|
||||||
##
|
##
|
||||||
@@ -419,7 +424,6 @@ class NWItem:
|
|||||||
self._name = simplified(name)
|
self._name = simplified(name)
|
||||||
else:
|
else:
|
||||||
self._name = ""
|
self._name = ""
|
||||||
self._notifyChange()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def setParent(self, handle: Any) -> None:
|
def setParent(self, handle: Any) -> None:
|
||||||
@@ -560,18 +564,3 @@ class NWItem:
|
|||||||
else:
|
else:
|
||||||
self._cursorPos = 0
|
self._cursorPos = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveInitialCount(self) -> None:
|
|
||||||
"""Save the initial word count."""
|
|
||||||
self._initCount = self._wordCount
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Internal Functions
|
|
||||||
##
|
|
||||||
|
|
||||||
def _notifyChange(self) -> None:
|
|
||||||
"""Notify project tree on user changes to the item."""
|
|
||||||
if not self._blocked:
|
|
||||||
self._project.tree.refreshNode(self._handle)
|
|
||||||
return
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt
|
from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
@@ -53,6 +53,8 @@ C_ACTIVE_TIP = 0x0200 | Qt.ItemDataRole.ToolTipRole
|
|||||||
C_STATUS_ICON = 0x0300 | Qt.ItemDataRole.DecorationRole
|
C_STATUS_ICON = 0x0300 | Qt.ItemDataRole.DecorationRole
|
||||||
C_STATUS_TIP = 0x0300 | Qt.ItemDataRole.ToolTipRole
|
C_STATUS_TIP = 0x0300 | Qt.ItemDataRole.ToolTipRole
|
||||||
|
|
||||||
|
T_NodeData = str | QIcon | Qt.AlignmentFlag | None
|
||||||
|
|
||||||
|
|
||||||
class ProjectNode:
|
class ProjectNode:
|
||||||
|
|
||||||
@@ -125,7 +127,7 @@ class ProjectNode:
|
|||||||
def childCount(self) -> int:
|
def childCount(self) -> int:
|
||||||
return len(self._children)
|
return len(self._children)
|
||||||
|
|
||||||
def data(self, column: int, role: Qt.ItemDataRole) -> str | QIcon | Qt.AlignmentFlag | None:
|
def data(self, column: int, role: Qt.ItemDataRole) -> T_NodeData:
|
||||||
""""""
|
""""""
|
||||||
return self._cache.get(COL_MASK*column | role)
|
return self._cache.get(COL_MASK*column | role)
|
||||||
|
|
||||||
@@ -168,7 +170,7 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
super().__init__(None)
|
super().__init__(None)
|
||||||
logger.debug("Create: ProjectModel")
|
logger.debug("Create: ProjectModel")
|
||||||
self._tree = tree
|
self._tree = tree
|
||||||
self._root = ProjectNode(NWItem(tree._project, ""))
|
self._root = ProjectNode(NWItem(tree._project, "invisibleRoot"))
|
||||||
return
|
return
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
@@ -177,6 +179,7 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def root(self) -> ProjectNode:
|
def root(self) -> ProjectNode:
|
||||||
|
"""Return the model root item."""
|
||||||
return self._root
|
return self._root
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -184,22 +187,26 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def rowCount(self, index: QModelIndex) -> int:
|
def rowCount(self, index: QModelIndex) -> int:
|
||||||
|
"""Return the number of rows for an entry."""
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
return index.internalPointer().childCount()
|
return index.internalPointer().childCount()
|
||||||
return self._root.childCount()
|
return self._root.childCount()
|
||||||
|
|
||||||
def columnCount(self, index: QModelIndex) -> int:
|
def columnCount(self, index: QModelIndex) -> int:
|
||||||
|
"""Return the number of columns for an entry."""
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
def parent(self, index: QModelIndex) -> QModelIndex:
|
def parent(self, index: QModelIndex) -> QModelIndex:
|
||||||
|
"""Get the parent model index of another index."""
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
if parent := index.internalPointer().parent():
|
if parent := index.internalPointer().parent():
|
||||||
return self.createIndex(parent.row(), 0, parent)
|
return self.createIndex(parent.row(), 0, parent)
|
||||||
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:
|
||||||
|
"""get the index of a child item of a parent."""
|
||||||
if parent.isValid():
|
if parent.isValid():
|
||||||
item = parent.internalPointer()
|
item: ProjectNode = parent.internalPointer()
|
||||||
else:
|
else:
|
||||||
item = self._root
|
item = self._root
|
||||||
|
|
||||||
@@ -211,10 +218,11 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
|
|
||||||
return QModelIndex()
|
return QModelIndex()
|
||||||
|
|
||||||
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> Any:
|
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> T_NodeData:
|
||||||
|
"""Return display data for a project node."""
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return None
|
return None
|
||||||
node = index.internalPointer()
|
node: ProjectNode = 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:
|
||||||
@@ -230,6 +238,7 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def node(self, index: QModelIndex) -> ProjectNode | None:
|
def node(self, index: QModelIndex) -> ProjectNode | None:
|
||||||
|
"""Return the node for a given model index."""
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
return index.internalPointer()
|
return index.internalPointer()
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -225,13 +225,21 @@ class NWTree:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def refreshNode(self, tHandle: str) -> None:
|
def refreshItems(self, items: list[str], isRange: bool = False) -> None:
|
||||||
"""Refresh node data on item change."""
|
"""Refresh these items on the GUI. If they are an ordered range,
|
||||||
if node := self._nodes.get(tHandle):
|
also set the isRange flag to True.
|
||||||
node.refresh()
|
"""
|
||||||
index = self._model.indexFromNode(node)
|
indices = []
|
||||||
SHARED.projectSignalProxy({"event": "projectItem", "handle": tHandle})
|
for tHandle in items:
|
||||||
self._model.dataChanged.emit(index, index)
|
if node := self._nodes.get(tHandle):
|
||||||
|
node.refresh()
|
||||||
|
SHARED.projectSignalProxy({"event": "projectItem", "handle": tHandle})
|
||||||
|
indices.append(self._model.indexFromNode(node))
|
||||||
|
if isRange and len(indices) >= 2:
|
||||||
|
self._model.dataChanged.emit(indices[0], indices[-1])
|
||||||
|
else:
|
||||||
|
for index in indices:
|
||||||
|
self._model.dataChanged.emit(index, index)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _buildTree(self, items: dict[str, NWItem]) -> dict[str, NWItem]:
|
def _buildTree(self, items: dict[str, NWItem]) -> dict[str, NWItem]:
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ class GuiProjectView(QWidget):
|
|||||||
newLabel, dlgOk = GuiEditLabel.getLabel(self, text=name or nwItem.itemName)
|
newLabel, dlgOk = GuiEditLabel.getLabel(self, text=name or nwItem.itemName)
|
||||||
if dlgOk:
|
if dlgOk:
|
||||||
nwItem.setName(newLabel)
|
nwItem.setName(newLabel)
|
||||||
|
nwItem.notifyToRefresh()
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(str, bool)
|
@pyqtSlot(str, bool)
|
||||||
|
|||||||
@@ -176,11 +176,6 @@ def testCoreItem_Setters(mockGUI, mockRnd, fncPath):
|
|||||||
item.setCursorPos(1)
|
item.setCursorPos(1)
|
||||||
assert item.cursorPos == 1
|
assert item.cursorPos == 1
|
||||||
|
|
||||||
# Initial Count
|
|
||||||
item.setWordCount(234)
|
|
||||||
item.saveInitialCount()
|
|
||||||
assert item.initCount == 234
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_Methods(mockGUI, mockRnd, fncPath):
|
def testCoreItem_Methods(mockGUI, mockRnd, fncPath):
|
||||||
|
|||||||
Reference in New Issue
Block a user