Rewrite most of the NWStatus class

This commit is contained in:
Veronica Berglyd Olsen
2022-04-05 22:08:14 +02:00
parent 7298e1f438
commit 40e7055f16
3 changed files with 162 additions and 127 deletions
+6 -6
View File
@@ -280,11 +280,11 @@ class NWItem():
the current item based on its class.
"""
if self._class in nwLists.CLS_NOVEL:
stName = self.theProject.statusItems.checkEntry(self._status)
stIcon = self.theProject.statusItems.getIcon(stName)
stName = self.theProject.statusItems.name(self._status)
stIcon = self.theProject.statusItems.icon(self._status)
else:
stName = self.theProject.importItems.checkEntry(self._import)
stIcon = self.theProject.importItems.getIcon(stName)
stName = self.theProject.importItems.name(self._import)
stIcon = self.theProject.importItems.icon(self._import)
return stName, stIcon
def setImportStatus(self, theLabel):
@@ -383,14 +383,14 @@ class NWItem():
"""Set the item status by looking it up in the valid status
items of the current project.
"""
self._status = self.theProject.statusItems.checkEntry(theStatus)
self._status = self.theProject.statusItems.check(theStatus)
return
def setImport(self, theImport):
"""Set the item importance by looking it up in the valid import
items of the current project.
"""
self._import = self.theProject.importItems.checkEntry(theImport)
self._import = self.theProject.importItems.check(theImport)
return
def setExpanded(self, expState):
+12 -12
View File
@@ -218,16 +218,16 @@ class NWProject():
}
self.spellCheck = False
self.autoOutline = True
self.statusItems = NWStatus()
self.statusItems.addEntry(self.tr("New"), (100, 100, 100))
self.statusItems.addEntry(self.tr("Note"), (200, 50, 0))
self.statusItems.addEntry(self.tr("Draft"), (200, 150, 0))
self.statusItems.addEntry(self.tr("Finished"), (50, 200, 0))
self.importItems = NWStatus()
self.importItems.addEntry(self.tr("New"), (100, 100, 100))
self.importItems.addEntry(self.tr("Minor"), (200, 50, 0))
self.importItems.addEntry(self.tr("Major"), (200, 150, 0))
self.importItems.addEntry(self.tr("Main"), (50, 200, 0))
self.statusItems = NWStatus("s")
self.statusItems.write(None, self.tr("New"), (100, 100, 100))
self.statusItems.write(None, self.tr("Note"), (200, 50, 0))
self.statusItems.write(None, self.tr("Draft"), (200, 150, 0))
self.statusItems.write(None, self.tr("Finished"), (50, 200, 0))
self.importItems = NWStatus("i")
self.importItems.write(None, self.tr("New"), (100, 100, 100))
self.importItems.write(None, self.tr("Minor"), (200, 50, 0))
self.importItems.write(None, self.tr("Major"), (200, 150, 0))
self.importItems.write(None, self.tr("Main"), (50, 200, 0))
self.lastEdited = None
self.lastViewed = None
self.lastWCount = 0
@@ -1205,9 +1205,9 @@ class NWProject():
self.importItems.resetCounts()
for nwItem in self.projTree:
if nwItem.itemClass in nwLists.CLS_NOVEL:
self.statusItems.countEntry(nwItem.itemStatus)
self.statusItems.increment(nwItem.itemStatus)
else:
self.importItems.countEntry(nwItem.itemImport)
self.importItems.increment(nwItem.itemImport)
return
def localLookup(self, theWord):
+144 -109
View File
@@ -23,6 +23,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import random
import logging
import novelwriter
@@ -30,134 +31,158 @@ from lxml import etree
from PyQt5.QtGui import QIcon, QPixmap, QColor
from novelwriter.common import checkInt
from novelwriter.common import checkInt, getMinMax, simplified
logger = logging.getLogger(__name__)
class NWStatus():
def __init__(self):
def __init__(self, type):
self._type = str(type)
self._store = {}
self._reverse = {}
self._default = None
self._theLabels = []
self._theColours = []
self._theCounts = []
self._theIcons = []
self._theMap = {}
self._theLength = 0
self._theIndex = 0
self._iconSize = novelwriter.CONFIG.pxInt(32)
pixmap = QPixmap(self._iconSize, self._iconSize)
pixmap.fill(QColor(100, 100, 100))
self._defaultIcon = QIcon(pixmap)
return
def addEntry(self, theLabel, theColours):
"""Add a status entry to the status object, but ensure it isn't
a duplicate.
def write(self, key, name, cols):
"""Add or update a status entry. If the key is invalid, a new
key is generated.
"""
theLabel = theLabel.strip()
if self._getIndex(theLabel) is None:
theIcon = QPixmap(self._iconSize, self._iconSize)
theIcon.fill(QColor(*theColours))
self._theIcons.append(QIcon(theIcon))
self._theLabels.append(theLabel)
self._theColours.append(theColours)
self._theCounts.append(0)
self._theMap[theLabel] = self._theLength
self._theLength += 1
if not self._isKey(key):
key = self._newKey()
if not isinstance(cols, tuple):
cols = (100, 100, 100)
if len(cols) != 3:
cols = (100, 100, 100)
return True
pixmap = QPixmap(self._iconSize, self._iconSize)
pixmap.fill(QColor(*cols))
def checkEntry(self, theStatus):
"""Check if a status value is valid, and returns the safe
reference to be used internally.
name = simplified(name)
count = self._store[key]["count"] if key in self._store else 0
self._store[key] = {
"name": name,
"icon": QIcon(pixmap),
"cols": cols,
"count": count,
}
self._reverse[name] = key
if self._default is None:
self._default = key
return key
def check(self, value):
"""Check the key against the stored status names.
"""
if isinstance(theStatus, str):
if self._getIndex(theStatus) is not None:
return theStatus.strip()
return self._theLabels[0]
if self._isKey(value) and value in self._store:
return value
elif value in self._reverse:
return self._reverse[value]
elif self._default is not None:
return self._default
else:
return ""
def getIcon(self, theLabel):
"""Return the icon for the given status item.
def name(self, key):
"""Return the name associated with a given key.
"""
theIndex = self._getIndex(theLabel)
if theIndex is not None:
return self._theIcons[theIndex]
return QIcon()
if key in self._store:
return self._store[key]["name"]
elif self._default is not None:
return self._store[self._default]["name"]
else:
return ""
def cols(self, key):
"""Return the colours associated with a given key.
"""
if key in self._store:
return self._store[key]["cols"]
elif self._default is not None:
return self._store[self._default]["cols"]
else:
return (100, 100, 100)
def count(self, key):
"""Return the count associated with a given key.
"""
if key in self._store:
return self._store[key]["count"]
elif self._default is not None:
return self._store[self._default]["count"]
else:
return 0
def icon(self, key):
"""Return the icon associated with a given key.
"""
if key in self._store:
return self._store[key]["icon"]
elif self._default is not None:
return self._store[self._default]["icon"]
else:
return self._defaultIcon
def setNewEntries(self, newList):
"""Update the list of entries after they have been modified by
the GUI tool.
"""
replaceMap = {}
if newList is not None:
self._theLabels = []
self._theColours = []
self._theCounts = []
self._theIcons = []
self._theMap = {}
self._theLength = 0
self._theIndex = 0
for nName, nR, nG, nB, oName in newList:
self.addEntry(nName, (nR, nG, nB))
if nName != oName and oName is not None:
replaceMap[oName] = nName
return replaceMap
return {}
def resetCounts(self):
"""Clear the counts of references to the status entries.
"""
self._theCounts = [0]*self._theLength
for key in self._store:
self._store[key]["count"] = 0
return
def countEntry(self, theLabel):
"""Increment the counter for a given label. This should be used
together with resetCounts in a loop over project items.
def increment(self, key):
"""Increment the counter for a given entry.
"""
theIndex = self._getIndex(theLabel)
if theIndex is not None:
self._theCounts[theIndex] += 1
if key in self._store:
self._store[key]["count"] += 1
return
def packXML(self, xParent):
"""Pack the status entries into an XML object for saving to the
main project file.
"""
for n in range(self._theLength):
for key, data in self._store.items():
xSub = etree.SubElement(xParent, "entry", attrib={
"red": str(self._theColours[n][0]),
"green": str(self._theColours[n][1]),
"blue": str(self._theColours[n][2]),
"key": key,
"red": str(data["cols"][0]),
"green": str(data["cols"][1]),
"blue": str(data["cols"][2]),
})
xSub.text = self._theLabels[n]
xSub.text = data["name"]
return True
def unpackXML(self, xParent):
"""Unpack an XML tree and set the class values.
"""
theLabels = []
theColours = []
self._store = {}
self._reverse = {}
self._default = None
for xChild in xParent:
theLabels.append(xChild.text)
cR = checkInt(xChild.attrib.get("red", 0), 0, False)
cG = checkInt(xChild.attrib.get("green", 0), 0, False)
cB = checkInt(xChild.attrib.get("blue", 0), 0, False)
theColours.append((cR, cG, cB))
if len(theLabels) > 0:
self._theLabels = []
self._theColours = []
self._theCounts = []
self._theIcons = []
self._theMap = {}
self._theLength = 0
self._theIndex = 0
for n in range(len(theLabels)):
self.addEntry(theLabels[n], theColours[n])
name = xChild.text.strip()
key = xChild.attrib.get("key", None)
cR = getMinMax(checkInt(xChild.attrib.get("red", 100), 100), 0, 255)
cG = getMinMax(checkInt(xChild.attrib.get("green", 100), 100), 0, 255)
cB = getMinMax(checkInt(xChild.attrib.get("blue", 100), 100), 0, 255)
self.write(key, name, (cR, cG, cB))
return True
@@ -165,39 +190,49 @@ class NWStatus():
# Internal Functions
##
def _getIndex(self, theLabel):
"""Look up a status entry in the object lists, and return it if
it exists.
def _newKey(self):
"""Generate a new key for a status flag. This method is
recursive, but should only fail if there is an issue with the
random number generator or the user has added a lot of status
flags. The Python recursion limit is given the job to handle
the extreme case and will cause an app crash.
"""
if theLabel is None:
return None
return self._theMap.get(theLabel.strip(), None)
key = f"{self._type}{random.randint(0, 0xffffff):06x}"
if key in self._store:
key = self._newKey()
return key
def _isKey(self, key):
"""Check if a string is a key or not.
"""
if not isinstance(key, str):
return False
if len(key) != 7:
return False
if key[0] != self._type:
return False
for c in key[1:]:
if c not in "0123456789abcdef":
return False
return True
##
# Iterator Bits
##
def __getitem__(self, n):
"""Return an entry by its index.
"""
if n >= 0 and n < self._theLength:
return self._theLabels[n], self._theColours[n], self._theCounts[n], self._theIcons[n]
return None, None, None, QIcon()
def __getitem__(self, key):
return self._store[key]
def __iter__(self):
"""Initialise the iterator.
"""
self._theIndex = 0
return self
return iter(self._store)
def __next__(self):
"""Return the next entry for the iterator.
"""
if self._theIndex < self._theLength:
theLabel, theColour, theCount, theIcon = self.__getitem__(self._theIndex)
self._theIndex += 1
return theLabel, theColour, theCount, theIcon
else:
raise StopIteration
def keys(self):
return self._store.keys()
def items(self):
return self._store.items()
def values(self):
return self._store.values()
# END Class NWStatus