Generalise the manuscript stats count values

This commit is contained in:
Veronica Berglyd Olsen
2024-10-28 16:17:31 +01:00
parent 8725edaf7e
commit f2d2b4c4f2
4 changed files with 140 additions and 82 deletions
+59 -4
View File
@@ -67,7 +67,7 @@ class nwRegEx:
FMT_EB = r"(?<![\w\\])(\*{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)" FMT_EB = r"(?<![\w\\])(\*{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
FMT_ST = r"(?<![\w\\])(~{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)" FMT_ST = r"(?<![\w\\])(~{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
FMT_SC = r"(?i)(?<!\\)(\[(?:b|/b|i|/i|s|/s|u|/u|m|/m|sup|/sup|sub|/sub|br)\])" FMT_SC = r"(?i)(?<!\\)(\[(?:b|/b|i|/i|s|/s|u|/u|m|/m|sup|/sup|sub|/sub|br)\])"
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote):)(.+?)(?<!\\)(\])" FMT_SV = r"(?i)(?<!\\)(\[(?:footnote|field):)(.+?)(?<!\\)(\])"
class nwShortcode: class nwShortcode:
@@ -95,6 +95,8 @@ class nwShortcode:
nwComment.COMMENT: "[comment:{0}]", nwComment.COMMENT: "[comment:{0}]",
} }
FIELD_VALUE = "[field:{0}]"
class nwStyles: class nwStyles:
@@ -159,11 +161,14 @@ class nwKeyWords:
STORY_KEY = "@story" STORY_KEY = "@story"
MENTION_KEY = "@mention" MENTION_KEY = "@mention"
# Set of Valid Keys # Note: The order here affects the order of menu entries
VALID_KEYS = { ALL_KEYS = [
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY, TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
} ]
# Set of Valid Keys
VALID_KEYS = set(ALL_KEYS)
# Map from Keys to Item Class # Map from Keys to Item Class
KEY_CLASS = { KEY_CLASS = {
@@ -193,6 +198,29 @@ class nwLists:
] ]
class nwStats:
CHARS_ALL = "allChars"
CHARS_TEXT = "textChars"
CHARS_TITLE = "titleChars"
PARAGRAPHS = "paragraphCount"
TITLES = "titleCount"
WCHARS_ALL = "allWordChars"
WCHARS_TEXT = "textWordChars"
WCHARS_TITLE = "titleWordChars"
WORDS_ALL = "allWords"
WORDS_TEXT = "textWords"
WORDS_TITLE = "titleWords"
# Note: The order here affects the order of menu entries
ALL_FIELDS = [
WORDS_ALL, WORDS_TEXT, WORDS_TITLE,
CHARS_ALL, CHARS_TEXT, CHARS_TITLE,
WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
PARAGRAPHS, TITLES,
]
class nwLabels: class nwLabels:
CLASS_NAME = { CLASS_NAME = {
@@ -253,6 +281,20 @@ class nwLabels:
nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"), nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"), nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"),
} }
KEY_SHORTCUT = {
nwKeyWords.TAG_KEY: "Ctrl+K, G",
nwKeyWords.POV_KEY: "Ctrl+K, V",
nwKeyWords.FOCUS_KEY: "Ctrl+K, F",
nwKeyWords.CHAR_KEY: "Ctrl+K, C",
nwKeyWords.PLOT_KEY: "Ctrl+K, P",
nwKeyWords.TIME_KEY: "Ctrl+K, T",
nwKeyWords.WORLD_KEY: "Ctrl+K, L",
nwKeyWords.OBJECT_KEY: "Ctrl+K, O",
nwKeyWords.ENTITY_KEY: "Ctrl+K, E",
nwKeyWords.CUSTOM_KEY: "Ctrl+K, X",
nwKeyWords.STORY_KEY: "Ctrl+K, N",
nwKeyWords.MENTION_KEY: "Ctrl+K, M",
}
OUTLINE_COLS = { OUTLINE_COLS = {
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"), nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"), nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
@@ -274,6 +316,19 @@ class nwLabels:
nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY], nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"), nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
} }
STATS_NAME = {
nwStats.CHARS_ALL: QT_TRANSLATE_NOOP("Constant", "Characters"),
nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Constant", "Characters in Text"),
nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Constant", "Characters in Headings"),
nwStats.PARAGRAPHS: QT_TRANSLATE_NOOP("Constant", "Paragraphs"),
nwStats.TITLES: QT_TRANSLATE_NOOP("Constant", "Headings"),
nwStats.WCHARS_ALL: QT_TRANSLATE_NOOP("Constant", "Characters, No Spaces"),
nwStats.WCHARS_TEXT: QT_TRANSLATE_NOOP("Constant", "Characters in Text, No Spaces"),
nwStats.WCHARS_TITLE: QT_TRANSLATE_NOOP("Constant", "Characters in Headings, No Spaces"),
nwStats.WORDS_ALL: QT_TRANSLATE_NOOP("Constant", "Words"),
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Constant", "Words in Text"),
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Constant", "Words in Headings"),
}
BUILD_FMT = { BUILD_FMT = {
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"), nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"), nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
+24 -23
View File
@@ -36,7 +36,8 @@ from PyQt5.QtGui import QColor, QFont
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.common import checkInt, numberToRoman from novelwriter.common import checkInt, numberToRoman
from novelwriter.constants import ( from novelwriter.constants import (
nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStyles, nwUnicode, trConst nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
trConst
) )
from novelwriter.core.index import processComment from novelwriter.core.index import processComment
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
@@ -948,20 +949,20 @@ class Tokenizer(ABC):
def countStats(self) -> None: def countStats(self) -> None:
"""Count stats on the tokenized text.""" """Count stats on the tokenized text."""
titleCount = self._counts.get("titleCount", 0) titleCount = self._counts.get(nwStats.TITLES, 0)
paragraphCount = self._counts.get("paragraphCount", 0) paragraphCount = self._counts.get(nwStats.PARAGRAPHS, 0)
allWords = self._counts.get("allWords", 0) allWords = self._counts.get(nwStats.WORDS_ALL, 0)
textWords = self._counts.get("textWords", 0) textWords = self._counts.get(nwStats.WORDS_TEXT, 0)
titleWords = self._counts.get("titleWords", 0) titleWords = self._counts.get(nwStats.WORDS_TITLE, 0)
allChars = self._counts.get("allChars", 0) allChars = self._counts.get(nwStats.CHARS_ALL, 0)
textChars = self._counts.get("textChars", 0) textChars = self._counts.get(nwStats.CHARS_TEXT, 0)
titleChars = self._counts.get("titleChars", 0) titleChars = self._counts.get(nwStats.CHARS_TITLE, 0)
allWordChars = self._counts.get("allWordChars", 0) allWordChars = self._counts.get(nwStats.WCHARS_ALL, 0)
textWordChars = self._counts.get("textWordChars", 0) textWordChars = self._counts.get(nwStats.WCHARS_TEXT, 0)
titleWordChars = self._counts.get("titleWordChars", 0) titleWordChars = self._counts.get(nwStats.WCHARS_TITLE, 0)
for tType, _, tText, _, _ in self._blocks: for tType, _, tText, _, _ in self._blocks:
tText = tText.replace(nwUnicode.U_ENDASH, " ") tText = tText.replace(nwUnicode.U_ENDASH, " ")
@@ -1006,20 +1007,20 @@ class Tokenizer(ABC):
allChars += len(tText) allChars += len(tText)
allWordChars += len("".join(words)) allWordChars += len("".join(words))
self._counts["titleCount"] = titleCount self._counts[nwStats.TITLES] = titleCount
self._counts["paragraphCount"] = paragraphCount self._counts[nwStats.PARAGRAPHS] = paragraphCount
self._counts["allWords"] = allWords self._counts[nwStats.WORDS_ALL] = allWords
self._counts["textWords"] = textWords self._counts[nwStats.WORDS_TEXT] = textWords
self._counts["titleWords"] = titleWords self._counts[nwStats.WORDS_TITLE] = titleWords
self._counts["allChars"] = allChars self._counts[nwStats.CHARS_ALL] = allChars
self._counts["textChars"] = textChars self._counts[nwStats.CHARS_TEXT] = textChars
self._counts["titleChars"] = titleChars self._counts[nwStats.CHARS_TITLE] = titleChars
self._counts["allWordChars"] = allWordChars self._counts[nwStats.WCHARS_ALL] = allWordChars
self._counts["textWordChars"] = textWordChars self._counts[nwStats.WCHARS_TEXT] = textWordChars
self._counts["titleWordChars"] = titleWordChars self._counts[nwStats.WCHARS_TITLE] = titleWordChars
return return
+1
View File
@@ -523,6 +523,7 @@ class TextBlockData(QTextBlockUserData):
self._metaData.append((s, e, res.group(0), "url")) self._metaData.append((s, e, res.group(0), "url"))
self._text = text self._text = text
self._offset = offset
return return
+56 -55
View File
@@ -43,6 +43,7 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import fuzzyTime from novelwriter.common import fuzzyTime
from novelwriter.constants import nwLabels, nwStats, trConst
from novelwriter.core.buildsettings import BuildCollection, BuildSettings from novelwriter.core.buildsettings import BuildCollection, BuildSettings
from novelwriter.core.docbuild import NWBuildDocument from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
@@ -928,8 +929,7 @@ class _StatsWidget(QWidget):
self.toggleButton = NIconToggleButton(self, SHARED.theme.baseIconSize, "unfold") self.toggleButton = NIconToggleButton(self, SHARED.theme.baseIconSize, "unfold")
self.toggleButton.toggled.connect(self._toggleView) self.toggleButton.toggled.connect(self._toggleView)
self._buildMinimal() self._buildStatsPanel()
self._buildMaximal()
self.mainStack = QStackedWidget(self) self.mainStack = QStackedWidget(self)
self.mainStack.addWidget(self.minWidget) self.mainStack.addWidget(self.minWidget)
@@ -949,23 +949,23 @@ class _StatsWidget(QWidget):
def updateStats(self, data: dict[str, int]) -> None: def updateStats(self, data: dict[str, int]) -> None:
"""Update the stats values from a Tokenizer stats dict.""" """Update the stats values from a Tokenizer stats dict."""
# Minimal # Minimal
self.minWordCount.setText("{0:n}".format(data.get("allWords", 0))) self.minWordCount.setText("{0:n}".format(data.get(nwStats.WORDS_ALL, 0)))
self.minCharCount.setText("{0:n}".format(data.get("allChars", 0))) self.minCharCount.setText("{0:n}".format(data.get(nwStats.CHARS_ALL, 0)))
# Maximal # Maximal
self.maxTotalWords.setText("{0:n}".format(data.get("allWords", 0))) self.maxTotalWords.setText("{0:n}".format(data.get(nwStats.WORDS_ALL, 0)))
self.maxHeadWords.setText("{0:n}".format(data.get("titleWords", 0))) self.maxHeadWords.setText("{0:n}".format(data.get(nwStats.WORDS_TITLE, 0)))
self.maxTextWords.setText("{0:n}".format(data.get("textWords", 0))) self.maxTextWords.setText("{0:n}".format(data.get(nwStats.WORDS_TITLE, 0)))
self.maxTitleCount.setText("{0:n}".format(data.get("titleCount", 0))) self.maxTitleCount.setText("{0:n}".format(data.get(nwStats.TITLES, 0)))
self.maxParCount.setText("{0:n}".format(data.get("paragraphCount", 0))) self.maxParCount.setText("{0:n}".format(data.get(nwStats.PARAGRAPHS, 0)))
self.maxTotalChars.setText("{0:n}".format(data.get("allChars", 0))) self.maxTotalChars.setText("{0:n}".format(data.get(nwStats.CHARS_ALL, 0)))
self.maxHeaderChars.setText("{0:n}".format(data.get("titleChars", 0))) self.maxHeaderChars.setText("{0:n}".format(data.get(nwStats.CHARS_TITLE, 0)))
self.maxTextChars.setText("{0:n}".format(data.get("textChars", 0))) self.maxTextChars.setText("{0:n}".format(data.get(nwStats.CHARS_TEXT, 0)))
self.maxTotalWordChars.setText("{0:n}".format(data.get("allWordChars", 0))) self.maxTotalWordChars.setText("{0:n}".format(data.get(nwStats.WCHARS_ALL, 0)))
self.maxHeadWordChars.setText("{0:n}".format(data.get("titleWordChars", 0))) self.maxHeadWordChars.setText("{0:n}".format(data.get(nwStats.WCHARS_TITLE, 0)))
self.maxTextWordChars.setText("{0:n}".format(data.get("textWordChars", 0))) self.maxTextWordChars.setText("{0:n}".format(data.get(nwStats.WCHARS_TEXT, 0)))
return return
@@ -994,37 +994,29 @@ class _StatsWidget(QWidget):
# Internal Functions # Internal Functions
## ##
def _buildMinimal(self) -> None: def _buildStatsPanel(self) -> None:
"""Build the minimal stats page.""" """Build the minimal stats page."""
mPx = CONFIG.pxInt(8) mPx = CONFIG.pxInt(8)
self.lblWordCount = QLabel(self.tr("Words"), self)
self.minWordCount = QLabel(self)
self.lblCharCount = QLabel(self.tr("Characters"), self)
self.minCharCount = QLabel(self)
# Assemble
self.minLayout = QHBoxLayout()
self.minLayout.addWidget(self.lblWordCount)
self.minLayout.addWidget(self.minWordCount)
self.minLayout.addSpacing(mPx)
self.minLayout.addWidget(self.lblCharCount)
self.minLayout.addWidget(self.minCharCount)
self.minLayout.addStretch(1)
self.minLayout.setSpacing(mPx)
self.minLayout.setContentsMargins(0, 0, 0, 0)
self.minWidget.setLayout(self.minLayout)
return
def _buildMaximal(self) -> None:
"""Build the maximal stats page."""
hPx = CONFIG.pxInt(12) hPx = CONFIG.pxInt(12)
vPx = CONFIG.pxInt(4) vPx = CONFIG.pxInt(4)
# Left Column trAllChars = trConst(nwLabels.STATS_NAME[nwStats.CHARS_ALL])
trTextChars = trConst(nwLabels.STATS_NAME[nwStats.CHARS_TEXT])
trTitleChars = trConst(nwLabels.STATS_NAME[nwStats.CHARS_TITLE])
trParagraphCount = trConst(nwLabels.STATS_NAME[nwStats.PARAGRAPHS])
trTitleCount = trConst(nwLabels.STATS_NAME[nwStats.TITLES])
trAllWordChars = trConst(nwLabels.STATS_NAME[nwStats.WCHARS_ALL])
trTextWordChars = trConst(nwLabels.STATS_NAME[nwStats.WCHARS_TEXT])
trTitleWordChars = trConst(nwLabels.STATS_NAME[nwStats.WCHARS_TITLE])
trAllWords = trConst(nwLabels.STATS_NAME[nwStats.WORDS_ALL])
trTextWords = trConst(nwLabels.STATS_NAME[nwStats.WORDS_TEXT])
trTitleWords = trConst(nwLabels.STATS_NAME[nwStats.WORDS_TITLE])
# Minimal Form
self.minWordCount = QLabel(self)
self.minCharCount = QLabel(self)
# Maximal Form, Left Column
self.maxTotalWords = QLabel(self) self.maxTotalWords = QLabel(self)
self.maxHeadWords = QLabel(self) self.maxHeadWords = QLabel(self)
self.maxTextWords = QLabel(self) self.maxTextWords = QLabel(self)
@@ -1038,20 +1030,19 @@ class _StatsWidget(QWidget):
self.maxParCount.setAlignment(QtAlignRight) self.maxParCount.setAlignment(QtAlignRight)
self.leftForm = QFormLayout() self.leftForm = QFormLayout()
self.leftForm.addRow(self.tr("Words"), self.maxTotalWords) self.leftForm.addRow(trAllWords, self.maxTotalWords)
self.leftForm.addRow(self.tr("Words in Headings"), self.maxHeadWords) self.leftForm.addRow(trTitleWords, self.maxHeadWords)
self.leftForm.addRow(self.tr("Words in Text"), self.maxTextWords) self.leftForm.addRow(trTextWords, self.maxTextWords)
self.leftForm.addRow("", QLabel(self)) self.leftForm.addRow("", QLabel(self))
self.leftForm.addRow(self.tr("Headings"), self.maxTitleCount) self.leftForm.addRow(trTitleCount, self.maxTitleCount)
self.leftForm.addRow(self.tr("Paragraphs"), self.maxParCount) self.leftForm.addRow(trParagraphCount, self.maxParCount)
self.leftForm.setHorizontalSpacing(hPx) self.leftForm.setHorizontalSpacing(hPx)
self.leftForm.setVerticalSpacing(vPx) self.leftForm.setVerticalSpacing(vPx)
# Right Column # Maximal Form, Right Column
self.maxTotalChars = QLabel(self) self.maxTotalChars = QLabel(self)
self.maxHeaderChars = QLabel(self) self.maxHeaderChars = QLabel(self)
self.maxTextChars = QLabel(self) self.maxTextChars = QLabel(self)
self.maxTotalWordChars = QLabel(self) self.maxTotalWordChars = QLabel(self)
self.maxHeadWordChars = QLabel(self) self.maxHeadWordChars = QLabel(self)
self.maxTextWordChars = QLabel(self) self.maxTextWordChars = QLabel(self)
@@ -1059,22 +1050,31 @@ class _StatsWidget(QWidget):
self.maxTotalChars.setAlignment(QtAlignRight) self.maxTotalChars.setAlignment(QtAlignRight)
self.maxHeaderChars.setAlignment(QtAlignRight) self.maxHeaderChars.setAlignment(QtAlignRight)
self.maxTextChars.setAlignment(QtAlignRight) self.maxTextChars.setAlignment(QtAlignRight)
self.maxTotalWordChars.setAlignment(QtAlignRight) self.maxTotalWordChars.setAlignment(QtAlignRight)
self.maxHeadWordChars.setAlignment(QtAlignRight) self.maxHeadWordChars.setAlignment(QtAlignRight)
self.maxTextWordChars.setAlignment(QtAlignRight) self.maxTextWordChars.setAlignment(QtAlignRight)
self.rightForm = QFormLayout() self.rightForm = QFormLayout()
self.rightForm.addRow(self.tr("Characters"), self.maxTotalChars) self.rightForm.addRow(trAllChars, self.maxTotalChars)
self.rightForm.addRow(self.tr("Characters in Headings"), self.maxHeaderChars) self.rightForm.addRow(trTitleChars, self.maxHeaderChars)
self.rightForm.addRow(self.tr("Characters in Text"), self.maxTextChars) self.rightForm.addRow(trTextChars, self.maxTextChars)
self.rightForm.addRow(self.tr("Characters, No Spaces"), self.maxTotalWordChars) self.rightForm.addRow(trAllWordChars, self.maxTotalWordChars)
self.rightForm.addRow(self.tr("Characters in Headings, No Spaces"), self.maxHeadWordChars) self.rightForm.addRow(trTitleWordChars, self.maxHeadWordChars)
self.rightForm.addRow(self.tr("Characters in Text, No Spaces"), self.maxTextWordChars) self.rightForm.addRow(trTextWordChars, self.maxTextWordChars)
self.rightForm.setHorizontalSpacing(hPx) self.rightForm.setHorizontalSpacing(hPx)
self.rightForm.setVerticalSpacing(vPx) self.rightForm.setVerticalSpacing(vPx)
# Assemble # Assemble
self.minLayout = QHBoxLayout()
self.minLayout.addWidget(QLabel(trAllWords, self))
self.minLayout.addWidget(self.minWordCount)
self.minLayout.addSpacing(mPx)
self.minLayout.addWidget(QLabel(trAllChars, self))
self.minLayout.addWidget(self.minCharCount)
self.minLayout.addStretch(1)
self.minLayout.setSpacing(mPx)
self.minLayout.setContentsMargins(0, 0, 0, 0)
self.maxLayout = QHBoxLayout() self.maxLayout = QHBoxLayout()
self.maxLayout.addLayout(self.leftForm) self.maxLayout.addLayout(self.leftForm)
self.maxLayout.addLayout(self.rightForm) self.maxLayout.addLayout(self.rightForm)
@@ -1082,6 +1082,7 @@ class _StatsWidget(QWidget):
self.maxLayout.setSpacing(CONFIG.pxInt(32)) self.maxLayout.setSpacing(CONFIG.pxInt(32))
self.maxLayout.setContentsMargins(0, 0, 0, 0) self.maxLayout.setContentsMargins(0, 0, 0, 0)
self.minWidget.setLayout(self.minLayout)
self.maxWidget.setLayout(self.maxLayout) self.maxWidget.setLayout(self.maxLayout)
return return