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",
|
||||
"_type", "_class", "_layout", "_status", "_import", "_active",
|
||||
"_expanded", "_heading", "_charCount", "_wordCount",
|
||||
"_paraCount", "_cursorPos", "_initCount", "_blocked",
|
||||
"_paraCount", "_cursorPos", "_initCount",
|
||||
)
|
||||
|
||||
def __init__(self, project: NWProject, handle: str) -> None:
|
||||
@@ -82,8 +82,6 @@ class NWItem:
|
||||
self._cursorPos = 0 # Last cursor position
|
||||
self._initCount = 0 # Initial word count
|
||||
|
||||
self._blocked = True
|
||||
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
@@ -259,7 +257,6 @@ class NWItem:
|
||||
self._cursorPos = 0
|
||||
|
||||
self._initCount = self._wordCount
|
||||
self._blocked = False
|
||||
|
||||
return True
|
||||
|
||||
@@ -284,9 +281,17 @@ class NWItem:
|
||||
cls._paraCount = source._paraCount
|
||||
cls._cursorPos = source._cursorPos
|
||||
cls._initCount = source._initCount
|
||||
cls._blocked = source._blocked
|
||||
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
|
||||
##
|
||||
@@ -419,7 +424,6 @@ class NWItem:
|
||||
self._name = simplified(name)
|
||||
else:
|
||||
self._name = ""
|
||||
self._notifyChange()
|
||||
return
|
||||
|
||||
def setParent(self, handle: Any) -> None:
|
||||
@@ -560,18 +564,3 @@ class NWItem:
|
||||
else:
|
||||
self._cursorPos = 0
|
||||
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
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt
|
||||
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_TIP = 0x0300 | Qt.ItemDataRole.ToolTipRole
|
||||
|
||||
T_NodeData = str | QIcon | Qt.AlignmentFlag | None
|
||||
|
||||
|
||||
class ProjectNode:
|
||||
|
||||
@@ -125,7 +127,7 @@ class ProjectNode:
|
||||
def childCount(self) -> int:
|
||||
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)
|
||||
|
||||
@@ -168,7 +170,7 @@ class ProjectModel(QAbstractItemModel):
|
||||
super().__init__(None)
|
||||
logger.debug("Create: ProjectModel")
|
||||
self._tree = tree
|
||||
self._root = ProjectNode(NWItem(tree._project, ""))
|
||||
self._root = ProjectNode(NWItem(tree._project, "invisibleRoot"))
|
||||
return
|
||||
|
||||
def __del__(self) -> None:
|
||||
@@ -177,6 +179,7 @@ class ProjectModel(QAbstractItemModel):
|
||||
|
||||
@property
|
||||
def root(self) -> ProjectNode:
|
||||
"""Return the model root item."""
|
||||
return self._root
|
||||
|
||||
##
|
||||
@@ -184,22 +187,26 @@ class ProjectModel(QAbstractItemModel):
|
||||
##
|
||||
|
||||
def rowCount(self, index: QModelIndex) -> int:
|
||||
"""Return the number of rows for an entry."""
|
||||
if index.isValid():
|
||||
return index.internalPointer().childCount()
|
||||
return self._root.childCount()
|
||||
|
||||
def columnCount(self, index: QModelIndex) -> int:
|
||||
"""Return the number of columns for an entry."""
|
||||
return 4
|
||||
|
||||
def parent(self, index: QModelIndex) -> QModelIndex:
|
||||
"""Get the parent model index of another index."""
|
||||
if index.isValid():
|
||||
if parent := index.internalPointer().parent():
|
||||
return self.createIndex(parent.row(), 0, parent)
|
||||
return 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():
|
||||
item = parent.internalPointer()
|
||||
item: ProjectNode = parent.internalPointer()
|
||||
else:
|
||||
item = self._root
|
||||
|
||||
@@ -211,10 +218,11 @@ class ProjectModel(QAbstractItemModel):
|
||||
|
||||
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():
|
||||
return None
|
||||
node = index.internalPointer()
|
||||
node: ProjectNode = index.internalPointer()
|
||||
return node.data(index.column(), role)
|
||||
|
||||
# def addChild(self, node: ProjectNode, parent: QModelIndex) -> None:
|
||||
@@ -230,6 +238,7 @@ class ProjectModel(QAbstractItemModel):
|
||||
##
|
||||
|
||||
def node(self, index: QModelIndex) -> ProjectNode | None:
|
||||
"""Return the node for a given model index."""
|
||||
if index.isValid():
|
||||
return index.internalPointer()
|
||||
return None
|
||||
|
||||
@@ -225,13 +225,21 @@ class NWTree:
|
||||
|
||||
return
|
||||
|
||||
def refreshNode(self, tHandle: str) -> None:
|
||||
"""Refresh node data on item change."""
|
||||
if node := self._nodes.get(tHandle):
|
||||
node.refresh()
|
||||
index = self._model.indexFromNode(node)
|
||||
SHARED.projectSignalProxy({"event": "projectItem", "handle": tHandle})
|
||||
self._model.dataChanged.emit(index, index)
|
||||
def refreshItems(self, items: list[str], isRange: bool = False) -> None:
|
||||
"""Refresh these items on the GUI. If they are an ordered range,
|
||||
also set the isRange flag to True.
|
||||
"""
|
||||
indices = []
|
||||
for tHandle in items:
|
||||
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
|
||||
|
||||
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)
|
||||
if dlgOk:
|
||||
nwItem.setName(newLabel)
|
||||
nwItem.notifyToRefresh()
|
||||
return
|
||||
|
||||
@pyqtSlot(str, bool)
|
||||
|
||||
@@ -176,11 +176,6 @@ def testCoreItem_Setters(mockGUI, mockRnd, fncPath):
|
||||
item.setCursorPos(1)
|
||||
assert item.cursorPos == 1
|
||||
|
||||
# Initial Count
|
||||
item.setWordCount(234)
|
||||
item.saveInitialCount()
|
||||
assert item.initCount == 234
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_Methods(mockGUI, mockRnd, fncPath):
|
||||
|
||||
Reference in New Issue
Block a user