Fix linting errors in the main code

This commit is contained in:
Veronica Berglyd Olsen
2025-04-07 17:18:43 +02:00
parent dab48d43a5
commit 8ed3bd889d
66 changed files with 434 additions and 344 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ import os
import sys
try:
import PyQt6.QtCore # noqa: F401
import PyQt6.QtGui # noqa: F401
import PyQt6.QtCore
import PyQt6.QtGui
import PyQt6.QtWidgets # noqa: F401
except Exception:
print("ERROR: Failed to load dependency PyQt6")
+2 -2
View File
@@ -36,7 +36,7 @@ from novelwriter.config import Config
from novelwriter.error import exceptionHandler
from novelwriter.shared import SharedData
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
# Package Meta
@@ -148,7 +148,7 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt)
except getopt.GetoptError as exc:
print(helpMsg)
print(f"ERROR: {str(exc)}")
print(f"ERROR: {exc!s}")
sys.exit(2)
if len(inRemain) > 0:
+11 -9
View File
@@ -29,11 +29,10 @@ import unicodedata
import uuid
import xml.etree.ElementTree as ET
from collections.abc import Callable
from configparser import ConfigParser
from datetime import datetime
from pathlib import Path
from typing import Any, Literal, TypeGuard, TypeVar
from typing import TYPE_CHECKING, Any, Literal, TypeGuard, TypeVar
from urllib.parse import urljoin
from urllib.request import pathname2url
@@ -45,6 +44,9 @@ from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
if TYPE_CHECKING:
from collections.abc import Callable
logger = logging.getLogger(__name__)
_Type = TypeVar("_Type")
@@ -347,7 +349,7 @@ def fuzzyTime(seconds: int) -> str:
elif seconds < 3300: # 55 minutes
return QCoreApplication.translate(
"Common", "{0} minutes ago"
).format(int(round(seconds/60)))
).format(round(seconds/60))
elif seconds < 5400: # 90 minutes
return QCoreApplication.translate(
"Common", "an hour ago"
@@ -355,7 +357,7 @@ def fuzzyTime(seconds: int) -> str:
elif seconds < 84600: # 23.5 hours
return QCoreApplication.translate(
"Common", "{0} hours ago"
).format(int(round(seconds/3600)))
).format(round(seconds/3600))
elif seconds < 129600: # 1.5 days
return QCoreApplication.translate(
"Common", "a day ago"
@@ -363,7 +365,7 @@ def fuzzyTime(seconds: int) -> str:
elif seconds < 561600: # 6.5 days
return QCoreApplication.translate(
"Common", "{0} days ago"
).format(int(round(seconds/86400)))
).format(round(seconds/86400))
elif seconds < 907200: # 10.5 days
return QCoreApplication.translate(
"Common", "a week ago"
@@ -371,7 +373,7 @@ def fuzzyTime(seconds: int) -> str:
elif seconds < 2419200: # 28 days
return QCoreApplication.translate(
"Common", "{0} weeks ago"
).format(int(round(seconds/604800)))
).format(round(seconds/604800))
elif seconds < 3888000: # 45 days
return QCoreApplication.translate(
"Common", "a month ago"
@@ -379,7 +381,7 @@ def fuzzyTime(seconds: int) -> str:
elif seconds < 29808000: # 345 days
return QCoreApplication.translate(
"Common", "{0} months ago"
).format(int(round(seconds/2592000)))
).format(round(seconds/2592000))
elif seconds < 47336400: # 1.5 years
return QCoreApplication.translate(
"Common", "a year ago"
@@ -387,7 +389,7 @@ def fuzzyTime(seconds: int) -> str:
else:
return QCoreApplication.translate(
"Common", "{0} years ago"
).format(int(round(seconds/31557600)))
).format(round(seconds/31557600))
def numberToRoman(value: int, toLower: bool = False) -> str:
@@ -423,7 +425,7 @@ def describeFont(font: QFont) -> str:
info = QFontInfo(font)
family = info.family()
styles = [v for v in info.styleName().split() if v not in family]
return " ".join([f"{info.pointSize()} pt", family] + styles)
return " ".join([f"{info.pointSize()} pt", family, *styles])
return "Error"
+30 -27
View File
@@ -29,10 +29,9 @@ import json
import logging
import sys
from datetime import datetime
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Final
from PyQt6.QtCore import (
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
@@ -48,7 +47,9 @@ from novelwriter.common import (
from novelwriter.constants import nwFiles, nwUnicode
from novelwriter.error import formatException, logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from datetime import datetime
from novelwriter.core.projectdata import NWProjectData
logger = logging.getLogger(__name__)
@@ -62,29 +63,27 @@ DEF_TREECOL = "theme"
class Config:
__slots__ = (
"_confPath", "_dataPath", "_homePath", "_backPath", "_appPath", "_appRoot", "_hasError",
"_errData", "_nwLangPath", "_qtLangPath", "_qLocale", "_dLocale", "_dShortDate",
"_dShortDateTime", "_qtTrans", "_manuals", "_recentProjects", "_recentPaths",
"_backupPath",
"appName", "appHandle", "guiLocale", "guiTheme", "guiSyntax", "guiFont", "hideVScroll",
"hideHScroll", "lastNotes", "nativeFont", "useCharCount", "iconTheme", "iconColTree",
"iconColDocs", "mainWinSize", "welcomeWinSize", "prefsWinSize", "mainPanePos",
"viewPanePos", "outlinePanePos", "autoSaveProj", "autoSaveDoc", "emphLabels",
"backupOnClose", "askBeforeBackup", "askBeforeExit", "textFont", "textWidth", "textMargin",
"tabWidth", "cursorWidth", "focusWidth", "hideFocusFooter", "showFullPath", "autoSelect",
"doJustify", "showTabsNSpaces", "showLineEndings", "showMultiSpaces", "doReplace",
"doReplaceSQuote", "doReplaceDQuote", "doReplaceDash", "doReplaceDots", "autoScroll",
"autoScrollPos", "scrollPastEnd", "dialogStyle", "allowOpenDial", "dialogLine",
"narratorBreak", "narratorDialog", "altDialogOpen", "altDialogClose", "highlightEmph",
"stopWhenIdle", "userIdleTime", "incNotesWCount", "fmtApostrophe", "fmtSQuoteOpen",
"fmtSQuoteClose", "fmtDQuoteOpen", "fmtDQuoteClose", "fmtPadBefore", "fmtPadAfter",
"fmtPadThin", "spellLanguage", "showViewerPanel", "showEditToolBar", "showSessionTime",
"viewComments", "viewSynopsis", "searchCase", "searchWord", "searchRegEx", "searchLoop",
"searchNextFile", "searchMatchCap", "searchProjCase", "searchProjWord", "searchProjRegEx",
"verQtString", "verQtValue", "verPyQtString", "verPyQtValue", "verPyString", "osType",
"osLinux", "osWindows", "osDarwin", "osUnknown", "hostName", "kernelVer", "isDebug",
"memInfo", "hasEnchant",
"_appPath", "_appRoot", "_backPath", "_backupPath", "_confPath", "_dLocale", "_dShortDate",
"_dShortDateTime", "_dataPath", "_errData", "_hasError", "_homePath", "_manuals",
"_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths", "_recentProjects",
"allowOpenDial", "altDialogClose", "altDialogOpen", "appHandle", "appName",
"askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj", "autoScroll",
"autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine", "dialogStyle",
"doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash", "doReplaceDots",
"doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose", "fmtDQuoteOpen",
"fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose", "fmtSQuoteOpen",
"focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme", "hasEnchant",
"hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
"iconColDocs", "iconColTree", "iconTheme", "incNotesWCount", "isDebug", "kernelVer",
"lastNotes", "mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog",
"nativeFont", "osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos",
"prefsWinSize", "scrollPastEnd", "searchCase", "searchLoop", "searchMatchCap",
"searchNextFile", "searchProjCase", "searchProjRegEx", "searchProjWord", "searchRegEx",
"searchWord", "showEditToolBar", "showFullPath", "showLineEndings", "showMultiSpaces",
"showSessionTime", "showTabsNSpaces", "showViewerPanel", "spellLanguage", "stopWhenIdle",
"tabWidth", "textFont", "textMargin", "textWidth", "useCharCount", "userIdleTime",
"verPyQtString", "verPyQtValue", "verPyString", "verQtString", "verQtValue",
"viewComments", "viewPanePos", "viewSynopsis", "welcomeWinSize",
)
LANG_NW = 1
@@ -306,6 +305,10 @@ class Config:
def pdfDocs(self) -> Path | None:
return self._manuals.get(f"manual_{self.locale.name()}", self._manuals.get("manual"))
@property
def nwLangPath(self) -> Path:
return self._nwLangPath
@property
def locale(self) -> QLocale:
return self._dLocale
@@ -918,7 +921,7 @@ class RecentProjects:
class RecentPaths:
KEYS = ["default", "project", "import", "outline", "stats"]
KEYS: Final[list[str]] = ["default", "project", "import", "outline", "stats"]
def __init__(self, config: Config) -> None:
self._conf = config
+37 -35
View File
@@ -23,6 +23,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from typing import Final
from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
from novelwriter.enum import (
@@ -99,7 +101,7 @@ class nwShortcode:
FOOTNOTE_B = "[footnote:"
FIELD_B = "[field:"
COMMENT_STYLES = {
COMMENT_STYLES: Final[dict[nwComment, str]] = {
nwComment.FOOTNOTE: "[footnote:{0}]",
nwComment.COMMENT: "[comment:{0}]",
}
@@ -110,13 +112,13 @@ class nwShortcode:
class nwStyles:
H_VALID = ("H0", "H1", "H2", "H3", "H4")
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
H_SIZES = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
H_LEVEL: Final[dict[str, int]] = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
H_SIZES: Final[dict[int, float]] = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
T_NORMAL = 1.0
T_SMALL = 0.8
T_LABEL = {
T_LABEL: Final[dict[str, str]] = {
"H0": QT_TRANSLATE_NOOP("Constant", "Title"),
"H1": QT_TRANSLATE_NOOP("Constant", "Heading 1 (Partition)"),
"H2": QT_TRANSLATE_NOOP("Constant", "Heading 2 (Chapter)"),
@@ -125,7 +127,7 @@ class nwStyles:
"TT": QT_TRANSLATE_NOOP("Constant", "Text Paragraph"),
"SP": QT_TRANSLATE_NOOP("Constant", "Scene Separator"),
}
T_MARGIN = {
T_MARGIN: Final[dict[str, tuple[float, float]]] = {
"H0": (1.50, 0.60), # Title margins (top, bottom)
"H1": (1.50, 0.60), # Heading 1 margins (top, bottom)
"H2": (1.50, 0.60), # Heading 2 margins (top, bottom)
@@ -174,20 +176,20 @@ class nwKeyWords:
MENTION_KEY = "@mention"
# Note: The order here affects the order of menu entries
ALL_KEYS = [
ALL_KEYS: Final[list[str]] = [
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
]
CAN_CREATE = [
CAN_CREATE: Final[list[str]] = [
POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY,
]
# Set of Valid Keys
VALID_KEYS = set(ALL_KEYS)
VALID_KEYS: Final[set[str]] = set(ALL_KEYS)
# Map from Keys to Item Class
KEY_CLASS = {
KEY_CLASS: Final[dict[str, nwItemClass]] = {
POV_KEY: nwItemClass.CHARACTER,
FOCUS_KEY: nwItemClass.CHARACTER,
CHAR_KEY: nwItemClass.CHARACTER,
@@ -203,7 +205,7 @@ class nwKeyWords:
class nwLists:
USER_CLASSES = [
USER_CLASSES: Final[list[nwItemClass]] = [
nwItemClass.CHARACTER,
nwItemClass.PLOT,
nwItemClass.WORLD,
@@ -229,7 +231,7 @@ class nwStats:
WORDS_TITLE = "titleWords"
# Note: The order here affects the order of menu entries
ALL_FIELDS = [
ALL_FIELDS: Final[list[str]] = [
WORDS, WORDS_TEXT, WORDS_TITLE,
CHARS, CHARS_TEXT, CHARS_TITLE,
WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
@@ -239,7 +241,7 @@ class nwStats:
class nwLabels:
CLASS_NAME = {
CLASS_NAME: Final[dict[nwItemClass, str]] = {
nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
nwItemClass.NOVEL: QT_TRANSLATE_NOOP("Constant", "Novel"),
nwItemClass.PLOT: QT_TRANSLATE_NOOP("Constant", "Plot"),
@@ -253,7 +255,7 @@ class nwLabels:
nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
}
CLASS_ICON = {
CLASS_ICON: Final[dict[nwItemClass, str]] = {
nwItemClass.NO_CLASS: "cls_none",
nwItemClass.NOVEL: "cls_novel",
nwItemClass.PLOT: "cls_plot",
@@ -267,12 +269,12 @@ class nwLabels:
nwItemClass.TEMPLATE: "cls_template",
nwItemClass.TRASH: "cls_trash",
}
LAYOUT_NAME = {
LAYOUT_NAME: Final[dict[nwItemLayout, str]] = {
nwItemLayout.NO_LAYOUT: QT_TRANSLATE_NOOP("Constant", "None"),
nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"),
nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Project Note"),
}
ITEM_DESCRIPTION = {
ITEM_DESCRIPTION: Final[dict[str, str]] = {
"none": QT_TRANSLATE_NOOP("Constant", "None"),
"root": QT_TRANSLATE_NOOP("Constant", "Root Folder"),
"folder": QT_TRANSLATE_NOOP("Constant", "Folder"),
@@ -283,11 +285,11 @@ class nwLabels:
"doc_h4": QT_TRANSLATE_NOOP("Constant", "Novel Section"),
"note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
}
ACTIVE_NAME = {
ACTIVE_NAME: Final[dict[str, str]] = {
"checked": QT_TRANSLATE_NOOP("Constant", "Active"),
"unchecked": QT_TRANSLATE_NOOP("Constant", "Inactive"),
}
KEY_NAME = {
KEY_NAME: Final[dict[str, str]] = {
nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"),
nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"),
nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"),
@@ -301,7 +303,7 @@ class nwLabels:
nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"),
}
KEY_SHORTCUT = {
KEY_SHORTCUT: Final[dict[str, str]] = {
nwKeyWords.TAG_KEY: "Ctrl+K, G",
nwKeyWords.POV_KEY: "Ctrl+K, V",
nwKeyWords.FOCUS_KEY: "Ctrl+K, F",
@@ -315,7 +317,7 @@ class nwLabels:
nwKeyWords.STORY_KEY: "Ctrl+K, N",
nwKeyWords.MENTION_KEY: "Ctrl+K, M",
}
OUTLINE_COLS = {
OUTLINE_COLS: Final[dict[nwOutline, str]] = {
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"),
@@ -337,7 +339,7 @@ class nwLabels:
nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
}
STATS_NAME = {
STATS_NAME: Final[dict[str, str]] = {
nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters"),
nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Stats", "Characters in Text"),
nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Stats", "Characters in Headings"),
@@ -350,7 +352,7 @@ class nwLabels:
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
}
BUILD_FMT = {
BUILD_FMT: Final[dict[nwBuildFmt, str]] = {
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
nwBuildFmt.DOCX: QT_TRANSLATE_NOOP("Constant", "Microsoft Word Document (.docx)"),
@@ -362,7 +364,7 @@ class nwLabels:
nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + HTML 5 (.json)"),
nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
}
BUILD_EXT = {
BUILD_EXT: Final[dict[nwBuildFmt, str]] = {
nwBuildFmt.ODT: ".odt",
nwBuildFmt.FODT: ".fodt",
nwBuildFmt.DOCX: ".docx",
@@ -374,7 +376,7 @@ class nwLabels:
nwBuildFmt.J_HTML: ".json",
nwBuildFmt.J_NWD: ".json",
}
SHAPES_PLAIN = {
SHAPES_PLAIN: Final[dict[nwStatusShape, str]] = {
nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
@@ -384,42 +386,42 @@ class nwLabels:
nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
}
SHAPES_CIRCLE = {
SHAPES_CIRCLE: Final[dict[nwStatusShape, str]] = {
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
}
SHAPES_BARS = {
SHAPES_BARS: Final[dict[nwStatusShape, str]] = {
nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
}
SHAPES_BLOCKS = {
SHAPES_BLOCKS: Final[dict[nwStatusShape, str]] = {
nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
}
FILE_FILTERS = {
FILE_FILTERS: Final[dict[str, str]] = {
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
"*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
}
UNIT_NAME = {
UNIT_NAME: Final[dict[str, str]] = {
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
"in": QT_TRANSLATE_NOOP("Constant", "Inches"),
}
UNIT_SCALE = {
UNIT_SCALE: Final[dict[str, float]] = {
"mm": 1.0,
"cm": 10.0,
"in": 25.4,
}
PAPER_NAME = {
PAPER_NAME: Final[dict[str, str]] = {
"A4": QT_TRANSLATE_NOOP("Constant", "A4"),
"A5": QT_TRANSLATE_NOOP("Constant", "A5"),
"A6": QT_TRANSLATE_NOOP("Constant", "A6"),
@@ -427,7 +429,7 @@ class nwLabels:
"Letter": QT_TRANSLATE_NOOP("Constant", "US Letter"),
"Custom": QT_TRANSLATE_NOOP("Constant", "Custom"),
}
PAPER_SIZE = {
PAPER_SIZE: Final[dict[str, tuple[float, float]]] = {
"A4": (210.0, 297.0),
"A5": (148.0, 210.0),
"A6": (105.0, 148.0),
@@ -435,7 +437,7 @@ class nwLabels:
"Letter": (215.9, 279.4),
"Custom": (-1.0, -1.0),
}
THEME_COLORS = {
THEME_COLORS: Final[dict[str, str]] = {
"theme": QT_TRANSLATE_NOOP("Constant", "Theme Colours"),
"default": QT_TRANSLATE_NOOP("Constant", "Foreground Colour"),
"faded": QT_TRANSLATE_NOOP("Constant", "Faded Colour"),
@@ -462,7 +464,7 @@ class nwHeadFmt:
CHAR_POV = "{Char:POV}"
CHAR_FOCUS = "{Char:Focus}"
PAGE_HEADERS = [
PAGE_HEADERS: Final[list[str]] = [
TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
CHAR_POV, CHAR_FOCUS
]
@@ -478,7 +480,7 @@ class nwQuotes:
"""Allowed quotation marks.
Source: https://en.wikipedia.org/wiki/Quotation_mark
"""
SYMBOLS = {
SYMBOLS: Final[dict[str, str]] = {
"\u0027": QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
"\u0022": QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
@@ -645,7 +647,7 @@ class nwUnicode:
class nwHtmlUnicode:
U_TO_H = {
U_TO_H: Final[dict[str, str]] = {
# Quotes
nwUnicode.U_QUOT: nwUnicode.H_QUOT,
nwUnicode.U_APOS: nwUnicode.H_APOS,
+12 -9
View File
@@ -28,9 +28,9 @@ import json
import logging
import uuid
from collections.abc import Iterable
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING
from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
@@ -41,6 +41,9 @@ from novelwriter.core.project import NWProject
from novelwriter.enum import nwBuildFmt
from novelwriter.error import logException
if TYPE_CHECKING:
from collections.abc import Iterable
logger = logging.getLogger(__name__)
T_BuildValue = str | int | float | bool
@@ -227,9 +230,9 @@ class BuildSettings:
@classmethod
def fromDict(cls, data: dict) -> BuildSettings:
"""Create a build settings object from a dict."""
cls = BuildSettings()
cls.unpack(data)
return cls
new = cls()
new.unpack(data)
return new
##
# Properties
@@ -514,11 +517,11 @@ class BuildSettings:
@classmethod
def duplicate(cls, source: BuildSettings) -> BuildSettings:
"""Make a copy of another build."""
cls = BuildSettings()
cls.unpack(source.pack())
cls._uuid = str(uuid.uuid4())
cls._name = f"{source.name} 2"
return cls
new = cls()
new.unpack(source.pack())
new._uuid = str(uuid.uuid4())
new._name = f"{source.name} 2"
return new
class BuildCollection:
+8 -4
View File
@@ -30,9 +30,9 @@ import logging
import re
import shutil
from collections.abc import Iterable
from functools import partial
from pathlib import Path
from typing import TYPE_CHECKING
from zipfile import ZipFile, is_zipfile
from PyQt6.QtCore import QCoreApplication
@@ -40,10 +40,14 @@ from PyQt6.QtCore import QCoreApplication
from novelwriter import CONFIG, SHARED
from novelwriter.common import isHandle, minmax, simplified
from novelwriter.constants import nwConst, nwFiles, nwItemClass, nwStats
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
from novelwriter.core.storage import NWStorageCreate
if TYPE_CHECKING:
from collections.abc import Iterable
from novelwriter.core.item import NWItem
logger = logging.getLogger(__name__)
@@ -284,7 +288,7 @@ class DocDuplicator:
class DocSearch:
def __init__(self) -> None:
self._regEx = re.compile("")
self._regEx = re.compile(r"")
self._opts = re.UNICODE | re.IGNORECASE
self._words = False
self._escape = True
@@ -381,7 +385,7 @@ class ProjectBuilder:
path = data.get("path", None) or None
if isinstance(path, str | Path):
self._path = Path(path).resolve()
if data.get("sample", False):
if data.get("sample"):
return self._extractSampleProject(self._path)
elif data.get("template"):
return self._copyProject(self._path, data)
+11 -7
View File
@@ -25,26 +25,30 @@ from __future__ import annotations
import logging
from collections.abc import Iterable
from pathlib import Path
from typing import TYPE_CHECKING
from PyQt6.QtGui import QFont
from novelwriter import CONFIG
from novelwriter.constants import nwLabels
from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
from novelwriter.enum import nwBuildFmt
from novelwriter.error import formatException, logException
from novelwriter.formats.todocx import ToDocX
from novelwriter.formats.tohtml import ToHtml
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.formats.tomarkdown import ToMarkdown
from novelwriter.formats.toodt import ToOdt
from novelwriter.formats.toqdoc import ToQTextDocument
from novelwriter.formats.toraw import ToRaw
if TYPE_CHECKING:
from collections.abc import Iterable
from pathlib import Path
from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import Tokenizer
logger = logging.getLogger(__name__)
@@ -56,8 +60,8 @@ class NWBuildDocument:
"""
__slots__ = (
"_project", "_build", "_queue", "_error", "_cache", "_count",
"_outline",
"_build", "_cache", "_count", "_error", "_outline", "_project",
"_queue",
)
def __init__(self, project: NWProject, build: BuildSettings) -> None:
+2 -2
View File
@@ -31,11 +31,11 @@ from time import time
from typing import TYPE_CHECKING
from novelwriter.common import formatTimeStamp, isHandle
from novelwriter.core.item import NWItem
from novelwriter.enum import nwItemClass, nwItemLayout
from novelwriter.error import formatException, logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+4 -3
View File
@@ -29,7 +29,6 @@ import json
import logging
import random
from collections.abc import ItemsView, Iterable
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
@@ -44,7 +43,9 @@ from novelwriter.error import logException
from novelwriter.text.comments import processComment
from novelwriter.text.counting import standardCounter
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from collections.abc import ItemsView, Iterable
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
@@ -906,7 +907,7 @@ class ItemIndex:
IndexHeading object for each heading of the text.
"""
__slots__ = ("_project", "_tags", "_items")
__slots__ = ("_items", "_project", "_tags")
def __init__(self, project: NWProject, tagsIndex: TagsIndex) -> None:
self._project = project
+6 -5
View File
@@ -28,14 +28,15 @@ from __future__ import annotations
import logging
from collections.abc import ItemsView, Sequence
from typing import TYPE_CHECKING, Literal
from novelwriter import CONFIG
from novelwriter.common import checkInt, isListInstance, isTitleTag
from novelwriter.constants import nwKeyWords, nwStyles
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from collections.abc import ItemsView, Sequence
from novelwriter.core.index import TagsIndex
from novelwriter.core.item import NWItem
@@ -57,7 +58,7 @@ class IndexNode:
must be reset each time the item is re-indexed.
"""
__slots__ = ("_tags", "_handle", "_item", "_headings", "_notes", "_count")
__slots__ = ("_count", "_handle", "_headings", "_item", "_notes", "_tags")
def __init__(self, tagsIndex: TagsIndex, tHandle: str, nwItem: NWItem) -> None:
self._tags = tagsIndex
@@ -205,8 +206,8 @@ class IndexHeading:
"""
__slots__ = (
"_tags", "_key", "_line", "_level", "_title",
"_counts", "_tag", "_refs", "_comments",
"_comments", "_counts", "_key", "_level", "_line", "_refs", "_tag",
"_tags", "_title",
)
def __init__(
+26 -26
View File
@@ -27,8 +27,6 @@ import logging
from typing import TYPE_CHECKING, Any
from PyQt6.QtGui import QFont, QIcon
from novelwriter import CONFIG, SHARED
from novelwriter.common import (
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified,
@@ -37,7 +35,9 @@ from novelwriter.common import (
from novelwriter.constants import nwLabels, nwStyles, trConst
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from PyQt6.QtGui import QFont, QIcon
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
@@ -53,10 +53,10 @@ class NWItem:
"""
__slots__ = (
"_project", "_name", "_handle", "_parent", "_root", "_order",
"_type", "_class", "_layout", "_status", "_import", "_active",
"_expanded", "_heading", "_charCount", "_wordCount",
"_paraCount", "_cursorPos", "_initCount",
"_active", "_charCount", "_class", "_cursorPos", "_expanded",
"_handle", "_heading", "_import", "_initCount", "_layout", "_name",
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
"_type", "_wordCount",
)
def __init__(self, project: NWProject, handle: str) -> None:
@@ -264,25 +264,25 @@ class NWItem:
@classmethod
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
"""Make a copy of an item."""
cls = NWItem(source._project, handle)
cls._name = source._name
cls._parent = source._parent
cls._root = source._root
cls._order = source._order
cls._type = source._type
cls._class = source._class
cls._layout = source._layout
cls._status = source._status
cls._import = source._import
cls._active = source._active
cls._expanded = source._expanded
cls._heading = source._heading
cls._charCount = source._charCount
cls._wordCount = source._wordCount
cls._paraCount = source._paraCount
cls._cursorPos = source._cursorPos
cls._initCount = source._initCount
return cls
new = cls(source._project, handle)
new._name = source._name
new._parent = source._parent
new._root = source._root
new._order = source._order
new._type = source._type
new._class = source._class
new._layout = source._layout
new._status = source._status
new._import = source._import
new._active = source._active
new._expanded = source._expanded
new._heading = source._heading
new._charCount = source._charCount
new._wordCount = source._wordCount
new._paraCount = source._paraCount
new._cursorPos = source._cursorPos
new._initCount = source._initCount
return new
##
# Action Methods
+17 -18
View File
@@ -37,7 +37,7 @@ from novelwriter.core.item import NWItem
from novelwriter.enum import nwItemClass
from novelwriter.types import QtAlignRight
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.tree import NWTree
logger = logging.getLogger(__name__)
@@ -89,7 +89,7 @@ class ProjectNode:
C_ACTIVE = 2
C_STATUS = 3
__slots__ = ("_item", "_children", "_parent", "_row", "_cache", "_flags", "_count")
__slots__ = ("_cache", "_children", "_count", "_flags", "_item", "_parent", "_row")
def __init__(self, item: NWItem) -> None:
self._item = item
@@ -162,7 +162,7 @@ class ProjectNode:
def updateCount(self, propagate: bool = True) -> None:
"""Update counts, and propagate upwards in the tree."""
self._count = self._item.wordCount + sum(c._count for c in self._children)
self._count = self._item.wordCount + sum(c._count for c in self._children) # noqa: SLF001
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
if propagate and (parent := self._parent):
parent.updateCount()
@@ -257,13 +257,13 @@ class ProjectNode:
"""Recursively add all nodes to a list."""
for node in self._children:
children.append(node)
node._recursiveAppendChildren(children)
node._recursiveAppendChildren(children) # noqa: SLF001
return
def _refreshChildrenPos(self) -> None:
"""Update the row value on all children."""
for n, child in enumerate(self._children):
child._row = n
child._row = n # noqa: SLF001
child.item.setOrder(n)
return
@@ -290,12 +290,12 @@ class ProjectModel(QAbstractItemModel):
methods needed primarily by the project tree GUI component.
"""
__slots__ = ("_tree", "_root")
__slots__ = ("_root", "_tree")
def __init__(self, tree: NWTree) -> None:
super().__init__()
self._tree = tree
self._root = ProjectNode(NWItem(tree._project, INV_ROOT))
self._root = ProjectNode(NWItem(tree.project, INV_ROOT))
self._root.item.setName("Invisible Root")
logger.debug("Ready: ProjectModel")
return
@@ -391,10 +391,10 @@ class ProjectModel(QAbstractItemModel):
) -> bool:
"""Process mime data drop."""
if self.canDropMimeData(data, action, row, column, parent):
items = []
for handle in decodeMimeHandles(data):
if (index := self.indexFromHandle(handle)).isValid():
items.append(index)
items = [
index for handle in decodeMimeHandles(data)
if (index := self.indexFromHandle(handle)).isValid()
]
self.multiMove(items, parent, row)
return True
return False
@@ -490,7 +490,7 @@ class ProjectModel(QAbstractItemModel):
if temp := self.removeChild(index.parent(), index.row()):
self.insertChild(temp, target, pos)
for child in reversed(node.allChildren()):
node._updateRelationships(child)
node._updateRelationships(child) # noqa: SLF001
child.item.notifyToRefresh()
node.item.notifyToRefresh()
return
@@ -501,16 +501,15 @@ class ProjectModel(QAbstractItemModel):
def clear(self) -> None:
"""Clear the project model."""
self._root._children.clear()
self._root.children.clear()
return
def allExpanded(self) -> list[QModelIndex]:
"""Return a list of all expanded items."""
expanded = []
for node in self._root.allChildren():
if node._item.isExpanded:
expanded.append(self.createIndex(node.row(), 0, node))
return expanded
return [
self.createIndex(node.row(), 0, node) for node in self._root.allChildren()
if node.item.isExpanded
]
def trashSelection(self, indices: list[QModelIndex]) -> bool:
"""Check if a selection of indices are all in trash or not."""
+6 -2
View File
@@ -25,15 +25,19 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt6.QtGui import QIcon, QPixmap
from novelwriter import SHARED
from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
from novelwriter.core.indexdata import IndexHeading, IndexNode
from novelwriter.enum import nwNovelExtra
from novelwriter.types import QtAlignRight
if TYPE_CHECKING:
from novelwriter.core.indexdata import IndexHeading, IndexNode
logger = logging.getLogger(__name__)
C_FACTOR = 0x0100
@@ -50,7 +54,7 @@ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None
class NovelModel(QAbstractTableModel):
__slots__ = ("_rows", "_more", "_columns", "_extraKey", "_extraLabel")
__slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows")
def __init__(self) -> None:
super().__init__()
+1 -1
View File
@@ -35,7 +35,7 @@ from novelwriter.common import checkBool, checkFloat, checkInt, checkString, jso
from novelwriter.constants import nwFiles
from novelwriter.error import logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+9 -6
View File
@@ -30,6 +30,7 @@ from enum import Enum
from functools import partial
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
from PyQt6.QtCore import QCoreApplication
@@ -44,12 +45,14 @@ from novelwriter.core.options import OptionState
from novelwriter.core.projectdata import NWProjectData
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
from novelwriter.core.sessions import NWSessionLog
from novelwriter.core.status import T_StatusKind, T_UpdateEntry
from novelwriter.core.storage import NWStorage, NWStorageOpen
from novelwriter.core.tree import NWTree
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
if TYPE_CHECKING:
from novelwriter.core.status import T_StatusKind, T_UpdateEntry
logger = logging.getLogger(__name__)
@@ -64,8 +67,8 @@ class NWProjectState(Enum):
class NWProject:
__slots__ = (
"_options", "_storage", "_data", "_tree", "_index", "_session",
"_langData", "_changed", "_valid", "_state", "tr",
"_changed", "_data", "_index", "_langData", "_options", "_session",
"_state", "_storage", "_tree", "_valid", "tr",
)
def __init__(self) -> None:
@@ -557,13 +560,13 @@ class NWProject:
def _loadProjectLocalisation(self) -> bool:
"""Load the language data for the current project language."""
if self._data.language is None or CONFIG._nwLangPath is None:
if self._data.language is None or CONFIG.nwLangPath is None:
self._langData = {}
return False
langFile = Path(CONFIG._nwLangPath) / f"project_{self._data.language}.json"
langFile = Path(CONFIG.nwLangPath) / f"project_{self._data.language}.json"
if not langFile.is_file():
langFile = Path(CONFIG._nwLangPath) / "project_en_GB.json"
langFile = Path(CONFIG.nwLangPath) / "project_en_GB.json"
try:
with open(langFile, mode="r", encoding="utf-8") as inFile:
+1 -1
View File
@@ -34,7 +34,7 @@ from novelwriter.common import (
)
from novelwriter.core.status import NWStatus
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -39,7 +39,7 @@ from novelwriter.common import (
hexToInt, simplified, xmlIndent, yesNo
)
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.projectdata import NWProjectData
from novelwriter.core.status import NWStatus
+3 -2
View File
@@ -26,7 +26,6 @@ from __future__ import annotations
import json
import logging
from collections.abc import Iterable
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
@@ -35,7 +34,9 @@ from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwFiles
from novelwriter.error import logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from collections.abc import Iterable
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+3 -2
View File
@@ -27,7 +27,6 @@ from __future__ import annotations
import json
import logging
from collections.abc import Iterator
from pathlib import Path
from typing import TYPE_CHECKING
@@ -36,7 +35,9 @@ from PyQt6.QtCore import QLocale
from novelwriter.constants import nwFiles
from novelwriter.error import logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from collections.abc import Iterator
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+9 -7
View File
@@ -28,8 +28,7 @@ import dataclasses
import logging
import random
from collections.abc import Iterable
from typing import Literal, TypeGuard
from typing import TYPE_CHECKING, Literal, TypeGuard
from PyQt6.QtCore import QPointF, Qt
from PyQt6.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygonF
@@ -39,6 +38,9 @@ from novelwriter.common import simplified
from novelwriter.enum import nwStatusShape
from novelwriter.types import QtPaintAntiAlias, QtTransparent
if TYPE_CHECKING:
from collections.abc import Iterable
logger = logging.getLogger(__name__)
@@ -54,10 +56,10 @@ class StatusEntry:
@classmethod
def duplicate(cls, source: StatusEntry) -> StatusEntry:
"""Create a deep copy of the source object."""
cls = dataclasses.replace(source)
cls.color = QColor(source.color)
cls.icon = QIcon(source.icon)
return cls
status = dataclasses.replace(source)
status.color = QColor(source.color)
status.icon = QIcon(source.icon)
return status
NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
@@ -71,7 +73,7 @@ class NWStatus:
STATUS = "s"
IMPORT = "i"
__slots__ = ("_store", "_default", "_prefix", "_height")
__slots__ = ("_default", "_height", "_prefix", "_store")
def __init__(self, prefix: T_StatusKind) -> None:
self._store: dict[str, StatusEntry] = {}
+1 -1
View File
@@ -40,7 +40,7 @@ from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
from novelwriter.core.spellcheck import UserDictionary
from novelwriter.error import logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+11 -6
View File
@@ -27,7 +27,6 @@ from __future__ import annotations
import logging
import random
from collections.abc import Iterable, Iterator
from pathlib import Path
from typing import TYPE_CHECKING, Literal, overload
@@ -40,7 +39,9 @@ from novelwriter.core.itemmodel import ProjectModel, ProjectNode
from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from collections.abc import Iterable, Iterator
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
@@ -60,7 +61,7 @@ class NWTree:
also used for file names.
"""
__slots__ = ("_project", "_model", "_items", "_nodes", "_trash", "_ready")
__slots__ = ("_items", "_model", "_nodes", "_project", "_ready", "_trash")
def __init__(self, project: NWProject) -> None:
self._project = project
@@ -107,6 +108,11 @@ class NWTree:
# Properties
##
@property
def project(self) -> NWProject:
"""Return the parent project."""
return self._project
@property
def trash(self) -> ProjectNode | None:
"""Return trash node, if it exists."""
@@ -396,7 +402,7 @@ class NWTree:
def checkType(self, tHandle: str, itemType: nwItemType) -> bool:
"""Check if item exists and is of the specified item type."""
if tItem := self.__getitem__(tHandle):
if tItem := self[tHandle]:
return tItem.itemType == itemType
return False
@@ -414,8 +420,7 @@ class NWTree:
node = parent
else:
return path
else:
logger.error("Max project tree depth reached")
logger.error("Max project tree depth reached")
return path
def subTree(self, tHandle: str) -> list[str]:
+5 -1
View File
@@ -25,7 +25,8 @@ from __future__ import annotations
import logging
from PyQt6.QtGui import QCloseEvent
from typing import TYPE_CHECKING
from PyQt6.QtWidgets import (
QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout, QWidget
)
@@ -37,6 +38,9 @@ from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.versioninfo import VersionInfoWidget
from novelwriter.types import QtAlignRightTop, QtDialogClose, QtHexArgb
if TYPE_CHECKING:
from PyQt6.QtGui import QCloseEvent
logger = logging.getLogger(__name__)
+5 -5
View File
@@ -131,11 +131,11 @@ class GuiDocMerge(NDialog):
@classmethod
def getData(cls, parent: QWidget, handle: str, items: list[str]) -> tuple[dict, bool]:
"""Pop the dialog and return the result."""
cls = GuiDocMerge(parent, handle, items)
cls.exec()
data = cls.data()
accepted = cls.result() == QtAccepted
cls.softDelete()
dialog = cls(parent, handle, items)
dialog.exec()
data = dialog.data()
accepted = dialog.result() == QtAccepted
dialog.softDelete()
return data, accepted
##
+5 -5
View File
@@ -178,11 +178,11 @@ class GuiDocSplit(NDialog):
@classmethod
def getData(cls, parent: QWidget, handle: str) -> tuple[dict, list[str], bool]:
"""Pop the dialog and return the result."""
cls = GuiDocSplit(parent, handle)
cls.exec()
data, text = cls.data()
accepted = cls.result() == QtAccepted
cls.softDelete()
dialog = cls(parent, handle)
dialog.exec()
data, text = dialog.data()
accepted = dialog.result() == QtAccepted
dialog.softDelete()
return data, text, accepted
##
+5 -5
View File
@@ -82,9 +82,9 @@ class GuiEditLabel(NDialog):
@classmethod
def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
"""Pop the dialog and return the result."""
cls = GuiEditLabel(parent, text=text)
cls.exec()
label = cls.itemLabel
accepted = cls.result() == QtAccepted
cls.softDelete()
dialog = cls(parent, text=text)
dialog.exec()
label = dialog.itemLabel
accepted = dialog.result() == QtAccepted
dialog.softDelete()
return label, accepted
+3 -3
View File
@@ -432,7 +432,7 @@ class GuiPreferences(NDialog):
self.textWidth.setSingleStep(10)
self.textWidth.setValue(CONFIG.textWidth)
self.mainForm.addRow(
self.tr("Maximum text width in \"Normal Mode\""), self.textWidth,
self.tr('Maximum text width in "Normal Mode"'), self.textWidth,
self.tr("Set to 0 to disable this feature."), unit=self.tr("px")
)
@@ -443,7 +443,7 @@ class GuiPreferences(NDialog):
self.focusWidth.setSingleStep(10)
self.focusWidth.setValue(CONFIG.focusWidth)
self.mainForm.addRow(
self.tr("Maximum text width in \"Focus Mode\""), self.focusWidth,
self.tr('Maximum text width in "Focus Mode"'), self.focusWidth,
self.tr("The maximum width cannot be disabled."), unit=self.tr("px")
)
@@ -451,7 +451,7 @@ class GuiPreferences(NDialog):
self.hideFocusFooter = NSwitch(self)
self.hideFocusFooter.setChecked(CONFIG.hideFocusFooter)
self.mainForm.addRow(
self.tr("Hide document footer in \"Focus Mode\""), self.hideFocusFooter,
self.tr('Hide document footer in "Focus Mode"'), self.hideFocusFooter,
self.tr("Hide the information bar in the document editor.")
)
+5 -5
View File
@@ -122,11 +122,11 @@ class GuiQuoteSelect(NDialog):
@classmethod
def getQuote(cls, parent: QWidget, current: str = "") -> tuple[str, bool]:
"""Pop the dialog and return the result."""
cls = GuiQuoteSelect(parent, current=current)
cls.exec()
quote = cls._selected
accepted = cls.result() == QtAccepted
cls.softDelete()
dialog = cls(parent, current=current)
dialog.exec()
quote = dialog._selected
accepted = dialog.result() == QtAccepted
dialog.softDelete()
return quote, accepted
##
+8 -6
View File
@@ -26,9 +26,9 @@ from __future__ import annotations
import logging
from pathlib import Path
from typing import TYPE_CHECKING
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QCloseEvent
from PyQt6.QtWidgets import (
QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
@@ -41,6 +41,9 @@ from novelwriter.extensions.configlayout import NColorLabel
from novelwriter.extensions.modified import NDialog, NIconToolButton
from novelwriter.types import QtDialogClose, QtDialogSave
if TYPE_CHECKING:
from PyQt6.QtGui import QCloseEvent
logger = logging.getLogger(__name__)
@@ -242,8 +245,7 @@ class GuiWordList(NDialog):
def _listWords(self) -> list[str]:
"""List all words in the list box."""
result = []
for i in range(self.listBox.count()):
if (item := self.listBox.item(i)) and (word := item.text().strip()):
result.append(word)
return result
return [
word for i in range(self.listBox.count())
if (item := self.listBox.item(i)) and (word := item.text().strip())
]
+4 -4
View File
@@ -36,7 +36,7 @@ from PyQt6.QtWidgets import (
QPlainTextEdit, QStyle, QWidget
)
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from types import TracebackType
logger = logging.getLogger(__name__)
@@ -46,7 +46,7 @@ def logException() -> None:
"""Log the content of an exception message."""
exType, exValue, _ = sys.exc_info()
if exType is not None:
logger.error(f"{exType.__name__}: {str(exValue)}", stacklevel=2)
logger.error(f"{exType.__name__}: {exValue!s}", stacklevel=2)
return
@@ -54,7 +54,7 @@ def formatException(exc: BaseException) -> str:
"""Format an exception as a string the same way the default
exception handler does.
"""
return f"{type(exc).__name__}: {str(exc)}"
return f"{type(exc).__name__}: {exc!s}"
class NWErrorMessage(QDialog):
@@ -151,7 +151,7 @@ class NWErrorMessage(QDialog):
f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n"
f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
f"enchant: {enchantVersion}\n\n"
f"{exType.__name__}:\n{str(exValue)}\n\n"
f"{exType.__name__}:\n{exValue!s}\n\n"
f"Traceback:\n{txtTrace}\n"
)
except Exception:
+6 -2
View File
@@ -24,9 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from PyQt6.QtCore import QEvent, QObject
from PyQt6.QtGui import QStatusTipEvent, QWheelEvent
from PyQt6.QtWidgets import QWidget
if TYPE_CHECKING:
from PyQt6.QtWidgets import QWidget
class WheelEventFilter(QObject):
@@ -40,7 +44,7 @@ class WheelEventFilter(QObject):
Reference: https://stackoverflow.com/a/17739995/5825851
"""
__slots__ = ("_parent", "_locked")
__slots__ = ("_locked", "_parent")
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
+5 -3
View File
@@ -27,11 +27,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from PyQt6.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QMouseEvent, QWheelEvent
from PyQt6.QtWidgets import (
QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox,
QToolButton, QTreeView, QWidget
@@ -40,7 +38,11 @@ from PyQt6.QtWidgets import (
from novelwriter import CONFIG, SHARED
from novelwriter.types import QtMouseLeft, QtMouseMiddle
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from enum import Enum
from PyQt6.QtGui import QMouseEvent, QWheelEvent
from novelwriter.guimain import GuiMain
+1 -1
View File
@@ -113,7 +113,7 @@ class NPagedSideBar(QToolBar):
class _PagedToolButton(QToolButton):
__slots__ = ("_bH", "_tM", "_aH")
__slots__ = ("_aH", "_bH", "_tM")
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
+2 -2
View File
@@ -44,8 +44,8 @@ class NProgressCircle(QProgressBar):
"""
__slots__ = (
"_text", "_point", "_dRect", "_cRect", "_dPen", "_dBrush",
"_cPen", "_bPen", "_tColor"
"_bPen", "_cPen", "_cRect", "_dBrush", "_dPen", "_dRect", "_point",
"_tColor", "_text",
)
def __init__(self, parent: QWidget, size: int, point: int) -> None:
+1 -1
View File
@@ -35,7 +35,7 @@ logger = logging.getLogger(__name__)
class StatusLED(QAbstractButton):
__slots__ = ("_neutral", "_postitve", "_negative", "_color", "_state")
__slots__ = ("_color", "_negative", "_neutral", "_postitve", "_state")
def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
super().__init__(parent=parent)
+1 -1
View File
@@ -33,7 +33,7 @@ from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAntiAlias, QtSizeFixe
class NSwitch(QAbstractButton):
__slots__ = ("_xW", "_xH", "_xR", "_rH", "_rR", "_offset")
__slots__ = ("_offset", "_rH", "_rR", "_xH", "_xR", "_xW")
def __init__(self, parent: QWidget, height: int = 0) -> None:
super().__init__(parent=parent)
+5 -1
View File
@@ -23,8 +23,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from PyQt6.QtCore import pyqtSignal
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QGridLayout, QLabel, QScrollArea, QWidget
from novelwriter.extensions.switch import NSwitch
@@ -33,6 +34,9 @@ from novelwriter.types import (
QtSizeMinimumExpanding
)
if TYPE_CHECKING:
from PyQt6.QtGui import QIcon
class NSwitchBox(QScrollArea):
"""Extension: Switch Box Widget
+11 -7
View File
@@ -29,21 +29,25 @@ import re
import xml.etree.ElementTree as ET
from datetime import datetime
from pathlib import Path
from typing import NamedTuple
from typing import TYPE_CHECKING, NamedTuple
from zipfile import ZIP_DEFLATED, ZipFile
from PyQt6.QtCore import QMargins, QSize
from PyQt6.QtGui import QColor
from novelwriter import __version__
from novelwriter.common import firstFloat, xmlElement, xmlSubElem
from novelwriter.constants import nwHeadFmt, nwStyles
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.types import QtHexRgb
if TYPE_CHECKING:
from pathlib import Path
from PyQt6.QtGui import QColor
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
# RegEx
@@ -1052,9 +1056,9 @@ class ToDocX(Tokenizer):
class DocXParagraph:
__slots__ = (
"_content", "_style", "_textAlign",
"_topMargin", "_bottomMargin", "_leftMargin", "_rightMargin",
"_indentFirst", "_breakBefore", "_breakAfter", "_footnoteRef",
"_bottomMargin", "_breakAfter", "_breakBefore", "_content",
"_footnoteRef", "_indentFirst", "_leftMargin", "_rightMargin",
"_style", "_textAlign", "_topMargin",
)
def __init__(self) -> None:
+7 -5
View File
@@ -26,16 +26,20 @@ from __future__ import annotations
import json
import logging
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHtmlUnicode, nwStyles
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS, QtHexRgb
if TYPE_CHECKING:
from pathlib import Path
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
# Each opener tag, with the id of its corresponding closer and tag format
@@ -311,10 +315,8 @@ class ToHtml(Tokenizer):
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None:
"""Replace tabs with spaces in the html."""
pages = []
tabSpace = spaceChar*nSpaces
for aLine in self._pages:
pages.append(aLine.replace("\t", tabSpace))
pages = [aLine.replace("\t", tabSpace) for aLine in self._pages]
self._pages = pages
return
+12 -11
View File
@@ -28,8 +28,7 @@ import logging
import re
from abc import ABC, abstractmethod
from pathlib import Path
from typing import NamedTuple
from typing import TYPE_CHECKING, NamedTuple
from PyQt6.QtCore import QLocale
from PyQt6.QtGui import QColor, QFont
@@ -40,7 +39,6 @@ from novelwriter.constants import (
nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
trConst
)
from novelwriter.core.project import NWProject
from novelwriter.enum import nwComment, nwItemLayout
from novelwriter.formats.shared import (
BlockFmt, BlockTyp, T_Block, T_Formats, T_Note, TextDocumentTheme, TextFmt
@@ -48,6 +46,11 @@ from novelwriter.formats.shared import (
from novelwriter.text.comments import processComment
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
if TYPE_CHECKING:
from pathlib import Path
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
@@ -72,7 +75,7 @@ HEADINGS = [
BlockTyp.TITLE, BlockTyp.PART, BlockTyp.HEAD1,
BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
]
SKIP_INDENT = HEADINGS + [BlockTyp.SEP, BlockTyp.SKIP]
SKIP_INDENT = [*HEADINGS, BlockTyp.SEP, BlockTyp.SKIP]
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
@@ -651,7 +654,7 @@ class Tokenizer(ABC):
tText = aLine[2:].strip()
tType = BlockTyp.HEAD1 if isPlain else BlockTyp.TITLE
sHide = self._hidePart if isPlain else False
if not (isPlain or isNovel and sHide):
if not (isPlain or (isNovel and sHide)):
tStyle |= self._titleStyle
if isNovel:
tType = BlockTyp.PART if isPlain else BlockTyp.TITLE
@@ -1124,12 +1127,10 @@ class Tokenizer(ABC):
temp.append((res.end(0), 0, TextFmt.HRF_E, ""))
# Match Shortcodes
for res in REGEX_PATTERNS.shortcodePlain.finditer(text):
temp.append((
res.start(1), res.end(1),
self._shortCodeFmt.get(res.group(1).lower(), 0),
"",
))
temp.extend(
(res.start(1), res.end(1), self._shortCodeFmt.get(res.group(1).lower(), 0), "")
for res in REGEX_PATTERNS.shortcodePlain.finditer(text)
)
# Match Shortcode w/Values
tHandle = self._handle or ""
+6 -2
View File
@@ -25,13 +25,17 @@ from __future__ import annotations
import logging
from pathlib import Path
from typing import TYPE_CHECKING
from novelwriter.constants import nwUnicode
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import Tokenizer
if TYPE_CHECKING:
from pathlib import Path
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+22 -18
View File
@@ -29,10 +29,9 @@ from __future__ import annotations
import logging
import xml.etree.ElementTree as ET
from collections.abc import Sequence
from datetime import datetime
from hashlib import sha256
from pathlib import Path
from typing import TYPE_CHECKING, Final
from zipfile import ZIP_DEFLATED, ZipFile
from PyQt6.QtGui import QColor, QFont
@@ -40,11 +39,16 @@ from PyQt6.QtGui import QColor, QFont
from novelwriter import __version__
from novelwriter.common import xmlElement, xmlIndent, xmlSubElem
from novelwriter.constants import nwHeadFmt, nwStyles
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt, stripEscape
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.types import FONT_STYLE, QtHexRgb
if TYPE_CHECKING:
from collections.abc import Sequence
from pathlib import Path
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
# Main XML NameSpaces
@@ -1000,11 +1004,11 @@ class ODTParagraphStyle:
exporter. Only the used settings are exposed here to keep the class
minimal and fast.
"""
VALID_ALIGN = ["start", "center", "end", "justify", "inside", "outside", "left", "right"]
VALID_BREAK = ["auto", "column", "page", "even-page", "odd-page", "inherit"]
VALID_LEVEL = ["1", "2", "3", "4"]
VALID_CLASS = ["text", "chapter", "extra"]
VALID_WEIGHT = ["normal", "bold"] + FONT_WEIGHT_NUM
VALID_ALIGN: Final[list[str]] = ["start", "center", "end", "justify", "left", "right"]
VALID_BREAK: Final[list[str]] = ["auto", "page", "even-page", "odd-page", "inherit"]
VALID_LEVEL: Final[list[str]] = ["1", "2", "3", "4"]
VALID_CLASS: Final[list[str]] = ["text", "chapter", "extra"]
VALID_WEIGHT: Final[list[str]] = ["normal", "bold", *FONT_WEIGHT_NUM]
def __init__(self, name: str) -> None:
@@ -1207,9 +1211,9 @@ class ODTParagraphStyle:
def getID(self) -> str:
"""Generate a unique ID from the settings."""
return sha256((
f"Paragraph:Main:{str(self._mAttr)}:"
f"Paragraph:Para:{str(self._pAttr)}:"
f"Paragraph:Text:{str(self._tAttr)}:"
f"Paragraph:Main:{self._mAttr!s}:"
f"Paragraph:Para:{self._pAttr!s}:"
f"Paragraph:Text:{self._tAttr!s}:"
).encode()).hexdigest()
def packXML(self, xParent: ET.Element) -> None:
@@ -1237,13 +1241,13 @@ class ODTTextStyle:
Only the used settings are exposed here to keep the class minimal
and fast.
"""
VALID_WEIGHT = ["normal", "bold"] + FONT_WEIGHT_NUM
VALID_STYLE = ["normal", "italic", "oblique"]
VALID_POS = ["super", "sub"]
VALID_LSTYLE = ["none", "solid"]
VALID_LTYPE = ["single", "double"]
VALID_LWIDTH = ["auto"]
VALID_LCOL = ["font-color"]
VALID_WEIGHT: Final[list[str]] = ["normal", "bold", *FONT_WEIGHT_NUM]
VALID_STYLE: Final[list[str]] = ["normal", "italic", "oblique"]
VALID_POS: Final[list[str]] = ["super", "sub"]
VALID_LSTYLE: Final[list[str]] = ["none", "solid"]
VALID_LTYPE: Final[list[str]] = ["single", "double"]
VALID_LWIDTH: Final[list[str]] = ["auto"]
VALID_LCOL: Final[list[str]] = ["font-color"]
def __init__(self, name: str) -> None:
self._name = name
+6 -2
View File
@@ -25,7 +25,7 @@ from __future__ import annotations
import logging
from pathlib import Path
from typing import TYPE_CHECKING
from PyQt6.QtCore import QMarginsF, QSizeF
from PyQt6.QtGui import (
@@ -36,7 +36,6 @@ from PyQt6.QtPrintSupport import QPrinter
from novelwriter import __version__
from novelwriter.constants import nwStyles, nwUnicode
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
from novelwriter.types import (
@@ -45,6 +44,11 @@ from novelwriter.types import (
QtPropLineHeight, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper
)
if TYPE_CHECKING:
from pathlib import Path
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat]
+6 -2
View File
@@ -26,13 +26,17 @@ from __future__ import annotations
import json
import logging
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
from novelwriter.common import formatTimeStamp
from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import Tokenizer
if TYPE_CHECKING:
from pathlib import Path
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+10 -10
View File
@@ -97,10 +97,10 @@ class GuiDocEditor(QPlainTextEdit):
"""Gui Widget: Main Document Editor"""
__slots__ = (
"_nwDocument", "_nwItem", "_docChanged", "_docHandle", "_vpMargin",
"_lastEdit", "_lastActive", "_lastFind", "_doReplace", "_autoReplace",
"_completer", "_qDocument", "_keyContext", "_followTag1", "_followTag2",
"_timerDoc", "_wCounterDoc", "_timerSel", "_wCounterSel",
"_autoReplace", "_completer", "_doReplace", "_docChanged", "_docHandle", "_followTag1",
"_followTag2", "_keyContext", "_lastActive", "_lastEdit", "_lastFind", "_nwDocument",
"_nwItem", "_qDocument", "_timerDoc", "_timerSel", "_vpMargin", "_wCounterDoc",
"_wCounterSel",
)
MOVE_KEYS = (
@@ -760,7 +760,7 @@ class GuiDocEditor(QPlainTextEdit):
elif action == nwDocAction.REPL_SNG:
self._replaceQuotes("'", CONFIG.fmtSQuoteOpen, CONFIG.fmtSQuoteClose)
elif action == nwDocAction.REPL_DBL:
self._replaceQuotes("\"", CONFIG.fmtDQuoteOpen, CONFIG.fmtDQuoteClose)
self._replaceQuotes('"', CONFIG.fmtDQuoteOpen, CONFIG.fmtDQuoteClose)
elif action == nwDocAction.RM_BREAKS:
self._removeInParLineBreaks()
elif action == nwDocAction.ALIGN_L:
@@ -2003,7 +2003,7 @@ class GuiDocEditor(QPlainTextEdit):
sPos = cPos - i - 1
cOne = str(self._qDocument.characterAt(sPos))
cTwo = str(self._qDocument.characterAt(sPos - 1))
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
if not (cOne.isalnum() or (cOne in apos and cTwo.isalnum())):
sPos += 1
break
@@ -2013,7 +2013,7 @@ class GuiDocEditor(QPlainTextEdit):
ePos = cPos + i
cOne = str(self._qDocument.characterAt(ePos))
cTwo = str(self._qDocument.characterAt(ePos + 1))
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
if not (cOne.isalnum() or (cOne in apos and cTwo.isalnum())):
break
if ePos - sPos <= 0:
@@ -2193,9 +2193,9 @@ class BackgroundWordCounterSignals(QObject):
class TextAutoReplace:
__slots__ = (
"_quoteSO", "_quoteSC", "_quoteDO", "_quoteDC",
"_replaceSQuote", "_replaceDQuote", "_replaceDash", "_replaceDots",
"_padChar", "_padBefore", "_padAfter", "_doPadBefore", "_doPadAfter",
"_doPadAfter", "_doPadBefore", "_padAfter", "_padBefore", "_padChar",
"_quoteDC", "_quoteDO", "_quoteSC", "_quoteSO", "_replaceDQuote",
"_replaceDash", "_replaceDots", "_replaceSQuote",
)
def __init__(self) -> None:
+3 -3
View File
@@ -58,8 +58,8 @@ BLOCK_TITLE = 4
class GuiDocHighlighter(QSyntaxHighlighter):
__slots__ = (
"_tHandle", "_isNovel", "_isInactive", "_spellCheck", "_spellErr",
"_hStyles", "_minRules", "_txtRules", "_cmnRules", "_dialogParser",
"_cmnRules", "_dialogParser", "_hStyles", "_isInactive", "_isNovel",
"_minRules", "_spellCheck", "_spellErr", "_tHandle", "_txtRules",
)
def __init__(self, document: QTextDocument) -> None:
@@ -466,7 +466,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
class TextBlockData(QTextBlockUserData):
__slots__ = ("_text", "_offset", "_metaData", "_spellErrors")
__slots__ = ("_metaData", "_offset", "_spellErrors", "_text")
def __init__(self) -> None:
super().__init__()
+4 -1
View File
@@ -26,6 +26,7 @@ from __future__ import annotations
import logging
from enum import Enum
from typing import TYPE_CHECKING
from PyQt6.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import (
@@ -36,12 +37,14 @@ from PyQt6.QtWidgets import (
from novelwriter import SHARED
from novelwriter.common import checkInt, qtAddAction
from novelwriter.constants import nwLabels, nwLists, nwStyles, trConst
from novelwriter.core.indexdata import IndexHeading, IndexNode
from novelwriter.enum import nwChange, nwDocMode, nwItemClass
from novelwriter.extensions.modified import NIconToolButton
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
from novelwriter.types import QtDecoration, QtHeaderFixed, QtHeaderToContents, QtUserRole
if TYPE_CHECKING:
from novelwriter.core.indexdata import IndexHeading, IndexNode
logger = logging.getLogger(__name__)
+4 -1
View File
@@ -25,8 +25,8 @@ from __future__ import annotations
import logging
from collections.abc import Iterable
from time import time
from typing import TYPE_CHECKING
from PyQt6.QtCore import QObject, pyqtSlot
from PyQt6.QtGui import QTextBlock, QTextCursor, QTextDocument
@@ -35,6 +35,9 @@ from PyQt6.QtWidgets import QApplication, QPlainTextDocumentLayout
from novelwriter import SHARED
from novelwriter.gui.dochighlight import GuiDocHighlighter, TextBlockData
if TYPE_CHECKING:
from collections.abc import Iterable
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -228,7 +228,7 @@ class GuiItemDetails(QWidget):
return
self._handle = tHandle
iPx = int(round(0.9*SHARED.theme.baseIconHeight))
iPx = round(0.9*SHARED.theme.baseIconHeight)
# Label
# =====
+3 -3
View File
@@ -41,7 +41,7 @@ from novelwriter.constants import (
from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
from novelwriter.extensions.eventfilters import StatusTipFilter
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
@@ -471,7 +471,7 @@ class GuiMainMenu(QMenuBar):
# Insert > Double Prime
self.aInsDPrime = qtAddAction(self.mInsPunct, self.tr("Double Prime"))
self.aInsDPrime.setShortcut("Ctrl+K, Ctrl+\"")
self.aInsDPrime.setShortcut('Ctrl+K, Ctrl+"')
self.aInsDPrime.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_DPRIME)
)
@@ -680,7 +680,7 @@ class GuiMainMenu(QMenuBar):
# Format > Double Quotes
self.aFmtDQuote = qtAddAction(self.fmtMenu, self.tr("Wrap Double Quotes"))
self.aFmtDQuote.setShortcut("Ctrl+\"")
self.aFmtDQuote.setShortcut('Ctrl+"')
self.aFmtDQuote.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.D_QUOTE)
)
+6 -5
View File
@@ -32,6 +32,7 @@ import logging
from enum import Enum
from time import time
from typing import Final
from PyQt6.QtCore import QT_TRANSLATE_NOOP, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QAction, QIcon
@@ -319,7 +320,7 @@ class GuiOutlineToolBar(QToolBar):
class GuiOutlineTree(QTreeWidget):
DEF_WIDTH = {
DEF_WIDTH: Final[dict[nwOutline, int]] = {
nwOutline.TITLE: 200,
nwOutline.LEVEL: 40,
nwOutline.LABEL: 150,
@@ -342,7 +343,7 @@ class GuiOutlineTree(QTreeWidget):
nwOutline.SYNOP: 200,
}
DEF_HIDDEN = {
DEF_HIDDEN: Final[dict[nwOutline, bool]] = {
nwOutline.TITLE: False,
nwOutline.LEVEL: True,
nwOutline.LABEL: False,
@@ -498,7 +499,7 @@ class GuiOutlineTree(QTreeWidget):
tree.
"""
# If it's the first time, we always build
if self._firstView or self._firstView and overRide:
if self._firstView or (self._firstView and overRide):
self._loadHeaderState()
self._populateTree(rootHandle)
self._firstView = False
@@ -558,7 +559,7 @@ class GuiOutlineTree(QTreeWidget):
logger.info("Writing CSV file: %s", path)
cols = [col for col in self._treeOrder if not self._colHidden[col]]
order = [self._colIdx[col] for col in cols]
with open(path, mode="w", newline="") as csvFile:
with open(path, mode="w", newline="", encoding="utf-8") as csvFile:
writer = csv.writer(csvFile, dialect="excel", quoting=csv.QUOTE_ALL)
writer.writerow([trConst(nwLabels.OUTLINE_COLS[col]) for col in cols])
for i in range(self.topLevelItemCount()):
@@ -795,7 +796,7 @@ class GuiOutlineHeaderMenu(QMenu):
class GuiOutlineDetails(QScrollArea):
LVL_MAP = {
LVL_MAP: Final[dict[str, str]] = {
"H1": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
"H2": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
"H3": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
+2 -2
View File
@@ -1099,7 +1099,7 @@ class _UpdatableMenu(QMenu):
class _TreeContextMenu(QMenu):
__slots__ = ("_tree", "_view", "_node", "_item", "_model", "_handle", "_indices", "_children")
__slots__ = ("_children", "_handle", "_indices", "_item", "_model", "_node", "_tree", "_view")
def __init__(
self, projTree: GuiProjectTree, model: ProjectModel,
@@ -1327,7 +1327,7 @@ class _TreeContextMenu(QMenu):
"""Add move to Trash action."""
if (
self._model.trashSelection(self._indices)
or len(self._indices) == 1 and self._item.isRootType()
or (len(self._indices) == 1 and self._item.isRootType())
):
text = self.tr("Delete Permanently")
else:
+4 -1
View File
@@ -26,6 +26,7 @@ from __future__ import annotations
import logging
from time import time
from typing import TYPE_CHECKING
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QAction, QCursor, QKeyEvent, QPalette
@@ -37,12 +38,14 @@ from PyQt6.QtWidgets import (
from novelwriter import CONFIG, SHARED
from novelwriter.common import checkInt, qtAddAction
from novelwriter.core.coretools import DocSearch
from novelwriter.core.item import NWItem
from novelwriter.types import (
QtAlignMiddle, QtAlignRight, QtHeaderStretch, QtHeaderToContents,
QtHexArgb, QtUserRole
)
if TYPE_CHECKING:
from novelwriter.core.item import NWItem
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -37,7 +37,7 @@ from novelwriter.extensions.eventfilters import StatusTipFilter
from novelwriter.extensions.modified import NIconToolButton
from novelwriter.gui.theme import STYLES_BIG_TOOLBUTTON
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
+20 -22
View File
@@ -27,7 +27,7 @@ from __future__ import annotations
import logging
from math import ceil
from pathlib import Path
from typing import TYPE_CHECKING, Final
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtGui import (
@@ -44,6 +44,9 @@ from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
from novelwriter.types import QtBlack, QtHexArgb, QtPaintAntiAlias, QtTransparent
if TYPE_CHECKING:
from pathlib import Path
logger = logging.getLogger(__name__)
STYLES_FLAT_TABS = "flatTabWidget"
@@ -93,18 +96,13 @@ class GuiTheme:
"""
__slots__ = (
# Attributes
"iconCache", "themeMeta", "isDarkTheme", "helpText", "fadedText", "errorText",
"syntaxMeta", "syntaxTheme", "guiFont", "guiFontB", "guiFontBU", "guiFontSmall",
"fontPointSize", "fontPixelSize", "baseIconHeight", "baseButtonHeight", "textNHeight",
"textNWidth", "baseIconSize", "buttonIconSize", "guiFontFixed",
# Functions
"getIcon", "getPixmap", "getItemIcon", "getIconColor", "getToggleIcon", "getDecoration",
"getHeaderDecoration", "getHeaderDecorationNarrow",
# Internal
"_guiPalette", "_themeList", "_syntaxList", "_availThemes", "_availSyntax", "_styleSheets",
"_availSyntax", "_availThemes", "_guiPalette", "_styleSheets", "_syntaxList", "_themeList",
"baseButtonHeight", "baseIconHeight", "baseIconSize", "buttonIconSize", "errorText",
"fadedText", "fontPixelSize", "fontPointSize", "getDecoration", "getHeaderDecoration",
"getHeaderDecorationNarrow", "getIcon", "getIconColor", "getItemIcon", "getPixmap",
"getToggleIcon", "guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall",
"helpText", "iconCache", "isDarkTheme", "syntaxMeta", "syntaxTheme", "textNHeight",
"textNWidth", "themeMeta",
)
def __init__(self) -> None:
@@ -164,9 +162,9 @@ class GuiTheme:
fHeight = qMetric.height()
fAscent = qMetric.ascent()
self.fontPointSize = self.guiFont.pointSizeF()
self.fontPixelSize = int(round(fHeight))
self.baseIconHeight = int(round(fAscent))
self.baseButtonHeight = int(round(1.35*fAscent))
self.fontPixelSize = round(fHeight)
self.baseIconHeight = round(fAscent)
self.baseButtonHeight = round(1.35*fAscent)
self.textNHeight = qMetric.boundingRect("N").height()
self.textNWidth = qMetric.boundingRect("N").width()
@@ -202,7 +200,7 @@ class GuiTheme:
qMetrics = QFontMetrics(font)
else:
qMetrics = QFontMetrics(self.guiFont)
return int(ceil(qMetrics.boundingRect(text).width()))
return ceil(qMetrics.boundingRect(text).width())
##
# Theme Methods
@@ -567,16 +565,16 @@ class GuiIcons:
"""
__slots__ = (
"mainTheme", "themeMeta", "_svgData", "_svgColors", "_qColors",
"_qIcons", "_headerDec", "_headerDecNarrow", "_availThemes",
"_themeList", "_noIcon",
"_availThemes", "_headerDec", "_headerDecNarrow", "_noIcon",
"_qColors", "_qIcons", "_svgColors", "_svgData", "_themeList",
"mainTheme", "themeMeta",
)
TOGGLE_ICON_KEYS: dict[str, tuple[str, str]] = {
TOGGLE_ICON_KEYS: Final[dict[str, tuple[str, str]]] = {
"bullet": ("bullet-on", "bullet-off"),
"unfold": ("unfold-show", "unfold-hide"),
}
IMAGE_MAP: dict[str, tuple[str, str]] = {
IMAGE_MAP: Final[dict[str, tuple[str, str]]] = {
"welcome": ("welcome-light.jpg", "welcome-dark.jpg"),
"nw-text": ("novelwriter-text-light.svg", "novelwriter-text-dark.svg"),
}
+1 -1
View File
@@ -1009,7 +1009,7 @@ class GuiMain(QMainWindow):
fN = self.novelView.treeHasFocus()
self._changeView(nwView.EDITOR)
if (vM and (vP and fP or vN and not fN)) or (not vM and vN):
if (vM and ((vP and fP) or (vN and not fN))) or (not vM and vN):
self._changeView(nwView.NOVEL)
self.novelView.setTreeFocus()
else:
+4 -4
View File
@@ -40,7 +40,7 @@ from novelwriter.constants import nwFiles
from novelwriter.core.spellcheck import NWSpellEnchant
from novelwriter.enum import nwChange, nwItemClass
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.core.project import NWProject
from novelwriter.core.status import T_StatusKind
from novelwriter.gui.theme import GuiTheme
@@ -54,8 +54,8 @@ NWWidget = TypeVar("NWWidget", bound=QWidget)
class SharedData(QObject):
__slots__ = (
"_gui", "_theme", "_project", "_spelling", "_lockedBy", "_lastAlert",
"_idleTime", "_idleRefTime",
"_gui", "_idleRefTime", "_idleTime", "_lastAlert", "_lockedBy",
"_project", "_spelling", "_theme",
)
focusModeChanged = pyqtSignal(bool)
@@ -480,7 +480,7 @@ class _GuiAlert(QMessageBox):
def setException(self, exception: Exception) -> None:
"""Add exception details."""
info = self.informativeText()
text = f"<b>{type(exception).__name__}</b>: {str(exception)}"
text = f"<b>{type(exception).__name__}</b>: {exception!s}"
self.setInformativeText(f"{info}<br>{text}" if info else text)
return
+2 -2
View File
@@ -128,8 +128,8 @@ REGEX_PATTERNS = RegExPatterns()
class DialogParser:
__slots__ = (
"_quotes", "_dialog", "_alternate", "_enabled",
"_narrator", "_breakD", "_breakQ", "_mode",
"_alternate", "_breakD", "_breakQ", "_dialog", "_enabled", "_mode",
"_narrator", "_quotes",
)
def __init__(self) -> None:
+4 -4
View File
@@ -124,10 +124,10 @@ class GuiLipsum(NDialog):
@classmethod
def getLipsum(cls, parent: QWidget) -> str:
"""Pop the dialog and return the lipsum text."""
cls = GuiLipsum(parent)
cls.exec()
text = cls.lipsumText
cls.softDelete()
dialog = cls(parent)
dialog.exec()
text = dialog.lipsumText
dialog.softDelete()
return text
##
+6 -2
View File
@@ -26,9 +26,9 @@ from __future__ import annotations
import logging
from pathlib import Path
from typing import TYPE_CHECKING
from PyQt6.QtCore import QTimer, pyqtSlot
from PyQt6.QtGui import QCloseEvent
from PyQt6.QtWidgets import (
QAbstractButton, QAbstractItemView, QDialogButtonBox, QFileDialog,
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
@@ -38,7 +38,6 @@ from PyQt6.QtWidgets import (
from novelwriter import SHARED
from novelwriter.common import makeFileNameSafe, openExternalPath
from novelwriter.constants import nwLabels
from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.item import NWItem
from novelwriter.enum import nwBuildFmt
@@ -46,6 +45,11 @@ from novelwriter.extensions.modified import NDialog, NIconToolButton
from novelwriter.extensions.progressbars import NProgressSimple
from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
if TYPE_CHECKING:
from PyQt6.QtGui import QCloseEvent
from novelwriter.core.buildsettings import BuildSettings
logger = logging.getLogger(__name__)
+6 -6
View File
@@ -59,7 +59,7 @@ from novelwriter.types import (
QtUserRole
)
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
@@ -427,10 +427,10 @@ class GuiManuscript(NToolDialog):
def _saveSettings(self) -> None:
"""Save the user GUI settings."""
buildOrder = []
for i in range(self.buildList.count()):
if item := self.buildList.item(i):
buildOrder.append(item.data(self.D_KEY))
buildOrder = [
item.data(self.D_KEY) for i in range(self.buildList.count())
if (item := self.buildList.item(i))
]
current = self.buildList.currentItem()
lastBuild = current.data(self.D_KEY) if isinstance(current, QListWidgetItem) else ""
@@ -744,7 +744,7 @@ class _PreviewWidget(QTextBrowser):
document.setDocumentMargin(CONFIG.textMargin)
self.setPlaceholderText(self.tr(
"Press the \"Preview\" button to generate ..."
'Press the "Preview" button to generate ...'
))
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
+1 -1
View File
@@ -55,7 +55,7 @@ from novelwriter.types import (
QtUserRole
)
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
+5 -1
View File
@@ -26,8 +26,9 @@ from __future__ import annotations
import logging
import math
from typing import TYPE_CHECKING
from PyQt6.QtCore import pyqtSlot
from PyQt6.QtGui import QCloseEvent
from PyQt6.QtWidgets import (
QAbstractItemView, QDialogButtonBox, QFormLayout, QGridLayout, QHBoxLayout,
QLabel, QSpinBox, QStackedWidget, QTreeWidget, QTreeWidgetItem,
@@ -44,6 +45,9 @@ from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch
from novelwriter.types import QtAlignRight, QtDecoration, QtDialogClose
if TYPE_CHECKING:
from PyQt6.QtGui import QCloseEvent
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -385,7 +385,7 @@ class _OpenProjectPage(QWidget):
class _ProjectListItem(QStyledItemDelegate):
__slots__ = ("_pPx", "_hPx", "_tFont", "_dFont", "_dPen", "_icon")
__slots__ = ("_dFont", "_dPen", "_hPx", "_icon", "_pPx", "_tFont")
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
+2 -2
View File
@@ -47,7 +47,7 @@ from novelwriter.types import (
QtDialogClose, QtRoleAction
)
if TYPE_CHECKING: # pragma: no cover
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
@@ -124,7 +124,7 @@ class GuiWritingStats(NToolDialog):
self.listBox.setSortingEnabled(True)
# Word Bar
self.barHeight = int(round(0.5*SHARED.theme.fontPixelSize))
self.barHeight = round(0.5*SHARED.theme.fontPixelSize)
self.barImage = QPixmap(self.barHeight, self.barHeight)
self.barImage.fill(self.palette().highlight().color())