From 8ed3bd889d82b304fdcf7a861a16792f6f115b1c Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:18:43 +0200 Subject: [PATCH] Fix linting errors in the main code --- novelWriter.py | 4 +- novelwriter/__init__.py | 4 +- novelwriter/common.py | 20 +++---- novelwriter/config.py | 57 ++++++++++---------- novelwriter/constants.py | 72 +++++++++++++------------- novelwriter/core/buildsettings.py | 21 ++++---- novelwriter/core/coretools.py | 12 +++-- novelwriter/core/docbuild.py | 18 ++++--- novelwriter/core/document.py | 4 +- novelwriter/core/index.py | 7 +-- novelwriter/core/indexdata.py | 11 ++-- novelwriter/core/item.py | 52 +++++++++---------- novelwriter/core/itemmodel.py | 35 ++++++------- novelwriter/core/novelmodel.py | 8 ++- novelwriter/core/options.py | 2 +- novelwriter/core/project.py | 15 +++--- novelwriter/core/projectdata.py | 2 +- novelwriter/core/projectxml.py | 2 +- novelwriter/core/sessions.py | 5 +- novelwriter/core/spellcheck.py | 5 +- novelwriter/core/status.py | 16 +++--- novelwriter/core/storage.py | 2 +- novelwriter/core/tree.py | 17 +++--- novelwriter/dialogs/about.py | 6 ++- novelwriter/dialogs/docmerge.py | 10 ++-- novelwriter/dialogs/docsplit.py | 10 ++-- novelwriter/dialogs/editlabel.py | 10 ++-- novelwriter/dialogs/preferences.py | 6 +-- novelwriter/dialogs/quotes.py | 10 ++-- novelwriter/dialogs/wordlist.py | 14 ++--- novelwriter/error.py | 8 +-- novelwriter/extensions/eventfilters.py | 8 ++- novelwriter/extensions/modified.py | 8 +-- novelwriter/extensions/pagedsidebar.py | 2 +- novelwriter/extensions/progressbars.py | 4 +- novelwriter/extensions/statusled.py | 2 +- novelwriter/extensions/switch.py | 2 +- novelwriter/extensions/switchbox.py | 6 ++- novelwriter/formats/todocx.py | 18 ++++--- novelwriter/formats/tohtml.py | 12 +++-- novelwriter/formats/tokenizer.py | 23 ++++---- novelwriter/formats/tomarkdown.py | 8 ++- novelwriter/formats/toodt.py | 40 +++++++------- novelwriter/formats/toqdoc.py | 8 ++- novelwriter/formats/toraw.py | 8 ++- novelwriter/gui/doceditor.py | 20 +++---- novelwriter/gui/dochighlight.py | 6 +-- novelwriter/gui/docviewerpanel.py | 5 +- novelwriter/gui/editordocument.py | 5 +- novelwriter/gui/itemdetails.py | 2 +- novelwriter/gui/mainmenu.py | 6 +-- novelwriter/gui/outline.py | 11 ++-- novelwriter/gui/projtree.py | 4 +- novelwriter/gui/search.py | 5 +- novelwriter/gui/sidebar.py | 2 +- novelwriter/gui/theme.py | 42 +++++++-------- novelwriter/guimain.py | 2 +- novelwriter/shared.py | 8 +-- novelwriter/text/patterns.py | 4 +- novelwriter/tools/lipsum.py | 8 +-- novelwriter/tools/manusbuild.py | 8 ++- novelwriter/tools/manuscript.py | 12 ++--- novelwriter/tools/manussettings.py | 2 +- novelwriter/tools/noveldetails.py | 6 ++- novelwriter/tools/welcome.py | 2 +- novelwriter/tools/writingstats.py | 4 +- 66 files changed, 434 insertions(+), 344 deletions(-) diff --git a/novelWriter.py b/novelWriter.py index 285cb5dd..63d437e1 100755 --- a/novelWriter.py +++ b/novelWriter.py @@ -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") diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py index efa6a177..8f5214dc 100644 --- a/novelwriter/__init__.py +++ b/novelwriter/__init__.py @@ -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: diff --git a/novelwriter/common.py b/novelwriter/common.py index 435efd4c..7cb6c5e0 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -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" diff --git a/novelwriter/config.py b/novelwriter/config.py index c5d1f166..51d6e164 100644 --- a/novelwriter/config.py +++ b/novelwriter/config.py @@ -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 diff --git a/novelwriter/constants.py b/novelwriter/constants.py index 9f4c0e1e..9928ece4 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -23,6 +23,8 @@ along with this program. If not, see . """ 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, diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index 6c6420b6..4032eb0b 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -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: diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py index d691835e..815504e2 100644 --- a/novelwriter/core/coretools.py +++ b/novelwriter/core/coretools.py @@ -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) diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 0b10bfe4..ce3f4930 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -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: diff --git a/novelwriter/core/document.py b/novelwriter/core/document.py index 9a512753..e0f469e9 100644 --- a/novelwriter/core/document.py +++ b/novelwriter/core/document.py @@ -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__) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 1097294c..4263c0cf 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -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 diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py index 9aee57c3..914263a0 100644 --- a/novelwriter/core/indexdata.py +++ b/novelwriter/core/indexdata.py @@ -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__( diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index 3bbb8cec..ca3ce498 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -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 diff --git a/novelwriter/core/itemmodel.py b/novelwriter/core/itemmodel.py index 505ee542..6fb2580a 100644 --- a/novelwriter/core/itemmodel.py +++ b/novelwriter/core/itemmodel.py @@ -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.""" diff --git a/novelwriter/core/novelmodel.py b/novelwriter/core/novelmodel.py index b55fa615..e5b49b15 100644 --- a/novelwriter/core/novelmodel.py +++ b/novelwriter/core/novelmodel.py @@ -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__() diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py index 40f8567d..2753c086 100644 --- a/novelwriter/core/options.py +++ b/novelwriter/core/options.py @@ -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__) diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index 040f12f1..9ad46682 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -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: diff --git a/novelwriter/core/projectdata.py b/novelwriter/core/projectdata.py index ff608874..1c2c1f42 100644 --- a/novelwriter/core/projectdata.py +++ b/novelwriter/core/projectdata.py @@ -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__) diff --git a/novelwriter/core/projectxml.py b/novelwriter/core/projectxml.py index 033bd463..461d9a40 100644 --- a/novelwriter/core/projectxml.py +++ b/novelwriter/core/projectxml.py @@ -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 diff --git a/novelwriter/core/sessions.py b/novelwriter/core/sessions.py index 4be8689d..6f7fcef4 100644 --- a/novelwriter/core/sessions.py +++ b/novelwriter/core/sessions.py @@ -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__) diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index 87b701ba..a9e8b443 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -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__) diff --git a/novelwriter/core/status.py b/novelwriter/core/status.py index 92c78d50..f7d9b584 100644 --- a/novelwriter/core/status.py +++ b/novelwriter/core/status.py @@ -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] = {} diff --git a/novelwriter/core/storage.py b/novelwriter/core/storage.py index 23e37909..b8c21948 100644 --- a/novelwriter/core/storage.py +++ b/novelwriter/core/storage.py @@ -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__) diff --git a/novelwriter/core/tree.py b/novelwriter/core/tree.py index be582526..2ff74a53 100644 --- a/novelwriter/core/tree.py +++ b/novelwriter/core/tree.py @@ -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]: diff --git a/novelwriter/dialogs/about.py b/novelwriter/dialogs/about.py index e122fd19..3c7070df 100644 --- a/novelwriter/dialogs/about.py +++ b/novelwriter/dialogs/about.py @@ -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__) diff --git a/novelwriter/dialogs/docmerge.py b/novelwriter/dialogs/docmerge.py index 14e6553a..97717dc6 100644 --- a/novelwriter/dialogs/docmerge.py +++ b/novelwriter/dialogs/docmerge.py @@ -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 ## diff --git a/novelwriter/dialogs/docsplit.py b/novelwriter/dialogs/docsplit.py index 447d1696..3f1f34ad 100644 --- a/novelwriter/dialogs/docsplit.py +++ b/novelwriter/dialogs/docsplit.py @@ -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 ## diff --git a/novelwriter/dialogs/editlabel.py b/novelwriter/dialogs/editlabel.py index caf97335..5386acb4 100644 --- a/novelwriter/dialogs/editlabel.py +++ b/novelwriter/dialogs/editlabel.py @@ -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 diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index a1166c04..aa4f9cd1 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -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.") ) diff --git a/novelwriter/dialogs/quotes.py b/novelwriter/dialogs/quotes.py index 2f7501f6..931558a2 100644 --- a/novelwriter/dialogs/quotes.py +++ b/novelwriter/dialogs/quotes.py @@ -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 ## diff --git a/novelwriter/dialogs/wordlist.py b/novelwriter/dialogs/wordlist.py index 65d19193..a667738a 100644 --- a/novelwriter/dialogs/wordlist.py +++ b/novelwriter/dialogs/wordlist.py @@ -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()) + ] diff --git a/novelwriter/error.py b/novelwriter/error.py index 53ac8846..03f5d07a 100644 --- a/novelwriter/error.py +++ b/novelwriter/error.py @@ -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: diff --git a/novelwriter/extensions/eventfilters.py b/novelwriter/extensions/eventfilters.py index 7e0939b6..09366590 100644 --- a/novelwriter/extensions/eventfilters.py +++ b/novelwriter/extensions/eventfilters.py @@ -24,9 +24,13 @@ along with this program. If not, see . """ 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) diff --git a/novelwriter/extensions/modified.py b/novelwriter/extensions/modified.py index 6a095d8e..9bea4c78 100644 --- a/novelwriter/extensions/modified.py +++ b/novelwriter/extensions/modified.py @@ -27,11 +27,9 @@ along with this program. If not, see . """ 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 diff --git a/novelwriter/extensions/pagedsidebar.py b/novelwriter/extensions/pagedsidebar.py index b58de63a..b1745816 100644 --- a/novelwriter/extensions/pagedsidebar.py +++ b/novelwriter/extensions/pagedsidebar.py @@ -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) diff --git a/novelwriter/extensions/progressbars.py b/novelwriter/extensions/progressbars.py index 456d1550..0c92564e 100644 --- a/novelwriter/extensions/progressbars.py +++ b/novelwriter/extensions/progressbars.py @@ -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: diff --git a/novelwriter/extensions/statusled.py b/novelwriter/extensions/statusled.py index 53f0c65e..5547a664 100644 --- a/novelwriter/extensions/statusled.py +++ b/novelwriter/extensions/statusled.py @@ -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) diff --git a/novelwriter/extensions/switch.py b/novelwriter/extensions/switch.py index 5a97edde..421fb712 100644 --- a/novelwriter/extensions/switch.py +++ b/novelwriter/extensions/switch.py @@ -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) diff --git a/novelwriter/extensions/switchbox.py b/novelwriter/extensions/switchbox.py index 9acc0cc9..c43def8a 100644 --- a/novelwriter/extensions/switchbox.py +++ b/novelwriter/extensions/switchbox.py @@ -23,8 +23,9 @@ along with this program. If not, see . """ 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 diff --git a/novelwriter/formats/todocx.py b/novelwriter/formats/todocx.py index 76577826..c173918d 100644 --- a/novelwriter/formats/todocx.py +++ b/novelwriter/formats/todocx.py @@ -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: diff --git a/novelwriter/formats/tohtml.py b/novelwriter/formats/tohtml.py index fb2c1ee5..c7bf7b17 100644 --- a/novelwriter/formats/tohtml.py +++ b/novelwriter/formats/tohtml.py @@ -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 = " ") -> 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 diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py index f44139a3..7d1aeec0 100644 --- a/novelwriter/formats/tokenizer.py +++ b/novelwriter/formats/tokenizer.py @@ -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 "" diff --git a/novelwriter/formats/tomarkdown.py b/novelwriter/formats/tomarkdown.py index 2f87813b..b87d0325 100644 --- a/novelwriter/formats/tomarkdown.py +++ b/novelwriter/formats/tomarkdown.py @@ -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__) diff --git a/novelwriter/formats/toodt.py b/novelwriter/formats/toodt.py index fce5eec1..15622845 100644 --- a/novelwriter/formats/toodt.py +++ b/novelwriter/formats/toodt.py @@ -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 diff --git a/novelwriter/formats/toqdoc.py b/novelwriter/formats/toqdoc.py index 0cccfd09..f4bc897b 100644 --- a/novelwriter/formats/toqdoc.py +++ b/novelwriter/formats/toqdoc.py @@ -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] diff --git a/novelwriter/formats/toraw.py b/novelwriter/formats/toraw.py index 241d7c77..c4ed979b 100644 --- a/novelwriter/formats/toraw.py +++ b/novelwriter/formats/toraw.py @@ -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__) diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index f4d1bff5..208db194 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -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: diff --git a/novelwriter/gui/dochighlight.py b/novelwriter/gui/dochighlight.py index 40d6c657..61082fa2 100644 --- a/novelwriter/gui/dochighlight.py +++ b/novelwriter/gui/dochighlight.py @@ -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__() diff --git a/novelwriter/gui/docviewerpanel.py b/novelwriter/gui/docviewerpanel.py index d395a4ec..7d996992 100644 --- a/novelwriter/gui/docviewerpanel.py +++ b/novelwriter/gui/docviewerpanel.py @@ -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__) diff --git a/novelwriter/gui/editordocument.py b/novelwriter/gui/editordocument.py index e2753038..d42ffaf1 100644 --- a/novelwriter/gui/editordocument.py +++ b/novelwriter/gui/editordocument.py @@ -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__) diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py index d873b8a1..9bece183 100644 --- a/novelwriter/gui/itemdetails.py +++ b/novelwriter/gui/itemdetails.py @@ -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 # ===== diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 7bc2d196..33f97dde 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -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) ) diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index a6551466..10a396b4 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -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"), diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 721324f7..f8b13e9f 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -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: diff --git a/novelwriter/gui/search.py b/novelwriter/gui/search.py index bf403012..323b2200 100644 --- a/novelwriter/gui/search.py +++ b/novelwriter/gui/search.py @@ -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__) diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index e69b1dad..f784623f 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -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__) diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index 729db7ee..207bc6fc 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -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"), } diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index ffff4560..fac283f3 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -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: diff --git a/novelwriter/shared.py b/novelwriter/shared.py index 9fbd5581..04add94c 100644 --- a/novelwriter/shared.py +++ b/novelwriter/shared.py @@ -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"{type(exception).__name__}: {str(exception)}" + text = f"{type(exception).__name__}: {exception!s}" self.setInformativeText(f"{info}
{text}" if info else text) return diff --git a/novelwriter/text/patterns.py b/novelwriter/text/patterns.py index c4e0bf07..d592b70c 100644 --- a/novelwriter/text/patterns.py +++ b/novelwriter/text/patterns.py @@ -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: diff --git a/novelwriter/tools/lipsum.py b/novelwriter/tools/lipsum.py index 48aefa2d..b2028dca 100644 --- a/novelwriter/tools/lipsum.py +++ b/novelwriter/tools/lipsum.py @@ -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 ## diff --git a/novelwriter/tools/manusbuild.py b/novelwriter/tools/manusbuild.py index f45d7a1f..22628f43 100644 --- a/novelwriter/tools/manusbuild.py +++ b/novelwriter/tools/manusbuild.py @@ -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__) diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index f06505d9..b6918b18 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -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) diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index f0f6d1c0..cfac8f11 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -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__) diff --git a/novelwriter/tools/noveldetails.py b/novelwriter/tools/noveldetails.py index 9f4bfb26..ec42ccf1 100644 --- a/novelwriter/tools/noveldetails.py +++ b/novelwriter/tools/noveldetails.py @@ -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__) diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py index 4d2a0679..3638c18d 100644 --- a/novelwriter/tools/welcome.py +++ b/novelwriter/tools/welcome.py @@ -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) diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py index 3a8aab06..f2c14be2 100644 --- a/novelwriter/tools/writingstats.py +++ b/novelwriter/tools/writingstats.py @@ -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())