Add inactive tags filter to viewer panel (#1654)

This commit is contained in:
Veronica Berglyd Olsen
2024-01-15 16:05:58 +01:00
committed by GitHub
7 changed files with 118 additions and 55 deletions
+15 -12
View File
@@ -517,28 +517,28 @@ class NWIndex:
return None return None
def novelStructure( def novelStructure(
self, rootHandle: str | None = None, skipExcl: bool = True self, rootHandle: str | None = None, activeOnly: bool = True
) -> Iterator[tuple[str, str, str, IndexHeading]]: ) -> Iterator[tuple[str, str, str, IndexHeading]]:
"""Iterate over all titles in the novel, in the correct order as """Iterate over all titles in the novel, in the correct order as
they appear in the tree view and in the respective document they appear in the tree view and in the respective document
files, but skipping all note files. files, but skipping all note files.
""" """
structure = self._itemIndex.iterNovelStructure(rHandle=rootHandle, skipExcl=skipExcl) structure = self._itemIndex.iterNovelStructure(rHandle=rootHandle, activeOnly=activeOnly)
for tHandle, sTitle, hItem in structure: for tHandle, sTitle, hItem in structure:
yield f"{tHandle}:{sTitle}", tHandle, sTitle, hItem yield f"{tHandle}:{sTitle}", tHandle, sTitle, hItem
return return
def getNovelWordCount(self, skipExcl: bool = True) -> int: def getNovelWordCount(self, activeOnly: bool = True) -> int:
"""Count the number of words in the novel project.""" """Count the number of words in the novel project."""
wCount = 0 wCount = 0
for _, _, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl): for _, _, hItem in self._itemIndex.iterNovelStructure(activeOnly=activeOnly):
wCount += hItem.wordCount wCount += hItem.wordCount
return wCount return wCount
def getNovelTitleCounts(self, skipExcl: bool = True) -> list[int]: def getNovelTitleCounts(self, activeOnly: bool = True) -> list[int]:
"""Count the number of titles in the novel project.""" """Count the number of titles in the novel project."""
hCount = [0, 0, 0, 0, 0] hCount = [0, 0, 0, 0, 0]
for _, _, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl): for _, _, hItem in self._itemIndex.iterNovelStructure(activeOnly=activeOnly):
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
hCount[iLevel] += 1 hCount[iLevel] += 1
return hCount return hCount
@@ -551,14 +551,14 @@ class NWIndex:
return 0 return 0
def getTableOfContents( def getTableOfContents(
self, rHandle: str | None, maxDepth: int, skipExcl: bool = True self, rHandle: str | None, maxDepth: int, activeOnly: bool = True
) -> list[tuple[str, int, str, int]]: ) -> list[tuple[str, int, str, int]]:
"""Generate a table of contents up to a maximum depth.""" """Generate a table of contents up to a maximum depth."""
tOrder = [] tOrder = []
tData = {} tData = {}
pKey = None pKey = None
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure( for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(
rHandle=rHandle, skipExcl=skipExcl rHandle=rHandle, activeOnly=activeOnly
): ):
tKey = f"{tHandle}:{sTitle}" tKey = f"{tHandle}:{sTitle}"
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
@@ -646,12 +646,15 @@ class NWIndex:
"""Return all tags based on itemClass.""" """Return all tags based on itemClass."""
return self._tagsIndex.filterTagNames(itemClass.name) return self._tagsIndex.filterTagNames(itemClass.name)
def getTagsData(self) -> Iterator[tuple[str, str, str, IndexItem | None, IndexHeading | None]]: def getTagsData(
self, activeOnly: bool = True
) -> Iterator[tuple[str, str, str, IndexItem | None, IndexHeading | None]]:
"""Return all known tags.""" """Return all known tags."""
for tag, data in self._tagsIndex.items(): for tag, data in self._tagsIndex.items():
iItem = self._itemIndex[data.get("handle")] iItem = self._itemIndex[data.get("handle")]
hItem = None if iItem is None else iItem[data.get("heading")] hItem = None if iItem is None else iItem[data.get("heading")]
yield tag, data.get("name", ""), data.get("class", ""), iItem, hItem if not activeOnly or (iItem and iItem.item.isActive):
yield tag, data.get("name", ""), data.get("class", ""), iItem, hItem
return return
def getSingleTag(self, tagKey: str) -> tuple[str, str, IndexItem | None, IndexHeading | None]: def getSingleTag(self, tagKey: str) -> tuple[str, str, IndexItem | None, IndexHeading | None]:
@@ -848,7 +851,7 @@ class ItemIndex:
return return
def iterNovelStructure( def iterNovelStructure(
self, rHandle: str | None = None, skipExcl: bool = False self, rHandle: str | None = None, activeOnly: bool = False
) -> Iterable[tuple[str, str, IndexHeading]]: ) -> Iterable[tuple[str, str, IndexHeading]]:
"""Iterate over all items and headers in the novel structure for """Iterate over all items and headers in the novel structure for
a given root handle, or for all if root handle is None. a given root handle, or for all if root handle is None.
@@ -856,7 +859,7 @@ class ItemIndex:
for tItem in self._project.tree: for tItem in self._project.tree:
if tItem.isNoteLayout(): if tItem.isNoteLayout():
continue continue
if skipExcl and not tItem.isActive: if activeOnly and not tItem.isActive:
continue continue
tHandle = tItem.itemHandle tHandle = tItem.itemHandle
+1 -1
View File
@@ -70,7 +70,7 @@ VALID_MAP: dict[str, set[str]] = {
"winWidth", "winHeight", "fmtWidth", "sumWidth", "winWidth", "winHeight", "fmtWidth", "sumWidth",
}, },
"GuiDocViewerPanel": { "GuiDocViewerPanel": {
"colWidths", "colWidths", "hideInactive",
} }
} }
+72 -24
View File
@@ -29,8 +29,8 @@ from enum import Enum
from PyQt5.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QFrame, QHeaderView, QTabWidget, QTreeWidget, QAbstractItemView, QFrame, QHeaderView, QMenu, QTabWidget, QToolButton,
QTreeWidgetItem, QVBoxLayout, QWidget QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -54,10 +54,24 @@ class GuiDocViewerPanel(QWidget):
self._lastHandle = None self._lastHandle = None
iPx = int(1.0*SHARED.theme.baseIconSize)
self.tabBackRefs = _ViewPanelBackRefs(self) self.tabBackRefs = _ViewPanelBackRefs(self)
self.optsMenu = QMenu(self)
self.aInactive = self.optsMenu.addAction(self.tr("Hide Inactive Tags"))
self.aInactive.setCheckable(True)
self.aInactive.toggled.connect(self._toggleHideInactive)
self.optsButton = QToolButton(self)
self.optsButton.setIconSize(QSize(iPx, iPx))
self.optsButton.setMenu(self.optsMenu)
self.optsButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
self.mainTabs = QTabWidget(self) self.mainTabs = QTabWidget(self)
self.mainTabs.addTab(self.tabBackRefs, self.tr("References")) self.mainTabs.addTab(self.tabBackRefs, self.tr("References"))
self.mainTabs.setCornerWidget(self.optsButton, Qt.Corner.TopLeftCorner)
self.kwTabs: dict[str, _ViewPanelKeyWords] = {} self.kwTabs: dict[str, _ViewPanelKeyWords] = {}
self.idTabs: dict[str, int] = {} self.idTabs: dict[str, int] = {}
@@ -85,20 +99,27 @@ class GuiDocViewerPanel(QWidget):
def updateTheme(self, updateTabs: bool = True) -> None: def updateTheme(self, updateTabs: bool = True) -> None:
"""Update theme elements.""" """Update theme elements."""
qPalette = self.palette()
mPx = CONFIG.pxInt(2)
vPx = CONFIG.pxInt(4) vPx = CONFIG.pxInt(4)
lPx = CONFIG.pxInt(2) hPx = CONFIG.pxInt(8)
rPx = CONFIG.pxInt(14) hCol = qPalette.highlight().color()
hCol = self.palette().highlight().color() fCol = qPalette.text().color()
buttonStyle = (
"QToolButton {{padding: {0}px; margin: 0 0 {1}px 0; border: none; "
"background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({2}, {3}, {4}, 0.2);}} "
"QToolButton::menu-indicator {{image: none;}} "
).format(mPx, mPx, fCol.red(), fCol.green(), fCol.blue())
self.optsButton.setIcon(SHARED.theme.getIcon("menu"))
self.optsButton.setStyleSheet(buttonStyle)
styleSheet = ( styleSheet = (
"QTabWidget::pane {border: 0;} " "QTabWidget::pane {{border: 0;}} "
"QTabWidget QTabBar::tab {" "QTabWidget QTabBar::tab {{border: 0; padding: {0}px {1}px;}} "
f"border: 0; padding: {vPx}px {rPx}px {vPx}px {lPx}px;" "QTabWidget QTabBar::tab:selected {{color: rgb({2}, {3}, {4});}} "
"} " ).format(vPx, hPx, hCol.red(), hCol.green(), hCol.blue())
"QTabWidget QTabBar::tab:selected {"
f"color: rgb({hCol.red()}, {hCol.green()}, {hCol.blue()});"
"} "
)
self.mainTabs.setStyleSheet(styleSheet) self.mainTabs.setStyleSheet(styleSheet)
self.updateHandle(self._lastHandle) self.updateHandle(self._lastHandle)
@@ -111,20 +132,22 @@ class GuiDocViewerPanel(QWidget):
def openProjectTasks(self) -> None: def openProjectTasks(self) -> None:
"""Run open project tasks.""" """Run open project tasks."""
widths = SHARED.project.options.getValue("GuiDocViewerPanel", "colWidths", {}) colWidths = SHARED.project.options.getValue("GuiDocViewerPanel", "colWidths", {})
if isinstance(widths, dict): hideInactive = SHARED.project.options.getBool("GuiDocViewerPanel", "hideInactive", False)
for key, value in widths.items(): self.aInactive.setChecked(hideInactive)
if isinstance(colWidths, dict):
for key, value in colWidths.items():
if key in self.kwTabs and isinstance(value, list): if key in self.kwTabs and isinstance(value, list):
self.kwTabs[key].setColumnWidths(value) self.kwTabs[key].setColumnWidths(value)
return return
def closeProjectTasks(self) -> None: def closeProjectTasks(self) -> None:
"""Run close project tasks.""" """Run close project tasks."""
widths = {}
for key, tab in self.kwTabs.items():
widths[key] = tab.getColumnWidths()
logger.debug("Saving State: GuiDocViewerPanel") logger.debug("Saving State: GuiDocViewerPanel")
SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", widths) colWidths = {k: t.getColumnWidths() for k, t in self.kwTabs.items()}
hideInactive = self.aInactive.isChecked()
SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", colWidths)
SHARED.project.options.setValue("GuiDocViewerPanel", "hideInactive", hideInactive)
return return
## ##
@@ -142,9 +165,7 @@ class GuiDocViewerPanel(QWidget):
@pyqtSlot() @pyqtSlot()
def indexHasAppeared(self) -> None: def indexHasAppeared(self) -> None:
"""Handle event when the index has appeared.""" """Handle event when the index has appeared."""
for key, name, tClass, iItem, hItem in SHARED.project.index.getTagsData(): self._loadAllTags()
if tClass in self.kwTabs and iItem and hItem:
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
self._updateTabVisibility() self._updateTabVisibility()
self.updateHandle(self._lastHandle) self.updateHandle(self._lastHandle)
return return
@@ -153,10 +174,15 @@ class GuiDocViewerPanel(QWidget):
def projectItemChanged(self, tHandle: str) -> None: def projectItemChanged(self, tHandle: str) -> None:
"""Update meta data for project item.""" """Update meta data for project item."""
self.tabBackRefs.refreshDocument(tHandle) self.tabBackRefs.refreshDocument(tHandle)
activeOnly = self.aInactive.isChecked()
for key in SHARED.project.index.getDocumentTags(tHandle): for key in SHARED.project.index.getDocumentTags(tHandle):
name, tClass, iItem, hItem = SHARED.project.index.getSingleTag(key) name, tClass, iItem, hItem = SHARED.project.index.getSingleTag(key)
if tClass in self.kwTabs and iItem and hItem: if tClass in self.kwTabs and iItem and hItem:
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem) if not activeOnly or (iItem and iItem.item.isActive):
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
else:
self.kwTabs[tClass].removeEntry(key)
self._updateTabVisibility()
return return
@pyqtSlot(str) @pyqtSlot(str)
@@ -182,6 +208,20 @@ class GuiDocViewerPanel(QWidget):
self._updateTabVisibility() self._updateTabVisibility()
return return
##
# Private Slots
##
@pyqtSlot(bool)
def _toggleHideInactive(self, state: bool) -> None:
"""Process toggling of active/inactive visibility."""
logger.debug("Setting inactive items to %s", "hidden" if state else "visible")
for cTab in self.kwTabs.values():
cTab.clearContent()
self._loadAllTags()
self._updateTabVisibility()
return
## ##
# Internal Functions # Internal Functions
## ##
@@ -193,6 +233,14 @@ class GuiDocViewerPanel(QWidget):
self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0) self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
return return
def _loadAllTags(self) -> None:
"""Load all tags into the tabs."""
data = SHARED.project.index.getTagsData(activeOnly=self.aInactive.isChecked())
for key, name, tClass, iItem, hItem in data:
if tClass in self.kwTabs and iItem and hItem:
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
return
# END Class GuiDocViewerPanel # END Class GuiDocViewerPanel
+1 -1
View File
@@ -674,7 +674,7 @@ class GuiNovelTree(QTreeWidget):
tStart = time() tStart = time()
logger.debug("Building novel tree for root item '%s'", rootHandle) logger.debug("Building novel tree for root item '%s'", rootHandle)
novStruct = SHARED.project.index.novelStructure(rootHandle=rootHandle, skipExcl=True) novStruct = SHARED.project.index.novelStructure(rootHandle=rootHandle, activeOnly=True)
for tKey, tHandle, sTitle, novIdx in novStruct: for tKey, tHandle, sTitle, novIdx in novStruct:
if novIdx.level == "H0": if novIdx.level == "H0":
continue continue
+1 -1
View File
@@ -646,7 +646,7 @@ class GuiOutlineTree(QTreeWidget):
headItem.setTextAlignment( headItem.setTextAlignment(
self._colIdx[nwOutline.PCOUNT], Qt.AlignmentFlag.AlignRight) self._colIdx[nwOutline.PCOUNT], Qt.AlignmentFlag.AlignRight)
novStruct = SHARED.project.index.novelStructure(rootHandle=rootHandle, skipExcl=True) novStruct = SHARED.project.index.novelStructure(rootHandle=rootHandle, activeOnly=True)
for _, tHandle, sTitle, novIdx in novStruct: for _, tHandle, sTitle, novIdx in novStruct:
iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0) iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
+16 -16
View File
@@ -579,7 +579,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
project.tree[nHandle].setActive(False) # type: ignore project.tree[nHandle].setActive(False) # type: ignore
keys = [] keys = []
for aKey, _, _, _ in index.novelStructure(skipExcl=False): for aKey, _, _, _ in index.novelStructure(activeOnly=False):
keys.append(aKey) keys.append(aKey)
assert keys == [ assert keys == [
@@ -590,7 +590,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
] ]
keys = [] keys = []
for aKey, _, _, _ in index.novelStructure(skipExcl=True): for aKey, _, _, _ in index.novelStructure(activeOnly=True):
keys.append(aKey) keys.append(aKey)
assert keys == [ assert keys == [
@@ -761,7 +761,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
assert index.scanText(sHandle, "### Scene One\n\n") # type: ignore assert index.scanText(sHandle, "### Scene One\n\n") # type: ignore
assert index.scanText(tHandle, "### Scene Two\n\n") # type: ignore assert index.scanText(tHandle, "### Scene Two\n\n") # type: ignore
assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(skipExcl=False)] == [ assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(activeOnly=False)] == [
(C.hTitlePage, "T0001"), (C.hTitlePage, "T0001"),
(C.hChapterDoc, "T0001"), (C.hChapterDoc, "T0001"),
(C.hSceneDoc, "T0001"), (C.hSceneDoc, "T0001"),
@@ -772,7 +772,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
(tHandle, "T0001"), (tHandle, "T0001"),
] ]
assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(skipExcl=True)] == [ assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(activeOnly=True)] == [
(C.hTitlePage, "T0001"), (C.hTitlePage, "T0001"),
(C.hChapterDoc, "T0001"), (C.hChapterDoc, "T0001"),
(C.hSceneDoc, "T0001"), (C.hSceneDoc, "T0001"),
@@ -783,7 +783,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
# Add a fake handle to the tree and check that it's ignored # Add a fake handle to the tree and check that it's ignored
project.tree._order.append("0000000000000") project.tree._order.append("0000000000000")
assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(skipExcl=False)] == [ assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(activeOnly=False)] == [
(C.hTitlePage, "T0001"), (C.hTitlePage, "T0001"),
(C.hChapterDoc, "T0001"), (C.hChapterDoc, "T0001"),
(C.hSceneDoc, "T0001"), (C.hSceneDoc, "T0001"),
@@ -796,22 +796,22 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
project.tree._order.remove("0000000000000") project.tree._order.remove("0000000000000")
# Extract stats # Extract stats
assert index.getNovelWordCount(skipExcl=False) == 43 assert index.getNovelWordCount(activeOnly=False) == 43
assert index.getNovelWordCount(skipExcl=True) == 15 assert index.getNovelWordCount(activeOnly=True) == 15
assert index.getNovelTitleCounts(skipExcl=False) == [0, 3, 2, 3, 0] assert index.getNovelTitleCounts(activeOnly=False) == [0, 3, 2, 3, 0]
assert index.getNovelTitleCounts(skipExcl=True) == [0, 1, 2, 3, 0] assert index.getNovelTitleCounts(activeOnly=True) == [0, 1, 2, 3, 0]
# Table of Contents # Table of Contents
assert index.getTableOfContents(C.hNovelRoot, 0, skipExcl=True) == [] assert index.getTableOfContents(C.hNovelRoot, 0, activeOnly=True) == []
assert index.getTableOfContents(C.hNovelRoot, 1, skipExcl=True) == [ assert index.getTableOfContents(C.hNovelRoot, 1, activeOnly=True) == [
(f"{C.hTitlePage}:T0001", 1, "New Novel", 15), (f"{C.hTitlePage}:T0001", 1, "New Novel", 15),
] ]
assert index.getTableOfContents(C.hNovelRoot, 2, skipExcl=True) == [ assert index.getTableOfContents(C.hNovelRoot, 2, activeOnly=True) == [
(f"{C.hTitlePage}:T0001", 1, "New Novel", 5), (f"{C.hTitlePage}:T0001", 1, "New Novel", 5),
(f"{C.hChapterDoc}:T0001", 2, "New Chapter", 4), (f"{C.hChapterDoc}:T0001", 2, "New Chapter", 4),
(f"{hHandle}:T0001", 2, "Chapter One", 6), (f"{hHandle}:T0001", 2, "Chapter One", 6),
] ]
assert index.getTableOfContents(C.hNovelRoot, 3, skipExcl=True) == [ assert index.getTableOfContents(C.hNovelRoot, 3, activeOnly=True) == [
(f"{C.hTitlePage}:T0001", 1, "New Novel", 5), (f"{C.hTitlePage}:T0001", 1, "New Novel", 5),
(f"{C.hChapterDoc}:T0001", 2, "New Chapter", 2), (f"{C.hChapterDoc}:T0001", 2, "New Chapter", 2),
(f"{C.hSceneDoc}:T0001", 3, "New Scene", 2), (f"{C.hSceneDoc}:T0001", 3, "New Scene", 2),
@@ -820,8 +820,8 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
(f"{tHandle}:T0001", 3, "Scene Two", 2), (f"{tHandle}:T0001", 3, "Scene Two", 2),
] ]
assert index.getTableOfContents(C.hNovelRoot, 0, skipExcl=False) == [] assert index.getTableOfContents(C.hNovelRoot, 0, activeOnly=False) == []
assert index.getTableOfContents(C.hNovelRoot, 1, skipExcl=False) == [ assert index.getTableOfContents(C.hNovelRoot, 1, activeOnly=False) == [
(f"{C.hTitlePage}:T0001", 1, "New Novel", 9), (f"{C.hTitlePage}:T0001", 1, "New Novel", 9),
(f"{nHandle}:T0001", 1, "Hello World!", 12), (f"{nHandle}:T0001", 1, "Hello World!", 12),
(f"{nHandle}:T0002", 1, "Hello World!", 22), (f"{nHandle}:T0002", 1, "Hello World!", 22),
@@ -1173,7 +1173,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd):
# Skip excluded # Skip excluded
project.tree[sHandle].setActive(False) # type: ignore project.tree[sHandle].setActive(False) # type: ignore
nStruct = list(itemIndex.iterNovelStructure(skipExcl=True)) nStruct = list(itemIndex.iterNovelStructure(activeOnly=True))
assert len(nStruct) == 3 assert len(nStruct) == 3
assert nStruct[0][0] == nHandle assert nStruct[0][0] == nHandle
assert nStruct[1][0] == cHandle assert nStruct[1][0] == cHandle
+12
View File
@@ -28,6 +28,7 @@ from PyQt5.QtGui import QIcon
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwLists from novelwriter.constants import nwLists
from novelwriter.core.item import NWItem
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
@@ -216,6 +217,17 @@ def testGuiViewerPanel_Tags(qtbot, monkeypatch, caplog, nwGUI, projPath, mockRnd
charTab._treeItemDoubleClicked(charTab.model().index(0, charTab.C_NAME)) charTab._treeItemDoubleClicked(charTab.model().index(0, charTab.C_NAME))
assert nwGUI.docViewer.docHandle == hJane assert nwGUI.docViewer.docHandle == hJane
# Hide Inactive Tags
viewPanel.aInactive.setChecked(True)
assert charTab.topLevelItemCount() == 2
nwJohn = SHARED.project.tree[hJohn]
assert isinstance(nwJohn, NWItem)
nwJohn.setActive(False)
projTree.setTreeItemValues(hJohn)
projTree._alertTreeChange(hJohn, flush=False)
assert charTab.topLevelItemCount() == 1
# qtbot.stop() # qtbot.stop()
# END Test testGuiViewerPanel_Tags # END Test testGuiViewerPanel_Tags