Rewrite how project items are refreshed on the GUI

This commit is contained in:
Veronica Berglyd Olsen
2024-11-18 17:51:05 +01:00
parent 337f05f60e
commit df0244095f
5 changed files with 41 additions and 39 deletions
+10 -21
View File
@@ -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
+15 -6
View File
@@ -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
+15 -7
View File
@@ -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]:
+1
View File
@@ -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)
-5
View File
@@ -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):