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_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_SV = r"(?i)(?<!\\)(\[(?:footnote):)(.+?)(?<!\\)(\])"
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote|field):)(.+?)(?<!\\)(\])"
class nwShortcode:
@@ -95,6 +95,8 @@ class nwShortcode:
nwComment.COMMENT: "[comment:{0}]",
}
FIELD_VALUE = "[field:{0}]"
class nwStyles:
@@ -159,11 +161,14 @@ class nwKeyWords:
STORY_KEY = "@story"
MENTION_KEY = "@mention"
# Set of Valid Keys
VALID_KEYS = {
# Note: The order here affects the order of menu entries
ALL_KEYS = [
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_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
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_NAME = {
@@ -253,6 +281,20 @@ class nwLabels:
nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
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 = {
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
@@ -274,6 +316,19 @@ class nwLabels:
nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
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 = {
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
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.common import checkInt, numberToRoman
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.project import NWProject
@@ -948,20 +949,20 @@ class Tokenizer(ABC):
def countStats(self) -> None:
"""Count stats on the tokenized text."""
titleCount = self._counts.get("titleCount", 0)
paragraphCount = self._counts.get("paragraphCount", 0)
titleCount = self._counts.get(nwStats.TITLES, 0)
paragraphCount = self._counts.get(nwStats.PARAGRAPHS, 0)
allWords = self._counts.get("allWords", 0)
textWords = self._counts.get("textWords", 0)
titleWords = self._counts.get("titleWords", 0)
allWords = self._counts.get(nwStats.WORDS_ALL, 0)
textWords = self._counts.get(nwStats.WORDS_TEXT, 0)
titleWords = self._counts.get(nwStats.WORDS_TITLE, 0)
allChars = self._counts.get("allChars", 0)
textChars = self._counts.get("textChars", 0)
titleChars = self._counts.get("titleChars", 0)
allChars = self._counts.get(nwStats.CHARS_ALL, 0)
textChars = self._counts.get(nwStats.CHARS_TEXT, 0)
titleChars = self._counts.get(nwStats.CHARS_TITLE, 0)
allWordChars = self._counts.get("allWordChars", 0)
textWordChars = self._counts.get("textWordChars", 0)
titleWordChars = self._counts.get("titleWordChars", 0)
allWordChars = self._counts.get(nwStats.WCHARS_ALL, 0)
textWordChars = self._counts.get(nwStats.WCHARS_TEXT, 0)
titleWordChars = self._counts.get(nwStats.WCHARS_TITLE, 0)
for tType, _, tText, _, _ in self._blocks:
tText = tText.replace(nwUnicode.U_ENDASH, " ")
@@ -1006,20 +1007,20 @@ class Tokenizer(ABC):
allChars += len(tText)
allWordChars += len("".join(words))
self._counts["titleCount"] = titleCount
self._counts["paragraphCount"] = paragraphCount
self._counts[nwStats.TITLES] = titleCount
self._counts[nwStats.PARAGRAPHS] = paragraphCount
self._counts["allWords"] = allWords
self._counts["textWords"] = textWords
self._counts["titleWords"] = titleWords
self._counts[nwStats.WORDS_ALL] = allWords
self._counts[nwStats.WORDS_TEXT] = textWords
self._counts[nwStats.WORDS_TITLE] = titleWords
self._counts["allChars"] = allChars
self._counts["textChars"] = textChars
self._counts["titleChars"] = titleChars
self._counts[nwStats.CHARS_ALL] = allChars
self._counts[nwStats.CHARS_TEXT] = textChars
self._counts[nwStats.CHARS_TITLE] = titleChars
self._counts["allWordChars"] = allWordChars
self._counts["textWordChars"] = textWordChars
self._counts["titleWordChars"] = titleWordChars
self._counts[nwStats.WCHARS_ALL] = allWordChars
self._counts[nwStats.WCHARS_TEXT] = textWordChars
self._counts[nwStats.WCHARS_TITLE] = titleWordChars
return
+1
View File
@@ -523,6 +523,7 @@ class TextBlockData(QTextBlockUserData):
self._metaData.append((s, e, res.group(0), "url"))
self._text = text
self._offset = offset
return
+56 -55
View File
@@ -43,6 +43,7 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED
from novelwriter.common import fuzzyTime
from novelwriter.constants import nwLabels, nwStats, trConst
from novelwriter.core.buildsettings import BuildCollection, BuildSettings
from novelwriter.core.docbuild import NWBuildDocument
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.toggled.connect(self._toggleView)
self._buildMinimal()
self._buildMaximal()
self._buildStatsPanel()
self.mainStack = QStackedWidget(self)
self.mainStack.addWidget(self.minWidget)
@@ -949,23 +949,23 @@ class _StatsWidget(QWidget):
def updateStats(self, data: dict[str, int]) -> None:
"""Update the stats values from a Tokenizer stats dict."""
# Minimal
self.minWordCount.setText("{0:n}".format(data.get("allWords", 0)))
self.minCharCount.setText("{0:n}".format(data.get("allChars", 0)))
self.minWordCount.setText("{0:n}".format(data.get(nwStats.WORDS_ALL, 0)))
self.minCharCount.setText("{0:n}".format(data.get(nwStats.CHARS_ALL, 0)))
# Maximal
self.maxTotalWords.setText("{0:n}".format(data.get("allWords", 0)))
self.maxHeadWords.setText("{0:n}".format(data.get("titleWords", 0)))
self.maxTextWords.setText("{0:n}".format(data.get("textWords", 0)))
self.maxTitleCount.setText("{0:n}".format(data.get("titleCount", 0)))
self.maxParCount.setText("{0:n}".format(data.get("paragraphCount", 0)))
self.maxTotalWords.setText("{0:n}".format(data.get(nwStats.WORDS_ALL, 0)))
self.maxHeadWords.setText("{0:n}".format(data.get(nwStats.WORDS_TITLE, 0)))
self.maxTextWords.setText("{0:n}".format(data.get(nwStats.WORDS_TITLE, 0)))
self.maxTitleCount.setText("{0:n}".format(data.get(nwStats.TITLES, 0)))
self.maxParCount.setText("{0:n}".format(data.get(nwStats.PARAGRAPHS, 0)))
self.maxTotalChars.setText("{0:n}".format(data.get("allChars", 0)))
self.maxHeaderChars.setText("{0:n}".format(data.get("titleChars", 0)))
self.maxTextChars.setText("{0:n}".format(data.get("textChars", 0)))
self.maxTotalChars.setText("{0:n}".format(data.get(nwStats.CHARS_ALL, 0)))
self.maxHeaderChars.setText("{0:n}".format(data.get(nwStats.CHARS_TITLE, 0)))
self.maxTextChars.setText("{0:n}".format(data.get(nwStats.CHARS_TEXT, 0)))
self.maxTotalWordChars.setText("{0:n}".format(data.get("allWordChars", 0)))
self.maxHeadWordChars.setText("{0:n}".format(data.get("titleWordChars", 0)))
self.maxTextWordChars.setText("{0:n}".format(data.get("textWordChars", 0)))
self.maxTotalWordChars.setText("{0:n}".format(data.get(nwStats.WCHARS_ALL, 0)))
self.maxHeadWordChars.setText("{0:n}".format(data.get(nwStats.WCHARS_TITLE, 0)))
self.maxTextWordChars.setText("{0:n}".format(data.get(nwStats.WCHARS_TEXT, 0)))
return
@@ -994,37 +994,29 @@ class _StatsWidget(QWidget):
# Internal Functions
##
def _buildMinimal(self) -> None:
def _buildStatsPanel(self) -> None:
"""Build the minimal stats page."""
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)
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.maxHeadWords = QLabel(self)
self.maxTextWords = QLabel(self)
@@ -1038,20 +1030,19 @@ class _StatsWidget(QWidget):
self.maxParCount.setAlignment(QtAlignRight)
self.leftForm = QFormLayout()
self.leftForm.addRow(self.tr("Words"), self.maxTotalWords)
self.leftForm.addRow(self.tr("Words in Headings"), self.maxHeadWords)
self.leftForm.addRow(self.tr("Words in Text"), self.maxTextWords)
self.leftForm.addRow(trAllWords, self.maxTotalWords)
self.leftForm.addRow(trTitleWords, self.maxHeadWords)
self.leftForm.addRow(trTextWords, self.maxTextWords)
self.leftForm.addRow("", QLabel(self))
self.leftForm.addRow(self.tr("Headings"), self.maxTitleCount)
self.leftForm.addRow(self.tr("Paragraphs"), self.maxParCount)
self.leftForm.addRow(trTitleCount, self.maxTitleCount)
self.leftForm.addRow(trParagraphCount, self.maxParCount)
self.leftForm.setHorizontalSpacing(hPx)
self.leftForm.setVerticalSpacing(vPx)
# Right Column
# Maximal Form, Right Column
self.maxTotalChars = QLabel(self)
self.maxHeaderChars = QLabel(self)
self.maxTextChars = QLabel(self)
self.maxTotalWordChars = QLabel(self)
self.maxHeadWordChars = QLabel(self)
self.maxTextWordChars = QLabel(self)
@@ -1059,22 +1050,31 @@ class _StatsWidget(QWidget):
self.maxTotalChars.setAlignment(QtAlignRight)
self.maxHeaderChars.setAlignment(QtAlignRight)
self.maxTextChars.setAlignment(QtAlignRight)
self.maxTotalWordChars.setAlignment(QtAlignRight)
self.maxHeadWordChars.setAlignment(QtAlignRight)
self.maxTextWordChars.setAlignment(QtAlignRight)
self.rightForm = QFormLayout()
self.rightForm.addRow(self.tr("Characters"), self.maxTotalChars)
self.rightForm.addRow(self.tr("Characters in Headings"), self.maxHeaderChars)
self.rightForm.addRow(self.tr("Characters in Text"), self.maxTextChars)
self.rightForm.addRow(self.tr("Characters, No Spaces"), self.maxTotalWordChars)
self.rightForm.addRow(self.tr("Characters in Headings, No Spaces"), self.maxHeadWordChars)
self.rightForm.addRow(self.tr("Characters in Text, No Spaces"), self.maxTextWordChars)
self.rightForm.addRow(trAllChars, self.maxTotalChars)
self.rightForm.addRow(trTitleChars, self.maxHeaderChars)
self.rightForm.addRow(trTextChars, self.maxTextChars)
self.rightForm.addRow(trAllWordChars, self.maxTotalWordChars)
self.rightForm.addRow(trTitleWordChars, self.maxHeadWordChars)
self.rightForm.addRow(trTextWordChars, self.maxTextWordChars)
self.rightForm.setHorizontalSpacing(hPx)
self.rightForm.setVerticalSpacing(vPx)
# 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.addLayout(self.leftForm)
self.maxLayout.addLayout(self.rightForm)
@@ -1082,6 +1082,7 @@ class _StatsWidget(QWidget):
self.maxLayout.setSpacing(CONFIG.pxInt(32))
self.maxLayout.setContentsMargins(0, 0, 0, 0)
self.minWidget.setLayout(self.minLayout)
self.maxWidget.setLayout(self.maxLayout)
return