diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py index aa6be34f..fa3dd30e 100644 --- a/novelwriter/__init__.py +++ b/novelwriter/__init__.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import getopt diff --git a/novelwriter/common.py b/novelwriter/common.py index f03fa823..8fd9386a 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -135,7 +135,7 @@ def checkPath(value: Any, default: Path) -> Path: def isHandle(value: Any) -> TypeGuard[str]: """Check if a string is a valid novelWriter handle. - Note: This is case sensitive. Must be lower case! + Note: This is case sensitive. Must be lower case. """ if not isinstance(value, str): return False @@ -219,7 +219,7 @@ def firstFloat(*args: Any) -> float: ## def formatInt(value: int) -> str: - """Formats an integer with k, M, G etc.""" + """Format an integer with k, M, G etc.""" if not isinstance(value, int): return "ERR" @@ -464,21 +464,21 @@ def fontMatcher(font: QFont) -> QFont: def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable: - """A replacement for Python lambdas that works for Qt slots.""" + """A replacement for Python lambdas that works for Qt slots.""" # noqa: D401 def wrapper(*a_: Any) -> None: func(*args, **kwargs) return wrapper def qtAddAction(parent: QWidget, label: str) -> QAction: - """Helper to add action to widget and always return the action.""" + """Helper to add action to widget and always return the action.""" # noqa: D401 action = QAction(label, parent) parent.addAction(action) return action def qtAddMenu(parent: QMenuBar | QMenu, label: str) -> QMenu: - """Helper to add menu to menu and always return the menu.""" + """Helper to add menu to menu and always return the menu.""" # noqa: D401 menu = QMenu(label, parent) parent.addMenu(menu) return menu @@ -487,7 +487,6 @@ def qtAddMenu(parent: QMenuBar | QMenu, label: str) -> QMenu: def encodeMimeHandles(mimeData: QMimeData, handles: list[str]) -> None: """Encode handles into a mime data object.""" mimeData.setData(nwConst.MIME_HANDLE, b"|".join(h.encode() for h in handles)) - return def decodeMimeHandles(mimeData: QMimeData) -> list[str]: @@ -536,7 +535,7 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str: elif first in ("{", "["): n += 1 indent = "\n"+" "*n - if n > nmax and nmax > 0: + if n > nmax > 0: buffer.append(chunk) else: buffer.append(chunk[0] + indent + chunk[1:]) @@ -544,13 +543,13 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str: elif first in ("}", "]"): n -= 1 indent = "\n"+" "*n - if n >= nmax and nmax > 0: + if n >= nmax > 0: buffer.append(chunk) else: buffer.append(indent + chunk) elif first == ",": - if n > nmax and nmax > 0: + if n > nmax > 0: buffer.append(chunk) else: buffer.append(chunk[0] + indent + chunk[1:].lstrip()) @@ -568,7 +567,7 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str: def xmlIndent(xml: ET.Element | ET.ElementTree) -> None: """A modified version of the XML indent function in the standard library. It behaves more closely to how the one from lxml does. - """ + """ # noqa: D401 tree = xml.getroot() if isinstance(xml, ET.ElementTree) else xml if not isinstance(tree, ET.Element): return @@ -598,8 +597,6 @@ def xmlIndent(xml: ET.Element | ET.ElementTree) -> None: if last is not None: last.tail = indentations[level] - return - if len(tree): indentChildren(tree, 0) tree.tail = "\n" @@ -614,7 +611,7 @@ def xmlElement( attrib: dict | None = None, tail: str | None = None, ) -> ET.Element: - """A custom implementation of Element with more arguments.""" + """A custom implementation of Element with more arguments.""" # noqa: D401 xSub = ET.Element(tag, attrib=attrib or {}) if text is not None: if isinstance(text, bool): @@ -634,7 +631,7 @@ def xmlSubElem( attrib: dict | None = None, tail: str | None = None, ) -> ET.Element: - """A custom implementation of SubElement with more arguments.""" + """A custom implementation of SubElement with more arguments.""" # noqa: D401 xSub = ET.SubElement(parent, tag, attrib=attrib or {}) if text is not None: if isinstance(text, bool): @@ -665,6 +662,7 @@ def readTextFile(path: str | Path) -> str: def makeFileNameSafe(text: str) -> str: """Return a filename-safe string. + See: https://unicode.org/reports/tr15/#Norm_Forms """ text = unicodedata.normalize("NFKC", text).strip() @@ -697,7 +695,7 @@ _T_Enum = TypeVar("_T_Enum", bound=Enum) class NWConfigParser(ConfigParser): - """Common: Adapted Config Parser + """Common: Adapted Config Parser. This is a subclass of the standard config parser that adds type safe helper functions, and support for lists. It also turns off @@ -706,7 +704,6 @@ class NWConfigParser(ConfigParser): def __init__(self) -> None: super().__init__(interpolation=None) - return def rdStr(self, section: str, option: str, default: str) -> str: """Read string value.""" diff --git a/novelwriter/config.py b/novelwriter/config.py index df820029..6ce9d434 100644 --- a/novelwriter/config.py +++ b/novelwriter/config.py @@ -22,7 +22,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -63,6 +63,13 @@ DEF_TREECOL = "theme" class Config: + """User Config. + + The main user config. The state of the config is stored in the + novelwriter.conf file between sessions. Most of the settings can be + modified by the user in the Preferences dialog, but some just record + various states of the GUI. + """ __slots__ = ( "_appPath", "_appRoot", "_backPath", "_backupPath", "_confPath", "_dLocale", "_dShortDate", @@ -300,8 +307,6 @@ class Config: # Packages self.hasEnchant = False # The pyenchant package - return - ## # Properties ## @@ -350,7 +355,6 @@ class Config: def setLastAuthor(self, value: str) -> None: """Set tle last used author name.""" self._lastAuthor = simplified(value) - return def setMainWinSize(self, width: int, height: int) -> None: """Set the size of the main window, but only if the change is @@ -362,17 +366,14 @@ class Config: self.mainWinSize[0] = width if abs(self.mainWinSize[1] - height) > 5: self.mainWinSize[1] = height - return def setWelcomeWinSize(self, width: int, height: int) -> None: """Set the size of the Preferences dialog window.""" self.welcomeWinSize = [width, height] - return def setPreferencesWinSize(self, width: int, height: int) -> None: """Set the size of the Preferences dialog window.""" self.prefsWinSize = [width, height] - return def setLastPath(self, key: str, path: str | Path) -> None: """Set the last used path. Only the folder is saved, so if the @@ -384,12 +385,10 @@ class Config: path = path.parent if path.is_dir(): self._recentPaths.setPath(key, path) - return def setBackupPath(self, path: Path | str) -> None: """Set the current backup path.""" self._backupPath = checkPath(path, self._backPath) - return def setGuiFont(self, value: QFont | str | None) -> None: """Update the GUI's font style from settings.""" @@ -410,7 +409,6 @@ class Config: self.guiFont = fontMatcher(font) logger.debug("Main font set to: %s", describeFont(font)) QApplication.setFont(self.guiFont) - return def setTextFont(self, value: QFont | str | None) -> None: """Set the text font if it exists. If it doesn't, or is None, @@ -436,14 +434,13 @@ class Config: font = QFontDatabase.systemFont(QFontDatabase.SystemFont.GeneralFont) self.textFont = fontMatcher(font) logger.debug("Text font set to: %s", describeFont(self.textFont)) - return ## # Methods ## def homePath(self) -> Path: - """The user's home folder.""" + """Return the user's home folder.""" return self._homePath def dataPath(self, target: str | None = None) -> Path: @@ -525,7 +522,6 @@ class Config: """Send a message to the splash screen.""" if self._splash: self._splash.showStatus(message) - return ## # Config Actions @@ -568,8 +564,6 @@ class Config: logger.debug("Config instance initialised") - return - def initLocalisation(self, nwApp: QApplication) -> None: """Initialise the localisation of the GUI.""" self.splashMessage("Loading localisation ...") @@ -597,8 +591,6 @@ class Config: nwApp.installTranslator(qTrans) self._qtTrans[lngFile] = qTrans - return - def loadConfig(self, splash: NSplashScreen | None = None) -> bool: """Load preferences from file and replace default settings.""" self._splash = splash @@ -872,7 +864,6 @@ class Config: def finishStartup(self) -> None: """Call after startup is complete.""" self._splash = None - return ## # Internal Functions @@ -894,7 +885,6 @@ class Config: else: self.hasEnchant = True logger.debug("Checking package 'pyenchant': OK") - return def _prepareFont(self, font: QFont, kind: str) -> None: """Check Unicode availability in font. This also initialises any @@ -905,16 +895,15 @@ class Config: for char in nwUnicode.UI_SYMBOLS: if not metrics.inFont(char): # type: ignore logger.warning("No glyph U+%04x in font", ord(char)) # pragma: no cover - return class RecentProjects: + """A record of recently opened projects.""" def __init__(self, config: Config) -> None: self._conf = config self._data: dict[str, dict[str, str | int]] = {} self._map: dict[str, str] = {} - return def loadCache(self) -> bool: """Load the cache file for recent projects.""" @@ -976,14 +965,12 @@ class RecentProjects: self.saveCache() except Exception: pass - return def remove(self, path: str | Path) -> None: """Try to remove a path from the recent projects cache.""" if self._data.pop(str(path), None) is not None: logger.debug("Removed recent: %s", path) self.saveCache() - return def _setEntry( self, puuid: str, path: str, title: str, words: int, chars: int, saved: int @@ -998,24 +985,22 @@ class RecentProjects: } if puuid: self._map[puuid] = path - return class RecentPaths: + """A record of recently used file paths.""" KEYS: Final[list[str]] = ["default", "project", "import", "outline", "stats"] def __init__(self, config: Config) -> None: self._conf = config self._data = {} - return def setPath(self, key: str, path: Path | str) -> None: """Set a path for a given key, and save the cache.""" if key in self.KEYS: self._data[key] = str(path) self.saveCache() - return def getPath(self, key: str) -> str | None: """Get a path for a given key, or return None.""" diff --git a/novelwriter/constants.py b/novelwriter/constants.py index e6aaa360..5ea3f8ff 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from typing import Final @@ -34,16 +34,17 @@ from novelwriter.enum import ( def trConst(text: str) -> str: - """Wrapper function for locally translating constants.""" + """Translate a constant.""" return QCoreApplication.translate("Constant", text) def trStats(text: str) -> str: - """Wrapper function for locally translating stats constants.""" + """Translate a stats constants.""" return QCoreApplication.translate("Stats", text) class nwConst: + """Various Constants.""" # Date and Time Formats FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format @@ -70,6 +71,7 @@ class nwConst: class nwRegEx: + """Common RegExes.""" URL = r"https?://(?:www\.|(?!www))[\w/()@:%_\+-.~#?&=]+" WORDS = r"\b[^\s\-\+\/–—\[\]:]+\b" @@ -83,6 +85,7 @@ class nwRegEx: class nwShortcode: + """Document ShortCodes.""" BOLD_O = "[b]" BOLD_C = "[/b]" @@ -112,6 +115,7 @@ class nwShortcode: class nwStyles: + """Style Settings for Headings.""" H_VALID = ("H0", "H1", "H2", "H3", "H4") H_LEVEL: Final[dict[str, int]] = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4} @@ -143,6 +147,7 @@ class nwStyles: class nwFiles: + """novelWriter Files.""" # Config Files CONF_FILE = "novelwriter.conf" @@ -163,6 +168,7 @@ class nwFiles: class nwKeyWords: + """Meta Data KeyWord Constants.""" TAG_KEY = "@tag" POV_KEY = "@pov" @@ -210,6 +216,7 @@ class nwKeyWords: class nwLists: + """Various Lists.""" USER_CLASSES: Final[list[nwItemClass]] = [ nwItemClass.CHARACTER, @@ -223,6 +230,7 @@ class nwLists: class nwStats: + """Text Statistics.""" CHARS = "allChars" CHARS_TEXT = "textChars" @@ -246,6 +254,7 @@ class nwStats: class nwLabels: + """Various Common GUI Labels.""" CLASS_NAME: Final[dict[nwItemClass, str]] = { nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"), @@ -472,6 +481,7 @@ class nwLabels: class nwHeadFmt: + """Manuscript Header Formats.""" BR = "{BR}" TITLE = "{Title}" @@ -498,8 +508,10 @@ class nwHeadFmt: class nwQuotes: """Allowed quotation marks. + Source: https://en.wikipedia.org/wiki/Quotation_mark """ + SYMBOLS: Final[dict[str, str]] = { "\u0027": QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"), "\u0022": QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"), @@ -541,6 +553,7 @@ class nwQuotes: class nwUnicode: """Supported unicode character constants and their HTML equivalents.""" + # Unicode Constants # ================= @@ -672,6 +685,7 @@ class nwUnicode: class nwHtmlUnicode: + """Unicode to HTML Map.""" U_TO_H: Final[dict[str, str]] = { # Quotes diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index c44d9223..7bf6484f 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -211,7 +211,7 @@ class FilterMode(Enum): class BuildSettings: - """Core: Build Settings Class + """Core: Build Settings Class. This class manages the build settings for a Manuscript build job. The settings can be packed/unpacked to/from a dictionary for JSON. @@ -229,7 +229,6 @@ class BuildSettings: self._included = set() self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()} self._changed = False - return @classmethod def fromDict(cls, data: dict) -> BuildSettings: @@ -315,7 +314,6 @@ class BuildSettings: def setName(self, name: str) -> None: """Set the build setting display name.""" self._name = str(name) - return def setBuildID(self, value: str | uuid.UUID) -> None: """Set a UUID build ID.""" @@ -324,13 +322,11 @@ class BuildSettings: self._uuid = str(uuid.uuid4()) elif value != self._uuid: self._uuid = value - return def setOrder(self, value: int) -> None: """Set the build order.""" if isinstance(value, int): self._order = value - return def setLastBuildPath(self, path: Path | str | None) -> None: """Set the last used build path.""" @@ -341,41 +337,35 @@ class BuildSettings: else: self._path = CONFIG.homePath() self._changed = True - return def setLastBuildName(self, name: str) -> None: """Set the last used build name.""" self._build = str(name).strip() self._changed = True - return def setLastFormat(self, value: nwBuildFmt) -> None: """Set the last used build format.""" if isinstance(value, nwBuildFmt): self._format = value self._changed = True - return def setFiltered(self, tHandle: str) -> None: """Set an item as filtered.""" self._excluded.discard(tHandle) self._included.discard(tHandle) self._changed = True - return def setIncluded(self, tHandle: str) -> None: """Set an item as explicitly included.""" self._excluded.discard(tHandle) self._included.add(tHandle) self._changed = True - return def setExcluded(self, tHandle: str) -> None: """Set an item as explicitly excluded.""" self._excluded.add(tHandle) self._included.discard(tHandle) self._changed = True - return def setAllowRoot(self, tHandle: str, state: bool) -> None: """Set a specific root folder as allowed or not.""" @@ -385,14 +375,12 @@ class BuildSettings: elif state is False: self._skipRoot.add(tHandle) self._changed = True - return def setValue(self, key: str, value: T_BuildValue) -> None: """Set a specific value for a build setting.""" if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]): self._changed |= (value != self._settings[key]) self._settings[key] = value - return ## # Methods @@ -463,7 +451,6 @@ class BuildSettings: called when the changes have been safely saved or passed on. """ self._changed = False - return def pack(self) -> dict: """Pack all content into a JSON compatible dictionary.""" @@ -516,8 +503,6 @@ class BuildSettings: self._changed = False - return - @classmethod def duplicate(cls, source: BuildSettings) -> BuildSettings: """Make a copy of another build.""" @@ -529,7 +514,7 @@ class BuildSettings: class BuildCollection: - """Core: Build Collection Class + """Core: Build Collection Class. This object holds all the build setting objects defined by the given project. The build settings are saved as a single JSON file in the @@ -542,7 +527,6 @@ class BuildCollection: self._defaultBuild = "" self._builds: dict[str, BuildSettings] = {} self._loadCollection() - return def __len__(self) -> int: """Return the number of builds.""" @@ -581,21 +565,18 @@ class BuildCollection: build.setOrder(i) self._lastBuild = lastBuild self._saveCollection() - return def setDefaultBuild(self, buildID: str) -> None: """Set the default build id.""" if buildID != self._defaultBuild: self._defaultBuild = buildID self._saveCollection() - return def setBuild(self, build: BuildSettings) -> None: """Set build settings data in the collection.""" if isinstance(build, BuildSettings): self._builds[build.buildID] = build self._saveCollection() - return ## # Methods @@ -605,7 +586,6 @@ class BuildCollection: """Remove a build from the collection.""" self._builds.pop(buildID, None) self._saveCollection() - return def builds(self) -> Iterable[tuple[str, str]]: """Iterate over all available builds.""" diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py index a46a9e72..2a99b236 100644 --- a/novelwriter/core/coretools.py +++ b/novelwriter/core/coretools.py @@ -23,7 +23,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -52,7 +52,9 @@ logger = logging.getLogger(__name__) class DocMerger: - """Document tool for merging a set of documents into a single new + """Tool: Merge Documents. + + Document tool for merging a set of documents into a single new document. The parameters are defined by the user using the GuiDocMerge dialog. """ @@ -62,7 +64,6 @@ class DocMerger: self._error = "" self._target = None self._text = [] - return @property def targetHandle(self) -> str | None: @@ -85,7 +86,6 @@ class DocMerger: """ self._target = self._project.tree[tHandle] self._text = [] - return def newTargetDoc(self, sHandle: str, label: str) -> None: """Create a brand new target document based on a source handle @@ -101,7 +101,6 @@ class DocMerger: nwItem.notifyToRefresh() self._target = nwItem self._text = [] - return def appendText(self, sHandle: str, addComment: bool, cmtPrefix: str) -> None: """Append text from an existing document to the text buffer.""" @@ -112,7 +111,6 @@ class DocMerger: status, _ = item.getImportStatus() text = f"% {cmtPrefix} {info}: {item.itemName} [{status}]\n\n{text}" self._text.append(text) - return def writeTargetDoc(self) -> bool: """Write the accumulated text into the designated target @@ -158,10 +156,7 @@ class DocSplitter: self._srcHandle = sHandle self._srcItem = srcItem - return - def __len__(self) -> int: - """The length of the split job.""" return len(self._rawData) ## @@ -178,7 +173,6 @@ class DocSplitter: """ self._parHandle = pHandle self._inFolder = False - return def newParentFolder(self, pHandle: str, folderLabel: str) -> None: """Create a new folder that will be the top level parent item @@ -192,7 +186,6 @@ class DocSplitter: nwItem.notifyToRefresh() self._parHandle = nHandle self._inFolder = True - return def splitDocument(self, splitData: list, splitText: list[str]) -> None: """Loop through the split data record and perform the split job @@ -204,12 +197,9 @@ class DocSplitter: chunk = buffer[lineNo:] buffer = buffer[:lineNo] self._rawData.insert(0, (chunk, hLevel, hLabel)) - return def writeDocuments(self, docHierarchy: bool) -> Iterable[bool]: - """An iterator that will write each document in the buffer, and - return its new handle, parent handle, and sibling handle. - """ + """Write each document in the buffer and yield if successful.""" if self._srcHandle and self._srcItem and self._parHandle: pHandle = self._parHandle hHandle = [self._parHandle, None, None, None, None] @@ -260,7 +250,6 @@ class DocDuplicator: def __init__(self, project: NWProject) -> None: self._project = project - return ## # Methods @@ -293,13 +282,16 @@ class DocDuplicator: class DocSearch: + """Tool> Search Documents. + + A global document search class. + """ def __init__(self) -> None: self._regEx = re.compile(r"") self._opts = re.IGNORECASE self._words = False self._escape = True - return ## # Methods @@ -308,22 +300,19 @@ class DocSearch: def setCaseSensitive(self, state: bool) -> None: """Set the case sensitive search flag.""" self._opts = 0 if state else re.IGNORECASE - return def setWholeWords(self, state: bool) -> None: """Set the whole words search flag.""" self._words = state - return def setUserRegEx(self, state: bool) -> None: """Set the escape flag to the opposite state.""" self._escape = not state - return def iterSearch( self, project: NWProject, search: str ) -> Iterable[tuple[NWItem, list[tuple[int, int, str]], bool]]: - """Iteratively search through documents in a project.""" + """Iterate through documents in a project and apply search.""" self._regEx = re.compile(self._buildPattern(search), self._opts) logger.debug("Searching with pattern '%s'", self._regEx.pattern) storage = project.storage @@ -376,7 +365,6 @@ class ProjectBuilder: def __init__(self) -> None: self._path = None self.tr = partial(QCoreApplication.translate, "ProjectBuilder") - return @property def projPath(self) -> Path | None: @@ -620,4 +608,3 @@ class ProjectBuilder: project.index.rebuild() project.saveProject() project.closeProject() - return diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 8e89095d..32bccb1d 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -53,7 +53,7 @@ logger = logging.getLogger(__name__) class NWBuildDocument: - """Core: Manuscript Document Build Class + """Core: Manuscript Document Build Class. This is the core tool that assembles a project and outputs a manuscript, based on a build definition object (BuildSettings). @@ -72,7 +72,6 @@ class NWBuildDocument: self._cache = None self._count = False self._outline = False - return ## # Properties @@ -106,7 +105,6 @@ class NWBuildDocument: def addDocument(self, tHandle: str) -> None: """Add a document to the build queue manually.""" self._queue.append(tHandle) - return def queueAll(self) -> None: """Queue all document as defined by the build settings.""" @@ -115,7 +113,6 @@ class NWBuildDocument: for item in self._project.tree: if filtered.get(item.itemHandle, False): self._queue.append(item.itemHandle) - return def iterBuildPreview(self, newPage: bool) -> Iterable[tuple[int, bool]]: """Build a preview QTextDocument.""" @@ -131,7 +128,7 @@ class NWBuildDocument: return def iterBuildDocument(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]: - """Wrapper for builders based on format.""" + """Select a builder based on format.""" self._error = None self._cache = None @@ -341,12 +338,10 @@ class NWBuildDocument: scale*self._build.getFloat("format.rightMargin"), ) - filtered = self._build.buildItemFilter( + return self._build.buildItemFilter( self._project, withRoots=self._build.getBool("text.addNoteHeadings") ) - return filtered - def _doBuild(self, bldObj: Tokenizer, tHandle: str, convert: bool = True) -> bool: """Build a single document and add it to the build object.""" tItem = self._project.tree[tHandle] diff --git a/novelwriter/core/document.py b/novelwriter/core/document.py index e0f469e9..1cb6809c 100644 --- a/novelwriter/core/document.py +++ b/novelwriter/core/document.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import hashlib @@ -42,7 +42,7 @@ logger = logging.getLogger(__name__) class NWDocument: - """Core: Document Class + """Core: Document Class. A Class wrapping a single novelWriter document file. It represents a project item of nwItemType FILE. The file is not guaranteed to @@ -68,8 +68,6 @@ class NWDocument: if self._handle is not None: self._item = self._project.tree[tHandle] - return - def __repr__(self) -> str: return f"" @@ -357,5 +355,3 @@ class NWDocument: else: logger.debug("Unknown meta data: '%s'", metaLine.strip()) - - return diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index a0c11ff1..2a9aba3a 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -22,7 +22,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -56,7 +56,7 @@ KEY_SOURCE = "0123456789bcdfghjklmnpqrstvwxz" class Index: - """Core: Project Index + """Core: Project Index. This class holds the entire index for a given project. The index contains the data that isn't stored in the project items themselves. @@ -99,8 +99,6 @@ class Index: self._indexChange = 0.0 self._rootChange = {} - return - def __repr__(self) -> str: return f"" @@ -129,7 +127,6 @@ class Index: def setNovelModelExtraColumn(self, extra: nwNovelExtra) -> None: """Set the data content type of the novel model extra column.""" self._novelExtra = extra - return ## # Public Methods @@ -142,7 +139,6 @@ class Index: self._indexChange = 0.0 self._rootChange = {} SHARED.emitIndexCleared(self._project) - return def rebuild(self) -> None: """Rebuild the entire index from scratch.""" @@ -158,7 +154,6 @@ class Index: for tHandle in self._novelModels: self.refreshNovelModel(tHandle) SHARED.clearMainProgress() - return def deleteHandle(self, tHandle: str) -> None: """Delete all entries of a given document handle.""" @@ -168,7 +163,6 @@ class Index: del self._tagsIndex[tTag] del self._itemIndex[tHandle] SHARED.emitIndexChangedTags(self._project, [], delTags) - return def reIndexHandle(self, tHandle: str | None) -> None: """Put a file back into the index. This is used when files are @@ -178,7 +172,6 @@ class Index: if tHandle and self._project.tree.checkType(tHandle, nwItemType.FILE): logger.debug("Re-indexing item '%s'", tHandle) self.scanText(tHandle, self._project.storage.getDocumentText(tHandle)) - return def refreshHandle(self, tHandle: str) -> None: """Update the class for all tags of a handle.""" @@ -188,7 +181,6 @@ class Index: self.deleteHandle(tHandle) else: self._tagsIndex.updateClass(tHandle, item.itemClass.name) - return def indexChangedSince(self, checkTime: int | float) -> bool: """Check if the index has changed since a given time.""" @@ -211,7 +203,6 @@ class Index: model.setExtraColumn(self._novelExtra) self._appendSubTreeToModel(tHandle, model) model.endResetModel() - return def updateNovelModelData(self, nwItem: NWItem) -> bool: """Refresh a novel model.""" @@ -428,8 +419,6 @@ class Index: if updated or deleted: SHARED.emitIndexChangedTags(self._project, updated, deleted) - return - def _scanInactive(self, nwItem: NWItem, text: str) -> None: """Scan an inactive document for meta data.""" for line in text.splitlines(): @@ -438,7 +427,6 @@ class Index: if hDepth != "H0": nwItem.setMainHeading(hDepth) break - return def _splitHeading(self, line: str) -> tuple[str, str]: """Split a heading into its heading level and text value.""" @@ -462,7 +450,6 @@ class Index: """Count text stats and save the counts to the index.""" cC, wC, pC = standardCounter(text) self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC) - return def _indexKeyword( self, tHandle: str, line: str, sTitle: str, itemClass: nwItemClass, tags: dict[str, bool] @@ -498,7 +485,6 @@ class Index: model.setExtraColumn(self._novelExtra) self._appendSubTreeToModel(tHandle, model) self._novelModels[tHandle] = model - return def _appendSubTreeToModel(self, tHandle: str, model: NovelModel) -> None: """Append all active novel documents to a novel model.""" @@ -509,7 +495,6 @@ class Index: and node.item.isActive ): model.append(node) - return ## # Check @ Lines @@ -683,15 +668,13 @@ class Index: "words": hItem.wordCount, } - result = [( + return [( tKey, tData[tKey]["level"], tData[tKey]["title"], tData[tKey]["words"] ) for tKey in tOrder] - return result - def getCounts(self, tHandle: str, sTitle: str | None = None) -> tuple[int, int, int]: """Return the counts for a file, or a section of a file, starting at title sTitle if it is provided. @@ -796,7 +779,7 @@ class Index: # ===================== class TagsIndex: - """Core: Tags Index Wrapper Class + """Core: Tags Index Wrapper Class. A wrapper class that holds the reverse lookup tags index. This is just a simple wrapper around a single dictionary to keep tighter @@ -807,14 +790,12 @@ class TagsIndex: def __init__(self) -> None: self._tags: dict[str, dict[str, str]] = {} - return def __contains__(self, tagKey: str) -> bool: return tagKey.lower() in self._tags def __delitem__(self, tagKey: str) -> None: self._tags.pop(tagKey.lower(), None) - return def __getitem__(self, tagKey: str) -> dict | None: return self._tags.get(tagKey.lower(), None) @@ -826,7 +807,6 @@ class TagsIndex: def clear(self) -> None: """Clear the index.""" self._tags = {} - return def items(self) -> ItemsView: """Return a dictionary view of all tags.""" @@ -842,7 +822,6 @@ class TagsIndex: "heading": sTitle, "class": className, } - return def tagName(self, tagKey: str, default: str = "") -> str: """Get the name of a given tag.""" @@ -882,7 +861,6 @@ class TagsIndex: for entry in self._tags.values(): if entry.get("handle") == tHandle: entry["class"] = className - return ## # Pack/Unpack @@ -925,11 +903,9 @@ class TagsIndex: self.add(name, display, handle, heading, className) - return - class IndexCache: - """Core: Item Index Lookup Data Class + """Core: Item Index Lookup Data Class. A small data class passed between all objects of the Item Index which provides lookup capabilities and caching for shared data. @@ -941,14 +917,13 @@ class IndexCache: self.tags: TagsIndex = tagsIndex self.story: set[str] = set() self.note: set[str] = set() - return # The Item Index Objects # ====================== class ItemIndex: - """Core: Item Index Wrapper Class + """Core: Item Index Wrapper Class. A wrapper object holding the indexed items. This is a wrapper class around a single storage dictionary with a set of utility @@ -963,14 +938,12 @@ class ItemIndex: self._project = project self._cache = IndexCache(tagsIndex) self._items: dict[str, IndexNode] = {} - return def __contains__(self, tHandle: str) -> bool: return tHandle in self._items def __delitem__(self, tHandle: str) -> None: self._items.pop(tHandle, None) - return def __getitem__(self, tHandle: str) -> IndexNode | None: return self._items.get(tHandle, None) @@ -982,14 +955,12 @@ class ItemIndex: def clear(self) -> None: """Clear the index.""" self._items = {} - return def add(self, tHandle: str, nwItem: NWItem) -> None: """Add a new item to the index. This will overwrite the item if it already exists. """ self._items[tHandle] = IndexNode(self._cache, tHandle, nwItem) - return def allStoryKeys(self) -> set[str]: """Return all story structure keys.""" @@ -1064,7 +1035,6 @@ class ItemIndex: """ if tHandle in self._items: self._items[tHandle].setHeadingCounts(sTitle, cC, wC, pC) - return def setHeadingComment( self, tHandle: str, sTitle: str, @@ -1073,25 +1043,21 @@ class ItemIndex: """Set a story comment for a heading on a given item.""" if tHandle in self._items: self._items[tHandle].setHeadingComment(sTitle, comment, key, text) - return def setHeadingTag(self, tHandle: str, sTitle: str, tagKey: str) -> None: """Set the main tag for a heading on a given item.""" if tHandle in self._items: self._items[tHandle].setHeadingTag(sTitle, tagKey) - return def addHeadingRef(self, tHandle: str, sTitle: str, tagKeys: list[str], refType: str) -> None: """Set the reference tags for a heading on a given item.""" if tHandle in self._items: self._items[tHandle].addHeadingRef(sTitle, tagKeys, refType) - return def addNoteKey(self, tHandle: str, style: T_NoteTypes, key: str) -> None: """Set notes key for a given item.""" if tHandle in self._items: self._items[tHandle].addNoteKey(style, key) - return def genNewNoteKey(self, tHandle: str, style: T_NoteTypes) -> str: """Set notes key for a given item.""" @@ -1131,5 +1097,3 @@ class ItemIndex: tItem = IndexNode(self._cache, tHandle, nwItem) tItem.unpackData(tData) self._items[tHandle] = tItem - - return diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py index b74ce4c4..b7867bd3 100644 --- a/novelwriter/core/indexdata.py +++ b/novelwriter/core/indexdata.py @@ -23,7 +23,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -50,7 +50,7 @@ NOTE_TYPES: list[T_NoteTypes] = ["footnotes", "comments"] class IndexNode: - """Core: Single Index Item Node Class + """Core: Single Index Item Node Class. This object represents the index data of a project item (NWItem). It holds a record of all the headings in the text, and the meta data @@ -68,7 +68,6 @@ class IndexNode: self._headings: dict[str, IndexHeading] = {TT_NONE: IndexHeading(self._cache, TT_NONE)} self._notes: dict[str, set[str]] = {} self._count = 0 - return def __repr__(self) -> str: return f"" @@ -107,39 +106,33 @@ class IndexNode: if TT_NONE in self._headings: self._headings.pop(TT_NONE) self._headings[tHeading.key] = tHeading - return def setHeadingCounts(self, sTitle: str, cCount: int, wCount: int, pCount: int) -> None: """Set the character, word and paragraph count of a heading.""" if sTitle in self._headings: self._headings[sTitle].setCounts([cCount, wCount, pCount]) - return def setHeadingComment(self, sTitle: str, comment: nwComment, key: str, text: str) -> None: """Set the comment text of a heading.""" if sTitle in self._headings: self._headings[sTitle].setComment(comment.name, key, text) - return def setHeadingTag(self, sTitle: str, tag: str) -> None: """Set the tag of a heading.""" if sTitle in self._headings: self._headings[sTitle].setTag(tag) - return def addHeadingRef(self, sTitle: str, tags: list[str], keyword: str) -> None: """Add a reference key and all its types to a heading.""" if sTitle in self._headings: for tag in tags: self._headings[sTitle].addReference(tag, keyword) - return def addNoteKey(self, style: T_NoteTypes, key: str) -> None: """Add a note key to the index.""" if style not in self._notes: self._notes[style] = set() self._notes[style].add(key) - return ## # Data Methods @@ -195,11 +188,10 @@ class IndexNode: self._notes[style] = set(keys) else: raise KeyError("Index node contains an invalid key") - return class IndexHeading: - """Core: Single Index Heading Class + """Core: Single Index Heading Class. This object represents a section of text in a project item associated with a single (valid) heading. It holds a separate record @@ -224,7 +216,6 @@ class IndexHeading: self._tag = "" self._refs: dict[str, set[str]] = {} self._comments: dict[str, str] = {} - return def __repr__(self) -> str: return f"" @@ -289,12 +280,10 @@ class IndexHeading: """Set the level of the heading if it's a valid value.""" if level in nwStyles.H_VALID: self._level = level - return def setLine(self, line: int) -> None: """Set the line number of a heading.""" self._line = max(0, checkInt(line, 0)) - return def setCounts(self, counts: Sequence[int]) -> None: """Set the character, word and paragraph count. Make sure the @@ -306,7 +295,6 @@ class IndexHeading: max(0, checkInt(counts[1], 0)), max(0, checkInt(counts[2], 0)), ) - return def setComment(self, comment: str, key: str, text: str) -> None: """Set the text for a comment and make sure it is a string.""" @@ -319,12 +307,10 @@ class IndexHeading: case "note" if key: self._cache.note.add(key) self._comments[f"note.{key}"] = str(text) - return def setTag(self, tag: str) -> None: """Set the tag for references, and make sure it is a string.""" self._tag = str(tag).lower() - return def addReference(self, tag: str, keyword: str) -> None: """Add a record of a reference tag, and what keyword types it is @@ -335,7 +321,6 @@ class IndexHeading: if tag not in self._refs: self._refs[tag] = set() self._refs[tag].add(keyword) - return ## # Getters @@ -409,4 +394,3 @@ class IndexHeading: self.setComment(comment, compact(kind), str(entry)) else: raise KeyError("Unknown key in heading entry") - return diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index 9bc52284..77b8d6c2 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -44,7 +44,7 @@ logger = logging.getLogger(__name__) class NWItem: - """Core: Item Data Class + """Core: Item Data Class. This class holds all the project information about a project item. Each item must be associated with a project and have a valid handle. @@ -84,16 +84,14 @@ class NWItem: self._wordInit = 0 # Initial character count self._charInit = 0 # Initial word count - return - def __repr__(self) -> str: return f"" def __bool__(self) -> bool: - """The truthiness of the class. The handle used to be initiated - to None, but this is no longer the case. It should always - evaluate to True since 2.1-beta1, although unpack and the NWTree - class can leave it as an empty string. + """Check the truthiness of the class. The handle used to be + initiated to None, but this is no longer the case. It should + always evaluate to True since 2.1-beta1, although unpack and the + NWTree class can leave it as an empty string. """ return bool(self._handle) @@ -206,15 +204,13 @@ class NWItem: meta["cursorPos"] = str(self._cursorPos) name["active"] = yesNo(self._active) - data = { + return { "name": str(self._name), "itemAttr": item, "metaAttr": meta, "nameAttr": name, } - return data - def unpack(self, data: dict) -> bool: """Set the values from a data dictionary.""" item = data.get("itemAttr", {}) @@ -298,13 +294,11 @@ class NWItem: def notifyToRefresh(self) -> None: """Notify GUI that item info needs to be refreshed.""" self._project.tree.refreshItems([self._handle]) - return def notifyNovelStructureChange(self) -> None: """Notify that the structure of a novel has changed.""" if self._root and self._class == nwItemClass.NOVEL: self._project.tree.novelStructureChanged(self._root) - return ## # Lookup Methods @@ -457,8 +451,6 @@ class NWItem: if self._import is None: self.setImport("New") # This forces a default value lookup - return - ## # Set Item Values ## @@ -469,7 +461,6 @@ class NWItem: self._name = simplified(name) else: self._name = "" - return def setParent(self, handle: Any) -> None: """Set the parent handle, and ensure it is valid.""" @@ -479,7 +470,6 @@ class NWItem: self._parent = handle else: self._parent = None - return def setRoot(self, handle: Any) -> None: """Set the root handle, and ensure it is valid.""" @@ -489,7 +479,6 @@ class NWItem: self._root = handle else: self._root = None - return def setOrder(self, order: Any) -> None: """Set the item order, and ensure that it is valid. This value @@ -497,7 +486,6 @@ class NWItem: the moment. """ self._order = checkInt(order, 0) - return def setType(self, value: Any) -> None: """Set the item type from either a proper nwItemType, or set it @@ -510,7 +498,6 @@ class NWItem: else: logger.error("Unrecognised item type '%s'", value) self._type = nwItemType.NO_TYPE - return def setClass(self, value: Any) -> None: """Set the item class from either a proper nwItemClass, or set @@ -523,7 +510,6 @@ class NWItem: else: logger.error("Unrecognised item class '%s'", value) self._class = nwItemClass.NO_CLASS - return def setLayout(self, value: Any) -> None: """Set the item layout from either a proper nwItemLayout, or set @@ -536,21 +522,18 @@ class NWItem: else: logger.error("Unrecognised item layout '%s'", value) self._layout = nwItemLayout.NO_LAYOUT - return def setStatus(self, value: Any) -> None: """Set the item status by looking it up in the valid status items of the current project. """ self._status = self._project.data.itemStatus.check(value) - return def setImport(self, value: Any) -> None: """Set the item importance by looking it up in the valid import items of the current project. """ self._import = self._project.data.itemImport.check(value) - return def setActive(self, state: Any) -> None: """Set the active flag.""" @@ -558,7 +541,6 @@ class NWItem: self._active = state else: self._active = False - return def setExpanded(self, state: Any) -> None: """Set the expanded status of an item in the project tree.""" @@ -566,7 +548,6 @@ class NWItem: self._expanded = state else: self._expanded = False - return ## # Set Document Meta Data @@ -576,7 +557,6 @@ class NWItem: """Set the main heading level.""" if value in nwStyles.H_LEVEL: self._heading = value - return def setCharCount(self, count: Any) -> None: """Set the character count, and ensure that it is an integer.""" @@ -584,7 +564,6 @@ class NWItem: self._charCount = max(0, count) else: self._charCount = 0 - return def setWordCount(self, count: Any) -> None: """Set the word count, and ensure that it is an integer.""" @@ -592,7 +571,6 @@ class NWItem: self._wordCount = max(0, count) else: self._wordCount = 0 - return def setParaCount(self, count: Any) -> None: """Set the paragraph count, and ensure that it is an integer.""" @@ -600,7 +578,6 @@ class NWItem: self._paraCount = max(0, count) else: self._paraCount = 0 - return def setCursorPos(self, position: Any) -> None: """Set the cursor position, and ensure that it is an integer.""" @@ -608,4 +585,3 @@ class NWItem: self._cursorPos = max(0, position) else: self._cursorPos = 0 - return diff --git a/novelwriter/core/itemmodel.py b/novelwriter/core/itemmodel.py index 89696997..10ebaf6d 100644 --- a/novelwriter/core/itemmodel.py +++ b/novelwriter/core/itemmodel.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -66,7 +66,7 @@ T_NodeData = str | QIcon | QFont | Qt.AlignmentFlag | None class ProjectNode: - """Core: Project Model Node Class + """Core: Project Model Node Class. The project tree structure is saved as nodes in a tree, starting from a root node. This class makes up these nodes. @@ -103,7 +103,6 @@ class ProjectNode: self._count = 0 self.refresh() self.updateCount() - return def __repr__(self) -> str: return ( @@ -114,7 +113,7 @@ class ProjectNode: ) def __bool__(self) -> bool: - """A node should always evaluate to True.""" + # A node should always evaluate to True. return True ## @@ -162,15 +161,12 @@ class ProjectNode: self._cache[C_STATUS_TIP] = sText self._cache[C_STATUS_ACCESS] = sText - return - def updateCount(self, propagate: bool = True) -> None: """Update counts, and propagate upwards in the tree.""" self._count = self._item.mainCount + 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() - return ## # Data Access @@ -223,7 +219,6 @@ class ProjectNode: self._children.append(child) self._refreshChildrenPos() self._item.notifyNovelStructureChange() - return def takeChild(self, pos: int) -> ProjectNode | None: """Remove a child item and return it.""" @@ -243,7 +238,6 @@ class ProjectNode: self._children.insert(target, node) self._refreshChildrenPos() self._item.notifyNovelStructureChange() - return def setExpanded(self, state: bool) -> None: """Set the node's expanded state.""" @@ -251,7 +245,6 @@ class ProjectNode: self._item.setExpanded(True) else: self._item.setExpanded(False) - return ## # Internal Functions @@ -262,14 +255,12 @@ class ProjectNode: for node in self._children: children.append(node) 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 # noqa: SLF001 child.item.setOrder(n) - return def _updateRelationships(self, child: ProjectNode) -> None: """Update a child item's relationships.""" @@ -282,11 +273,10 @@ class ProjectNode: child.item.setParent(None) child.item.setRoot(child.item.itemHandle) child.item.setClassDefaults(child.item.itemClass) - return class ProjectModel(QAbstractItemModel): - """Core: Project Model Class + """Core: Project Model Class. This class provides the interface for the tree widget used on the GUI. It implements the QModelIndex based interface required, adds @@ -302,11 +292,9 @@ class ProjectModel(QAbstractItemModel): self._root = ProjectNode(NWItem(tree.project, INV_ROOT)) self._root.item.setName("Invisible Root") logger.debug("Ready: ProjectModel") - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: ProjectModel") - return ## # Properties @@ -363,7 +351,7 @@ class ProjectModel(QAbstractItemModel): ## def supportedDropActions(self) -> Qt.DropAction: - """Return supported drop actions""" + """Return supported drop actions.""" return Qt.DropAction.MoveAction def mimeTypes(self) -> list[str]: @@ -445,7 +433,6 @@ class ProjectModel(QAbstractItemModel): self.beginInsertRows(parent, row, row) node.addChild(child, row) self.endInsertRows() - return def removeChild(self, parent: QModelIndex, pos: int) -> ProjectNode | None: """Remove a node from the model and return it.""" @@ -469,7 +456,6 @@ class ProjectModel(QAbstractItemModel): self.beginMoveRows(index.parent(), pos, pos, index.parent(), end) parent.moveChild(pos, new) self.endMoveRows() - return def multiMove(self, indices: list[QModelIndex], target: QModelIndex, pos: int = -1) -> None: """Move multiple items to a new location.""" @@ -497,7 +483,6 @@ class ProjectModel(QAbstractItemModel): node._updateRelationships(child) # noqa: SLF001 child.item.notifyToRefresh() node.item.notifyToRefresh() - return ## # Other Methods @@ -506,7 +491,6 @@ class ProjectModel(QAbstractItemModel): def clear(self) -> None: """Clear the project model.""" self._root.children.clear() - return def allExpanded(self) -> list[QModelIndex]: """Return a list of all expanded items.""" diff --git a/novelwriter/core/novelmodel.py b/novelwriter/core/novelmodel.py index 0447f526..7b1cd426 100644 --- a/novelwriter/core/novelmodel.py +++ b/novelwriter/core/novelmodel.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -54,6 +54,7 @@ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None class NovelModel(QAbstractTableModel): + """Core: Novel Model CLass.""" __slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows") @@ -64,11 +65,9 @@ class NovelModel(QAbstractTableModel): self._columns = 3 self._extraKey = "" self._extraLabel = "" - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NovelModel") - return ## # Properties @@ -102,7 +101,6 @@ class NovelModel(QAbstractTableModel): self._columns = 4 self._extraKey = nwKeyWords.PLOT_KEY self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]) - return ## # Model Interface @@ -147,7 +145,6 @@ class NovelModel(QAbstractTableModel): def clear(self) -> None: """Clear the model.""" self._rows.clear() - return def append(self, node: IndexNode) -> None: """Append a node to the model.""" @@ -155,7 +152,6 @@ class NovelModel(QAbstractTableModel): for key, head in node.items(): if key != "T0000": self._rows.append(self._generateEntry(handle, key, head)) - return def refresh(self, node: IndexNode) -> bool: """Refresh an index node.""" diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py index 2753c086..de8c5759 100644 --- a/novelwriter/core/options.py +++ b/novelwriter/core/options.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -80,7 +80,7 @@ VALID_MAP: dict[str, set[str]] = { class OptionState: - """Core: GUI Options Storage + """Core: GUI Options Storage. A class for storing the state of the GUI. The data is stored per project. Settings that should be project-independent are stored in @@ -90,7 +90,6 @@ class OptionState: def __init__(self, project: NWProject) -> None: self._project = project self._state = {} - return ## # Load and Save Cache diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index 3e3152ee..2a10fd92 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -57,6 +57,7 @@ logger = logging.getLogger(__name__) class NWProjectState(Enum): + """The state of the loaded project.""" UNKNOWN = 0 LOCKED = 1 @@ -65,6 +66,11 @@ class NWProjectState(Enum): class NWProject: + """Core: novelWriter Project Class. + + This class is the parent class of the project, and holds instances + of project data, the project tree, and the project index. + """ __slots__ = ( "_changed", "_data", "_index", "_langData", "_options", "_session", @@ -92,17 +98,13 @@ class NWProject: logger.debug("Ready: NWProject") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NWProject") - return def clear(self) -> None: """Clear the project.""" self._tree.clear() self._index.clear() - return ## # Properties @@ -263,7 +265,6 @@ class NWProject: if rHandle and (tHandle := SHARED.project.newFile(tag.title(), rHandle)): self.writeNewFile(tHandle, 1, False, f"@tag: {tag}\n\n") self._tree.refreshItems([tHandle]) - return ## # Project Methods @@ -441,7 +442,6 @@ class NWProject: self._tree.writeToCFile() self._session.appendSession(idleTime) self._storage.closeSession() - return def backupProject(self, doNotify: bool) -> bool: """Create a zip file of the entire project.""" @@ -499,7 +499,6 @@ class NWProject: self._data.itemImport.add(None, self.tr("Minor"), "purple", "BLOCK_2", 0) self._data.itemImport.add(None, self.tr("Major"), "purple", "BLOCK_3", 0) self._data.itemImport.add(None, self.tr("Main"), "purple", "BLOCK_4", 0) - return def setProjectLang(self, language: str | None) -> None: """Set the project-specific language.""" @@ -508,7 +507,6 @@ class NWProject: self._data.setLanguage(language) self._loadProjectLocalisation() self.setProjectChanged(True) - return def setProjectChanged(self, status: bool) -> bool: """Toggle the project changed flag, and propagate the @@ -527,7 +525,6 @@ class NWProject: """Update the total word and character count values.""" wNovel, wNotes, cNovel, cNotes = self._tree.sumCounts() self._data.setCurrCounts(wNovel=wNovel, wNotes=wNotes, cNovel=cNovel, cNotes=cNotes) - return def countStatus(self) -> None: """Count how many times the various status flags are used in the @@ -541,7 +538,6 @@ class NWProject: self._data.itemStatus.increment(nwItem.itemStatus) else: self._data.itemImport.increment(nwItem.itemImport) - return def updateStatus(self, kind: T_StatusKind, update: T_UpdateEntry) -> None: """Update status or import entries.""" @@ -553,13 +549,11 @@ class NWProject: self._data.itemImport.update(update) SHARED.emitStatusLabelsChanged(self, kind) self._tree.refreshAllItems() - return def updateTheme(self) -> None: """Update theme elements.""" self._data.itemStatus.refreshIcons() self._data.itemImport.refreshIcons() - return def localLookup(self, word: str | int) -> str: """Look up a word or number in the translation map for the diff --git a/novelwriter/core/projectdata.py b/novelwriter/core/projectdata.py index 15201970..12471e11 100644 --- a/novelwriter/core/projectdata.py +++ b/novelwriter/core/projectdata.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -41,7 +41,7 @@ logger = logging.getLogger(__name__) class NWProjectData: - """Core: Project Data Class + """Core: Project Data Class. The class holds all project data from the main XML file, aside from the list of project items. @@ -86,8 +86,6 @@ class NWProjectData: self._status = NWStatus(NWStatus.STATUS) self._import = NWStatus(NWStatus.IMPORT) - return - ## # Properties ## @@ -191,13 +189,11 @@ class NWProjectData: """Increment the save count by one.""" self._saveCount += 1 self._project.setProjectChanged(True) - return def incAutoCount(self) -> None: """Increment the auto save count by one.""" self._autoCount += 1 self._project.setProjectChanged(True) - return ## # Getters @@ -219,67 +215,57 @@ class NWProjectData: elif value != self._uuid: self._uuid = value self._project.setProjectChanged(True) - return def setName(self, value: str | None) -> None: """Set a new project name.""" if value != self._name: self._name = simplified(str(value or "")) self._project.setProjectChanged(True) - return def setAuthor(self, value: str | None) -> None: """Set the author value.""" if value != self._author: self._author = simplified(str(value or "")) self._project.setProjectChanged(True) - return def setSaveCount(self, value: Any) -> None: """Set the save count from last session.""" self._saveCount = checkInt(value, 0) self._project.setProjectChanged(True) - return def setAutoCount(self, value: Any) -> None: """Set the auto save count from last session.""" self._autoCount = checkInt(value, 0) self._project.setProjectChanged(True) - return def setEditTime(self, value: Any) -> None: """Set the edit time from last session.""" self._editTime = checkInt(value, 0) self._project.setProjectChanged(True) - return def setDoBackup(self, value: Any) -> None: """Set the do write backup flag.""" if value != self._doBackup: self._doBackup = checkBool(value, False) self._project.setProjectChanged(True) - return def setLanguage(self, value: str | None) -> None: """Set the project language.""" if value != self._language: self._language = checkStringNone(value, None) self._project.setProjectChanged(True) - return def setSpellCheck(self, value: Any) -> None: """Set the spell check flag.""" if value != self._spellCheck: self._spellCheck = checkBool(value, False) self._project.setProjectChanged(True) - return def setSpellLang(self, value: str | None) -> None: """Set the spell check language.""" if value != self._spellLang: self._spellLang = checkStringNone(value, None) self._project.setProjectChanged(True) - return def setLastHandle(self, value: str | None, component: str) -> None: """Set a last used handle into the handle registry for a given @@ -288,7 +274,6 @@ class NWProjectData: if isinstance(component, str): self._lastHandle[component] = checkStringNone(value, None) self._project.setProjectChanged(True) - return def setLastHandles(self, value: dict) -> None: """Set the full last handles dictionary to a new set of values. @@ -299,7 +284,6 @@ class NWProjectData: if key in self._lastHandle: self._lastHandle[key] = str(entry) if isHandle(entry) else None self._project.setProjectChanged(True) - return def setInitCounts( self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None @@ -321,7 +305,6 @@ class NWProjectData: count = checkInt(cNotes, 0) self._initCounts[3] = count self._currCounts[3] = count - return def setCurrCounts( self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None @@ -335,7 +318,6 @@ class NWProjectData: self._currCounts[2] = checkInt(cNovel, 0) if cNotes is not None: self._currCounts[3] = checkInt(cNotes, 0) - return def setAutoReplace(self, value: dict) -> None: """Set the auto-replace dictionary.""" @@ -345,4 +327,3 @@ class NWProjectData: if isinstance(entry, str): self._autoReplace[key] = simplified(entry) self._project.setProjectChanged(True) - return diff --git a/novelwriter/core/projectxml.py b/novelwriter/core/projectxml.py index 10395f28..8434b0fa 100644 --- a/novelwriter/core/projectxml.py +++ b/novelwriter/core/projectxml.py @@ -22,7 +22,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -72,7 +72,7 @@ class XMLReadState(Enum): class ProjectXMLReader: - """Core: Project XML Reader + """Core: Project XML Reader. All data is read into a NWProjectData instance, which must be provided. @@ -124,7 +124,6 @@ class ProjectXMLReader: self._appVersion = "" self._hexVersion = 0x0 self._timeStamp = "" - return ## # Properties @@ -254,8 +253,6 @@ class ProjectXMLReader: elif xItem.tag == "editTime": # Moved to attribute in 1.5 data.setEditTime(xItem.text) - return - def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None: """Parse the settings section of the XML file.""" logger.debug("Parsing section") @@ -294,8 +291,6 @@ class ProjectXMLReader: elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5 data.setInitCounts(wNotes=xItem.text) - return - def _parseProjectContent( self, xSection: ET.Element, data: NWProjectData, content: list ) -> None: @@ -356,8 +351,6 @@ class ProjectXMLReader: "nameAttr": name, }) - return - def _parseProjectContentLegacy( self, xSection: ET.Element, data: NWProjectData, content: list ) -> None: @@ -434,8 +427,6 @@ class ProjectXMLReader: "nameAttr": name, }) - return - def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None: """Parse a status or importance entry.""" for xEntry in xItem: @@ -450,7 +441,6 @@ class ProjectXMLReader: if color is None: color = f"{red}, {green}, {blue}" sObject.add(key, xEntry.text or "", color, shape, count) - return def _parseDictKeyText(self, xItem: ET.Element) -> dict: """Parse a dictionary stored with key as an attribute and the @@ -470,7 +460,7 @@ class ProjectXMLReader: class ProjectXMLWriter: - """Core: Project XML Writer + """Core: Project XML Writer. The project writer class will only write a file according to the very latest spec. @@ -479,7 +469,6 @@ class ProjectXMLWriter: def __init__(self, path: str | Path) -> None: self._path = Path(path) self._error = None - return ## # Properties @@ -580,7 +569,6 @@ class ProjectXMLWriter: """Pack a single value into an XML element.""" xItem = ET.SubElement(xParent, name, attrib=attrib or {}) xItem.text = str(value) or "" - return def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None: """Pack the entries of a dictionary into an XML element.""" @@ -589,4 +577,3 @@ class ProjectXMLWriter: if len(key) > 0: xEntry = ET.SubElement(xItem, "entry", attrib={"key": key}) xEntry.text = str(value) or "" - return diff --git a/novelwriter/core/sessions.py b/novelwriter/core/sessions.py index 6936ed99..58625c81 100644 --- a/novelwriter/core/sessions.py +++ b/novelwriter/core/sessions.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -43,7 +43,7 @@ logger = logging.getLogger(__name__) class NWSessionLog: - """Core: Session JSON Lines Log File + """Core: Session JSON Lines Log File. The class that wraps the session log file, which is in JSON Lines format. That is, one JSON object per line. @@ -52,7 +52,6 @@ class NWSessionLog: def __init__(self, project: NWProject) -> None: self._project = project self._start = 0.0 - return ## # Properties @@ -70,7 +69,6 @@ class NWSessionLog: def startSession(self) -> None: """Start the writing session.""" self._start = time() - return def appendSession(self, idleTime: float) -> bool: """Append session statistics to the sessions log file.""" diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index a9e8b443..e2b0dd56 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -44,7 +44,7 @@ logger = logging.getLogger(__name__) class NWSpellEnchant: - """Core: Enchant Spell Checking Wrapper + """Core: Enchant Spell Checking Wrapper. This is a rapper class for Enchant to keep the API consistent between spell check tools. @@ -57,11 +57,9 @@ class NWSpellEnchant: self._language = None self._broker = None logger.debug("Ready: NWSpellEnchant") - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NWSpellEnchant") - return ## # Properties @@ -106,21 +104,19 @@ class NWSpellEnchant: for word in self._userDict: self._enchant.add_to_session(word) - return - ## # Methods ## def checkWord(self, word: str) -> bool: - """Wrapper function for pyenchant.""" + """Forward check to pyenchant.""" try: return bool(self._enchant.check(word)) except Exception: return True def suggestWords(self, word: str) -> list[str]: - """Wrapper function for pyenchant.""" + """Ask pyenchant for suggestions.""" try: return self._enchant.suggest(word) except Exception: @@ -172,24 +168,29 @@ class FakeEnchant: self.tag = "" self.provider = FakeProvider() - return - def check(self, word: str) -> bool: + """Return True for all words.""" return True def suggest(self, word: str) -> list[str]: + """Return an empty suggestion list.""" return [] def add_to_session(self, word: str) -> None: + """Do nothing.""" return class UserDictionary: + """Core: User Word Dictionary. + + This class holds all the user's own words for spell checking + purposes. The dictionary is per-project. + """ def __init__(self, project: NWProject) -> None: self._project = project self._words = set() - return def __contains__(self, word: str) -> bool: return word in self._words @@ -219,7 +220,6 @@ class UserDictionary: except Exception: logger.error("Failed to load user dictionary") logException() - return def save(self) -> None: """Save the user's dictionary.""" @@ -232,4 +232,3 @@ class UserDictionary: except Exception: logger.error("Failed to save user dictionary") logException() - return diff --git a/novelwriter/core/status.py b/novelwriter/core/status.py index 3576e4a3..fc8bf85c 100644 --- a/novelwriter/core/status.py +++ b/novelwriter/core/status.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import dataclasses @@ -49,6 +49,7 @@ CUSTOM_COL = "custom" @dataclasses.dataclass class StatusEntry: + """DataClass: Status Label Values.""" name: str color: QColor @@ -73,6 +74,7 @@ T_StatusKind = Literal["s", "i"] class NWStatus: + """Core: Status/Importance Label Class.""" STATUS = "s" IMPORT = "i" @@ -84,7 +86,6 @@ class NWStatus: self._default = None self._prefix = prefix[:1] self._height = SHARED.theme.baseIconHeight - return def __len__(self) -> int: return len(self._store) @@ -133,8 +134,6 @@ class NWStatus: if self._default not in self._store: self._default = next(iter(self._store)) if self._store else None - return - def check(self, value: str) -> str: """Check the key against the stored status names.""" if self._isKey(value) and value in self._store: @@ -147,13 +146,11 @@ class NWStatus: """Clear the counts of references to the status entries.""" for entry in self._store.values(): entry.count = 0 - return def increment(self, key: str | None) -> None: """Increment the counter for a given entry.""" if key and key in self._store: self._store[key].count += 1 - return def pack(self) -> Iterable[tuple[str, dict]]: """Pack the status entries into a dictionary.""" @@ -195,7 +192,6 @@ class NWStatus: if entry.theme != CUSTOM_COL: entry.color = SHARED.theme.parseColor(entry.theme) entry.icon = NWStatus.createIcon(self._height, entry.color, entry.shape) - return @staticmethod def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon: @@ -252,7 +248,6 @@ class _ShapeCache: def __init__(self) -> None: self._cache: dict[nwStatusShape, QPainterPath] = {} - return def getShape(self, shape: nwStatusShape) -> QPainterPath: """Return a painter shape for an icon.""" diff --git a/novelwriter/core/storage.py b/novelwriter/core/storage.py index b8c21948..dfa0269b 100644 --- a/novelwriter/core/storage.py +++ b/novelwriter/core/storage.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -47,6 +47,7 @@ logger = logging.getLogger(__name__) class NWStorageOpen(Enum): + """The status of a storage location.""" UNKOWN = 0 NOT_FOUND = 1 @@ -56,6 +57,7 @@ class NWStorageOpen(Enum): class NWStorageCreate(Enum): + """The status of a new storage location.""" NOT_EMPTY = 0 OS_ERROR = 1 @@ -63,7 +65,7 @@ class NWStorageCreate(Enum): class NWStorage: - """Core: Project Storage Class + """Core: Project Storage Class. The class that handles all paths related to the project storage. """ @@ -81,7 +83,6 @@ class NWStorage: self._openMode = self.MODE_INACTIVE self._ready = False self._exception = None - return def clear(self) -> None: """Reset internal variables.""" @@ -90,7 +91,6 @@ class NWStorage: self._lockFilePath = None self._openMode = self.MODE_INACTIVE self._ready = False - return ## # Properties @@ -252,13 +252,11 @@ class NWStorage: """Lock the session when the project is successfully opened.""" if self._ready: self._writeLockFile() - return def closeSession(self) -> None: """Run tasks related to closing the session.""" self._clearLockFile() self.clear() - return ## # Content Access Methods @@ -394,7 +392,7 @@ class NWStorage: class _LegacyStorage: - """Core: Legacy Storage Converter Utils + """Core: Legacy Storage Converter Utils. A class with various functions to convert old file formats and file/folder layouts to the current project format. @@ -402,7 +400,6 @@ class _LegacyStorage: def __init__(self, project: NWProject) -> None: self._project = project - return def legacyDataFolder(self, path: Path, child: Path) -> None: """Handle the content of a legacy data folder from a version 1.0 @@ -484,8 +481,6 @@ class _LegacyStorage: except Exception as exc: logger.warning("Failed to delete: %s", item, exc_info=exc) - return - ## # Internal Functions ## diff --git a/novelwriter/core/tree.py b/novelwriter/core/tree.py index 14158041..4ee4b626 100644 --- a/novelwriter/core/tree.py +++ b/novelwriter/core/tree.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -50,7 +50,7 @@ MAX_DEPTH = 999 # Cap of tree traversing for loops (recursion limit) class NWTree: - """Core: Project Tree Data Class + """Core: Project Tree Data Class. Only one instance of this class should exist in the project class. This class holds all the project items of the project as instances @@ -71,18 +71,16 @@ class NWTree: self._trash = None self._ready = False logger.debug("Ready: NWTree") - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NWTree") - return def __len__(self) -> int: - """The number of items in the project.""" + """Return the number of items in the project.""" return len(self._items) def __bool__(self) -> bool: - """True if there are any items in the project.""" + """Return True if there are any items in the project.""" return bool(self._items) def __getitem__(self, tHandle: str | None) -> NWItem | None: @@ -95,7 +93,7 @@ class NWTree: return None def __contains__(self, tHandle: str) -> bool: - """Checks if a handle exists in the tree.""" + """Check if a handle exists in the tree.""" return tHandle in self._items def __iter__(self) -> Iterator[NWItem]: @@ -142,7 +140,6 @@ class NWTree: self._trash = None oldModel.deleteLater() del oldModel - return def add(self, item: NWItem, pos: int = -1) -> bool: """Add a project item into the project tree.""" @@ -260,8 +257,6 @@ class NWTree: self._model.endInsertRows() self._model.layoutChanged.emit() - return - def pickParent(self, sNode: ProjectNode, hLevel: int, isNote: bool) -> tuple[str | None, int]: """Pick an appropriate parent handle for adding a new item.""" if sNode.item.isFolderType() or sNode.item.isRootType(): @@ -299,7 +294,6 @@ class NWTree: indexE = self._model.indexFromNode(node, 3) self._model.dataChanged.emit(indexS, indexE) self._itemChange(node.item, nwChange.UPDATE) - return def refreshAllItems(self) -> None: """Refresh all items in the tree.""" @@ -309,13 +303,11 @@ class NWTree: self._model.root.refresh() self._model.root.updateCount(propagate=False) self._model.layoutChanged.emit() - return def novelStructureChanged(self, tHandle: str) -> None: """Emit a novel structure change signal.""" if self._ready: SHARED.novelStructureChanged.emit(tHandle) - return def checkConsistency(self, prefix: str) -> tuple[int, int]: """Check the project tree consistency. Also check the content @@ -496,7 +488,6 @@ class NWTree: SHARED.emitProjectItemChanged(self._project, tHandle, change) if item.isRootType(): SHARED.emitRootFolderChanged(self._project, tHandle, change) - return def _getTrashNode(self) -> ProjectNode | None: """Get the trash node. If it doesn't exist, create it.""" diff --git a/novelwriter/dialogs/about.py b/novelwriter/dialogs/about.py index 3c7070df..59d4251f 100644 --- a/novelwriter/dialogs/about.py +++ b/novelwriter/dialogs/about.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -45,6 +45,7 @@ logger = logging.getLogger(__name__) class GuiAbout(NDialog): + """GUI: About novelWriter Dialog.""" def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -109,11 +110,8 @@ class GuiAbout(NDialog): logger.debug("Ready: GuiAbout") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiAbout") - return ## # Events @@ -123,7 +121,6 @@ class GuiAbout(NDialog): """Capture the close event and perform cleanup.""" event.accept() self.softDelete() - return ## # Internal Functions @@ -135,7 +132,6 @@ class GuiAbout(NDialog): self.txtCredits.setHtml(html) else: self.txtCredits.setHtml("Error loading credits text ...") - return def _setStyleSheet(self) -> None: """Set stylesheet text document.""" @@ -143,4 +139,3 @@ class GuiAbout(NDialog): self.txtCredits.setStyleSheet( f"QTextBrowser {{border: none; background: {baseCol};}} " ) - return diff --git a/novelwriter/dialogs/docmerge.py b/novelwriter/dialogs/docmerge.py index 9345efd6..2c392374 100644 --- a/novelwriter/dialogs/docmerge.py +++ b/novelwriter/dialogs/docmerge.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -42,6 +42,7 @@ logger = logging.getLogger(__name__) class GuiDocMerge(NDialog): + """GUI: Document Merge Tool.""" D_HANDLE = QtUserRole @@ -110,11 +111,8 @@ class GuiDocMerge(NDialog): logger.debug("Ready: GuiDocMerge") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiDocMerge") - return def data(self) -> dict: """Return the user's choices.""" @@ -150,7 +148,6 @@ class GuiDocMerge(NDialog): if sHandle := self._data.get("sHandle"): itemList = self._data.get("origItems", []) self._loadContent(sHandle, itemList) - return ## # Internal Functions @@ -170,4 +167,3 @@ class GuiDocMerge(NDialog): item.setData(self.D_HANDLE, tHandle) item.setCheckState(Qt.CheckState.Checked) self.listBox.addItem(item) - return diff --git a/novelwriter/dialogs/docsplit.py b/novelwriter/dialogs/docsplit.py index a2acd53f..5850aff3 100644 --- a/novelwriter/dialogs/docsplit.py +++ b/novelwriter/dialogs/docsplit.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -42,6 +42,7 @@ logger = logging.getLogger(__name__) class GuiDocSplit(NDialog): + """GUI: Document Split Tool.""" LINE_ROLE = QtUserRole LEVEL_ROLE = QtUserRole + 1 @@ -139,11 +140,8 @@ class GuiDocSplit(NDialog): logger.debug("Ready: GuiDocSplit") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiDocSplit") - return def data(self) -> tuple[dict, list[str]]: """Return the user's choices. Also save the users options for @@ -197,7 +195,6 @@ class GuiDocSplit(NDialog): """Reload the content of the list box.""" if sHandle := self._data.get("sHandle"): self._loadContent(sHandle) - return ## # Internal Functions diff --git a/novelwriter/dialogs/editlabel.py b/novelwriter/dialogs/editlabel.py index 97bb269c..b8b4bae4 100644 --- a/novelwriter/dialogs/editlabel.py +++ b/novelwriter/dialogs/editlabel.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -34,6 +34,7 @@ logger = logging.getLogger(__name__) class GuiEditLabel(NDialog): + """GUI: Edit Item Label Dialog.""" def __init__(self, parent: QWidget, text: str = "") -> None: super().__init__(parent=parent) @@ -72,11 +73,8 @@ class GuiEditLabel(NDialog): logger.debug("Ready: GuiEditLabel") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiEditLabel") - return @property def itemLabel(self) -> str: diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 7f51dd38..758f7908 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -50,6 +50,7 @@ logger = logging.getLogger(__name__) class GuiPreferences(NDialog): + """GUI: Preferences Dialog.""" newPreferencesReady = pyqtSignal(bool, bool, bool, bool) @@ -125,11 +126,8 @@ class GuiPreferences(NDialog): logger.debug("Ready: GuiPreferences") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiPreferences") - return def buildForm(self) -> None: """Build the settings form.""" @@ -856,8 +854,6 @@ class GuiPreferences(NDialog): self.mainForm.finalise() self.sidebar.setSelected(1) - return - ## # Events ## @@ -868,7 +864,6 @@ class GuiPreferences(NDialog): self._saveWindowSize() event.accept() self.softDelete() - return def keyPressEvent(self, event: QKeyEvent) -> None: """Overload keyPressEvent and only accept escape. The main @@ -878,7 +873,6 @@ class GuiPreferences(NDialog): if event.matches(QKeySequence.StandardKey.Cancel): self.close() event.ignore() - return ## # Private Slots @@ -888,13 +882,11 @@ class GuiPreferences(NDialog): def _sidebarClicked(self, section: int) -> None: """Process a user request to switch page.""" self.mainForm.scrollToSection(section) - return @pyqtSlot() def _gotoSearch(self) -> None: """Go to the setting indicated by the search text.""" self.mainForm.scrollToLabel(self.searchText.text().strip()) - return @pyqtSlot() def _selectGuiFont(self) -> None: @@ -904,7 +896,6 @@ class GuiPreferences(NDialog): self.guiFont.setText(describeFont(font)) self.guiFont.setCursorPosition(0) self._guiFont = font - return @pyqtSlot() def _selectTextFont(self) -> None: @@ -914,7 +905,6 @@ class GuiPreferences(NDialog): self.textFont.setText(describeFont(font)) self.textFont.setCursorPosition(0) self._textFont = font - return @pyqtSlot() def _backupFolder(self) -> None: @@ -925,13 +915,11 @@ class GuiPreferences(NDialog): ): self.backupPath = path self.mainForm.setHelpText("backupPath", self.tr("Path: {0}").format(path)) - return @pyqtSlot(bool) def _toggledBackupOnClose(self, state: bool) -> None: """Toggle switch that depends on the backup on close switch.""" self.askBeforeBackup.setEnabled(state) - return @pyqtSlot(str) def _insertDialogLineSymbol(self, symbol: str) -> None: @@ -939,7 +927,6 @@ class GuiPreferences(NDialog): current = self.dialogLine.text() values = processDialogSymbols(f"{current} {symbol}") self.dialogLine.setText(" ".join(values)) - return @pyqtSlot(bool) def _toggleAutoReplaceMain(self, state: bool) -> None: @@ -949,7 +936,6 @@ class GuiPreferences(NDialog): self.doReplaceDash.setEnabled(state) self.doReplaceDots.setEnabled(state) self.fmtPadThin.setEnabled(state) - return @pyqtSlot() def _changeSingleQuoteOpen(self) -> None: @@ -957,7 +943,6 @@ class GuiPreferences(NDialog): quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtSQuoteOpen.text()) if status: self.fmtSQuoteOpen.setText(quote) - return @pyqtSlot() def _changeSingleQuoteClose(self) -> None: @@ -965,7 +950,6 @@ class GuiPreferences(NDialog): quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtSQuoteClose.text()) if status: self.fmtSQuoteClose.setText(quote) - return @pyqtSlot() def _changeDoubleQuoteOpen(self) -> None: @@ -973,7 +957,6 @@ class GuiPreferences(NDialog): quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtDQuoteOpen.text()) if status: self.fmtDQuoteOpen.setText(quote) - return @pyqtSlot() def _changeDoubleQuoteClose(self) -> None: @@ -981,7 +964,6 @@ class GuiPreferences(NDialog): quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtDQuoteClose.text()) if status: self.fmtDQuoteClose.setText(quote) - return ## # Internal Functions @@ -990,7 +972,6 @@ class GuiPreferences(NDialog): def _saveWindowSize(self) -> None: """Save the dialog window size.""" CONFIG.setPreferencesWinSize(self.width(), self.height()) - return def _doSave(self) -> None: """Save the values set in the form.""" @@ -1134,5 +1115,3 @@ class GuiPreferences(NDialog): self.newPreferencesReady.emit(needsRestart, refreshTree, updateTheme, updateSyntax) self.close() - - return diff --git a/novelwriter/dialogs/projectsettings.py b/novelwriter/dialogs/projectsettings.py index 071664fc..5e16d52e 100644 --- a/novelwriter/dialogs/projectsettings.py +++ b/novelwriter/dialogs/projectsettings.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import csv @@ -55,6 +55,7 @@ logger = logging.getLogger(__name__) class GuiProjectSettings(NDialog): + """GUI: Project Settings DIalog.""" PAGE_SETTINGS = 0 PAGE_STATUS = 1 @@ -137,11 +138,8 @@ class GuiProjectSettings(NDialog): logger.debug("Ready: GuiProjectSettings") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiProjectSettings") - return ## # Events @@ -152,7 +150,6 @@ class GuiProjectSettings(NDialog): self._saveSettings() event.accept() self.softDelete() - return ## # Private Slots @@ -169,7 +166,6 @@ class GuiProjectSettings(NDialog): self.mainStack.setCurrentWidget(self.importPage) elif pageId == self.PAGE_REPLACE: self.mainStack.setCurrentWidget(self.replacePage) - return @pyqtSlot() def _doSave(self) -> None: @@ -203,8 +199,6 @@ class GuiProjectSettings(NDialog): QApplication.processEvents() self.close() - return - ## # Internal Functions ## @@ -223,8 +217,6 @@ class GuiProjectSettings(NDialog): options.setValue("GuiProjectSettings", "importColW", importColW) options.setValue("GuiProjectSettings", "replaceColW", replaceColW) - return - class _SettingsPage(NScrollableForm): @@ -295,8 +287,6 @@ class _SettingsPage(NScrollableForm): self.finalise() - return - class _StatusPage(NFixedPage): @@ -478,8 +468,6 @@ class _StatusPage(NFixedPage): self.setCentralLayout(self.outerBox) self._setButtonIcons() - return - @property def changed(self) -> bool: """The user changed these settings.""" @@ -518,7 +506,6 @@ class _StatusPage(NFixedPage): entry.name = name item.setText(self.C_LABEL, name) self._changed = True - return @pyqtSlot(int) def _onThemeSelect(self, index: int) -> None: @@ -526,7 +513,6 @@ class _StatusPage(NFixedPage): self._theme = str(self.iconColor.currentData()) self._setButtonIcons() self._updateIcon() - return @pyqtSlot() def _onColorSelect(self) -> None: @@ -536,7 +522,6 @@ class _StatusPage(NFixedPage): self._theme = CUSTOM_COL self._setButtonIcons() self._updateIcon() - return @pyqtSlot() def _onItemCreate(self) -> None: @@ -547,7 +532,6 @@ class _StatusPage(NFixedPage): theme = str(self.iconColor.currentData()) self._addItem(None, StatusEntry(self.tr("New Item"), color, theme, shape, icon, 0)) self._changed = True - return @pyqtSlot() def _onItemDelete(self) -> None: @@ -560,7 +544,6 @@ class _StatusPage(NFixedPage): else: self.listBox.takeTopLevelItem(iRow) self._changed = True - return @pyqtSlot() def _onSelectionChanged(self) -> None: @@ -593,7 +576,6 @@ class _StatusPage(NFixedPage): self.iconColor.setEnabled(False) self.colorButton.setEnabled(False) self.shapeButton.setEnabled(False) - return @pyqtSlot() def _importLabels(self) -> None: @@ -630,7 +612,6 @@ class _StatusPage(NFixedPage): writer.writerow([entry.shape.name, entry.color.name(), entry.name]) except Exception as exc: SHARED.error("Could not write file.", exc=exc) - return ## # Internal Functions @@ -641,7 +622,6 @@ class _StatusPage(NFixedPage): self._shape = shape self._setButtonIcons() self._updateIcon() - return def _updateIcon(self) -> None: """Apply changes made to a status icon.""" @@ -654,7 +634,6 @@ class _StatusPage(NFixedPage): entry.icon = icon item.setIcon(self.C_LABEL, icon) self._changed = True - return def _addItem(self, key: str | None, entry: StatusEntry) -> None: """Add a status item to the list.""" @@ -665,7 +644,6 @@ class _StatusPage(NFixedPage): item.setData(self.C_DATA, self.D_KEY, key) item.setData(self.C_DATA, self.D_ENTRY, entry) self.listBox.addTopLevelItem(item) - return def _moveItem(self, step: int) -> None: """Move and item up or down step.""" @@ -678,7 +656,6 @@ class _StatusPage(NFixedPage): self.listBox.clearSelection() cItem.setSelected(True) self._changed = True - return def _getSelectedItem(self) -> QTreeWidgetItem | None: """Get the currently selected item.""" @@ -701,7 +678,6 @@ class _StatusPage(NFixedPage): self.iconColor.setCurrentData(self._theme, CUSTOM_COL) self.colorButton.setIcon(icon) self.shapeButton.setIcon(self._icons[self._shape]) - return def _pickColor(self) -> QColor: """Get the correct colour value based on selections.""" @@ -789,8 +765,6 @@ class _ReplacePage(NFixedPage): self.setCentralLayout(self.outerBox) - return - @property def changed(self) -> bool: """The user changed these settings.""" @@ -823,7 +797,6 @@ class _ReplacePage(NFixedPage): if (item := self._getSelectedItem()) and (key := self._stripKey(text)): item.setText(self.C_KEY, f"<{key}>") self._changed = True - return @pyqtSlot(str) def _onValueEdit(self, text: str) -> None: @@ -831,7 +804,6 @@ class _ReplacePage(NFixedPage): if item := self._getSelectedItem(): item.setText(self.C_REPL, text) self._changed = True - return @pyqtSlot() def _onSelectionChanged(self) -> None: @@ -850,14 +822,12 @@ class _ReplacePage(NFixedPage): self.editValue.setText("") self.editKey.setEnabled(False) self.editValue.setEnabled(False) - return @pyqtSlot() def _onEntryCreated(self) -> None: """Add a new list entry.""" key = f"" self.listBox.addTopLevelItem(QTreeWidgetItem([key, ""])) - return @pyqtSlot() def _onEntryDeleted(self) -> None: @@ -865,7 +835,6 @@ class _ReplacePage(NFixedPage): if item := self._getSelectedItem(): self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(item)) self._changed = True - return ## # Internal Functions diff --git a/novelwriter/dialogs/quotes.py b/novelwriter/dialogs/quotes.py index 931558a2..6030ecfe 100644 --- a/novelwriter/dialogs/quotes.py +++ b/novelwriter/dialogs/quotes.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -43,6 +43,7 @@ logger = logging.getLogger(__name__) class GuiQuoteSelect(NDialog): + """GUI: Quote Selector Dialog.""" _selected = "" @@ -108,11 +109,8 @@ class GuiQuoteSelect(NDialog): logger.debug("Ready: GuiQuoteSelect") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiQuoteSelect") - return @property def selectedQuote(self) -> str: @@ -140,4 +138,3 @@ class GuiQuoteSelect(NDialog): quote = items[0].data(self.D_KEY) self.previewLabel.setText(quote) self._selected = quote - return diff --git a/novelwriter/dialogs/wordlist.py b/novelwriter/dialogs/wordlist.py index a667738a..19e2da9d 100644 --- a/novelwriter/dialogs/wordlist.py +++ b/novelwriter/dialogs/wordlist.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -48,6 +48,7 @@ logger = logging.getLogger(__name__) class GuiWordList(NDialog): + """GUI: User Dictionary Edit Tool.""" newWordListReady = pyqtSignal() @@ -128,11 +129,8 @@ class GuiWordList(NDialog): logger.debug("Ready: GuiWordList") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiWordList") - return ## # Events @@ -143,7 +141,6 @@ class GuiWordList(NDialog): self._saveGuiSettings() event.accept() self.softDelete() - return ## # Private Slots @@ -159,14 +156,12 @@ class GuiWordList(NDialog): if items := self.listBox.findItems(word, Qt.MatchFlag.MatchExactly): self.listBox.setCurrentItem(items[0]) self.listBox.scrollToItem(items[0], QAbstractItemView.ScrollHint.PositionAtCenter) - return @pyqtSlot() def _doDelete(self) -> None: """Delete the selected items.""" for item in self.listBox.selectedItems(): self.listBox.takeItem(self.listBox.row(item)) - return @pyqtSlot() def _doSave(self) -> None: @@ -178,7 +173,6 @@ class GuiWordList(NDialog): self.newWordListReady.emit() QApplication.processEvents() self.close() - return @pyqtSlot() def _importWords(self) -> None: @@ -213,7 +207,6 @@ class GuiWordList(NDialog): fo.write("\n".join(self._listWords())) except Exception as exc: SHARED.error("Could not write file.", exc=exc) - return ## # Internal Functions @@ -226,7 +219,6 @@ class GuiWordList(NDialog): self.listBox.clear() for word in userDict: self.listBox.addItem(word) - return def _saveGuiSettings(self) -> None: """Save GUI settings.""" @@ -234,14 +226,12 @@ class GuiWordList(NDialog): pOptions = SHARED.project.options pOptions.setValue("GuiWordList", "winWidth", self.width()) pOptions.setValue("GuiWordList", "winHeight", self.height()) - return def _addWord(self, word: str) -> None: """Add a single word to the list.""" if word and not self.listBox.findItems(word, Qt.MatchFlag.MatchExactly): self.listBox.addItem(word) self._changed = True - return def _listWords(self) -> list[str]: """List all words in the list box.""" diff --git a/novelwriter/enum.py b/novelwriter/enum.py index 9e50620e..7ea60294 100644 --- a/novelwriter/enum.py +++ b/novelwriter/enum.py @@ -20,12 +20,13 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from enum import Enum class nwItemType(Enum): + """Enum: Project Item Types.""" NO_TYPE = 0 ROOT = 1 @@ -34,6 +35,7 @@ class nwItemType(Enum): class nwItemClass(Enum): + """Enum: Project Item Classes.""" NO_CLASS = 0 NOVEL = 1 @@ -50,6 +52,7 @@ class nwItemClass(Enum): class nwItemLayout(Enum): + """A project item's layout.""" NO_LAYOUT = 0 DOCUMENT = 1 @@ -57,6 +60,7 @@ class nwItemLayout(Enum): class nwComment(Enum): + """Types of text comments.""" PLAIN = 0 IGNORE = 1 @@ -69,6 +73,7 @@ class nwComment(Enum): class nwChange(Enum): + """Change request modes.""" CREATE = 0 UPDATE = 1 @@ -76,12 +81,14 @@ class nwChange(Enum): class nwDocMode(Enum): + """Document open modes.""" VIEW = 0 EDIT = 1 class nwDocAction(Enum): + """Document actions.""" NO_ACTION = 0 UNDO = 1 @@ -125,6 +132,7 @@ class nwDocAction(Enum): class nwDocInsert(Enum): + """Document insert actions.""" NO_INSERT = 0 QUOTE_LS = 1 @@ -142,6 +150,7 @@ class nwDocInsert(Enum): class nwView(Enum): + """Main GUI view modes.""" EDITOR = 0 PROJECT = 1 @@ -151,6 +160,7 @@ class nwView(Enum): class nwFocus(Enum): + """Main GUI panel focus.""" TREE = 1 DOCUMENT = 2 @@ -158,6 +168,7 @@ class nwFocus(Enum): class nwTheme(Enum): + """GUI theme colour modes.""" AUTO = 0 LIGHT = 1 @@ -165,6 +176,7 @@ class nwTheme(Enum): class nwOutline(Enum): + """Enum: Project Outline Columns.""" TITLE = 0 LEVEL = 1 @@ -189,6 +201,7 @@ class nwOutline(Enum): class nwNovelExtra(Enum): + """Enum: Novel View Extra Columns.""" HIDDEN = 0 POV = 1 @@ -197,6 +210,7 @@ class nwNovelExtra(Enum): class nwBuildFmt(Enum): + """Enum: Manuscript Document Formats.""" ODT = 0 FODT = 1 @@ -211,6 +225,7 @@ class nwBuildFmt(Enum): class nwStatusShape(Enum): + """Enum: Status/Importance Icon Shapes.""" SQUARE = 0 TRIANGLE = 1 diff --git a/novelwriter/error.py b/novelwriter/error.py index 03f5d07a..59fafdff 100644 --- a/novelwriter/error.py +++ b/novelwriter/error.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -47,7 +47,6 @@ def logException() -> None: exType, exValue, _ = sys.exc_info() if exType is not None: logger.error(f"{exType.__name__}: {exValue!s}", stacklevel=2) - return def formatException(exc: BaseException) -> str: @@ -58,6 +57,7 @@ def formatException(exc: BaseException) -> str: class NWErrorMessage(QDialog): + """GUI: Error Dialog.""" def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -110,8 +110,6 @@ class NWErrorMessage(QDialog): self.setSizeGripEnabled(True) self.resize(800, 400) - return - def setMessage(self, exType: type, exValue: BaseException, exTrace: TracebackType) -> None: """Generate a message and append session data, error info and error traceback. @@ -143,7 +141,7 @@ class NWErrorMessage(QDialog): enchantVersion = "Unknown" try: - txtTrace = "\n".join(format_tb(exTrace)) + trace = "\n".join(format_tb(exTrace)) self.msgBody.setPlainText( "Environment:\n" f"novelWriter Version: {__version__}\n" @@ -152,13 +150,11 @@ class NWErrorMessage(QDialog): f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n" f"enchant: {enchantVersion}\n\n" f"{exType.__name__}:\n{exValue!s}\n\n" - f"Traceback:\n{txtTrace}\n" + f"Traceback:\n{trace}\n" ) except Exception: self.msgBody.setPlainText("Failed to generate error report ...") - return - ## # Slots ## @@ -167,11 +163,10 @@ class NWErrorMessage(QDialog): def _doClose(self) -> None: """Close the dialog.""" self.close() - return def exceptionHandler(exType: type, exValue: BaseException, exTrace: TracebackType) -> None: - """Function to catch unhandled global exceptions.""" + """Catch unhandled global exceptions.""" from traceback import print_tb from PyQt6.QtWidgets import QApplication diff --git a/novelwriter/extensions/configlayout.py b/novelwriter/extensions/configlayout.py index b82a0997..14800a5c 100644 --- a/novelwriter/extensions/configlayout.py +++ b/novelwriter/extensions/configlayout.py @@ -24,7 +24,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from PyQt6.QtGui import QColor, QFont, QPalette, QPixmap @@ -39,7 +39,7 @@ DEFAULT_SCALE = 0.9 class NFixedPage(QFrame): - """Extension: Fixed Page Widget + """Extension: Fixed Page Widget. A custom widget that holds a layout. This is just a wrapper around a QFrame that sets the same frame style as the other Page widgets. @@ -49,23 +49,20 @@ class NFixedPage(QFrame): super().__init__(parent=parent) self.setFrameShadow(QFrame.Shadow.Sunken) self.setFrameShape(QFrame.Shape.StyledPanel) - return def setCentralLayout(self, layout: QLayout) -> None: """Set a layout as the central object.""" self.setLayout(layout) - return def setCentralWidget(self, widget: QWidget) -> None: """Set a layout as the central object.""" layout = QHBoxLayout() layout.addWidget(widget) self.setLayout(layout) - return class NScrollablePage(QScrollArea): - """Extension: Scrollable Page Widget + """Extension: Scrollable Page Widget. A custom widget that holds a layout within a scrollable area. """ @@ -79,16 +76,14 @@ class NScrollablePage(QScrollArea): self.setVerticalScrollBarPolicy(QtScrollAsNeeded) self.setFrameShadow(QFrame.Shadow.Sunken) self.setFrameShape(QFrame.Shape.StyledPanel) - return def setCentralLayout(self, layout: QLayout) -> None: """Set the central layout of the scroll page.""" self._widget.setLayout(layout) - return class NScrollableForm(QScrollArea): - """Extension: Scrollable Form Widget + """Extension: Scrollable Form Widget. A custom widget that creates a form within a scrollable area. """ @@ -117,8 +112,6 @@ class NScrollableForm(QScrollArea): self.setFrameShadow(QFrame.Shadow.Sunken) self.setFrameShape(QFrame.Shape.StyledPanel) - return - ## # Properties ## @@ -135,18 +128,15 @@ class NScrollableForm(QScrollArea): """Set the text color for the help text.""" self._helpCol = color self._fontScale = scale - return def setHelpText(self, key: str, text: str) -> None: """Set the text for the help label.""" if qHelp := self._editable.get(key): qHelp.setText(text) - return def setRowIndent(self, indent: int) -> None: """Set the indentation of each row.""" self._indent = max(indent, 0) - return ## # Methods @@ -158,7 +148,6 @@ class NScrollableForm(QScrollArea): yPos = self._sections[identifier].pos().y() - 8 if vBar := self.verticalScrollBar(): vBar.setValue(yPos) - return def scrollToLabel(self, label: str) -> None: """Scroll to the requested label.""" @@ -166,7 +155,6 @@ class NScrollableForm(QScrollArea): yPos = self._index[label].pos().y() - 8 if vBar := self.verticalScrollBar(): vBar.setValue(yPos) - return def addGroupLabel(self, label: str, identifier: int | None = None) -> None: """Add a text label to separate groups of settings.""" @@ -178,7 +166,6 @@ class NScrollableForm(QScrollArea): self._first = False if identifier is not None: self._sections[identifier] = qLabel - return def addRow( self, @@ -252,17 +239,14 @@ class NScrollableForm(QScrollArea): self._index[label.strip()] = qWidget qLabel.setAccessibleName(text) - return - def finalise(self) -> None: """Finalise the layout when the form is built.""" self._layout.addSpacing(20) self._layout.addStretch(1) - return class NColorLabel(QLabel): - """Extension: A Coloured Label + """Extension: A Coloured Label. A custom widget that draws a label in a specific colour, and optionally at a specific size, and word wrapped. @@ -298,21 +282,17 @@ class NColorLabel(QLabel): self.setWordWrap(wrap) self.setColorState(True) - return - def setTextColors(self, *, color: QColor | None = None, faded: QColor | None = None) -> None: """Set or update the text colours.""" self._color = color or self._color self._faded = faded or self._faded self._refeshTextColor() - return def setColorState(self, state: bool) -> None: """Change the colour state.""" if self._state is not state: self._state = state self._refeshTextColor() - return def _refeshTextColor(self) -> None: """Refresh the colour of the text on the label.""" @@ -322,11 +302,10 @@ class NColorLabel(QLabel): self._color if self._state else self._faded, ) self.setPalette(palette) - return class NWrappedWidgetBox(QHBoxLayout): - """Extension: A Text-Wrapped Widget Box + """Extension: A Text-Wrapped Widget Box. A custom layout box where a widget is wrapped in text labels on either side within a layout box. The widget is inserted at the {0} @@ -341,4 +320,3 @@ class NWrappedWidgetBox(QHBoxLayout): self.addWidget(widget) if after: self.addWidget(QLabel(after.lstrip())) - return diff --git a/novelwriter/extensions/eventfilters.py b/novelwriter/extensions/eventfilters.py index 09366590..844aacd6 100644 --- a/novelwriter/extensions/eventfilters.py +++ b/novelwriter/extensions/eventfilters.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from typing import TYPE_CHECKING @@ -34,7 +34,7 @@ if TYPE_CHECKING: class WheelEventFilter(QObject): - """Extensions: Wheel Event Filter + """Extensions: Wheel Event Filter. An event filter that filters mouse wheel events for a widget and forward them to the root widget. This solves the lack of mouse wheel @@ -50,7 +50,6 @@ class WheelEventFilter(QObject): super().__init__(parent=parent) self._parent = parent self._locked = False - return def eventFilter(self, obj: QObject, event: QEvent) -> bool: """Filter events of type QWheelEvent and forward them to the @@ -69,6 +68,7 @@ class WheelEventFilter(QObject): class StatusTipFilter(QObject): + """Filter: Remove StatusBar ToolTips.""" def eventFilter(self, obj: QObject, event: QEvent) -> bool: """Filter out status tip events on menus.""" diff --git a/novelwriter/extensions/modified.py b/novelwriter/extensions/modified.py index 4b2f663e..bb256ff1 100644 --- a/novelwriter/extensions/modified.py +++ b/novelwriter/extensions/modified.py @@ -24,7 +24,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from typing import TYPE_CHECKING @@ -47,6 +47,7 @@ if TYPE_CHECKING: class NDialog(QDialog): + """Custom: Modified QDialog.""" def softDelete(self) -> None: """Since calling deleteLater is sometimes not safe from Python, @@ -55,53 +56,54 @@ class NDialog(QDialog): so that it gets garbage collected when it runs out of scope. """ self.setParent(None) # type: ignore - return @pyqtSlot() def reject(self) -> None: """Overload the reject slot and also call close.""" super().reject() self.close() - return class NToolDialog(NDialog): + """Custom: Modified QDialog for Tools.""" def __init__(self, parent: GuiMain) -> None: super().__init__(parent=parent) self.setModal(False) if CONFIG.osDarwin: self.setWindowFlag(Qt.WindowType.Tool) - return def activateDialog(self) -> None: - """Helper function to activate dialog on various systems.""" + """Activate dialog on various operating systems.""" self.show() if CONFIG.osWindows: self.activateWindow() self.raise_() QApplication.processEvents() - return class NNonBlockingDialog(NDialog): + """Custom: Modified Non-Blocking QDialog.""" def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent=parent) self.setModal(True) - return def activateDialog(self) -> None: - """Helper function to activate dialog on various systems.""" + """Activate dialog on various operating systems.""" self.show() if CONFIG.osWindows: self.activateWindow() self.raise_() QApplication.processEvents() - return class NTreeView(QTreeView): + """Custom: Modified QTreeView. + + The main purpose is to provide the middleClicked signal that matches + clicked and doubleCLicked. + """ middleClicked = pyqtSignal(QModelIndex) @@ -116,6 +118,12 @@ class NTreeView(QTreeView): class NComboBox(QComboBox): + """Custom: Modified QComboBox. + + The main purpose is to provide a combo box that doesn't scroll when + the mousewheel is active on it while scrolling through a scrollable + window of many widgets. + """ def __init__(self, parent: QWidget | None = None, maxItems: int = 15) -> None: super().__init__(parent=parent) @@ -126,7 +134,30 @@ class NComboBox(QComboBox): # and allows for scrolling of long lists of items self.setStyleSheet("QComboBox {combobox-popup: 0;}") - return + def wheelEvent(self, event: QWheelEvent) -> None: + """Only capture the mouse wheel if the widget has focus.""" + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() + + def setCurrentData(self, data: str | int | Enum, default: str | int | Enum) -> None: + """Set the current index from data, with a fallback.""" + idx = self.findData(data) + self.setCurrentIndex(self.findData(default) if idx < 0 else idx) + + +class NSpinBox(QSpinBox): + """Custom: Modified QSpinBox. + + The main purpose is to provide a spin box that doesn't scroll when + the mousewheel is active on it while scrolling through a scrollable + window of many widgets. + """ + + def __init__(self, parent: QWidget | None = None) -> None: + super().__init__(parent=parent) + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) def wheelEvent(self, event: QWheelEvent) -> None: """Only capture the mouse wheel if the widget has focus.""" @@ -134,31 +165,15 @@ class NComboBox(QComboBox): super().wheelEvent(event) else: event.ignore() - return - - def setCurrentData(self, data: str | int | Enum, default: str | int | Enum) -> None: - """Set the current index from data, with a fallback.""" - idx = self.findData(data) - self.setCurrentIndex(self.findData(default) if idx < 0 else idx) - return - - -class NSpinBox(QSpinBox): - - def __init__(self, parent: QWidget | None = None) -> None: - super().__init__(parent=parent) - self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) - return - - def wheelEvent(self, event: QWheelEvent) -> None: - if self.hasFocus(): - super().wheelEvent(event) - else: - event.ignore() - return class NDoubleSpinBox(QDoubleSpinBox): + """Custom: Modified QDoubleSpinBox. + + The main purpose is to provide a float spin box that doesn't scroll + when the mousewheel is active on it while scrolling through a + scrollable window of many widgets. + """ def __init__( self, @@ -175,17 +190,20 @@ class NDoubleSpinBox(QDoubleSpinBox): self.setMaximum(maxVal) self.setSingleStep(step) self.setDecimals(prec) - return def wheelEvent(self, event: QWheelEvent) -> None: + """Only capture the mouse wheel if the widget has focus.""" if self.hasFocus(): super().wheelEvent(event) else: event.ignore() - return class NIconToolButton(QToolButton): + """Custom: Modified QToolButton. + + A quicker way to create a tool button using the app theme. + """ def __init__( self, parent: QWidget, iconSize: QSize, @@ -197,15 +215,17 @@ class NIconToolButton(QToolButton): self.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) if icon: self.setThemeIcon(icon, color) - return def setThemeIcon(self, iconKey: str, color: str | None = None) -> None: """Set an icon from the current theme.""" self.setIcon(SHARED.theme.getIcon(iconKey, color)) - return class NIconToggleButton(QToolButton): + """Custom: Modified QToolButton. + + A quicker way to create a toggle button using the app theme. + """ def __init__(self, parent: QWidget, iconSize: QSize, icon: str | None = None) -> None: super().__init__(parent=parent) @@ -216,16 +236,15 @@ class NIconToggleButton(QToolButton): self.setStyleSheet("border: none; background: transparent;") if icon: self.setThemeIcon(icon) - return def setThemeIcon(self, iconKey: str) -> None: """Set an icon from the current theme.""" iconSize = self.iconSize() self.setIcon(SHARED.theme.getToggleIcon(iconKey, (iconSize.width(), iconSize.height()))) - return class NClickableLabel(QLabel): + """Custom: Clickable QLabel.""" mouseClicked = pyqtSignal() diff --git a/novelwriter/extensions/novelselector.py b/novelwriter/extensions/novelselector.py index e3b1e93f..27d217c6 100644 --- a/novelwriter/extensions/novelselector.py +++ b/novelwriter/extensions/novelselector.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -42,6 +42,7 @@ logger = logging.getLogger(__name__) class NovelSelector(QComboBox): + """Custom: Novel Root Folder Selector.""" novelSelectionChanged = pyqtSignal(str) @@ -53,7 +54,6 @@ class NovelSelector(QComboBox): self._listFormat = None self.currentIndexChanged.connect(self._indexChanged) self.updateTheme() - return ## # Properties @@ -80,18 +80,15 @@ class NovelSelector(QComboBox): self._blockSignal = blockSignal self.setCurrentIndex(index) self._blockSignal = False - return def setIncludeAll(self, value: bool) -> None: """Set flag to add an "All Novel Folders" option.""" self._includeAll = value - return def setListFormat(self, value: str | None) -> None: """Set a format string for the list entries.""" if value is None or "{0}" in value: self._listFormat = value - return def updateTheme(self) -> None: """Update theme colours.""" @@ -99,7 +96,6 @@ class NovelSelector(QComboBox): palette.setBrush(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, palette.text()) self.setPalette(palette) self.refreshNovelList() - return ## # Public Slots @@ -133,8 +129,6 @@ class NovelSelector(QComboBox): self.setEnabled(self.count() > 1) self._blockSignal = False - return - ## # Private Slots ## @@ -144,4 +138,3 @@ class NovelSelector(QComboBox): """Re-emit the change of selection signal, unless blocked.""" if not self._blockSignal: self.novelSelectionChanged.emit(self.currentData()) - return diff --git a/novelwriter/extensions/pagedsidebar.py b/novelwriter/extensions/pagedsidebar.py index 89df0c7f..fd332495 100644 --- a/novelwriter/extensions/pagedsidebar.py +++ b/novelwriter/extensions/pagedsidebar.py @@ -22,7 +22,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from PyQt6.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot @@ -39,7 +39,7 @@ from novelwriter.types import ( class NPagedSideBar(QToolBar): - """Extensions: Paged Side Bar + """Extensions: Paged Side Bar. A side bar widget that holds buttons that mimic tabs. It is designed to be used in combination with a QStackedWidget for options panels. @@ -65,8 +65,6 @@ class NPagedSideBar(QToolBar): stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding) self._stretchAction = self.addWidget(stretch) - return - def button(self, buttonId: int) -> _PagedToolButton: """Return a specific button.""" return self._buttons[buttonId] @@ -74,14 +72,12 @@ class NPagedSideBar(QToolBar): def setLabelColor(self, color: QColor) -> None: """Set the text color for the labels.""" self._labelCol = color - return def addLabel(self, text: str) -> None: """Add a new label to the toolbar.""" label = _NPagedToolLabel(self, self._labelCol) label.setText(text) self.insertWidget(self._stretchAction, label) - return def addButton(self, text: str, buttonId: int = -1) -> None: """Add a new button to the toolbar.""" @@ -90,13 +86,11 @@ class NPagedSideBar(QToolBar): self.insertWidget(self._stretchAction, button) self._group.addButton(button, id=buttonId) self._buttons[buttonId] = button - return def setSelected(self, buttonId: int) -> None: """Set the selected button.""" if button := self._group.button(buttonId): button.setChecked(True) - return ## # Private Slots @@ -104,11 +98,10 @@ class NPagedSideBar(QToolBar): @pyqtSlot("QAbstractButton*") def _buttonClicked(self, button: QAbstractButton) -> None: - """A button was clicked in the group, emit its id.""" + """Handle a button click in the group and emit its id.""" buttonId = self._group.id(button) if buttonId != -1: self.buttonClicked.emit(buttonId) - return class _PagedToolButton(QToolButton): @@ -127,8 +120,6 @@ class _PagedToolButton(QToolButton): self._aH = 2*fH//7 self.setFixedHeight(self._bH) - return - def sizeHint(self) -> QSize: """Return a size hint that includes the arrow.""" return super().sizeHint() + QSize(4*self._aH, 0) @@ -180,8 +171,6 @@ class _PagedToolButton(QToolButton): ])) painter.end() - return - class _NPagedToolLabel(QLabel): @@ -199,8 +188,6 @@ class _NPagedToolLabel(QLabel): self._textCol = textColor or self.palette().text().color() - return - def paintEvent(self, event: QPaintEvent) -> None: """Overload the paint event to draw a simple, left aligned text label that matches the button style. @@ -215,5 +202,3 @@ class _NPagedToolLabel(QLabel): painter.setOpacity(1.0) painter.drawText(QRectF(4, self._tM, tW, tH), QtAlignLeft, self.text()) painter.end() - - return diff --git a/novelwriter/extensions/progressbars.py b/novelwriter/extensions/progressbars.py index 0c92564e..8c0b1d1d 100644 --- a/novelwriter/extensions/progressbars.py +++ b/novelwriter/extensions/progressbars.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from math import ceil @@ -37,7 +37,7 @@ from novelwriter.types import ( class NProgressCircle(QProgressBar): - """Extension: Circular Progress Widget + """Extension: Circular Progress Widget. A custom widget that paints a circular progress indicator instead of a straight bar. It is also possible to set custom text for iṫ. @@ -64,7 +64,6 @@ class NProgressCircle(QProgressBar): self.setSizePolicy(QtSizeFixed, QtSizeFixed) self.setFixedWidth(size) self.setFixedHeight(size) - return def setColors( self, back: QColor | None = None, track: QColor | None = None, @@ -80,16 +79,14 @@ class NProgressCircle(QProgressBar): self._bPen = QPen(QBrush(track), self._point, QtSolidLine, QtRoundCap) if isinstance(text, QColor): self._tColor = text - return def setCentreText(self, text: str | None) -> None: """Replace the progress text with a custom string.""" self._text = text self.setValue(self.value()) # Triggers a redraw - return def paintEvent(self, event: QPaintEvent) -> None: - """Custom painter for the progress bar.""" + """Paint the progress bar.""" progress = 100.0*self.value()/self.maximum() angle = ceil(16*3.6*progress) painter = QPainter(self) @@ -103,21 +100,19 @@ class NProgressCircle(QProgressBar): painter.drawArc(self._cRect, 90*16, -angle) painter.setPen(self._tColor) painter.drawText(self._cRect, QtAlignCenter, self._text or f"{progress:.1f} %") - return class NProgressSimple(QProgressBar): - """Extension: Simple Progress Widget + """Extension: Simple Progress Widget. A custom widget that paints a plain bar with no other styling. """ def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) - return def paintEvent(self, event: QPaintEvent) -> None: - """Custom painter for the progress bar.""" + """Paint the progress bar.""" if (value := self.value()) > 0: progress = ceil(self.width()*float(value)/self.maximum()) painter = QPainter(self) @@ -125,4 +120,3 @@ class NProgressSimple(QProgressBar): painter.setPen(self.palette().highlight().color()) painter.setBrush(self.palette().highlight()) painter.drawRect(0, 0, progress, self.height()) - return diff --git a/novelwriter/extensions/statusled.py b/novelwriter/extensions/statusled.py index 5547a664..9152abb2 100644 --- a/novelwriter/extensions/statusled.py +++ b/novelwriter/extensions/statusled.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -34,6 +34,7 @@ logger = logging.getLogger(__name__) class StatusLED(QAbstractButton): + """Custom: LED Style Indicator.""" __slots__ = ("_color", "_negative", "_neutral", "_postitve", "_state") @@ -46,7 +47,6 @@ class StatusLED(QAbstractButton): self._state = None self.setFixedWidth(sW) self.setFixedHeight(sH) - return @property def state(self) -> bool | None: @@ -59,7 +59,6 @@ class StatusLED(QAbstractButton): self._postitve = positive self._negative = negative self.setState(self._state) - return def setState(self, state: bool | None) -> None: """Set the colour state.""" @@ -71,7 +70,6 @@ class StatusLED(QAbstractButton): self._color = self._neutral self._state = state self.update() - return def paintEvent(self, event: QPaintEvent) -> None: """Draw the LED.""" @@ -82,4 +80,3 @@ class StatusLED(QAbstractButton): painter.setOpacity(1.0) painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2) painter.end() - return diff --git a/novelwriter/extensions/switch.py b/novelwriter/extensions/switch.py index 0a11f52f..cc0d43a9 100644 --- a/novelwriter/extensions/switch.py +++ b/novelwriter/extensions/switch.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty, pyqtSlot # pyright: ignore @@ -32,6 +32,7 @@ from novelwriter.types import QtNoPen, QtPaintAntiAlias, QtSizeFixed class NSwitch(QAbstractButton): + """Custom: Toggle Switch.""" __slots__ = ("_cOff", "_cOn", "_offset", "_rH", "_rR", "_xH", "_xR", "_xW") @@ -55,8 +56,6 @@ class NSwitch(QAbstractButton): self.clicked.connect(self._onClick) - return - ## # Properties ## @@ -69,7 +68,6 @@ class NSwitch(QAbstractButton): def offset(self, offset: int) -> None: self._offset = offset self.update() - return ## # Getters and Setters @@ -79,7 +77,6 @@ class NSwitch(QAbstractButton): """Overload setChecked to also alter the offset.""" super().setChecked(checked) self._offset = (self._xW - self._xR) if checked else self._xR - return ## # Events @@ -89,7 +86,6 @@ class NSwitch(QAbstractButton): """Overload resize to ensure correct offset.""" super().resizeEvent(event) self._offset = (self._xW - self._xR) if self.isChecked() else self._xR - return def paintEvent(self, event: QPaintEvent) -> None: """Drawing the switch itself.""" @@ -109,13 +105,10 @@ class NSwitch(QAbstractButton): painter.end() - return - def enterEvent(self, event: QEnterEvent) -> None: """Change the cursor when hovering the button.""" self.setCursor(Qt.CursorShape.PointingHandCursor) super().enterEvent(event) - return @pyqtSlot(bool) def _onClick(self, checked: bool) -> None: @@ -125,4 +118,3 @@ class NSwitch(QAbstractButton): anim.setStartValue(self._offset) anim.setEndValue((self._xW - self._xR) if checked else self._xR) anim.start() - return diff --git a/novelwriter/extensions/switchbox.py b/novelwriter/extensions/switchbox.py index e4732f97..e287cf25 100644 --- a/novelwriter/extensions/switchbox.py +++ b/novelwriter/extensions/switchbox.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from typing import TYPE_CHECKING @@ -39,7 +39,7 @@ if TYPE_CHECKING: class NSwitchBox(QScrollArea): - """Extension: Switch Box Widget + """Extension: Switch Box Widget. A widget that can hold a list of switches with labels and optional icons. The switch toggles emits a common signal with a switch key. @@ -55,7 +55,6 @@ class NSwitchBox(QScrollArea): self._sIcon = baseSize self._widgets = [] self.clear() - return def clear(self) -> None: """Rebuild the content of the core widget.""" @@ -72,8 +71,6 @@ class NSwitchBox(QScrollArea): self.setWidgetResizable(True) self.setWidget(self._widget) - return - def addLabel(self, text: str) -> None: """Add a header label to the content box.""" label = QLabel(text, self) @@ -83,7 +80,6 @@ class NSwitchBox(QScrollArea): self._content.addWidget(label, self._index, 0, 1, 3, QtAlignLeft) self._widgets.append(label) self._bumpIndex() - return def addItem(self, qIcon: QIcon, text: str, identifier: str, default: bool = False) -> None: """Add an item to the content box.""" @@ -104,8 +100,6 @@ class NSwitchBox(QScrollArea): self._widgets.append(switch) self._bumpIndex() - return - def addSeparator(self) -> None: """Add a blank entry in the content box.""" spacer = QWidget(self) @@ -113,7 +107,6 @@ class NSwitchBox(QScrollArea): self._content.addWidget(spacer, self._index, 0, 1, 3, QtAlignLeft) self._widgets.append(spacer) self._bumpIndex() - return ## # Internal Functions @@ -122,7 +115,6 @@ class NSwitchBox(QScrollArea): def _emitSwitchSignal(self, identifier: str, state: bool) -> None: """Emit a signal for a switch toggle.""" self.switchToggled.emit(identifier, state) - return def _bumpIndex(self) -> None: """Increase the index counter and make sure only the last @@ -131,4 +123,3 @@ class NSwitchBox(QScrollArea): self._content.setRowStretch(self._index, 0) self._content.setRowStretch(self._index + 1, 1) self._index += 1 - return diff --git a/novelwriter/extensions/versioninfo.py b/novelwriter/extensions/versioninfo.py index 2f13afaa..d92a71c8 100644 --- a/novelwriter/extensions/versioninfo.py +++ b/novelwriter/extensions/versioninfo.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -45,6 +45,11 @@ API_URL = "https://api.github.com/repos/vkbo/novelwriter/releases/latest" class VersionInfoWidget(QWidget): + """Custom: version Info Label. + + A custom widget that will show a clickable area for contacting + GitHub and pulling the latest release version info. + """ def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -75,8 +80,6 @@ class VersionInfoWidget(QWidget): self.setLayout(self._layout) - return - ## # Private Slots ## @@ -93,7 +96,6 @@ class VersionInfoWidget(QWidget): lookup = _Retriever() lookup.signals.dataReady.connect(self._updateReleaseInfo) SHARED.runInThreadPool(lookup) - return ## # Private Slots @@ -109,7 +111,6 @@ class VersionInfoWidget(QWidget): )) else: self._lblRelease.setText(self._trLatest.format(reason or self.tr("Failed"))) - return class _Retriever(QRunnable): @@ -117,7 +118,6 @@ class _Retriever(QRunnable): def __init__(self) -> None: super().__init__() self.signals = _RetrieverSignal() - return @pyqtSlot() def run(self) -> None: @@ -140,7 +140,6 @@ class _Retriever(QRunnable): except Exception as e: logger.error("Failed to retrieve release info") self.signals.dataReady.emit("", str(e)) - return class _RetrieverSignal(QObject): diff --git a/novelwriter/formats/shared.py b/novelwriter/formats/shared.py index 3f7b3a42..f6468125 100644 --- a/novelwriter/formats/shared.py +++ b/novelwriter/formats/shared.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import re diff --git a/novelwriter/formats/todocx.py b/novelwriter/formats/todocx.py index 778f7867..53d8884c 100644 --- a/novelwriter/formats/todocx.py +++ b/novelwriter/formats/todocx.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -100,7 +100,7 @@ def _wText(parent: ET.Element, text: str) -> ET.Element: def _mmToSz(value: float) -> int: - """Convert millimetres to internal margin size units""" + """Convert millimetres to internal margin size units.""" return int(value*20.0*72.0/25.4) @@ -143,6 +143,7 @@ S_FNOTE = "FootnoteText" class DocXXmlRel(NamedTuple): + """DocX XML Rel Data.""" rId: str relType: str @@ -150,6 +151,7 @@ class DocXXmlRel(NamedTuple): class DocXXmlFile(NamedTuple): + """DocX XML File Data.""" xml: ET.Element path: str @@ -157,6 +159,7 @@ class DocXXmlFile(NamedTuple): class DocXParStyle(NamedTuple): + """DocX XML Paragraph Style Data.""" name: str styleId: str @@ -176,7 +179,7 @@ class DocXParStyle(NamedTuple): class ToDocX(Tokenizer): - """Core: DocX Document Writer + """Core: DocX Document Writer. Extend the Tokenizer class to writer DocX Document files. """ @@ -202,8 +205,6 @@ class ToDocX(Tokenizer): self._usedNotes: dict[str, int] = {} self._usedFields: list[tuple[ET.Element, str]] = [] - return - ## # Setters ## @@ -214,25 +215,22 @@ class ToDocX(Tokenizer): """Set the document page size and margins in millimetres.""" self._pageSize = QSize(_mmToSz(width), _mmToSz(height)) self._pageMargins = QMargins(_mmToSz(left), _mmToSz(top), _mmToSz(right), _mmToSz(bottom)) - return def setHeaderFormat(self, value: str, offset: int) -> None: """Set the document header format.""" self._headerFormat = value.strip() self._pageOffset = offset - return ## # Class Methods ## def initDocument(self) -> None: - """Initialises the DocX document structure.""" + """Initialise the DocX document structure.""" super().initDocument() self._fontFamily = self._textFont.family() self._fontSize = self._textFont.pointSizeF() self._generateStyles() - return def doConvert(self) -> None: """Convert the list of text tokens into XML elements.""" @@ -302,8 +300,6 @@ class ToDocX(Tokenizer): elif tType == BlockTyp.KEYWORD: self._processFragments(par, S_META, tText, tFormat) - return - def closeDocument(self) -> None: """Generate all the XML.""" self._coreXml() @@ -322,8 +318,6 @@ class ToDocX(Tokenizer): if self._usedNotes: self._footnotesXml() - return - def saveDocument(self, path: Path) -> None: """Save the data to a .docx file.""" # Content Lists @@ -373,8 +367,6 @@ class ToDocX(Tokenizer): xmlToZip(f"{rel.path}/{name}", rel.xml, outZip) xmlToZip("[Content_Types].xml", dTypes, outZip) - return - ## # Internal Functions ## @@ -454,8 +446,6 @@ class ToDocX(Tokenizer): if temp := text[fStart:]: par.addContent(self._textRunToXml(temp, xFmt, fClass, fLink)) - return - def _textRunToXml(self, text: str | None, fmt: int, fClass: str, fLink: str) -> ET.Element: """Encode the text run into XML.""" xR = xmlElement(_wTag("r")) @@ -668,8 +658,6 @@ class ToDocX(Tokenizer): for style in styles: self._styles[style.styleId] = style - return - def _nextRelId(self) -> str: """Generate the next unique rId.""" return f"rId{len(self._rels) + 1}" @@ -1054,6 +1042,10 @@ class ToDocX(Tokenizer): class DocXParagraph: + """DocX Text Paragraph. + + This class holds a single paragraph of a DocX document. + """ __slots__ = ( "_bottomMargin", "_breakAfter", "_breakBefore", "_content", @@ -1073,7 +1065,6 @@ class DocXParagraph: self._breakBefore = False self._breakAfter = False self._footnoteRef = False - return ## # Properties @@ -1091,53 +1082,43 @@ class DocXParagraph: def setStyle(self, style: DocXParStyle | None) -> None: """Set the paragraph style.""" self._style = style - return def setAlignment(self, value: str) -> None: """Set paragraph alignment.""" if value in ("left", "center", "right", "both"): self._textAlign = value - return def setMarginTop(self, value: float) -> None: """Set margin above in pt.""" self._topMargin = value - return def setMarginBottom(self, value: float) -> None: """Set margin below in pt.""" self._bottomMargin = value - return def setMarginLeft(self, value: float) -> None: """Set margin left in pt.""" self._leftMargin = value - return def setMarginRight(self, value: float) -> None: """Set margin right in pt.""" self._rightMargin = value - return def setIndentFirst(self, state: bool) -> None: """Set first line indent.""" self._indentFirst = state - return def setPageBreakBefore(self, state: bool) -> None: """Set page break before flag.""" self._breakBefore = state - return def setPageBreakAfter(self, state: bool) -> None: """Set page break after flag.""" self._breakAfter = state - return def setIsFootnote(self, state: bool) -> None: """Set is footnote flag.""" self._footnoteRef = state - return ## # Methods @@ -1146,10 +1127,9 @@ class DocXParagraph: def addContent(self, run: ET.Element) -> None: """Add a run segment to the paragraph.""" self._content.append(run) - return def toXml(self, body: ET.Element) -> None: - """Called after all content is set.""" + """Generate the XML. Call after all content is set.""" if style := self._style: xP = xmlSubElem(body, _wTag("p")) @@ -1191,5 +1171,3 @@ class DocXParagraph: if self._breakAfter: xR = xmlSubElem(xP, _wTag("r")) xmlSubElem(xR, _wTag("br"), attrib={_wTag("type"): "page"}) - - return diff --git a/novelwriter/formats/tohtml.py b/novelwriter/formats/tohtml.py index 4614050a..db9b0061 100644 --- a/novelwriter/formats/tohtml.py +++ b/novelwriter/formats/tohtml.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -77,7 +77,7 @@ HTML_NONE = (0, "") class ToHtml(Tokenizer): - """Core: HTML Document Writer + """Core: HTML Document Writer. Extend the Tokenizer class to writer HTML output. This class is also used by the Document Viewer, and Manuscript Build Preview. @@ -90,7 +90,6 @@ class ToHtml(Tokenizer): self._usedNotes: dict[str, int] = {} self._usedFields: list[tuple[int, str]] = [] self.setReplaceUnicode(False) - return ## # Setters @@ -101,7 +100,6 @@ class ToHtml(Tokenizer): class tags. """ self._cssStyles = cssStyles - return def setReplaceUnicode(self, doReplace: bool) -> None: """Set the translation map to either minimal or full unicode for @@ -114,7 +112,6 @@ class ToHtml(Tokenizer): if doReplace: # Extend to all relevant Unicode characters self._trMap.update(str.maketrans(nwHtmlUnicode.U_TO_H)) - return ## # Class Methods @@ -130,7 +127,6 @@ class ToHtml(Tokenizer): """ super().doPreProcessing() self._text = self._text.translate(self._trMap) - return def doConvert(self) -> None: """Convert the list of text tokens into an HTML document.""" @@ -237,8 +233,6 @@ class ToHtml(Tokenizer): self._pages.append("".join(lines)) - return - def closeDocument(self) -> None: """Run close document tasks.""" # Replace fields if there are stats available @@ -265,8 +259,6 @@ class ToHtml(Tokenizer): self._pages.append("".join(lines)) - return - def saveDocument(self, path: Path) -> None: """Save the data to an HTML file.""" if path.suffix.lower() == ".json": @@ -309,14 +301,11 @@ class ToHtml(Tokenizer): logger.info("Wrote file: %s", path) - return - def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None: """Replace tabs with spaces in the html.""" tabSpace = spaceChar*nSpaces pages = [aLine.replace("\t", tabSpace) for aLine in self._pages] self._pages = pages - return def getStyleSheet(self) -> list[str]: """Generate a stylesheet for the current settings.""" diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py index b4cce890..a82105b4 100644 --- a/novelwriter/formats/tokenizer.py +++ b/novelwriter/formats/tokenizer.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -55,6 +55,7 @@ logger = logging.getLogger(__name__) class ComStyle(NamedTuple): + """Comment style info.""" label: str = "" labelClass: str = "" @@ -92,7 +93,7 @@ B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE) class Tokenizer(ABC): - """Core: Text Tokenizer Abstract Base Class + """Core: Text Tokenizer Abstract Base Class. This is the base class for all document build classes. It parses the novelWriter markup format and generates a registry of tokens and @@ -224,8 +225,6 @@ class Tokenizer(ABC): self._dialogParser = DialogParser() self._dialogParser.initParser() - return - ## # Properties ## @@ -253,94 +252,78 @@ class Tokenizer(ABC): """Set language for the document.""" if language: self._dLocale = QLocale(language) - return def setTheme(self, theme: TextDocumentTheme) -> None: """Set the document colour theme.""" self._theme = theme - return def setPartitionFormat(self, hFormat: str, hide: bool = False) -> None: """Set the partition format pattern.""" self._fmtPart = hFormat.strip() self._hidePart = hide - return def setChapterFormat(self, hFormat: str, hide: bool = False) -> None: """Set the chapter format pattern.""" self._fmtChapter = hFormat.strip() self._hideChapter = hide - return def setUnNumberedFormat(self, hFormat: str, hide: bool = False) -> None: """Set the unnumbered format pattern.""" self._fmtUnNum = hFormat.strip() self._hideUnNum = hide - return def setSceneFormat(self, hFormat: str, hide: bool = False) -> None: """Set the scene format pattern and hidden status.""" self._fmtScene = hFormat.strip() self._hideScene = hide - return def setHardSceneFormat(self, hFormat: str, hide: bool = False) -> None: """Set the hard scene format pattern and hidden status.""" self._fmtHScene = hFormat.strip() self._hideHScene = hide - return def setSectionFormat(self, hFormat: str, hide: bool = False) -> None: """Set the section format pattern and hidden status.""" self._fmtSection = hFormat.strip() self._hideSection = hide - return def setTitleStyle(self, center: bool, pageBreak: bool) -> None: """Set the title heading style.""" self._titleStyle = BlockFmt.CENTRE if center else BlockFmt.NONE self._titleStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE - return def setPartitionStyle(self, center: bool, pageBreak: bool) -> None: """Set the partition heading style.""" self._partStyle = BlockFmt.CENTRE if center else BlockFmt.NONE self._partStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE - return def setChapterStyle(self, center: bool, pageBreak: bool) -> None: """Set the chapter heading style.""" self._chapterStyle = BlockFmt.CENTRE if center else BlockFmt.NONE self._chapterStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE - return def setSceneStyle(self, center: bool, pageBreak: bool) -> None: """Set the scene heading style.""" self._sceneStyle = BlockFmt.CENTRE if center else BlockFmt.NONE self._sceneStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE - return def setTextFont(self, font: QFont) -> None: """Set the build font.""" self._textFont = fontMatcher(font) - return def setLineHeight(self, height: float) -> None: """Set the line height between 0.5 and 5.0.""" self._lineHeight = min(max(float(height), 0.5), 5.0) - return def setHeadingStyles(self, color: bool, scale: bool, bold: bool) -> None: """Set text style for headings.""" self._colorHeads = color self._scaleHeads = scale self._boldHeads = bold - return def setBlockIndent(self, indent: float) -> None: """Set the block indent between 0.0 and 10.0.""" self._blockIndent = min(max(float(indent), 0.0), 10.0) - return def setFirstLineIndent(self, state: bool, indent: float, first: bool) -> None: """Set first line indent and whether to also indent first @@ -349,67 +332,54 @@ class Tokenizer(ABC): self._firstIndent = state self._firstWidth = indent self._indentFirst = first - return def setJustify(self, state: bool) -> None: """Enable or disable text justification.""" self._doJustify = state - return def setDialogHighlight(self, state: bool) -> None: """Enable or disable dialogue highlighting.""" self._hlightDialog = state - return def setTitleMargins(self, upper: float, lower: float) -> None: """Set the upper and lower title margin.""" self._marginTitle = (float(upper), float(lower)) - return def setHead1Margins(self, upper: float, lower: float) -> None: """Set the upper and lower heading 1 margin.""" self._marginHead1 = (float(upper), float(lower)) - return def setHead2Margins(self, upper: float, lower: float) -> None: """Set the upper and lower heading 2 margin.""" self._marginHead2 = (float(upper), float(lower)) - return def setHead3Margins(self, upper: float, lower: float) -> None: """Set the upper and lower heading 3 margin.""" self._marginHead3 = (float(upper), float(lower)) - return def setHead4Margins(self, upper: float, lower: float) -> None: """Set the upper and lower heading 4 margin.""" self._marginHead4 = (float(upper), float(lower)) - return def setTextMargins(self, upper: float, lower: float) -> None: """Set the upper and lower text margin.""" self._marginText = (float(upper), float(lower)) - return def setMetaMargins(self, upper: float, lower: float) -> None: """Set the upper and lower meta text margin.""" self._marginMeta = (float(upper), float(lower)) - return def setSeparatorMargins(self, upper: float, lower: float) -> None: """Set the upper and lower meta text margin.""" self._marginSep = (float(upper), float(lower)) - return def setLinkHeadings(self, state: bool) -> None: """Enable or disable adding an anchor before headings.""" self._linkHeadings = state - return def setBodyText(self, state: bool) -> None: """Include body text in build.""" self._doBodyText = state - return def setCommentType(self, comment: nwComment, state: bool) -> None: """Toggle the inclusion og certain comment types.""" @@ -417,22 +387,18 @@ class Tokenizer(ABC): self._doComments.add(comment) else: self._doComments.discard(comment) - return def setKeywords(self, state: bool) -> None: """Include keywords in build.""" self._doKeywords = state - return def setIgnoredKeywords(self, keywords: str) -> None: """Comma separated string of keywords to ignore.""" self._skipKeywords = set(x.lower().strip() for x in keywords.split(",")) - return def setKeepLineBreaks(self, state: bool) -> None: """Keep line breaks in paragraphs.""" self._keepBreaks = state - return ## # Class Methods @@ -460,12 +426,10 @@ class Tokenizer(ABC): self._classes["tag"] = self._theme.tag self._classes["keyword"] = self._theme.keyword self._classes["optional"] = self._theme.optional - return def setBreakNext(self) -> None: """Set a page break for next block.""" self._breakNext = True - return def addRootHeading(self, tHandle: str) -> None: """Add a heading at the start of a new root folder.""" @@ -491,8 +455,6 @@ class Tokenizer(ABC): if self._keepRaw: self._raw.append(f"#! {title}\n\n") - return - def setText(self, tHandle: str, text: str | None = None) -> None: """Set the text for the tokenizer from a handle. If text is not set, it's is loaded from the file. @@ -503,7 +465,6 @@ class Tokenizer(ABC): self._text = text or self._project.storage.getDocumentText(tHandle) self._handle = tHandle self._isNovel = nwItem.itemLayout == nwItemLayout.DOCUMENT - return def doPreProcessing(self) -> None: """Run pre-processing jobs before the text is tokenized.""" @@ -512,7 +473,6 @@ class Tokenizer(ABC): replace = {f"<{k}>": v for k, v in entry.items()} rxRep = re.compile("|".join([re.escape(k) for k in replace]), flags=re.DOTALL) self._text = rxRep.sub(lambda x: replace[x.group(0)], self._text) - return def tokenizeText(self) -> None: """Scan the text for either lines starting with specific @@ -590,13 +550,13 @@ class Tokenizer(ABC): self._breakNext = True continue - elif sLine == "[vspace]": + if sLine == "[vspace]": tBlocks.append( (BlockTyp.SKIP, "", "", [], tStyle) ) continue - elif sLine.startswith("[vspace:") and sLine.endswith("]"): + if sLine.startswith("[vspace:") and sLine.endswith("]"): nSkip = checkInt(sLine[8:-1], 0) if nSkip >= 1: tBlocks.append( @@ -962,8 +922,6 @@ class Tokenizer(ABC): text = tText.replace(nwHeadFmt.BR, " ").replace("&", "&") self._outline[tKey] = f"{prefix}|{text}" - return - def countStats(self) -> None: """Count stats on the tokenized text.""" titleCount = self._counts.get(nwStats.TITLES, 0) @@ -1039,8 +997,6 @@ class Tokenizer(ABC): self._counts[nwStats.WCHARS_TEXT] = textWordChars self._counts[nwStats.WCHARS_TITLE] = titleWordChars - return - ## # Internal Functions ## @@ -1182,6 +1138,12 @@ class Tokenizer(ABC): class HeadingFormatter: + """Core: Format Text Headings. + + This class holds the various chapter and scene counters and can + apply the Build Settings header format settings based on internal + counter state. + """ def __init__( self, @@ -1195,35 +1157,29 @@ class HeadingFormatter: self._chapter = chapter self._scene = scene self._absolute = absolute - return def setHandle(self, tHandle: str | None) -> None: """Set the handle currently being processed.""" self._handle = tHandle - return def incChapter(self) -> None: """Increment the chapter counter.""" self._chapter += 1 - return def incScene(self) -> None: """Increment the scene counters.""" self._scene += 1 self._absolute += 1 - return def resetAll(self) -> None: """Reset all counters.""" self._chapter = 0 self._scene = 0 self._absolute = 0 - return def resetScene(self) -> None: """Reset the chapter scene counter.""" self._scene = 0 - return def apply(self, hFormat: str, text: str, nHead: int) -> str: """Apply formatting to a specific heading.""" diff --git a/novelwriter/formats/tomarkdown.py b/novelwriter/formats/tomarkdown.py index 0b477302..37391b4b 100644 --- a/novelwriter/formats/tomarkdown.py +++ b/novelwriter/formats/tomarkdown.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -79,7 +79,7 @@ EXT_MD = { class ToMarkdown(Tokenizer): - """Core: Markdown Document Writer + """Core: Markdown Document Writer. Extend the Tokenizer class to writer Markdown output. It supports both Standard Markdown and Extended Markdown. The class also @@ -91,7 +91,6 @@ class ToMarkdown(Tokenizer): self._extended = extended self._usedNotes: dict[str, int] = {} self._usedFields: list[tuple[int, str]] = [] - return ## # Class Methods @@ -153,8 +152,6 @@ class ToMarkdown(Tokenizer): self._pages.append("".join(lines)) - return - def closeDocument(self) -> None: """Run close document tasks.""" # Replace fields if there are stats available @@ -181,20 +178,16 @@ class ToMarkdown(Tokenizer): lines.append("\n") self._pages.append("".join(lines)) - return - def saveDocument(self, path: Path) -> None: """Save the data to a plain text file.""" with open(path, mode="w", encoding="utf-8") as outFile: outFile.write("".join(self._pages)) logger.info("Wrote file: %s", path) - return def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None: """Replace tabs with spaces.""" spaces = spaceChar*nSpaces self._pages = [p.replace("\t", spaces) for p in self._pages] - return ## # Internal Functions diff --git a/novelwriter/formats/toodt.py b/novelwriter/formats/toodt.py index ddd554ba..0bd00189 100644 --- a/novelwriter/formats/toodt.py +++ b/novelwriter/formats/toodt.py @@ -23,7 +23,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -128,7 +128,7 @@ FONT_WEIGHT_MAP = {"400": "normal", "700": "bold"} class ToOdt(Tokenizer): - """Core: Open Document Writer + """Core: Open Document Writer. Extend the Tokenizer class to writer Open Document files. The output should conform to the 1.3 Extended standard. @@ -189,8 +189,6 @@ class ToOdt(Tokenizer): self._mDocLeft = "2.000cm" self._mDocRight = "2.000cm" - return - ## # Setters ## @@ -205,20 +203,18 @@ class ToOdt(Tokenizer): self._mDocBtm = f"{bottom/10.0:.3f}cm" self._mDocLeft = f"{left/10.0:.3f}cm" self._mDocRight = f"{right/10.0:.3f}cm" - return def setHeaderFormat(self, value: str, offset: int) -> None: """Set the document header format.""" self._headerFormat = value.strip() self._pageOffset = offset - return ## # Class Methods ## def initDocument(self) -> None: - """Initialises a new open document XML tree.""" + """Initialise a new open document XML tree.""" super().initDocument() # Initialise Variables @@ -325,8 +321,6 @@ class ToOdt(Tokenizer): self._useableStyles() self._writeHeader() - return - def doConvert(self) -> None: """Convert the list of text tokens into XML elements.""" xText = self._xText @@ -395,8 +389,6 @@ class ToOdt(Tokenizer): elif tType == BlockTyp.KEYWORD: self._addTextPar(xText, S_META, oStyle, tText, tFmt=tFormat) - return - def closeDocument(self) -> None: """Add additional collected information to the XML.""" for style in self._autoPara.values(): @@ -412,7 +404,6 @@ class ToOdt(Tokenizer): _mkTag("text", "name"): f"Manuscript{key[:1].upper()}{key[1:]}", }) self._xText.insert(0, xFields) - return def saveDocument(self, path: Path) -> None: """Save the data to an .fodt or .odt file.""" @@ -456,8 +447,6 @@ class ToOdt(Tokenizer): logger.info("Wrote file: %s", path) - return - ## # Internal Functions ## @@ -670,7 +659,7 @@ class ToOdt(Tokenizer): return None def _emToCm(self, value: float) -> str: - """Converts an em value to centimetres.""" + """Convert an em value to centimetres.""" return f"{value*self._fontSize*2.54/72.0:.3f}cm" def _emToPt(self, scale: float) -> str: @@ -705,8 +694,6 @@ class ToOdt(Tokenizer): _mkTag("fo", "margin-bottom"): self._emToCm(0.5), }) - return - def _defaultStyles(self) -> None: """Set the default styles.""" hScale = self._scaleHeads @@ -783,8 +770,6 @@ class ToOdt(Tokenizer): _mkTag("number", "min-integer-digits"): "1", }) - return - def _useableStyles(self) -> None: """Set the usable styles.""" hScale = self._scaleHeads @@ -954,8 +939,6 @@ class ToOdt(Tokenizer): style.packXML(self._xStyl) self._mainPara[style.name] = style - return - def _writeHeader(self) -> None: """Write the header elements.""" xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib={ @@ -993,8 +976,6 @@ class ToOdt(Tokenizer): _mkTag("text", "style-name"): "Header" }) - return - # Auto-Style Classes # ================== @@ -1004,6 +985,7 @@ class ODTParagraphStyle: exporter. Only the used settings are exposed here to keep the class minimal and fast. """ + 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"] @@ -1046,8 +1028,6 @@ class ODTParagraphStyle: "opacity": ["loext", None], } - return - @property def name(self) -> str: return self._name @@ -1059,7 +1039,6 @@ class ODTParagraphStyle: def setName(self, name: str) -> None: """Set the paragraph style name.""" self._name = name - return ## # Attribute Setters @@ -1068,17 +1047,14 @@ class ODTParagraphStyle: def setDisplayName(self, value: str | None) -> None: """Set style display name.""" self._mAttr["display-name"][1] = value - return def setParentStyleName(self, value: str | None) -> None: """Set parent style name.""" self._mAttr["parent-style-name"][1] = value - return def setNextStyleName(self, value: str | None) -> None: """Set next style name.""" self._mAttr["next-style-name"][1] = value - return def setOutlineLevel(self, value: str | None) -> None: """Set paragraph outline level.""" @@ -1086,7 +1062,6 @@ class ODTParagraphStyle: self._mAttr["default-outline-level"][1] = value else: self._mAttr["default-outline-level"][1] = None - return def setClass(self, value: str | None) -> None: """Set paragraph class.""" @@ -1094,7 +1069,6 @@ class ODTParagraphStyle: self._mAttr["class"][1] = value else: self._mAttr["class"][1] = None - return ## # Paragraph Setters @@ -1103,32 +1077,26 @@ class ODTParagraphStyle: def setMarginTop(self, value: str | None) -> None: """Set paragraph top margin.""" self._pAttr["margin-top"][1] = value - return def setMarginBottom(self, value: str | None) -> None: """Set paragraph bottom margin.""" self._pAttr["margin-bottom"][1] = value - return def setMarginLeft(self, value: str | None) -> None: """Set paragraph left margin.""" self._pAttr["margin-left"][1] = value - return def setMarginRight(self, value: str | None) -> None: """Set paragraph right margin.""" self._pAttr["margin-right"][1] = value - return def setTextIndent(self, value: str | None) -> None: """Set text indentation.""" self._pAttr["text-indent"][1] = value - return def setLineHeight(self, value: str | None) -> None: """Set line height.""" self._pAttr["line-height"][1] = value - return def setTextAlign(self, value: str | None) -> None: """Set paragraph text alignment.""" @@ -1136,7 +1104,6 @@ class ODTParagraphStyle: self._pAttr["text-align"][1] = value else: self._pAttr["text-align"][1] = None - return def setBreakBefore(self, value: str | None) -> None: """Set page break before policy.""" @@ -1144,7 +1111,6 @@ class ODTParagraphStyle: self._pAttr["break-before"][1] = value else: self._pAttr["break-before"][1] = None - return def setBreakAfter(self, value: str | None) -> None: """Set page break after policy.""" @@ -1152,7 +1118,6 @@ class ODTParagraphStyle: self._pAttr["break-after"][1] = value else: self._pAttr["break-after"][1] = None - return ## # Text Setters @@ -1161,17 +1126,14 @@ class ODTParagraphStyle: def setFontName(self, value: str | None) -> None: """Set font name.""" self._tAttr["font-name"][1] = value - return def setFontFamily(self, value: str | None) -> None: """Set font family.""" self._tAttr["font-family"][1] = value - return def setFontSize(self, value: str | None) -> None: """Set font size.""" self._tAttr["font-size"][1] = value - return def setFontWeight(self, value: str | None) -> None: """Set font weight.""" @@ -1179,7 +1141,6 @@ class ODTParagraphStyle: self._tAttr["font-weight"][1] = value else: self._tAttr["font-weight"][1] = None - return def setColor(self, value: QColor | None) -> None: """Set text colour.""" @@ -1189,7 +1150,6 @@ class ODTParagraphStyle: else: self._tAttr["color"][1] = None self._tAttr["opacity"][1] = None - return ## # Methods @@ -1233,14 +1193,13 @@ class ODTParagraphStyle: if attr := {_mkTag(n, m): v for m, (n, v) in self._tAttr.items() if v}: ET.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=attr) - return - class ODTTextStyle: """Wrapper class for the text style setting used by the exporter. Only the used settings are exposed here to keep the class minimal and fast. """ + 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"] @@ -1263,7 +1222,6 @@ class ODTTextStyle: "text-underline-width": ["style", None], "text-underline-color": ["style", None], } - return @property def name(self) -> str: @@ -1279,7 +1237,6 @@ class ODTTextStyle: self._tAttr["font-weight"][1] = value else: self._tAttr["font-weight"][1] = None - return def setFontStyle(self, value: str | None) -> None: """Set text font style.""" @@ -1287,7 +1244,6 @@ class ODTTextStyle: self._tAttr["font-style"][1] = value else: self._tAttr["font-style"][1] = None - return def setColor(self, value: QColor | None) -> None: """Set text colour.""" @@ -1295,7 +1251,6 @@ class ODTTextStyle: self._tAttr["color"][1] = value.name(QtHexRgb) else: self._tAttr["color"][1] = None - return def setBackgroundColor(self, value: QColor | None) -> None: """Set text background colour.""" @@ -1303,7 +1258,6 @@ class ODTTextStyle: self._tAttr["background-color"][1] = value.name(QtHexRgb) else: self._tAttr["background-color"][1] = None - return def setTextPosition(self, value: str | None) -> None: """Set text vertical position.""" @@ -1311,7 +1265,6 @@ class ODTTextStyle: self._tAttr["text-position"][1] = f"{value} 58%" else: self._tAttr["text-position"][1] = None - return def setStrikeStyle(self, value: str | None) -> None: """Set text line-trough style.""" @@ -1319,7 +1272,6 @@ class ODTTextStyle: self._tAttr["text-line-through-style"][1] = value else: self._tAttr["text-line-through-style"][1] = None - return def setStrikeType(self, value: str | None) -> None: """Set text line-through type.""" @@ -1327,7 +1279,6 @@ class ODTTextStyle: self._tAttr["text-line-through-type"][1] = value else: self._tAttr["text-line-through-type"][1] = None - return def setUnderlineStyle(self, value: str | None) -> None: """Set text underline style.""" @@ -1335,7 +1286,6 @@ class ODTTextStyle: self._tAttr["text-underline-style"][1] = value else: self._tAttr["text-underline-style"][1] = None - return def setUnderlineWidth(self, value: str | None) -> None: """Set text underline width.""" @@ -1343,7 +1293,6 @@ class ODTTextStyle: self._tAttr["text-underline-width"][1] = value else: self._tAttr["text-underline-width"][1] = None - return def setUnderlineColor(self, value: str | None) -> None: """Set text underline colour.""" @@ -1351,7 +1300,6 @@ class ODTTextStyle: self._tAttr["text-underline-color"][1] = value else: self._tAttr["text-underline-color"][1] = None - return ## # Methods @@ -1365,7 +1313,6 @@ class ODTTextStyle: }) if attr := {_mkTag(n, m): v for m, (n, v) in self._tAttr.items() if v}: ET.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=attr) - return # XML Complex Element Helper Class @@ -1378,7 +1325,9 @@ X_SPAN_SING = 3 class XMLParagraph: - """This is a helper class to manage the text content of a single + """ODT Text Paragraph. + + This is a helper class to manage the text content of a single XML element using mixed content tags. Rules: @@ -1408,8 +1357,6 @@ class XMLParagraph: self._rawTxt = "" self._xRoot.text = "" - return - def appendText(self, text: str) -> None: """Append text to the XML element. We do this one character at the time in order to be able to process line breaks, tabs and @@ -1424,7 +1371,7 @@ class XMLParagraph: if c == " ": nSpaces += 1 continue - elif nSpaces > 0: + if nSpaces > 0: self._processSpaces(nSpaces) nSpaces = 0 @@ -1468,8 +1415,6 @@ class XMLParagraph: # Handle trailing spaces self._processSpaces(nSpaces) - return - def appendSpan(self, text: str, style: str, link: str) -> None: """Append a text span to the XML element. The span is always closed since we do not produce nested spans (like Libre Office). @@ -1491,7 +1436,6 @@ class XMLParagraph: self._nState = X_SPAN_TEXT self.appendText(text) self._nState = X_ROOT_TAIL - return def appendNode(self, xNode: ET.Element | None) -> None: """Append an XML node to the paragraph. We only check for the @@ -1504,7 +1448,6 @@ class XMLParagraph: self._xTail = xNode self._xTail.tail = "" self._nState = X_ROOT_TAIL - return def checkError(self) -> tuple[int, str]: """Check that the number of characters written matches the @@ -1575,5 +1518,3 @@ class XMLParagraph: self._xSing.tail = "" self._nState = X_SPAN_SING self._chrPos += nSpaces - 1 - - return diff --git a/novelwriter/formats/toqdoc.py b/novelwriter/formats/toqdoc.py index 28186b69..b53eb60c 100644 --- a/novelwriter/formats/toqdoc.py +++ b/novelwriter/formats/toqdoc.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -56,6 +56,7 @@ T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat] def newBlock(cursor: QTextCursor, bFmt: QTextBlockFormat) -> None: + """Insert a new block if not at the beginning of the document.""" if cursor.position() > 0: cursor.insertBlock(bFmt) else: @@ -63,7 +64,7 @@ def newBlock(cursor: QTextCursor, bFmt: QTextBlockFormat) -> None: class ToQTextDocument(Tokenizer): - """Core: QTextDocument Writer + """Core: QTextDocument Writer. Extend the Tokenizer class to generate a QTextDocument output. This is intended for usage in the document viewer and build tool preview. @@ -92,8 +93,6 @@ class ToQTextDocument(Tokenizer): self._pageSize = QPageSize(QPageSize.PageSizeId.A4) self._pageMargins = QMarginsF(20.0, 20.0, 20.0, 20.0) - return - ## # Properties ## @@ -113,17 +112,14 @@ class ToQTextDocument(Tokenizer): """Set the document page size and margins in millimetres.""" self._pageSize = QPageSize(QSizeF(width, height), QPageSize.Unit.Millimeter) self._pageMargins = QMarginsF(left, top, right, bottom) - return def setShowNewPage(self, state: bool) -> None: """Add markers for page breaks.""" self._newPage = state - return def disableAnchors(self) -> None: """Disable anchors for when writing to file.""" self._anchors = False - return ## # Class Methods @@ -200,8 +196,6 @@ class ToQTextDocument(Tokenizer): self._init = True - return - def doConvert(self) -> None: """Write text tokens into the document.""" if not self._init: @@ -297,8 +291,6 @@ class ToQTextDocument(Tokenizer): self._document.setPageSize(printer.pageRect(QPrinter.Unit.DevicePixel).size()) self._document.print(printer) - return - def closeDocument(self) -> None: """Run close document tasks.""" self._document.blockSignals(True) @@ -333,8 +325,6 @@ class ToQTextDocument(Tokenizer): self._document.blockSignals(False) - return - ## # Internal Functions ## @@ -439,8 +429,6 @@ class ToQTextDocument(Tokenizer): # Insert whatever is left in the buffer cursor.insertText(stripEscape(temp[start:]), cFmt) - return - def _insertNewPageMarker(self, cursor: QTextCursor) -> None: """Insert a new page marker.""" if self._newPage: @@ -475,8 +463,6 @@ class ToQTextDocument(Tokenizer): if root := self._document.rootFrame(): cursor.swap(root.lastCursorPosition()) - return - def _genHeadStyle(self, hType: BlockTyp, hKey: str, rFmt: QTextBlockFormat) -> T_TextStyle: """Generate a heading style set.""" mTop, mBottom = self._mHead.get(hType, (0.0, 0.0)) diff --git a/novelwriter/formats/toraw.py b/novelwriter/formats/toraw.py index c4ed979b..6eab8c52 100644 --- a/novelwriter/formats/toraw.py +++ b/novelwriter/formats/toraw.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -41,7 +41,7 @@ logger = logging.getLogger(__name__) class ToRaw(Tokenizer): - """Core: Raw novelWriter Text Writer + """Core: Raw novelWriter Text Writer. A class that will collect the minimally altered original source text and write it to either a text or JSON file. @@ -51,7 +51,6 @@ class ToRaw(Tokenizer): super().__init__(project) self._keepRaw = True self._noTokens = True - return def doConvert(self) -> None: """No conversion to perform.""" @@ -86,10 +85,7 @@ class ToRaw(Tokenizer): logger.info("Wrote file: %s", path) - return - def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None: """Replace tabs with spaces.""" spaces = spaceChar*nSpaces self._raw = [p.replace("\t", spaces) for p in self._raw] - return diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index a4191963..a7349699 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -29,7 +29,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import bisect @@ -98,7 +98,7 @@ class _TagAction(IntFlag): class GuiDocEditor(QPlainTextEdit): - """Gui Widget: Main Document Editor""" + """Gui Widget: Main Document Editor.""" __slots__ = ( "_autoReplace", "_completer", "_doReplace", "_docChanged", "_docHandle", "_followTag1", @@ -236,8 +236,6 @@ class GuiDocEditor(QPlainTextEdit): logger.debug("Ready: GuiDocEditor") - return - ## # Properties ## @@ -290,15 +288,12 @@ class GuiDocEditor(QPlainTextEdit): self.itemHandleChanged.emit("") - return - def updateTheme(self) -> None: """Update theme elements.""" self.docSearch.updateTheme() self.docHeader.updateTheme() self.docFooter.updateTheme() self.docToolBar.updateTheme() - return def updateSyntaxColors(self) -> None: """Update the syntax highlighting theme.""" @@ -323,8 +318,6 @@ class GuiDocEditor(QPlainTextEdit): self._selection.format.setBackground(self._lineColor) self._selection.format.setProperty(QTextFormat.Property.FullWidthSelection, True) - return - def initEditor(self) -> None: """Initialise or re-initialise the editor with the user's settings. This function is both called when the editor is @@ -392,8 +385,6 @@ class GuiDocEditor(QPlainTextEdit): else: self.clearEditor() - return - def loadText(self, tHandle: str, tLine: int | None = None) -> bool: """Load text from a document into the editor. If we have an I/O error, we must handle this and clear the editor so that we don't @@ -471,7 +462,6 @@ class GuiDocEditor(QPlainTextEdit): self.updateDocMargins() self.setDocumentChanged(True) QApplication.restoreOverrideCursor() - return def saveText(self) -> bool: """Save the text currently in the editor to the NWDocument @@ -539,7 +529,6 @@ class GuiDocEditor(QPlainTextEdit): vBar.setValue(vBar.value() + 1) count += 1 QApplication.processEvents() - return def updateDocMargins(self) -> None: """Automatically adjust the margins so the text is centred if @@ -580,8 +569,6 @@ class GuiDocEditor(QPlainTextEdit): lM = max(self._vpMargin, fH) self.setViewportMargins(tM, uM, tM, lM) - return - ## # Getters ## @@ -591,20 +578,19 @@ class GuiDocEditor(QPlainTextEdit): QTextDocument->toRawText instead of toPlainText. The former preserves non-breaking spaces, the latter does not. We still want to get rid of paragraph and line separators though. + See: https://doc.qt.io/qt-6/qtextdocument.html#toPlainText """ text = self._qDocument.toRawText() text = text.replace(nwUnicode.U_LSEP, "\n") # Line separators - text = text.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators - return text + return text.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators def getSelectedText(self) -> str: """Get currently selected text.""" if (cursor := self.textCursor()).hasSelection(): text = cursor.selectedText() text = text.replace(nwUnicode.U_LSEP, "\n") # Line separators - text = text.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators - return text + return text.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators return "" def getCursorPosition(self) -> int: @@ -625,7 +611,6 @@ class GuiDocEditor(QPlainTextEdit): logger.debug("Document changed status is '%s'", state) self._docChanged = state self.editedStatusChanged.emit(self._docChanged) - return def setCursorPosition(self, position: int) -> None: """Move the cursor to a given position in the document.""" @@ -634,14 +619,12 @@ class GuiDocEditor(QPlainTextEdit): cursor.setPosition(minmax(position, 0, chars-1)) self.setTextCursor(cursor) self.centerCursor() - return def saveCursorPosition(self) -> None: """Save the cursor position to the current project item.""" if self._nwItem is not None: cursPos = self.getCursorPosition() self._nwItem.setCursorPos(cursPos) - return def setCursorLine(self, line: int | None) -> None: """Move the cursor to a given line in the document.""" @@ -650,7 +633,6 @@ class GuiDocEditor(QPlainTextEdit): if block: self.setCursorPosition(block.position()) logger.debug("Cursor moved to line %d", line) - return def setCursorSelection(self, start: int, length: int) -> None: """Make a text selection.""" @@ -659,14 +641,15 @@ class GuiDocEditor(QPlainTextEdit): cursor.setPosition(start, QtMoveAnchor) cursor.setPosition(start + length, QtKeepAnchor) self.setTextCursor(cursor) - return ## # Spell Checking ## def toggleSpellCheck(self, state: bool | None) -> None: - """This is the main spell check setting function, and this one + """Toggle spell checking. + + This is the main spell check setting function, and this one should call all other setSpellCheck functions in other classes. If the spell check state is not defined (None), then toggle the current status saved in this class. @@ -690,8 +673,6 @@ class GuiDocEditor(QPlainTextEdit): logger.debug("Spell check is set to '%s'", str(state)) - return - def spellCheckDocument(self) -> None: """Rerun the highlighter to update spell checking status of the currently loaded text. @@ -703,7 +684,6 @@ class GuiDocEditor(QPlainTextEdit): QApplication.restoreOverrideCursor() logger.debug("Document highlighted in %.3f ms", 1000*(time() - start)) self.updateStatusMessage.emit(self.tr("Spell check complete")) - return ## # General Class Methods @@ -832,7 +812,6 @@ class GuiDocEditor(QPlainTextEdit): details=self.tr("File Location: {0}").format(self._nwDocument.fileLocation), log=False ) - return def insertText(self, insert: str | nwDocInsert) -> None: """Insert a specific type of text at the cursor position.""" @@ -974,7 +953,6 @@ class GuiDocEditor(QPlainTextEdit): event.acceptProposedAction() else: super().dragEnterEvent(event) - return def dragMoveEvent(self, event: QDragMoveEvent) -> None: """Overload drag move event to handle dragged items.""" @@ -982,7 +960,6 @@ class GuiDocEditor(QPlainTextEdit): event.acceptProposedAction() else: super().dragMoveEvent(event) - return def dropEvent(self, event: QDropEvent) -> None: """Overload drop event to handle dragged items.""" @@ -992,7 +969,6 @@ class GuiDocEditor(QPlainTextEdit): self.openDocumentRequest.emit(handles[0], nwDocMode.EDIT, "", True) else: super().dropEvent(event) - return def focusNextPrevChild(self, _next: bool) -> bool: """Capture the focus request from the tab key on the text @@ -1019,7 +995,6 @@ class GuiDocEditor(QPlainTextEdit): else: self._processTag(cursor) super().mouseReleaseEvent(event) - return def resizeEvent(self, event: QResizeEvent) -> None: """If the text editor is resized, we must make sure the document @@ -1027,7 +1002,6 @@ class GuiDocEditor(QPlainTextEdit): """ self.updateDocMargins() super().resizeEvent(event) - return ## # Public Slots @@ -1035,14 +1009,13 @@ class GuiDocEditor(QPlainTextEdit): @pyqtSlot(str, Enum) def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None: - """Called when an item label is changed to check if the document - title bar needs updating, + """Process project item change. Called when an item label is + changed to check if the document title bar needs updating. """ if tHandle == self._docHandle and change == nwChange.UPDATE: self.docHeader.setHandle(tHandle) self.docFooter.updateInfo() self.updateDocMargins() - return @pyqtSlot(str) def insertKeyWord(self, keyword: str) -> bool: @@ -1053,8 +1026,7 @@ class GuiDocEditor(QPlainTextEdit): logger.error("Invalid keyword '%s'", keyword) return False logger.debug("Inserting keyword '%s'", keyword) - state = self.insertNewBlock(f"{keyword}: ") - return state + return self.insertNewBlock(f"{keyword}: ") @pyqtSlot() def toggleSearch(self) -> None: @@ -1063,14 +1035,12 @@ class GuiDocEditor(QPlainTextEdit): self.closeSearch() else: self.beginSearch() - return @pyqtSlot(list, list) def updateChangedTags(self, updated: list[str], deleted: list[str]) -> None: """Tags have changed, so just in case we rehighlight them.""" if updated or deleted: self._qDocument.syntaxHighlighter.rehighlightByType(BLOCK_META) - return ## # Private Slots @@ -1116,8 +1086,6 @@ class GuiDocEditor(QPlainTextEdit): if self._autoReplace.process(text, cursor): self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block()) - return - @pyqtSlot() def _cursorMoved(self) -> None: """Triggered when the cursor moved in the editor.""" @@ -1126,7 +1094,6 @@ class GuiDocEditor(QPlainTextEdit): self._selection.cursor = self.textCursor() self._selection.cursor.clearSelection() self.setExtraSelections([self._selection]) - return @pyqtSlot(int, int, str) def _insertCompletion(self, pos: int, length: int, text: str) -> None: @@ -1138,13 +1105,11 @@ class GuiDocEditor(QPlainTextEdit): cursor.setPosition(check + length, QtKeepAnchor) cursor.insertText(text) self._completer.hide() - return @pyqtSlot() def _openContextFromCursor(self) -> None: """Open the spell check context menu at the cursor.""" self._openContextMenu(self.cursorRect().center()) - return @pyqtSlot("QPoint") def _openContextMenu(self, pos: QPoint) -> None: @@ -1231,8 +1196,6 @@ class GuiDocEditor(QPlainTextEdit): ctxMenu.setParent(None) - return - @pyqtSlot() def _runDocumentTasks(self) -> None: """Run timer document tasks.""" @@ -1269,11 +1232,10 @@ class GuiDocEditor(QPlainTextEdit): if not self.textCursor().hasSelection(): # Selection counter should take precedence (#2155) self.docFooter.updateMainCount(mCount, False) - return @pyqtSlot() def _updateSelectedStatus(self) -> None: - """The user made a change in text selection. Forward this + """Process user change in text selection. Forward this information to the footer, and start the selection word counter. """ if self.textCursor().hasSelection(): @@ -1282,7 +1244,6 @@ class GuiDocEditor(QPlainTextEdit): else: self._timerSel.stop() self.docFooter.updateMainCount(0, False) - return @pyqtSlot() def _runSelCounter(self) -> None: @@ -1300,14 +1261,12 @@ class GuiDocEditor(QPlainTextEdit): if self._docHandle and self._nwItem: self.docFooter.updateMainCount(cCount if CONFIG.useCharCount else wCount, True) self._timerSel.stop() - return @pyqtSlot() def _closeCurrentDocument(self) -> None: """Close the document. Forwarded to the main Gui.""" self.closeEditorRequest.emit() self.docToolBar.setVisible(False) - return @pyqtSlot() def _toggleToolBarVisibility(self) -> None: @@ -1315,7 +1274,6 @@ class GuiDocEditor(QPlainTextEdit): state = not self.docToolBar.isVisible() self.docToolBar.setVisible(state) CONFIG.showEditToolBar = state - return ## # Search & Replace @@ -1326,14 +1284,12 @@ class GuiDocEditor(QPlainTextEdit): self.docSearch.setSearchText(self.getSelectedText() or None) resS, _ = self.findAllOccurences() self.docSearch.setResultCount(None, len(resS)) - return def beginReplace(self) -> None: """Initialise the search box and reset the replace text box.""" self.beginSearch() self.docSearch.setReplaceText("") self.updateDocMargins() - return def findNext(self, goBack: bool = False) -> None: """Search for the next or previous occurrence of the search bar @@ -1621,8 +1577,6 @@ class GuiDocEditor(QPlainTextEdit): self.setTextCursor(cursor) - return - def _replaceQuotes(self, sQuote: str, oQuote: str, cQuote: str) -> None: """Replace all straight quotes in the selected text.""" cursor = self.textCursor() @@ -1882,8 +1836,6 @@ class GuiDocEditor(QPlainTextEdit): cursor.insertText(cleanText.rstrip() + "\n") cursor.endEditBlock() - return - def _insertCommentStructure(self, style: nwComment) -> None: """Insert a shortcut/comment combo.""" if self._docHandle and style == nwComment.FOOTNOTE: @@ -1925,7 +1877,6 @@ class GuiDocEditor(QPlainTextEdit): cursor.endEditBlock() cursor.setPosition(pos) self.setTextCursor(cursor) - return def _addWord(self, word: str, block: QTextBlock, save: bool) -> None: """Slot for the spell check context menu triggered when the user @@ -1934,7 +1885,6 @@ class GuiDocEditor(QPlainTextEdit): logger.debug("Added '%s' to project dictionary, %s", word, "saved" if save else "unsaved") SHARED.spelling.addWord(word, save=save) self._qDocument.syntaxHighlighter.rehighlightBlock(block) - return def _processTag( self, cursor: QTextCursor | None = None, follow: bool = True, create: bool = False @@ -2008,7 +1958,6 @@ class GuiDocEditor(QPlainTextEdit): if self._docHandle: text = block.text().lstrip("#").lstrip("!").strip() self.requestProjectItemRenamed.emit(self._docHandle, text) - return def _autoSelect(self) -> QTextCursor: """Return a cursor which may or may not have a selection based @@ -2078,14 +2027,11 @@ class GuiDocEditor(QPlainTextEdit): self.setTextCursor(cursor) - return - def _makePosSelection(self, mode: QTextCursor.SelectionType, pos: QPoint) -> None: """Select text based on selection mode, but first move cursor.""" cursor = self.cursorForPosition(pos) self.setTextCursor(cursor) self._makeSelection(mode) - return def _allowAutoReplace(self, state: bool) -> None: """Enable/disable the auto-replace feature temporarily.""" @@ -2093,11 +2039,10 @@ class GuiDocEditor(QPlainTextEdit): self._doReplace = CONFIG.doReplace else: self._doReplace = False - return class CommandCompleter(QMenu): - """GuiWidget: Command Completer Menu + """GuiWidget: Command Completer Menu. This is a context menu with options populated from the user's defined tags and keys. It also helps to type the meta data keyword @@ -2109,7 +2054,6 @@ class CommandCompleter(QMenu): def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) - return def updateMetaText(self, text: str, pos: int) -> bool: """Update the menu options based on the line of text.""" @@ -2203,7 +2147,6 @@ class CommandCompleter(QMenu): super().keyPressEvent(event) elif isinstance(parent, GuiDocEditor): parent.keyPressEvent(event) - return ## # Internal Functions @@ -2212,11 +2155,10 @@ class CommandCompleter(QMenu): def _emitComplete(self, pos: int, length: int, value: str) -> None: """Emit the signal to indicate a selection has been made.""" self.complete.emit(pos, length, value) - return class BackgroundWordCounter(QRunnable): - """The Off-GUI Thread Word Counter + """The Off-GUI Thread Word Counter. A runnable for the word counter to be run in the thread pool off the main GUI thread. @@ -2228,9 +2170,9 @@ class BackgroundWordCounter(QRunnable): self._forSelection = forSelection self._isRunning = False self.signals = BackgroundWordCounterSignals() - return def isRunning(self) -> bool: + """Return True if the word counter is already running.""" return self._isRunning @pyqtSlot() @@ -2248,17 +2190,17 @@ class BackgroundWordCounter(QRunnable): self.signals.countsReady.emit(cC, wC, pC) self._isRunning = False - return - class BackgroundWordCounterSignals(QObject): """The QRunnable cannot emit a signal, so we need a simple QObject to hold the word counter signal. """ + countsReady = pyqtSignal(int, int, int) class TextAutoReplace: + """Encapsulates the editor auto replace feature.""" __slots__ = ( "_doPadAfter", "_doPadBefore", "_padAfter", "_padBefore", "_padChar", @@ -2268,7 +2210,6 @@ class TextAutoReplace: def __init__(self) -> None: self.initSettings() - return def initSettings(self) -> None: """Initialise the auto-replace settings from config.""" @@ -2287,7 +2228,6 @@ class TextAutoReplace: self._padAfter = CONFIG.fmtPadAfter self._doPadBefore = bool(CONFIG.fmtPadBefore) self._doPadAfter = bool(CONFIG.fmtPadAfter) - return def process(self, text: str, cursor: QTextCursor) -> bool: """Auto-replace text elements based on main configuration. @@ -2401,7 +2341,7 @@ class TextAutoReplace: class GuiDocToolBar(QWidget): - """The Formatting and Options Fold Out Menu + """The Formatting and Options Fold Out Menu. Only used by DocEditor, and is opened by the first button in the header. @@ -2513,8 +2453,6 @@ class GuiDocToolBar(QWidget): logger.debug("Ready: GuiDocToolBar") - return - def updateTheme(self) -> None: """Initialise GUI elements that depend on specific settings.""" syntax = SHARED.theme.syntaxTheme @@ -2537,11 +2475,9 @@ class GuiDocToolBar(QWidget): self.tbSuperscript.setThemeIcon("fmt_superscript") self.tbSubscript.setThemeIcon("fmt_subscript") - return - class GuiDocEditSearch(QFrame): - """The Embedded Document Search/Replace Feature + """The Embedded Document Search/Replace Feature. Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport. @@ -2671,8 +2607,6 @@ class GuiDocEditSearch(QFrame): logger.debug("Ready: GuiDocEditSearch") - return - ## # Properties ## @@ -2721,14 +2655,12 @@ class GuiDocEditSearch(QFrame): self.searchBox.selectAll() if CONFIG.searchRegEx: self._alertSearchValid(True) - return def setReplaceText(self, text: str) -> None: """Set the replace text.""" self.showReplace.setChecked(True) self.replaceBox.setFocus() self.replaceBox.setText(text) - return def setResultCount(self, currRes: int | None, resCount: int | None) -> None: """Set the count values for the current search.""" @@ -2743,7 +2675,6 @@ class GuiDocEditSearch(QFrame): self.resultLabel.setMinimumWidth(minWidth) self.adjustSize() self.docEditor.updateDocMargins() - return ## # Methods @@ -2759,7 +2690,6 @@ class GuiDocEditSearch(QFrame): self.resultLabel.setMinimumWidth( SHARED.theme.getTextWidth("?/?", SHARED.theme.guiFontSmall) ) - return def updateTheme(self) -> None: """Update theme elements.""" @@ -2784,11 +2714,9 @@ class GuiDocEditSearch(QFrame): self.searchOpt.setStyleSheet("QToolBar {padding: 0;}") self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}") - return - def cycleFocus(self) -> bool: - """The tab key just alternates focus between the two input - boxes, if the replace box is visible. + """Cycle focus on tab key press. This just alternates focus + between the two input boxes, if the replace box is visible. """ if self.searchBox.hasFocus(): self.replaceBox.setFocus() @@ -2813,7 +2741,6 @@ class GuiDocEditSearch(QFrame): self.setVisible(False) self.docEditor.updateDocMargins() self.docEditor.setFocus() - return ## # Private Slots @@ -2823,13 +2750,11 @@ class GuiDocEditSearch(QFrame): def _doSearch(self) -> None: """Call the search action function for the document editor.""" self.docEditor.findNext(goBack=(QApplication.keyboardModifiers() == QtModShift)) - return @pyqtSlot() def _doReplace(self) -> None: """Call the replace action function for the document editor.""" self.docEditor.replaceNext() - return @pyqtSlot(bool) def _doToggleReplace(self, state: bool) -> None: @@ -2838,43 +2763,36 @@ class GuiDocEditSearch(QFrame): self.replaceButton.setVisible(state) self.adjustSize() self.docEditor.updateDocMargins() - return @pyqtSlot(bool) def _doToggleCase(self, state: bool) -> None: """Enable/disable case sensitive mode.""" CONFIG.searchCase = state - return @pyqtSlot(bool) def _doToggleWord(self, state: bool) -> None: """Enable/disable whole word search mode.""" CONFIG.searchWord = state - return @pyqtSlot(bool) def _doToggleRegEx(self, state: bool) -> None: """Enable/disable regular expression search mode.""" CONFIG.searchRegEx = state - return @pyqtSlot(bool) def _doToggleLoop(self, state: bool) -> None: """Enable/disable looping the search.""" CONFIG.searchLoop = state - return @pyqtSlot(bool) def _doToggleProject(self, state: bool) -> None: """Enable/disable continuing search in next project file.""" CONFIG.searchNextFile = state - return @pyqtSlot(bool) def _doToggleMatchCap(self, state: bool) -> None: """Enable/disable preserving capitalisation when replacing.""" CONFIG.searchMatchCap = state - return ## # Internal Functions @@ -2890,11 +2808,10 @@ class GuiDocEditSearch(QFrame): palette.text().color() if isValid else SHARED.theme.errorText ) self.searchBox.setPalette(palette) - return class GuiDocEditHeader(QWidget): - """The Embedded Document Header + """The Embedded Document Header. Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport. @@ -2985,8 +2902,6 @@ class GuiDocEditHeader(QWidget): logger.debug("Ready: GuiDocEditHeader") - return - ## # Methods ## @@ -3003,7 +2918,6 @@ class GuiDocEditHeader(QWidget): self.searchButton.setVisible(False) self.closeButton.setVisible(False) self.minmaxButton.setVisible(False) - return def setOutline(self, data: dict[int, str]) -> None: """Set the document outline dataset.""" @@ -3015,13 +2929,11 @@ class GuiDocEditHeader(QWidget): action.triggered.connect(qtLambda(self._gotoBlock, number)) self._docOutline = data logger.debug("Document outline updated in %.3f ms", 1000*(time() - tStart)) - return def updateFont(self) -> None: """Update the font settings.""" self.setFont(SHARED.theme.guiFont) self.itemTitle.setFont(SHARED.theme.guiFontSmall) - return def updateTheme(self) -> None: """Update theme elements.""" @@ -3040,8 +2952,6 @@ class GuiDocEditHeader(QWidget): self.matchColors() - return - def matchColors(self) -> None: """Update the colours of the widget to match those of the syntax theme rather than the main GUI. @@ -3055,12 +2965,10 @@ class GuiDocEditHeader(QWidget): self.itemTitle.setTextColors( color=palette.windowText().color(), faded=SHARED.theme.fadedText ) - return def changeFocusState(self, state: bool) -> None: """Toggle focus state.""" self.itemTitle.setColorState(state) - return def setHandle(self, tHandle: str) -> None: """Set the document title from the handle, or alternatively, set @@ -3081,8 +2989,6 @@ class GuiDocEditHeader(QWidget): self.closeButton.setVisible(True) self.minmaxButton.setVisible(True) - return - ## # Private Slots ## @@ -3092,19 +2998,16 @@ class GuiDocEditHeader(QWidget): """Trigger the close editor on the main window.""" self.clearHeader() self.closeDocumentRequest.emit() - return @pyqtSlot(int) def _gotoBlock(self, blockNumber: int) -> None: """Move cursor to a specific heading.""" self.docEditor.setCursorLine(blockNumber + 1) - return @pyqtSlot(bool) def _focusModeChanged(self, focusMode: bool) -> None: """Update minimise/maximise icon of the Focus Mode button.""" self.minmaxButton.setThemeIcon("minimise" if focusMode else "maximise", "blue") - return ## # Events @@ -3116,11 +3019,10 @@ class GuiDocEditHeader(QWidget): """ if event.button() == QtMouseLeft: self.docEditor.requestProjectItemSelected.emit(self._docHandle or "", True) - return class GuiDocEditFooter(QWidget): - """The Embedded Document Footer + """The Embedded Document Footer. Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport. @@ -3205,8 +3107,6 @@ class GuiDocEditFooter(QWidget): logger.debug("Ready: GuiDocEditFooter") - return - ## # Methods ## @@ -3216,7 +3116,6 @@ class GuiDocEditFooter(QWidget): self._trMainCount = trStats(nwLabels.STATS_DISPLAY[ nwStats.CHARS if CONFIG.useCharCount else nwStats.WORDS ]) - return def updateFont(self) -> None: """Update the font settings.""" @@ -3224,7 +3123,6 @@ class GuiDocEditFooter(QWidget): self.statusText.setFont(SHARED.theme.guiFontSmall) self.linesText.setFont(SHARED.theme.guiFontSmall) self.wordsText.setFont(SHARED.theme.guiFontSmall) - return def updateTheme(self) -> None: """Update theme elements.""" @@ -3232,7 +3130,6 @@ class GuiDocEditFooter(QWidget): self.linesIcon.setPixmap(SHARED.theme.getPixmap("lines", (iPx, iPx))) self.wordsIcon.setPixmap(SHARED.theme.getPixmap("stats", (iPx, iPx))) self.matchColors() - return def matchColors(self) -> None: """Update the colours of the widget to match those of the syntax @@ -3250,8 +3147,6 @@ class GuiDocEditFooter(QWidget): self.linesText.setPalette(palette) self.wordsText.setPalette(palette) - return - def setHandle(self, tHandle: str | None) -> None: """Set the handle that will populate the footer's data.""" self._docHandle = tHandle @@ -3264,8 +3159,6 @@ class GuiDocEditFooter(QWidget): self.updateInfo() self.updateMainCount(0, False) - return - def updateInfo(self) -> None: """Update the content of text labels.""" if self._tItem is None: @@ -3280,8 +3173,6 @@ class GuiDocEditFooter(QWidget): self.statusIcon.setPixmap(sIcon) self.statusText.setText(sText) - return - def updateLineCount(self, cursor: QTextCursor) -> None: """Update the line and document position counter.""" if document := cursor.document(): @@ -3291,7 +3182,6 @@ class GuiDocEditFooter(QWidget): self.linesText.setText( self._trLineCount.format(f"{cLine:n}", f"{100*cPos//cCount:d} %") ) - return def updateMainCount(self, count: int, selection: bool) -> None: """Update main counter information.""" @@ -3304,4 +3194,3 @@ class GuiDocEditFooter(QWidget): else: text = self._trMainCount.format("0", "+0") self.wordsText.setText(text) - return diff --git a/novelwriter/gui/dochighlight.py b/novelwriter/gui/dochighlight.py index 60c28d4b..3cd1cb1d 100644 --- a/novelwriter/gui/dochighlight.py +++ b/novelwriter/gui/dochighlight.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -57,6 +57,7 @@ BLOCK_TITLE = 4 class GuiDocHighlighter(QSyntaxHighlighter): + """GUI: Editor Syntax Highlighter.""" __slots__ = ( "_cmnRules", "_dialogParser", "_hStyles", "_isInactive", "_isNovel", @@ -85,8 +86,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): logger.debug("Ready: GuiDocHighlighter") - return - def initHighlighter(self) -> None: """Initialise the syntax highlighter, setting all the colour rules and building the RegExes. @@ -255,8 +254,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): self._txtRules.append((rxRule, hlRule)) self._cmnRules.append((rxRule, hlRule)) - return - ## # Setters ## @@ -264,7 +261,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): def setSpellCheck(self, state: bool) -> None: """Enable/disable the real time spell checker.""" self._spellCheck = state - return def setHandle(self, tHandle: str) -> None: """Set the handle of the currently highlighted document.""" @@ -275,7 +271,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): self._isNovel = item.isDocumentLayout() self._isInactive = item.isInactiveClass() logger.debug("Syntax highlighter enabled for item '%s'", tHandle) - return ## # Methods @@ -293,7 +288,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): if block.userState() & cType > 0: self.rehighlightBlock(block) logger.debug("Document highlighted in %.3f ms" % (1000*(time() - tStart))) - return ## # Highlight Block @@ -506,10 +500,13 @@ class GuiDocHighlighter(QSyntaxHighlighter): self._hStyles[name] = charFormat - return - class TextBlockData(QTextBlockUserData): + """Custom QTextBlock Data. + + Custom data stored in a single text block. The spell check state is + cached here and used when correcting misspelled text. + """ __slots__ = ("_metaData", "_offset", "_spellErrors", "_text") @@ -519,7 +516,6 @@ class TextBlockData(QTextBlockUserData): self._offset = 0 self._metaData: list[tuple[int, int, str, str]] = [] self._spellErrors: list[tuple[int, int, str]] = [] - return @property def metaData(self) -> list[tuple[int, int, str, str]]: @@ -553,8 +549,6 @@ class TextBlockData(QTextBlockUserData): self._text = text.replace("\u02bc", "'").replace("_", " ") self._offset = offset - return - def spellCheck(self, utf16Map: list[int] | None) -> list[tuple[int, int, str]]: """Run the spell checker and cache the result, and return the list of spell check errors. diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index d5445536..10ef9c46 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -23,7 +23,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -60,6 +60,7 @@ logger = logging.getLogger(__name__) class GuiDocViewer(QTextBrowser): + """GUI: Document Viewer.""" closeDocumentRequest = pyqtSignal() documentLoaded = pyqtSignal(str) @@ -110,8 +111,6 @@ class GuiDocViewer(QTextBrowser): logger.debug("Ready: GuiDocViewer") - return - ## # Properties ## @@ -138,13 +137,11 @@ class GuiDocViewer(QTextBrowser): self.setSearchPaths([""]) self._docHandle = None self.docHeader.clearHeader() - return def updateTheme(self) -> None: """Update theme elements.""" self.docHeader.updateTheme() self.docFooter.updateTheme() - return def initViewer(self) -> None: """Set editor settings from main config.""" @@ -206,8 +203,6 @@ class GuiDocViewer(QTextBrowser): # If we have a document open, we should reload it in case the font changed self.reloadText() - return - def loadText(self, tHandle: str, updateHistory: bool = True) -> bool: """Load text into the viewer from an item handle.""" if not SHARED.project.tree.checkType(tHandle, nwItemType.FILE): @@ -280,7 +275,6 @@ class GuiDocViewer(QTextBrowser): """Reload the text in the current document.""" if self._docHandle: self.loadText(self._docHandle, updateHistory=False) - return def docAction(self, action: nwDocAction) -> bool: """Process document actions on the current document.""" @@ -308,7 +302,6 @@ class GuiDocViewer(QTextBrowser): def clearNavHistory(self) -> None: """Clear the navigation history.""" self.docHistory.clear() - return def updateDocMargins(self) -> None: """Automatically adjust the margins so the text is centred.""" @@ -337,8 +330,6 @@ class GuiDocViewer(QTextBrowser): self.docFooter.setGeometry(tB, fY, tW, fH) self.setViewportMargins(tM, max(cM, tH), tM, max(cM, fH)) - return - ## # Setters ## @@ -347,7 +338,6 @@ class GuiDocViewer(QTextBrowser): """Set the scrollbar position.""" if (vBar := self.verticalScrollBar()) and vBar.isVisible(): vBar.setValue(pos) - return ## # Public Slots @@ -359,7 +349,6 @@ class GuiDocViewer(QTextBrowser): if tHandle == self._docHandle and change == nwChange.UPDATE: self.docHeader.setHandle(tHandle) self.updateDocMargins() - return @pyqtSlot(str) def navigateTo(self, anchor: str) -> None: @@ -367,7 +356,6 @@ class GuiDocViewer(QTextBrowser): if isinstance(anchor, str) and anchor.startswith("#"): logger.debug("Moving to anchor '%s'", anchor) self.setSource(QUrl(anchor)) - return ## # Private Slots @@ -377,13 +365,11 @@ class GuiDocViewer(QTextBrowser): def navBackward(self) -> None: """Navigate backwards in the document view history.""" self.docHistory.backward() - return @pyqtSlot() def navForward(self) -> None: """Navigate forwards in the document view history.""" self.docHistory.forward() - return @pyqtSlot("QUrl") def _linkClicked(self, url: QUrl) -> None: @@ -396,7 +382,6 @@ class GuiDocViewer(QTextBrowser): self.navigateTo(link) elif link.startswith("http"): QDesktopServices.openUrl(QUrl(url)) - return @pyqtSlot("QPoint") def _openContextMenu(self, point: QPoint) -> None: @@ -430,8 +415,6 @@ class GuiDocViewer(QTextBrowser): ctxMenu.setParent(None) - return - ## # Events ## @@ -440,7 +423,6 @@ class GuiDocViewer(QTextBrowser): """Update document margins when widget is resized.""" self.updateDocMargins() super().resizeEvent(event) - return def mouseReleaseEvent(self, event: QMouseEvent) -> None: """Capture mouse click events on the document.""" @@ -450,7 +432,6 @@ class GuiDocViewer(QTextBrowser): self.navForward() else: super().mouseReleaseEvent(event) - return def dragEnterEvent(self, event: QDragEnterEvent) -> None: """Overload drag enter event to handle dragged items.""" @@ -458,7 +439,6 @@ class GuiDocViewer(QTextBrowser): event.acceptProposedAction() else: super().dragEnterEvent(event) - return def dragMoveEvent(self, event: QDragMoveEvent) -> None: """Overload drag move event to handle dragged items.""" @@ -466,7 +446,6 @@ class GuiDocViewer(QTextBrowser): event.acceptProposedAction() else: super().dragMoveEvent(event) - return def dropEvent(self, event: QDropEvent) -> None: """Overload drop event to handle dragged items.""" @@ -476,7 +455,6 @@ class GuiDocViewer(QTextBrowser): self.openDocumentRequest.emit(handles[0], nwDocMode.VIEW, "", True) else: super().dropEvent(event) - return ## # Internal Functions @@ -500,16 +478,18 @@ class GuiDocViewer(QTextBrowser): self.setTextCursor(cursor) - return - def _makePosSelection(self, selType: QTextCursor.SelectionType, pos: QPoint) -> None: """Handle text selection at a given location.""" self.setTextCursor(self.cursorForPosition(pos)) self._makeSelection(selType) - return class GuiDocViewHistory: + """GUI: Document Viewer History. + + This class holds the navigation history for the viewer panel, which + is used for backward/forward navigation. + """ def __init__(self, docViewer: GuiDocViewer) -> None: self.docViewer = docViewer @@ -517,7 +497,6 @@ class GuiDocViewHistory: self._posHistory = [] self._currPos = -1 self._prevPos = -1 - return def clear(self) -> None: """Clear the view history.""" @@ -526,7 +505,6 @@ class GuiDocViewHistory: self._posHistory = [] self._currPos = -1 self._prevPos = -1 - return def append(self, tHandle: str) -> bool: """Append a document handle and its scroll bar position to the @@ -566,7 +544,6 @@ class GuiDocViewHistory: self._currPos = newPos self._updateNavButtons() self._dumpHistory() - return def backward(self) -> None: """Navigate to the previous entry in the view history.""" @@ -580,7 +557,6 @@ class GuiDocViewHistory: self._currPos = newPos self._updateNavButtons() self._dumpHistory() - return ## # Internal Functions @@ -590,12 +566,10 @@ class GuiDocViewHistory: """Update the scrollbar position of the previous entry.""" if self._prevPos >= 0 and self._prevPos < len(self._posHistory): self._posHistory[self._prevPos] = self.docViewer.scrollPosition - return def _updateNavButtons(self) -> None: """Update the navigation buttons in the document header.""" self.docViewer.docHeader.updateNavButtons(0, len(self._navHistory) - 1, self._currPos) - return def _truncateHistory(self, atPos: int) -> None: """Truncate the navigation history to the given position. Also @@ -606,7 +580,6 @@ class GuiDocViewHistory: self._posHistory = self._posHistory[nSkip:atPos + 1] self._currPos -= nSkip self._prevPos -= nSkip - return def _dumpHistory(self) -> None: """Debug function to dump history to the logger. Since it is a @@ -616,11 +589,10 @@ class GuiDocViewHistory: for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory, strict=False)): a = ">" if i == self._currPos else " " logger.debug(f"History {i + 1:02d}: {a} {h:13s} [x:{p}]") - return class GuiDocViewHeader(QWidget): - """The Embedded Document Header + """The Embedded Document Header. Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport. @@ -711,8 +683,6 @@ class GuiDocViewHeader(QWidget): logger.debug("Ready: GuiDocViewHeader") - return - ## # Methods ## @@ -730,7 +700,6 @@ class GuiDocViewHeader(QWidget): self.editButton.setVisible(False) self.refreshButton.setVisible(False) self.closeButton.setVisible(False) - return def setOutline(self, data: dict[str, tuple[str, int]]) -> None: """Set the document outline dataset.""" @@ -750,13 +719,11 @@ class GuiDocViewHeader(QWidget): lambda _, title=title: self.docViewer.navigateTo(f"#{tHandle}:{title}") ) self._docOutline = data - return def updateFont(self) -> None: """Update the font settings.""" self.setFont(SHARED.theme.guiFont) self.itemTitle.setFont(SHARED.theme.guiFontSmall) - return def updateTheme(self) -> None: """Update theme elements.""" @@ -777,8 +744,6 @@ class GuiDocViewHeader(QWidget): self.matchColors() - return - def matchColors(self) -> None: """Update the colours of the widget to match those of the syntax theme rather than the main GUI. @@ -792,15 +757,13 @@ class GuiDocViewHeader(QWidget): self.itemTitle.setTextColors( color=palette.windowText().color(), faded=SHARED.theme.fadedText ) - return def changeFocusState(self, state: bool) -> None: """Toggle focus state.""" self.itemTitle.setColorState(state) - return def setHandle(self, tHandle: str) -> None: - """Sets the document title from the handle, or alternatively, + """Set the document title from the handle, or alternatively, set the whole document path. """ self._docHandle = tHandle @@ -819,13 +782,10 @@ class GuiDocViewHeader(QWidget): self.refreshButton.setVisible(True) self.closeButton.setVisible(True) - return - def updateNavButtons(self, firstIdx: int, lastIdx: int, currIdx: int) -> None: """Enable and disable nav buttons based on index in history.""" self.backButton.setEnabled(currIdx > firstIdx) self.forwardButton.setEnabled(currIdx < lastIdx) - return ## # Private Slots @@ -836,20 +796,17 @@ class GuiDocViewHeader(QWidget): """Trigger the close editor/viewer on the main window.""" self.clearHeader() self.docViewer.closeDocumentRequest.emit() - return @pyqtSlot() def _refreshDocument(self) -> None: """Reload the content of the document.""" self.docViewer.reloadDocumentRequest.emit() - return @pyqtSlot() def _editDocument(self) -> None: """Open the document in the editor.""" if tHandle := self._docHandle: self.docViewer.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, "", True) - return ## # Events @@ -861,11 +818,10 @@ class GuiDocViewHeader(QWidget): """ if event.button() == QtMouseLeft: self.docViewer.requestProjectItemSelected.emit(self._docHandle, True) - return class GuiDocViewFooter(QWidget): - """The Embedded Document Footer + """The Embedded Document Footer. Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport. @@ -944,8 +900,6 @@ class GuiDocViewFooter(QWidget): logger.debug("Ready: GuiDocViewFooter") - return - ## # Methods ## @@ -956,7 +910,6 @@ class GuiDocViewFooter(QWidget): self.showComments.setFont(SHARED.theme.guiFontSmall) self.showSynopsis.setFont(SHARED.theme.guiFontSmall) self.showNotes.setFont(SHARED.theme.guiFontSmall) - return def updateTheme(self) -> None: """Update theme elements.""" @@ -977,8 +930,6 @@ class GuiDocViewFooter(QWidget): self.matchColors() - return - def matchColors(self) -> None: """Update the colours of the widget to match those of the syntax theme rather than the main GUI. @@ -989,7 +940,6 @@ class GuiDocViewFooter(QWidget): palette.setColor(QPalette.ColorRole.WindowText, syntax.text) palette.setColor(QPalette.ColorRole.Text, syntax.text) self.setPalette(palette) - return ## # Private Slots @@ -1000,18 +950,15 @@ class GuiDocViewFooter(QWidget): """Toggle the view comment button and reload the document.""" CONFIG.viewComments = state self.docViewer.reloadText() - return @pyqtSlot(bool) def _doToggleSynopsis(self, state: bool) -> None: """Toggle the view synopsis button and reload the document.""" CONFIG.viewSynopsis = state self.docViewer.reloadText() - return @pyqtSlot(bool) def _doToggleNotes(self, state: bool) -> None: """Toggle the view notes button and reload the document.""" CONFIG.viewNotes = state self.docViewer.reloadText() - return diff --git a/novelwriter/gui/docviewerpanel.py b/novelwriter/gui/docviewerpanel.py index 7d996992..8ac9816a 100644 --- a/novelwriter/gui/docviewerpanel.py +++ b/novelwriter/gui/docviewerpanel.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -49,6 +49,10 @@ logger = logging.getLogger(__name__) class GuiDocViewerPanel(QWidget): + """GUI: Document Viewer Panel. + + The panel of project meta data below the viewer. + """ openDocumentRequest = pyqtSignal(str, Enum, str, bool) loadDocumentTagRequest = pyqtSignal(str, Enum) @@ -96,8 +100,6 @@ class GuiDocViewerPanel(QWidget): logger.debug("Ready: GuiDocViewerPanel") - return - ## # Methods ## @@ -113,7 +115,6 @@ class GuiDocViewerPanel(QWidget): for tab in self.kwTabs.values(): tab.updateTheme() self._loadAllTags() - return def openProjectTasks(self) -> None: """Run open project tasks.""" @@ -124,7 +125,6 @@ class GuiDocViewerPanel(QWidget): for key, value in colWidths.items(): if key in self.kwTabs and isinstance(value, list): self.kwTabs[key].setColumnWidths(value) - return def closeProjectTasks(self) -> None: """Run close project tasks.""" @@ -133,7 +133,6 @@ class GuiDocViewerPanel(QWidget): hideInactive = self.aInactive.isChecked() SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", colWidths) SHARED.project.options.setValue("GuiDocViewerPanel", "hideInactive", hideInactive) - return ## # Public Slots @@ -145,7 +144,6 @@ class GuiDocViewerPanel(QWidget): self.tabBackRefs.clearContent() for cTab in self.kwTabs.values(): cTab.clearContent() - return @pyqtSlot() def indexHasAppeared(self) -> None: @@ -153,7 +151,6 @@ class GuiDocViewerPanel(QWidget): self._loadAllTags() self._updateTabVisibility() self.updateHandle(self._lastHandle) - return @pyqtSlot(str, Enum) def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None: @@ -168,14 +165,12 @@ class GuiDocViewerPanel(QWidget): else: self.kwTabs[tClass].removeEntry(key) self._updateTabVisibility() - return @pyqtSlot(str) def updateHandle(self, tHandle: str | None) -> None: """Update the document handle.""" self._lastHandle = tHandle self.tabBackRefs.refreshContent(tHandle or None) - return @pyqtSlot(list, list) def updateChangedTags(self, updated: list[str], deleted: list[str]) -> None: @@ -191,14 +186,12 @@ class GuiDocViewerPanel(QWidget): else: logger.warning("Could not remove tag '%s' from view panel", key) self._updateTabVisibility() - return @pyqtSlot(str) def updateStatusLabels(self, kind: str) -> None: """Update the importance labels.""" if kind == "i": self._loadAllTags() - return ## # Private Slots @@ -212,7 +205,6 @@ class GuiDocViewerPanel(QWidget): cTab.clearContent() self._loadAllTags() self._updateTabVisibility() - return ## # Internal Functions @@ -222,7 +214,6 @@ class GuiDocViewerPanel(QWidget): """Hide class tabs with no content.""" for tClass, cTab in self.kwTabs.items(): self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0) - return def _loadAllTags(self) -> None: """Load all tags into the tabs.""" @@ -230,7 +221,6 @@ class GuiDocViewerPanel(QWidget): for key, name, tClass, iItem, hItem in data: if tClass in self.kwTabs and iItem and hItem: self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem) - return class _ViewPanelBackRefs(QTreeWidget): @@ -278,8 +268,6 @@ class _ViewPanelBackRefs(QTreeWidget): self.clicked.connect(self._treeItemClicked) self.doubleClicked.connect(self._treeItemDoubleClicked) - return - def updateTheme(self) -> None: """Update theme elements.""" self._editIcon = SHARED.theme.getIcon("edit", "green") @@ -288,13 +276,11 @@ class _ViewPanelBackRefs(QTreeWidget): if item := self.topLevelItem(i): item.setIcon(self.C_EDIT, self._editIcon) item.setIcon(self.C_VIEW, self._viewIcon) - return def clearContent(self) -> None: """Clear the widget.""" self.clear() self._treeMap = {} - return def refreshContent(self, dHandle: str | None) -> None: """Update the content.""" @@ -303,7 +289,6 @@ class _ViewPanelBackRefs(QTreeWidget): refs = SHARED.project.index.getBackReferenceList(dHandle) for tHandle, (sTitle, hItem) in refs.items(): self._setTreeItemValues(tHandle, sTitle, hItem) - return def refreshDocument(self, tHandle: str) -> None: """Refresh document meta data.""" @@ -311,7 +296,6 @@ class _ViewPanelBackRefs(QTreeWidget): for sTitle, hItem in iItem.items(): if f"{tHandle}:{sTitle}" in self._treeMap: self._setTreeItemValues(tHandle, sTitle, hItem) - return ## # Private Slots @@ -325,7 +309,6 @@ class _ViewPanelBackRefs(QTreeWidget): self._parent.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, "", True) elif index.column() == self.C_VIEW: self._parent.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", True) - return @pyqtSlot("QModelIndex") def _treeItemDoubleClicked(self, index: QModelIndex) -> None: @@ -333,7 +316,6 @@ class _ViewPanelBackRefs(QTreeWidget): tHandle = index.siblingAtColumn(self.C_DATA).data(self.D_HANDLE) if index.column() not in (self.C_EDIT, self.C_VIEW): self._parent.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", True) - return ## # Internal Functions @@ -362,8 +344,6 @@ class _ViewPanelBackRefs(QTreeWidget): self.addTopLevelItem(trItem) self._treeMap[tKey] = trItem - return - class _ViewPanelKeyWords(QTreeWidget): @@ -418,14 +398,11 @@ class _ViewPanelKeyWords(QTreeWidget): self.clicked.connect(self._treeItemClicked) self.doubleClicked.connect(self._treeItemDoubleClicked) - return - def updateTheme(self) -> None: """Update theme elements.""" self._classIcon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[self._class], "root") self._editIcon = SHARED.theme.getIcon("edit", "green") self._viewIcon = SHARED.theme.getIcon("view", "blue") - return def countEntries(self) -> int: """Return the number of items in the list.""" @@ -435,7 +412,6 @@ class _ViewPanelKeyWords(QTreeWidget): """Clear the list.""" self._treeMap = {} self.clear() - return def addUpdateEntry(self, tag: str, name: str, iItem: IndexNode, hItem: IndexHeading) -> None: """Add a new entry, or update an existing one.""" @@ -470,8 +446,6 @@ class _ViewPanelKeyWords(QTreeWidget): self.addTopLevelItem(trItem) self._treeMap[tag] = trItem - return - def removeEntry(self, tag: str) -> bool: """Remove a tag from the list.""" if tag in self._treeMap: @@ -487,7 +461,6 @@ class _ViewPanelKeyWords(QTreeWidget): self.setColumnWidth(self.C_IMPORT, checkInt(widths[1], 100)) self.setColumnWidth(self.C_DOC, checkInt(widths[2], 100)) self.setColumnWidth(self.C_TITLE, checkInt(widths[3], 100)) - return def getColumnWidths(self) -> list[int]: """Get the widths of the user-adjustable columns.""" @@ -510,7 +483,6 @@ class _ViewPanelKeyWords(QTreeWidget): self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.EDIT) elif index.column() == self.C_VIEW: self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW) - return @pyqtSlot("QModelIndex") def _treeItemDoubleClicked(self, index: QModelIndex) -> None: @@ -518,4 +490,3 @@ class _ViewPanelKeyWords(QTreeWidget): tag = index.siblingAtColumn(self.C_DATA).data(self.D_TAG) if index.column() not in (self.C_EDIT, self.C_VIEW): self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW) - return diff --git a/novelwriter/gui/editordocument.py b/novelwriter/gui/editordocument.py index 04286e9f..2f20c6d8 100644 --- a/novelwriter/gui/editordocument.py +++ b/novelwriter/gui/editordocument.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -42,6 +42,11 @@ logger = logging.getLogger(__name__) class GuiTextDocument(QTextDocument): + """Custom: Modified QTextDocument. + + A special text document format that incorporates a few additional + features including spell checking. + """ def __init__(self, parent: QObject) -> None: super().__init__(parent=parent) @@ -52,11 +57,8 @@ class GuiTextDocument(QTextDocument): logger.debug("Ready: GuiTextDocument") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiTextDocument") - return ## # Properties @@ -96,8 +98,6 @@ class GuiTextDocument(QTextDocument): logger.debug("Loaded %d text blocks in %.3f ms", count, 1000*(tMid - tStart)) logger.debug("Highlighted document in %.3f ms", 1000*(tEnd - tMid)) - return - def metaDataAtPos(self, pos: int) -> tuple[str, str]: """Check if there is meta data available at a given position in the document, and if so, return it. @@ -146,4 +146,3 @@ class GuiTextDocument(QTextDocument): def setSpellCheckState(self, state: bool) -> None: """Set the spell check state of the syntax highlighter.""" self._syntax.setSpellCheck(state) - return diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py index 7bffb923..d087f742 100644 --- a/novelwriter/gui/itemdetails.py +++ b/novelwriter/gui/itemdetails.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -43,6 +43,7 @@ logger = logging.getLogger(__name__) class GuiItemDetails(QWidget): + """GUI: Project Item Details Panel.""" def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -190,8 +191,6 @@ class GuiItemDetails(QWidget): logger.debug("Ready: GuiItemDetails") - return - ### # Class Methods ## @@ -210,7 +209,6 @@ class GuiItemDetails(QWidget): self.cCountData.clear() self.wCountData.clear() self.pCountData.clear() - return def refreshDetails(self) -> None: """Reload the content of the details panel.""" @@ -219,7 +217,6 @@ class GuiItemDetails(QWidget): def updateTheme(self) -> None: """Update theme elements.""" self.updateViewBox(self._handle) - return def updateViewBox(self, tHandle: str | None) -> None: """Populate the details box from a given handle.""" @@ -283,4 +280,3 @@ class GuiItemDetails(QWidget): self.updateViewBox(tHandle) elif change == nwChange.DELETE: self.updateViewBox(None) - return diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 25cc6edb..3509291e 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -82,8 +82,6 @@ class GuiMainMenu(QMenuBar): logger.debug("Ready: GuiMainMenu") - return - ## # Public Slots ## @@ -92,7 +90,6 @@ class GuiMainMenu(QMenuBar): def setSpellCheckState(self, state: bool) -> None: """Forward spell check check state to its action.""" self.aSpellCheck.setChecked(state) - return ## # Private Slots @@ -105,21 +102,18 @@ class GuiMainMenu(QMenuBar): decision, just pass a None to the function and let it decide. """ self.mainGui.docEditor.toggleSpellCheck(None) - return @pyqtSlot() def _openUserManualFile(self) -> None: """Open the documentation in PDF format.""" if isinstance(CONFIG.pdfDocs, Path): openExternalPath(CONFIG.pdfDocs) - return @pyqtSlot(str) def _changeSpelling(self, language: str) -> None: """Change the spell check language.""" SHARED.project.data.setSpellLang(language) SHARED.updateSpellCheckLanguage() - return ## # Internal Functions @@ -188,8 +182,6 @@ class GuiMainMenu(QMenuBar): self.aExitNW.triggered.connect(qtLambda(self.mainGui.closeMain)) self.mainGui.addAction(self.aExitNW) - return - def _buildDocumentMenu(self) -> None: """Assemble the Document menu.""" # Document @@ -236,8 +228,6 @@ class GuiMainMenu(QMenuBar): self.aImportFile = qtAddAction(self.docuMenu, self.tr("Import Text from File")) self.aImportFile.triggered.connect(qtLambda(self.mainGui.importDocument)) - return - def _buildEditMenu(self) -> None: """Assemble the Edit menu.""" # Edit @@ -305,8 +295,6 @@ class GuiMainMenu(QMenuBar): ) self.mainGui.addAction(self.aSelectPar) - return - def _buildViewMenu(self) -> None: """Assemble the View menu.""" # View @@ -367,8 +355,6 @@ class GuiMainMenu(QMenuBar): self.aFullScreen.triggered.connect(self.mainGui.toggleFullScreenMode) self.mainGui.addAction(self.aFullScreen) - return - def _buildInsertMenu(self) -> None: """Assemble the Insert menu.""" # Insert @@ -646,8 +632,6 @@ class GuiMainMenu(QMenuBar): lambda: self.requestDocInsert.emit(nwDocInsert.FOOTNOTE) ) - return - def _buildFormatMenu(self) -> None: """Assemble the Format menu.""" # Format @@ -901,8 +885,6 @@ class GuiMainMenu(QMenuBar): lambda: self.requestDocAction.emit(nwDocAction.RM_BREAKS) ) - return - def _buildSearchMenu(self) -> None: """Assemble the Search menu.""" # Search @@ -948,8 +930,6 @@ class GuiMainMenu(QMenuBar): self.aFindProj.setShortcut("Ctrl+Shift+F") self.aFindProj.triggered.connect(qtLambda(self.requestViewChange.emit, nwView.SEARCH)) - return - def _buildToolsMenu(self) -> None: """Assemble the Tools menu.""" # Tools @@ -1019,8 +999,6 @@ class GuiMainMenu(QMenuBar): self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog) self.mainGui.addAction(self.aPreferences) - return - def _buildHelpMenu(self) -> None: """Assemble the Help menu.""" # Help @@ -1066,5 +1044,3 @@ class GuiMainMenu(QMenuBar): # Document > Main Website self.aWebsite = qtAddAction(self.helpMenu, self.tr("The novelWriter Website")) self.aWebsite.triggered.connect(qtLambda(SHARED.openWebsite, nwConst.URL_WEB)) - - return diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py index 86913b61..a5521ef8 100644 --- a/novelwriter/gui/noveltree.py +++ b/novelwriter/gui/noveltree.py @@ -24,7 +24,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -55,6 +55,7 @@ logger = logging.getLogger(__name__) class GuiNovelView(QWidget): + """GUI: Novel View Panel.""" # Signals for user interaction with the novel tree selectedItemChanged = pyqtSignal(str) @@ -82,8 +83,6 @@ class GuiNovelView(QWidget): self.getSelectedHandle = self.novelTree.getSelectedHandle self.refreshCurrentTree = self.novelBar.forceRefreshNovelTree - return - ## # Methods ## @@ -91,19 +90,16 @@ class GuiNovelView(QWidget): def updateTheme(self) -> None: """Update theme elements.""" self.novelBar.updateTheme() - return def initSettings(self) -> None: """Initialise GUI elements that depend on specific settings.""" self.novelTree.initSettings() - return def clearNovelView(self) -> None: """Clear project-related GUI content.""" self.novelBar.clearContent() self.novelBar.setEnabled(False) self.novelTree.clearContent() - return def openProjectTasks(self) -> None: """Run open project tasks.""" @@ -125,8 +121,6 @@ class GuiNovelView(QWidget): self.novelTree.setLastColSize(lastColSize) - return - def closeProjectTasks(self) -> None: """Run closing project tasks.""" logger.debug("Saving State: GuiNovelView") @@ -140,12 +134,9 @@ class GuiNovelView(QWidget): self.clearNovelView() - return - def setTreeFocus(self) -> None: """Set the focus to the tree widget.""" self.novelTree.setFocus() - return def treeHasFocus(self) -> bool: """Check if the novel tree has focus.""" @@ -159,22 +150,20 @@ class GuiNovelView(QWidget): def setCurrentNovel(self, rootHandle: str | None) -> None: """Set the current novel to display.""" self.novelTree.setNovelModel(rootHandle) - return @pyqtSlot(str) def setActiveHandle(self, tHandle: str) -> None: """Highlight the rows associated with a given handle.""" self.novelTree.setActiveHandle(tHandle) - return @pyqtSlot(str, Enum) def updateRootItem(self, tHandle: str, change: nwChange) -> None: """If any root item changes, rebuild the novel root menu.""" self.novelBar.buildNovelRootMenu() - return class GuiNovelToolBar(QWidget): + """GUI: Novel View Panel ToolBar.""" def __init__(self, novelView: GuiNovelView) -> None: super().__init__(parent=novelView) @@ -249,8 +238,6 @@ class GuiNovelToolBar(QWidget): logger.debug("Ready: GuiNovelToolBar") - return - ## # Methods ## @@ -277,20 +264,16 @@ class GuiNovelToolBar(QWidget): self.forceRefreshNovelTree() - return - def clearContent(self) -> None: """Run clearing project tasks.""" self.novelValue.clear() self.novelValue.setToolTip("") - return def buildNovelRootMenu(self) -> None: """Build the novel root menu.""" self.novelValue.refreshNovelList() self.novelView.setCurrentNovel(self.novelValue.handle) self.tbNovel.setVisible(self.novelValue.count() > 1) - return def setCurrentRoot(self, rootHandle: str | None) -> None: """Set the current active root handle.""" @@ -300,7 +283,6 @@ class GuiNovelToolBar(QWidget): SHARED.project.data.setLastHandle(rootHandle, "novel") self.novelView.setCurrentNovel(rootHandle) self.novelView.novelTree.setAccessibleName(self.novelValue.currentText()) - return def setLastColType(self, colType: nwNovelExtra, doRefresh: bool = True) -> None: """Set the last column type.""" @@ -309,7 +291,6 @@ class GuiNovelToolBar(QWidget): if doRefresh: self.forceRefreshNovelTree() self.novelView.novelTree.resizeColumns() - return def setActive(self, state: bool) -> None: """Set the widget active state, which enables automatic tree @@ -322,7 +303,6 @@ class GuiNovelToolBar(QWidget): and self._refresh.get(handle, False) ): self._refreshNovelTree(self.novelValue.handle) - return ## # Public Slots @@ -335,7 +315,6 @@ class GuiNovelToolBar(QWidget): self.novelView.setCurrentNovel(tHandle) SHARED.project.index.refreshNovelModel(tHandle) self._refresh[tHandle] = False - return ## # Private Slots @@ -349,7 +328,6 @@ class GuiNovelToolBar(QWidget): self._refresh[tHandle] = False else: self._refresh[tHandle] = True - return @pyqtSlot() def _selectLastColumnSize(self) -> None: @@ -361,7 +339,6 @@ class GuiNovelToolBar(QWidget): if isOk: self.novelView.novelTree.setLastColSize(newSize) self.novelView.novelTree.resizeColumns() - return ## # Internal Functions @@ -374,10 +351,10 @@ class GuiNovelToolBar(QWidget): aLast.setActionGroup(self.gLastCol) aLast.triggered.connect(qtLambda(self.setLastColType, colType)) self.aLastCol[colType] = aLast - return class GuiNovelTree(NTreeView): + """GUI: Novel View Panel Tree.""" def __init__(self, novelView: GuiNovelView) -> None: super().__init__(parent=novelView) @@ -414,8 +391,6 @@ class GuiNovelTree(NTreeView): logger.debug("Ready: GuiNovelTree") - return - def initSettings(self) -> None: """Set or update tree widget settings.""" if CONFIG.hideVScroll: @@ -426,7 +401,6 @@ class GuiNovelTree(NTreeView): self.setHorizontalScrollBarPolicy(QtScrollAlwaysOff) else: self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) - return ## # Properties @@ -466,25 +440,21 @@ class GuiNovelTree(NTreeView): self.resizeColumns() else: self.clearContent() - return def setActiveHandle(self, tHandle: str | None) -> None: """Set the handle to be highlighted.""" self._actHandle = tHandle if viewport := self.viewport(): viewport.repaint() - return def setLastColType(self, colType: nwNovelExtra) -> None: """Set the extra column type.""" self._lastColType = colType SHARED.project.index.setNovelModelExtraColumn(colType) - return def setLastColSize(self, colSize: int) -> None: """Set the extra column size between 15% and 75%.""" self._lastColSize = minmax(colSize, 15, 75)/100.0 - return ## # Class Methods @@ -493,7 +463,6 @@ class GuiNovelTree(NTreeView): def clearContent(self) -> None: """Clear the tree view.""" self.setModel(None) - return def resizeColumns(self) -> None: """Set the correct column sizes.""" @@ -506,7 +475,6 @@ class GuiNovelTree(NTreeView): if model.columns == 4: header.setSectionResizeMode(3, QtHeaderToContents) header.setMaximumSectionSize(int(self._lastColSize * vp.width())) - return ## # Overloads @@ -517,7 +485,6 @@ class GuiNovelTree(NTreeView): if (model := self._getModel()) and model.handle(index) == self._actHandle: painter.fillRect(opt.rect, self.palette().alternateBase()) super().drawRow(painter, opt, index) - return ## # Events @@ -527,7 +494,6 @@ class GuiNovelTree(NTreeView): """Process size changed.""" super().resizeEvent(event) self.resizeColumns() - return ## # Private Slots @@ -535,36 +501,33 @@ class GuiNovelTree(NTreeView): @pyqtSlot(QModelIndex) def _onSingleClick(self, index: QModelIndex) -> None: - """The user single-clicked an index.""" + """Process user single-click on an index.""" if index.isValid() and (model := self._getModel()): if (tHandle := model.handle(index)) and (sTitle := model.key(index)): self.novelView.selectedItemChanged.emit(tHandle) if index.column() == model.columnCount(index) - 1: pos = self.mapToGlobal(self.visualRect(index).topRight()) self._popMetaBox(pos, tHandle, sTitle) - return @pyqtSlot(QModelIndex) def _onDoubleClick(self, index: QModelIndex) -> None: - """The user double-clicked an index.""" + """Process user double-click on an index.""" if ( (model := self._getModel()) and (tHandle := model.handle(index)) and (sTitle := model.key(index)) ): self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle, False) - return @pyqtSlot(QModelIndex) def _onMiddleClick(self, index: QModelIndex) -> None: - """The user middle-clicked an index.""" + """Process user middle-click on an index.""" if ( (model := self._getModel()) and (tHandle := model.handle(index)) and (sTitle := model.key(index)) ): self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, sTitle, False) - return ## # Internal Functions @@ -582,7 +545,6 @@ class GuiNovelTree(NTreeView): """Generate a reference list for a given reference key.""" if tags := ", ".join(refs.get(key, [])): lines.append(f"{trConst(nwLabels.KEY_NAME[key])}: {tags}") - return if head := SHARED.project.index.getItemHeading(tHandle, sTitle): logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle) @@ -610,4 +572,3 @@ class GuiNovelTree(NTreeView): text = f"

{refs}

" if tooltip := (text + synopsis or self.tr("No meta data")): QToolTip.showText(qPos, tooltip) - return diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index 4ab18e50..3df01d3f 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -24,7 +24,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import csv @@ -58,6 +58,7 @@ logger = logging.getLogger(__name__) class GuiOutlineView(QWidget): + """GUI: Project Outline Panel.""" loadDocumentTagRequest = pyqtSignal(str, Enum) openDocumentRequest = pyqtSignal(str, Enum, str, bool) @@ -96,8 +97,6 @@ class GuiOutlineView(QWidget): # Function Mappings self.getSelectedHandle = self.outlineTree.getSelectedHandle - return - ## # Methods ## @@ -109,24 +108,20 @@ class GuiOutlineView(QWidget): self.outlineTree.refreshTree( rootHandle=SHARED.project.data.getLastHandle("outline"), overRide=True ) - return def initSettings(self) -> None: """Initialise GUI elements that depend on specific settings.""" self.outlineTree.initSettings() self.outlineData.initSettings() - return def refreshTree(self) -> None: """Refresh the current tree.""" self.outlineTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("outline")) - return def clearOutline(self) -> None: """Clear project-related GUI content.""" self.outlineData.clearDetails() self.outlineBar.setEnabled(False) - return def openProjectTasks(self) -> None: """Run open project tasks.""" @@ -142,8 +137,6 @@ class GuiOutlineView(QWidget): self.outlineBar.setEnabled(True) self.outlineData.loadGuiSettings() - return - def closeProjectTasks(self) -> None: """Run closing project tasks.""" if self.outlineTree.wasRendered: @@ -152,7 +145,6 @@ class GuiOutlineView(QWidget): self.outlineTree.closeProjectTasks() self.outlineData.updateClasses() self.clearOutline() - return def splitSizes(self) -> list[int]: """Get the sizes of the splitter widget.""" @@ -175,7 +167,6 @@ class GuiOutlineView(QWidget): """Handle tasks whenever a root folders changes.""" self.outlineBar.populateNovelList() self.outlineData.updateClasses() - return ## # Private Slots @@ -188,23 +179,21 @@ class GuiOutlineView(QWidget): of columns has changed. """ self.outlineBar.setColumnHiddenState(self.outlineTree.hiddenColumns) - return @pyqtSlot(str) def _tagClicked(self, link: str) -> None: """Capture the click of a tag in the details panel.""" if link: self.loadDocumentTagRequest.emit(link, nwDocMode.VIEW) - return @pyqtSlot(str) def _rootItemChanged(self, tHandle: str) -> None: """Handle root novel changed or needs to be refreshed.""" self.outlineTree.refreshTree(rootHandle=(tHandle or None), overRide=True) - return class GuiOutlineToolBar(QToolBar): + """GUI: Project Outline Panel ToolBar.""" loadNovelRootRequest = pyqtSignal(str) outlineExportRequest = pyqtSignal() @@ -263,8 +252,6 @@ class GuiOutlineToolBar(QToolBar): logger.debug("Ready: GuiOutlineToolBar") - return - ## # Methods ## @@ -278,22 +265,18 @@ class GuiOutlineToolBar(QToolBar): self.tbColumns.setIcon(SHARED.theme.getIcon("more_vertical")) self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}") self.novelLabel.setTextColors(color=self.palette().windowText().color()) - return def populateNovelList(self) -> None: """Reload the content of the novel list.""" self.novelValue.refreshNovelList() - return def setCurrentRoot(self, rootHandle: str | None) -> None: """Set the current active root handle.""" self.novelValue.setHandle(rootHandle) - return def setColumnHiddenState(self, hiddenState: dict[nwOutline, bool]) -> None: """Forward the change of column hidden states to the menu.""" self.mColumns.setHiddenState(hiddenState) - return ## # Private Slots @@ -303,22 +286,20 @@ class GuiOutlineToolBar(QToolBar): def _novelValueChanged(self, tHandle: str) -> None: """Emit a signal containing the handle of the selected item.""" self.loadNovelRootRequest.emit(tHandle) - return @pyqtSlot() def _refreshRequested(self) -> None: """Emit a signal containing the handle of the selected item.""" self.loadNovelRootRequest.emit(self.novelValue.handle) - return @pyqtSlot() def _exportRequested(self) -> None: """Emit a signal that an export of the outline was requested.""" self.outlineExportRequest.emit() - return class GuiOutlineTree(QTreeWidget): + """GUI: Project Outline Panel Tree.""" DEF_WIDTH: Final[dict[nwOutline, int]] = { nwOutline.TITLE: 200, @@ -422,8 +403,6 @@ class GuiOutlineTree(QTreeWidget): logger.debug("Ready: GuiOutlineTree") - return - ## # Properties ## @@ -451,7 +430,6 @@ class GuiOutlineTree(QTreeWidget): self.setHorizontalScrollBarPolicy(QtScrollAlwaysOff) else: self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) - return def clearContent(self) -> None: """Clear the tree and header and set the default values for the @@ -474,8 +452,6 @@ class GuiOutlineTree(QTreeWidget): self._treeNCols = len(self._treeOrder) - return - def updateTheme(self) -> None: """Update theme elements.""" iType = nwItemType.FILE @@ -488,15 +464,14 @@ class GuiOutlineTree(QTreeWidget): "H3": SHARED.theme.getItemIcon(iType, iClass, iLayout, "H3"), "H4": SHARED.theme.getItemIcon(iType, iClass, iLayout, "H4"), } - return def refreshTree( self, rootHandle: str | None = None, overRide: bool = False, novelChanged: bool = False ) -> None: - """Called whenever the Outline tab is activated and controls - what data to load, and if necessary, force a rebuild of the - tree. + """Refresh the outline tree. Called whenever the Outline tab is + activated and controls what data to load, and if necessary, + force a rebuild of the tree. """ # If it's the first time, we always build if self._firstView or (self._firstView and overRide): @@ -518,11 +493,10 @@ class GuiOutlineTree(QTreeWidget): return def closeProjectTasks(self) -> None: - """Called before a project is closed.""" + """Call before a project is closed.""" self._saveHeaderState() self.clearContent() self._firstView = True - return def getSelectedHandle(self) -> tuple[str | None, str | None]: """Get the currently selected handle. If multiple items are @@ -546,7 +520,6 @@ class GuiOutlineTree(QTreeWidget): if hItem in self._colIdx: self.setColumnHidden(self._colIdx[hItem], not isChecked) self._saveHeaderState() - return @pyqtSlot() def exportOutline(self) -> None: @@ -562,7 +535,6 @@ class GuiOutlineTree(QTreeWidget): writer.writerows( self._dumpNovelData(self.outlineView.outlineBar.novelValue.handle) ) - return ## # Private Slots @@ -577,7 +549,6 @@ class GuiOutlineTree(QTreeWidget): tHandle, sTitle = self.getSelectedHandle() if tHandle: self.outlineView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle or "", True) - return @pyqtSlot() def _onItemSelectionChanged(self) -> None: @@ -588,7 +559,6 @@ class GuiOutlineTree(QTreeWidget): tHandle = items[0].data(self._colIdx[nwOutline.TITLE], self.D_HANDLE) sTitle = items[0].data(self._colIdx[nwOutline.TITLE], self.D_TITLE) self.activeItemChanged.emit(tHandle, sTitle) - return @pyqtSlot(int, int, int) def _columnMoved(self, logIdx: int, oldVisualIdx: int, newVisualIdx: int) -> None: @@ -597,7 +567,6 @@ class GuiOutlineTree(QTreeWidget): """ self._treeOrder.insert(newVisualIdx, self._treeOrder.pop(oldVisualIdx)) self._saveHeaderState() - return ## # Internal Functions @@ -637,8 +606,6 @@ class GuiOutlineTree(QTreeWidget): self.hiddenStateChanged.emit() - return - def _saveHeaderState(self) -> None: """Save the state of the main tree header, that is, column order, column width and column hidden state. We don't want to @@ -661,7 +628,6 @@ class GuiOutlineTree(QTreeWidget): pOptions = SHARED.project.options pOptions.setValue("GuiOutline", "columnState", colState) pOptions.saveSettings() - return def _populateTree(self, rootHandle: str | None) -> None: """Build the tree based on the project index, and the header @@ -746,8 +712,6 @@ class GuiOutlineTree(QTreeWidget): self._lastBuild = time() logger.debug("Project outline built in %.3f ms", 1000.0*(time() - tStart)) - return - def _dumpNovelData(self, rootHandle: str | None) -> list[list[str | int]]: """Dump all novel data into a table.""" sLabel = SHARED.project.localLookup("Story Structure") @@ -821,6 +785,7 @@ class GuiOutlineTree(QTreeWidget): class GuiOutlineHeaderMenu(QMenu): + """GUI: Project Outline Panel Header Selection Menu.""" columnToggled = pyqtSignal(bool, Enum) @@ -844,8 +809,6 @@ class GuiOutlineHeaderMenu(QMenu): ) self.addAction(self.actionMap[hItem]) - return - def setHiddenState(self, hiddenState: dict[nwOutline, bool]) -> None: """Overwrite the checked state of the columns as the inverse of the hidden state. Skip the TITLE column as it cannot be hidden. @@ -859,10 +822,9 @@ class GuiOutlineHeaderMenu(QMenu): self.acceptToggle = True - return - class GuiOutlineDetails(QScrollArea): + """GUI: Project Outline Panel Details View.""" LVL_MAP: Final[dict[str, str]] = { "H1": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"), @@ -1007,8 +969,6 @@ class GuiOutlineDetails(QScrollArea): logger.debug("Ready: GuiOutlineDetails") - return - def initSettings(self) -> None: """Set or update outline settings.""" if CONFIG.hideVScroll: @@ -1020,7 +980,6 @@ class GuiOutlineDetails(QScrollArea): else: self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) self.updateClasses() - return def loadGuiSettings(self) -> None: """Run open project tasks.""" @@ -1031,7 +990,6 @@ class GuiOutlineDetails(QScrollArea): pOptions.getInt("GuiOutlineDetails", "detailsWidth", width//3), pOptions.getInt("GuiOutlineDetails", "tagsWidth", 2*width//3), ]) - return def saveGuiSettings(self) -> None: """Run close project tasks.""" @@ -1040,7 +998,6 @@ class GuiOutlineDetails(QScrollArea): pOptions = SHARED.project.options pOptions.setValue("GuiOutlineDetails", "detailsWidth", mainSplit[0]) pOptions.setValue("GuiOutlineDetails", "tagsWidth", mainSplit[1]) - return def clearDetails(self) -> None: """Clear all the data labels.""" @@ -1057,7 +1014,6 @@ class GuiOutlineDetails(QScrollArea): value.clear() self.updateClasses() - return ## # Slots @@ -1090,8 +1046,6 @@ class GuiOutlineDetails(QScrollArea): for key, (_, value) in self.tagValues.items(): value.setText(self._formatTags(novRefs, key)) - return - @pyqtSlot() def updateClasses(self) -> None: """Update the visibility status of class details.""" @@ -1102,7 +1056,6 @@ class GuiOutlineDetails(QScrollArea): label, value = self.tagValues[key] label.setVisible(visible) value.setVisible(visible) - return @staticmethod def _formatTags(refs: dict[str, list[str]], key: str) -> str: diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 87a30855..ab79cfb6 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -25,7 +25,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -61,7 +61,9 @@ logger = logging.getLogger(__name__) class GuiProjectView(QWidget): - """This is a wrapper class holding all the elements of the project + """GUI: Project View. + + This is a wrapper class holding all the elements of the project tree. The core object is the project tree itself. Most methods available are mapped through to the project tree class. """ @@ -130,8 +132,6 @@ class GuiProjectView(QWidget): # Function Mappings self.getSelectedHandle = self.projTree.getSelectedHandle - return - ## # Methods ## @@ -139,19 +139,16 @@ class GuiProjectView(QWidget): def updateTheme(self) -> None: """Update theme elements.""" self.projBar.updateTheme() - return def initSettings(self) -> None: """Initialise GUI elements that depend on specific settings.""" self.projTree.initSettings() - return def closeProjectTasks(self) -> None: """Clear project-related GUI content.""" self.projBar.clearContent() self.projBar.setEnabled(False) self.projTree.clearTree() - return def openProjectTasks(self) -> None: """Run open project tasks.""" @@ -159,26 +156,23 @@ class GuiProjectView(QWidget): self.projBar.buildTemplatesMenu() self.projBar.buildQuickLinksMenu() self.projBar.setEnabled(True) - return def setTreeFocus(self) -> None: """Forward the set focus call to the tree widget.""" self.projTree.setFocus() - return def treeHasFocus(self) -> bool: """Check if the project tree has focus.""" return self.projTree.hasFocus() def connectMenuActions(self, rename: QAction, delete: QAction, trash: QAction) -> None: - """Main menu actions passed to the project tree.""" + """Connect main menu actions passed to the project tree.""" self.projTree.addAction(rename) self.projTree.addAction(delete) self.projTree.addAction(trash) rename.triggered.connect(self.renameTreeItem) delete.triggered.connect(self.projTree.processDeleteRequest) trash.triggered.connect(self.projTree.emptyTrash) - return ## # Public Slots @@ -197,41 +191,36 @@ class GuiProjectView(QWidget): if dlgOk: nwItem.setName(newLabel) nwItem.notifyToRefresh() - return @pyqtSlot(str, bool) def setSelectedHandle(self, tHandle: str, doScroll: bool = False) -> None: """Select an item and optionally scroll it into view.""" self.projTree.setSelectedHandle(tHandle, doScroll=doScroll) - return @pyqtSlot(str) def setActiveHandle(self, tHandle: str | None) -> None: """Highlight the active handle.""" self.projTree.setActiveHandle(tHandle) - return @pyqtSlot(str, Enum) def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None: """Refresh other content when project item changed.""" self.projBar.processTemplateDocuments(tHandle) - return @pyqtSlot(str) def createFileFromTemplate(self, tHandle: str) -> None: """Create a new document from a template.""" logger.debug("Template selected: '%s'", tHandle) self.projTree.newTreeItem(nwItemType.FILE, copyDoc=tHandle) - return @pyqtSlot(str, Enum) def updateRootItem(self, tHandle: str, change: nwChange) -> None: """Process root item changes.""" self.projBar.buildQuickLinksMenu() - return class GuiProjectToolBar(QWidget): + """GUI> Project View ToolBar.""" newDocumentFromTemplate = pyqtSignal(str) @@ -351,8 +340,6 @@ class GuiProjectToolBar(QWidget): logger.debug("Ready: GuiProjectToolBar") - return - ## # Methods ## @@ -383,13 +370,10 @@ class GuiProjectToolBar(QWidget): self.buildQuickLinksMenu() self._buildRootMenu() - return - def clearContent(self) -> None: """Clear dynamic content on the tool bar.""" self.mQuick.clear() self.mTemplates.clearMenu() - return def buildQuickLinksMenu(self) -> None: """Build the quick link menu.""" @@ -402,14 +386,12 @@ class GuiProjectToolBar(QWidget): action.triggered.connect( qtLambda(self.projView.setSelectedHandle, tHandle, doScroll=True) ) - return def buildTemplatesMenu(self) -> None: """Build the templates menu.""" for tHandle, _ in SHARED.project.tree.iterRoots(nwItemClass.TEMPLATE): for dHandle in SHARED.project.tree.subTree(tHandle): self.processTemplateDocuments(dHandle) - return def processTemplateDocuments(self, tHandle: str) -> None: """Process change in tree items to update menu content.""" @@ -418,7 +400,6 @@ class GuiProjectToolBar(QWidget): self.mTemplates.addUpdate(tHandle, item.itemName, item.getMainIcon()) elif tHandle in self.mTemplates: self.mTemplates.remove(tHandle) - return ## # Public Slots @@ -436,7 +417,6 @@ class GuiProjectToolBar(QWidget): self.aAddChap.setVisible(allowDoc) self.aAddPart.setVisible(allowDoc) self.aAddEmpty.setVisible(allowDoc) - return ## # Internal Functions @@ -451,7 +431,6 @@ class GuiProjectToolBar(QWidget): qtLambda(self.projTree.newTreeItem, nwItemType.ROOT, itemClass) ) self.mAddRoot.addAction(aNew) - return self.mAddRoot.clear() addClass(nwItemClass.NOVEL) @@ -467,10 +446,9 @@ class GuiProjectToolBar(QWidget): addClass(nwItemClass.ARCHIVE) addClass(nwItemClass.TEMPLATE) - return - class GuiProjectTree(QTreeView): + """GUI: Project View Tree.""" def __init__(self, projView: GuiProjectView) -> None: super().__init__(parent=projView) @@ -520,8 +498,6 @@ class GuiProjectTree(QTreeView): logger.debug("Ready: GuiProjectTree") - return - def initSettings(self) -> None: """Set or update tree widget settings.""" if CONFIG.hideVScroll: @@ -532,7 +508,6 @@ class GuiProjectTree(QTreeView): self.setHorizontalScrollBarPolicy(QtScrollAlwaysOff) else: self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) - return ## # External Methods @@ -541,7 +516,6 @@ class GuiProjectTree(QTreeView): def setActiveHandle(self, tHandle: str | None) -> None: """Set the handle to be highlighted.""" self._actHandle = tHandle - return def getSelectedHandle(self) -> str | None: """Get the currently selected handle.""" @@ -556,7 +530,6 @@ class GuiProjectTree(QTreeView): def clearTree(self) -> None: """Clear the tree view.""" self.setModel(None) - return def loadModel(self) -> None: """Load and prepare a new project model.""" @@ -583,8 +556,6 @@ class GuiProjectTree(QTreeView): self.restoreExpandedState() - return - def restoreExpandedState(self) -> None: """Expand all nodes that were previously expanded.""" if model := self._getModel(): @@ -592,7 +563,6 @@ class GuiProjectTree(QTreeView): for index in model.allExpanded(): self.setExpanded(index, True) self.blockSignals(False) - return def setSelectedHandle(self, tHandle: str | None, doScroll: bool = False) -> None: """Set a specific handle as the selected item.""" @@ -601,7 +571,6 @@ class GuiProjectTree(QTreeView): if doScroll: self.scrollTo(index, QAbstractItemView.ScrollHint.PositionAtCenter) self.projView.selectedItemChanged.emit(tHandle) - return def newTreeItem( self, itemType: nwItemType, itemClass: nwItemClass | None = None, @@ -805,7 +774,6 @@ class GuiProjectTree(QTreeView): SHARED.warn(self.tr("Could not duplicate all items.")) self.setEnabled(True) self.restoreExpandedState() - return ## # Events and Overloads @@ -825,14 +793,12 @@ class GuiProjectTree(QTreeView): self.projView.openDocumentRequest.emit( node.item.itemHandle, nwDocMode.VIEW, "", False ) - return def drawRow(self, painter: QPainter, opt: QStyleOptionViewItem, index: QModelIndex) -> None: """Draw a box on the active row.""" if (node := self._getNode(index)) and node.item.itemHandle == self._actHandle: painter.fillRect(opt.rect, self.palette().alternateBase()) super().drawRow(painter, opt, index) - return ## # Public Slots @@ -843,14 +809,12 @@ class GuiProjectTree(QTreeView): """Move an item up in the tree.""" if model := self._getModel(): model.internalMove(self.currentIndex(), -1) - return @pyqtSlot() def moveItemDown(self) -> None: """Move an item down in the tree.""" if model := self._getModel(): model.internalMove(self.currentIndex(), 1) - return @pyqtSlot() def goToSiblingUp(self) -> None: @@ -858,7 +822,6 @@ class GuiProjectTree(QTreeView): if (node := self._getNode(self.currentIndex())) and (parent := node.parent()): if (move := parent.child(node.row() - 1)) and (model := self._getModel()): self.setCurrentIndex(model.indexFromNode(move)) - return @pyqtSlot() def goToSiblingDown(self) -> None: @@ -866,7 +829,6 @@ class GuiProjectTree(QTreeView): if (node := self._getNode(self.currentIndex())) and (parent := node.parent()): if (move := parent.child(node.row() + 1)) and (model := self._getModel()): self.setCurrentIndex(model.indexFromNode(move)) - return @pyqtSlot() def goToParent(self) -> None: @@ -877,7 +839,6 @@ class GuiProjectTree(QTreeView): and (parent := node.parent()) ): self.setCurrentIndex(model.indexFromNode(parent)) - return @pyqtSlot() def goToFirstChild(self) -> None: @@ -888,13 +849,11 @@ class GuiProjectTree(QTreeView): and (child := node.child(0)) ): self.setCurrentIndex(model.indexFromNode(child)) - return @pyqtSlot(QModelIndex) def expandFromIndex(self, index: QModelIndex) -> None: """Expand all nodes from index.""" self.expandRecursively(index) - return @pyqtSlot(QModelIndex) def collapseFromIndex(self, index: QModelIndex) -> None: @@ -902,7 +861,6 @@ class GuiProjectTree(QTreeView): if (model := self._getModel()) and (node := model.node(index)): for child in node.allChildren(): self.setExpanded(model.indexFromNode(child), False) - return @pyqtSlot() def processDeleteRequest( @@ -968,9 +926,7 @@ class GuiProjectTree(QTreeView): @pyqtSlot() @pyqtSlot("QPoint") def openContextMenu(self, point: QPoint | None = None) -> None: - """The user right clicked an element in the project tree, so we - open a context menu in-place. - """ + """Open a context menu in-place where the user clicked.""" if model := self._getModel(): if point is None: point = self.visualRect(self.currentIndex()).center() @@ -987,7 +943,6 @@ class GuiProjectTree(QTreeView): if viewport := self.viewport(): ctxMenu.exec(viewport.mapToGlobal(point)) ctxMenu.setParent(None) - return ## # Private Slots @@ -995,10 +950,9 @@ class GuiProjectTree(QTreeView): @pyqtSlot(QModelIndex, QModelIndex) def _onSelectionChange(self, current: QModelIndex, previous: QModelIndex) -> None: - """The user changed which item is selected.""" + """Process user changing which item is selected.""" if node := self._getNode(current): self.projView.selectedItemChanged.emit(node.item.itemHandle) - return @pyqtSlot(QModelIndex) def _onDoubleClick(self, index: QModelIndex) -> None: @@ -1012,21 +966,18 @@ class GuiProjectTree(QTreeView): ) else: self.setExpanded(index, not self.isExpanded(index)) - return @pyqtSlot(QModelIndex) def _onNodeCollapsed(self, index: QModelIndex) -> None: """Capture a node collapse, and pass it to the model.""" if node := self._getNode(index): node.setExpanded(False) - return @pyqtSlot(QModelIndex) def _onNodeExpanded(self, index: QModelIndex) -> None: """Capture a node expand, and pass it to the model.""" if node := self._getNode(index): node.setExpanded(True) - return ## # Internal Functions @@ -1038,7 +989,6 @@ class GuiProjectTree(QTreeView): if model := self.selectionModel(): # Selection model can be None (#2173) model.clearCurrentIndex() - return def _selectedRows(self) -> list[QModelIndex]: """Return all column 0 indexes.""" @@ -1066,7 +1016,6 @@ class _UpdatableMenu(QMenu): self._map: dict[str, QAction] = {} self.setTitle(self.tr("From Template")) self.triggered.connect(self._actionTriggered) - return def __contains__(self, tHandle: str) -> bool: """Look up a handle in the menu.""" @@ -1088,7 +1037,6 @@ class _UpdatableMenu(QMenu): self.addAction(action) self._map[tHandle] = action self.setActionsVisible(True) - return def remove(self, tHandle: str) -> None: """Remove a template item.""" @@ -1096,19 +1044,16 @@ class _UpdatableMenu(QMenu): self.removeAction(action) if not self._map: self.setActionsVisible(False) - return def clearMenu(self) -> None: """Clear all menu content.""" self._map.clear() self.clear() - return def setActionsVisible(self, value: bool) -> None: """Set the visibility of root action.""" if action := self.menuAction(): action.setVisible(value) - return ## # Private Slots @@ -1118,7 +1063,6 @@ class _UpdatableMenu(QMenu): def _actionTriggered(self, action: QAction) -> None: """Translate the menu trigger into an item trigger.""" self.menuItemTriggered.emit(str(action.data())) - return class _TreeContextMenu(QMenu): @@ -1139,11 +1083,9 @@ class _TreeContextMenu(QMenu): self._indices = indices self._children = node.childCount() > 0 logger.debug("Ready: _TreeContextMenu") - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: _TreeContextMenu") - return ## # Methods @@ -1155,7 +1097,6 @@ class _TreeContextMenu(QMenu): action.triggered.connect(self._tree.emptyTrash) if self._children: self._expandCollapse() - return def buildSingleSelectMenu(self) -> None: """Build the single-select menu.""" @@ -1191,15 +1132,12 @@ class _TreeContextMenu(QMenu): action.triggered.connect(qtLambda(self._tree.duplicateFromHandle, self._handle)) self._deleteOrTrash() - return - def buildMultiSelectMenu(self) -> None: """Build the multi-select menu.""" self._itemActive() self._itemStatusImport(True) self.addSeparator() self._deleteOrTrash() - return ## # Menu Builders @@ -1217,7 +1155,6 @@ class _TreeContextMenu(QMenu): self._view.openDocumentRequest.emit, self._handle, nwDocMode.VIEW, "", False )) - return def _itemCreation(self) -> None: """Add create item actions.""" @@ -1228,7 +1165,6 @@ class _TreeContextMenu(QMenu): menu.addAction(self._view.projBar.aAddEmpty) menu.addAction(self._view.projBar.aAddNote) menu.addAction(self._view.projBar.aAddFolder) - return def _itemHeader(self) -> None: """Check if there is a header that can be used for rename.""" @@ -1238,7 +1174,6 @@ class _TreeContextMenu(QMenu): action.triggered.connect( qtLambda(self._view.renameTreeItem, self._handle, hItem.title) ) - return def _itemActive(self) -> None: """Add Active/Inactive actions.""" @@ -1253,7 +1188,6 @@ class _TreeContextMenu(QMenu): else: action = qtAddAction(self, self.tr("Toggle Active")) action.triggered.connect(self._toggleItemActive) - return def _itemStatusImport(self, multi: bool) -> None: """Add actions for changing status or importance.""" @@ -1295,7 +1229,6 @@ class _TreeContextMenu(QMenu): self._view.projectSettingsRequest.emit, GuiProjectSettings.PAGE_IMPORT )) - return def _itemTransform(self, isFile: bool, isFolder: bool) -> None: """Add actions for the Transform menu.""" @@ -1338,15 +1271,12 @@ class _TreeContextMenu(QMenu): action = qtAddAction(menu, self.tr("Split Document by Headings")) action.triggered.connect(qtLambda(self._tree.splitDocument, self._handle)) - return - def _expandCollapse(self) -> None: """Add actions for expand and collapse.""" action = qtAddAction(self, self.tr("Expand All")) action.triggered.connect(qtLambda(self._tree.expandFromIndex, self._indices[0])) action = qtAddAction(self, self.tr("Collapse All")) action.triggered.connect(qtLambda(self._tree.collapseFromIndex, self._indices[0])) - return def _deleteOrTrash(self) -> None: """Add move to Trash action.""" @@ -1359,7 +1289,6 @@ class _TreeContextMenu(QMenu): text = self.tr("Move to Trash") action = qtAddAction(self, text) action.triggered.connect(self._tree.processDeleteRequest) - return ## # Private Slots @@ -1371,7 +1300,6 @@ class _TreeContextMenu(QMenu): if self._item.isFileType(): self._item.setActive(not self._item.isActive) self._item.notifyToRefresh() - return ## # Internal Functions @@ -1385,13 +1313,11 @@ class _TreeContextMenu(QMenu): node.item.setActive(state) refresh.append(node.item.itemHandle) SHARED.project.tree.refreshItems(refresh) - return def _changeItemStatus(self, key: str) -> None: """Set a new status value of an item.""" self._item.setStatus(key) self._item.notifyToRefresh() - return def _iterSetItemStatus(self, key: str) -> None: """Change the status value for multiple items.""" @@ -1401,13 +1327,11 @@ class _TreeContextMenu(QMenu): node.item.setStatus(key) refresh.append(node.item.itemHandle) SHARED.project.tree.refreshItems(refresh) - return def _changeItemImport(self, key: str) -> None: """Set a new importance value of an item.""" self._item.setImport(key) self._item.notifyToRefresh() - return def _iterSetItemImport(self, key: str) -> None: """Change the status value for multiple items.""" @@ -1417,7 +1341,6 @@ class _TreeContextMenu(QMenu): node.item.setImport(key) refresh.append(node.item.itemHandle) SHARED.project.tree.refreshItems(refresh) - return def _changeItemLayout(self, itemLayout: nwItemLayout) -> None: """Set a new item layout value of an item.""" @@ -1428,7 +1351,6 @@ class _TreeContextMenu(QMenu): elif itemLayout == nwItemLayout.NOTE: self._item.setLayout(nwItemLayout.NOTE) self._item.notifyToRefresh() - return def _convertFolderToFile(self, itemLayout: nwItemLayout) -> None: """Convert a folder to a note or document.""" @@ -1448,4 +1370,3 @@ class _TreeContextMenu(QMenu): self._item.notifyToRefresh() else: logger.info("Folder conversion cancelled") - return diff --git a/novelwriter/gui/search.py b/novelwriter/gui/search.py index 51bf1a15..5313d676 100644 --- a/novelwriter/gui/search.py +++ b/novelwriter/gui/search.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -50,6 +50,7 @@ logger = logging.getLogger(__name__) class GuiProjectSearch(QWidget): + """GUI: Project Search Panel.""" C_NAME = 0 C_RESULT = 0 @@ -151,8 +152,6 @@ class GuiProjectSearch(QWidget): logger.debug("Ready: GuiProjectSearch") - return - ## # Methods ## @@ -174,8 +173,6 @@ class GuiProjectSearch(QWidget): self.toggleWord.setIcon(SHARED.theme.getIcon("search_word")) self.toggleRegEx.setIcon(SHARED.theme.getIcon("search_regex")) - return - def processReturn(self) -> None: """Process a return keypress forwarded from the main GUI.""" if self.searchText.hasFocus(): @@ -189,7 +186,6 @@ class GuiProjectSearch(QWidget): self.openDocumentSelectRequest.emit( str(data[0]), checkInt(data[1], -1), checkInt(data[2], -1), False ) - return def beginSearch(self, text: str = "") -> None: """Focus the search box and select its text, if any.""" @@ -198,20 +194,17 @@ class GuiProjectSearch(QWidget): if text: self.searchText.setText(text.partition("\n")[0]) self.searchText.selectAll() - return def closeProjectTasks(self) -> None: """Run close project tasks.""" self._map = {} self.searchText.clear() self.searchResult.clear() - return def refreshCurrentSearch(self) -> None: """Refresh the search if there is one.""" if self.searchResult.topLevelItemCount() > 0: self._processSearch() - return ## # Events @@ -238,7 +231,6 @@ class GuiProjectSearch(QWidget): self.searchText.setFocus() else: super().keyPressEvent(event) - return ## # Public Slots @@ -252,7 +244,6 @@ class GuiProjectSearch(QWidget): results, capped = self._search.searchText(SHARED.mainGui.docEditor.getText()) self._displayResultSet(SHARED.project.tree[tHandle], results, capped) logger.debug("Updated search for '%s' in %.3f ms", tHandle, 1000*(time() - start)) - return ## # Private Slots @@ -278,7 +269,6 @@ class GuiProjectSearch(QWidget): self._time = time() QApplication.restoreOverrideCursor() self._blocked = False - return @pyqtSlot() def _searchResultSelected(self) -> None: @@ -288,7 +278,6 @@ class GuiProjectSearch(QWidget): self.selectedItemChanged.emit(str(data[0])) elif data := items[0].data(0, self.D_HANDLE): self.selectedItemChanged.emit(str(data)) - return @pyqtSlot("QTreeWidgetItem*", int) def _searchResultDoubleClicked(self, item: QTreeWidgetItem, column: int) -> None: @@ -297,28 +286,24 @@ class GuiProjectSearch(QWidget): self.openDocumentSelectRequest.emit( str(data[0]), checkInt(data[1], -1), checkInt(data[2], -1), True ) - return @pyqtSlot(bool) def _toggleCase(self, state: bool) -> None: """Enable/disable case sensitive mode.""" CONFIG.searchProjCase = state self.refreshCurrentSearch() - return @pyqtSlot(bool) def _toggleWord(self, state: bool) -> None: """Enable/disable whole word search mode.""" CONFIG.searchProjWord = state self.refreshCurrentSearch() - return @pyqtSlot(bool) def _toggleRegEx(self, state: bool) -> None: """Enable/disable regular expression search mode.""" CONFIG.searchProjRegEx = state self.refreshCurrentSearch() - return ## # Internal Functions @@ -360,5 +345,3 @@ class GuiProjectSearch(QWidget): self.searchResult.setFirstColumnSpanned(i, parent, True) QApplication.processEvents() - - return diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index e0ccb9e5..6440fc15 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -45,6 +45,7 @@ logger = logging.getLogger(__name__) class GuiSideBar(QWidget): + """GUI: Main Window SideBar.""" requestViewChange = pyqtSignal(nwView) @@ -126,8 +127,6 @@ class GuiSideBar(QWidget): logger.debug("Ready: GuiSideBar") - return - def updateTheme(self) -> None: """Initialise GUI elements that depend on specific settings.""" buttonStyle = SHARED.theme.getStyleSheet(STYLES_BIG_TOOLBUTTON) @@ -153,8 +152,6 @@ class GuiSideBar(QWidget): self._setThemeModeIcon() - return - ## # Private Slots ## @@ -171,7 +168,6 @@ class GuiSideBar(QWidget): CONFIG.themeMode = nwTheme.AUTO self.mainGui.checkThemeUpdate() self._setThemeModeIcon() - return ## # Internal Functions @@ -181,7 +177,6 @@ class GuiSideBar(QWidget): """Set the theme button icon.""" self.tbTheme.setThemeIcon(nwLabels.THEME_MODE_ICON[CONFIG.themeMode]) self.tbTheme.setToolTip(trConst(nwLabels.THEME_MODE_LABEL[CONFIG.themeMode])) - return class _PopRightMenu(QMenu): diff --git a/novelwriter/gui/statusbar.py b/novelwriter/gui/statusbar.py index 315633da..0c8288b5 100644 --- a/novelwriter/gui/statusbar.py +++ b/novelwriter/gui/statusbar.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -41,6 +41,7 @@ logger = logging.getLogger(__name__) class GuiMainStatus(QStatusBar): + """GUI: Main Window Status Bar.""" def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -112,8 +113,6 @@ class GuiMainStatus(QStatusBar): self.updateTheme() self.clearStatus() - return - def initSettings(self) -> None: """Apply user settings.""" if CONFIG.useCharCount: @@ -122,7 +121,6 @@ class GuiMainStatus(QStatusBar): else: self._trStatsCount = trStats(nwLabels.STATS_DISPLAY[nwStats.WORDS]) self._trStatsTip = self.tr("Total word count (session change)") - return def clearStatus(self) -> None: """Reset all widgets on the status bar to default values.""" @@ -132,7 +130,6 @@ class GuiMainStatus(QStatusBar): self.setProjectStatus(None) self.setDocumentStatus(None) self.updateTime() - return def updateTheme(self) -> None: """Update theme elements.""" @@ -149,8 +146,6 @@ class GuiMainStatus(QStatusBar): self.docIcon.setColors(colNone, colSaved, colUnsaved) self.projIcon.setColors(colNone, colSaved, colUnsaved) - return - ## # Setters ## @@ -158,17 +153,14 @@ class GuiMainStatus(QStatusBar): def setRefTime(self, refTime: float) -> None: """Set the reference time for the status bar clock.""" self._refTime = refTime - return def setProjectStatus(self, state: bool | None) -> None: """Set the project status colour icon.""" self.projIcon.setState(state) - return def setDocumentStatus(self, state: bool | None) -> None: """Set the document status colour icon.""" self.docIcon.setState(state) - return def setUserIdle(self, idle: bool) -> None: """Change the idle status icon.""" @@ -180,13 +172,11 @@ class GuiMainStatus(QStatusBar): else: self.timeIcon.setPixmap(self.timePixmap) self._userIdle = idle - return def setProjectStats(self, pWC: int, sWC: int) -> None: """Update the current project statistics.""" self.statsText.setText(self._trStatsCount.format(f"{pWC:n}", f"{sWC:+n}")) self.statsText.setToolTip(self._trStatsTip) - return def updateTime(self, idleTime: float = 0.0) -> None: """Update the session clock.""" @@ -198,7 +188,6 @@ class GuiMainStatus(QStatusBar): else: sessTime = round(time() - self._refTime) self.timeText.setText(formatTime(sessTime)) - return ## # Public Slots @@ -209,7 +198,6 @@ class GuiMainStatus(QStatusBar): """Set the status bar message to display.""" self.showMessage(message, nwConst.STATUS_MSG_TIMEOUT) QApplication.processEvents() - return @pyqtSlot(str, str) def setLanguage(self, language: str, provider: str) -> None: @@ -220,19 +208,16 @@ class GuiMainStatus(QStatusBar): else: self.langText.setText(QLocale(language).nativeLanguageName().title()) self.langText.setToolTip(f"{language} ({provider})" if provider else language) - return @pyqtSlot(bool) def updateProjectStatus(self, status: bool) -> None: """Update the project status.""" self.setProjectStatus(not status) - return @pyqtSlot(bool) def updateDocumentStatus(self, status: bool) -> None: """Update the document status.""" self.setDocumentStatus(not status) - return ## # Private Slots @@ -244,7 +229,6 @@ class GuiMainStatus(QStatusBar): state = not CONFIG.showSessionTime self.timeText.setVisible(state) CONFIG.showSessionTime = state - return ## # Debug @@ -279,4 +263,3 @@ class GuiMainStatus(QStatusBar): ) self.showMessage(f"Debug [{stamp}] {message}", 6000) logger.debug("[MEMINFO] %s", message) - return diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index 5dd370ab..b83de9b7 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -58,6 +58,7 @@ STYLES_BIG_TOOLBUTTON = "bigToolButton" @dataclass class ThemeEntry: + """Theme data.""" name: str dark: bool @@ -65,6 +66,7 @@ class ThemeEntry: class ThemeMeta: + """Theme meta data.""" name: str = "" mode: str = "" @@ -74,6 +76,7 @@ class ThemeMeta: class IconsMeta: + """Icon theme meta data.""" name: str = "" author: str = "" @@ -81,6 +84,7 @@ class IconsMeta: class SyntaxColors: + """Colours for the syntax highlighter.""" back: QColor = QColor(255, 255, 255) text: QColor = QColor(0, 0, 0) @@ -106,7 +110,7 @@ class SyntaxColors: class GuiTheme: - """Gui Theme Class + """Gui Theme Class. Handles the look and feel of novelWriter. """ @@ -190,8 +194,6 @@ class GuiTheme: logger.debug("Text 'N' Height: %d", self.textNHeight) logger.debug("Text 'N' Width: %d", self.textNWidth) - return - ## # Properties ## @@ -206,7 +208,7 @@ class GuiTheme: ## def getTextWidth(self, text: str, font: QFont | None = None) -> int: - """Returns the width needed to contain a given piece of text in + """Return the width needed to contain a given piece of text in pixels. """ if isinstance(font, QFont): @@ -238,8 +240,6 @@ class GuiTheme: self.iconCache.initIcons() self.loadTheme() - return - def isDesktopDarkMode(self) -> bool: """Check if the desktop is in dark mode.""" if CONFIG.verQtValue >= 0x060500 and (hint := QGuiApplication.styleHints()): @@ -507,7 +507,6 @@ class GuiTheme: """Set the colour for a named colour.""" self._qColors[key] = QColor(color) self._svgColors[key] = color.name(QColor.NameFormat.HexRgb).encode("utf-8") - return def _resetTheme(self) -> None: """Reset GUI colours to default values.""" @@ -559,8 +558,6 @@ class GuiTheme: self._setBaseColor("inactive", red) self._setBaseColor("disabled", faded) - return - def _readColor(self, parser: ConfigParser, section: str, name: str) -> QColor: """Parse a colour value from a config string.""" return self.parseColor(parser.get(section, name, fallback="default")) @@ -570,7 +567,6 @@ class GuiTheme: ) -> None: """Set a palette colour value from a config string.""" self._guiPalette.setBrush(value, self._readColor(parser, section, name)) - return def _buildStyleSheets(self, palette: QPalette) -> None: """Build default style sheets.""" @@ -602,8 +598,6 @@ class GuiTheme: "QToolButton::menu-indicator {image: none;} " ) - return - def _scanThemes(self, files: list[Path]) -> None: """Scan the GUI themes folder and list all themes.""" parser = ConfigParser() @@ -631,8 +625,6 @@ class GuiTheme: logger.debug("Checking theme config '%s'", key) self._allThemes[key] = ThemeEntry(name, dark, item) - return - class GuiIcons: """The icon class manages the content of the assets/icons folder, @@ -672,8 +664,6 @@ class GuiIcons: # None Icon self._noIcon = QIcon(str(CONFIG.assetPath("icons") / "none.svg")) - return - def clear(self) -> None: """Clear the icon cache.""" self._svgData = {} @@ -681,7 +671,6 @@ class GuiIcons: self._headerDec = [] self._headerDecNarrow = [] self._meta = ThemeMeta() - return ## # Properties @@ -703,7 +692,6 @@ class GuiIcons: _listContent(icons, CONFIG.assetPath("icons"), ".icons") _listContent(icons, CONFIG.dataPath("icons"), ".icons") self._scanThemes(icons) - return def loadTheme(self, theme: str) -> None: """Update the theme map. This is more of an init, since many of @@ -781,7 +769,7 @@ class GuiIcons: self, tType: nwItemType, tClass: nwItemClass, tLayout: nwItemLayout, hLevel: str = "H0" ) -> QIcon: """Get the correct icon for a project item based on type, class - and heading level + and heading level. """ name = None color = "default" @@ -942,8 +930,6 @@ class GuiIcons: logger.debug("Checking icon theme '%s'", key) self._allThemes[key] = ThemeEntry(name, False, item) - return - # Module Functions # ================ @@ -952,4 +938,3 @@ def _listContent(data: list[Path], path: Path, extension: str) -> None: """List files of a specific type and extend the list.""" if path.is_dir(): data.extend(n for n in path.iterdir() if n.is_file() and n.suffix == extension) - return diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 523b1ae4..91d7ff59 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -68,7 +68,7 @@ logger = logging.getLogger(__name__) class GuiMain(QMainWindow): - """Main GUI Window + """Main GUI Window. The Main GUI window class is the entry point of the application. It is split up into GUI components, assembled in the init function. @@ -323,13 +323,10 @@ class GuiMain(QMainWindow): self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ...")) CONFIG.splashMessage("novelWriter is ready ...") - return - def initMain(self) -> None: """Initialise elements that depend on user settings.""" self.asProjTimer.setInterval(int(CONFIG.autoSaveProj*1000)) self.asDocTimer.setInterval(int(CONFIG.autoSaveDoc*1000)) - return def postLaunchTasks(self, cmdOpen: str | None) -> None: """Process tasks after the main window has been created.""" @@ -351,8 +348,6 @@ class GuiMain(QMainWindow): # before showing any dialogs QTimer.singleShot(50, self.showPostLaunchDialogs) - return - @pyqtSlot() def showPostLaunchDialogs(self) -> None: """Show post launch dialogs.""" @@ -370,8 +365,6 @@ class GuiMain(QMainWindow): ).format(f"", "") SHARED.info(f"{trVersion}
{trRelease}") - return - ## # Project Actions ## @@ -534,7 +527,6 @@ class GuiMain(QMainWindow): SHARED.setFocusMode(False) self.saveDocument() self.docEditor.clearEditor() - return def openDocument( self, @@ -591,7 +583,6 @@ class GuiMain(QMainWindow): self.openDocument(nHandle, tLine=1, doScroll=True) elif wrapAround: self.openDocument(fHandle, tLine=1, doScroll=True) - return def saveDocument(self, force: bool = False) -> None: """Save the current documents.""" @@ -599,13 +590,11 @@ class GuiMain(QMainWindow): self.docEditor.saveCursorPosition() if force or self.docEditor.docChanged: self.docEditor.saveText() - return @pyqtSlot() def forceSaveDocument(self) -> None: """Save document even of it has not changed.""" self.saveDocument(force=True) - return def viewDocument(self, tHandle: str | None = None, sTitle: str | None = None) -> bool: """Load a document for viewing in the view panel.""" @@ -760,8 +749,6 @@ class GuiMain(QMainWindow): if not beQuiet: SHARED.info(self.tr("The project index has been successfully rebuilt.")) - return - ## # Main Dialogs ## @@ -772,7 +759,6 @@ class GuiMain(QMainWindow): dialog = GuiWelcome(self) dialog.openProjectRequest.connect(self._openProjectFromWelcome) dialog.exec() - return @pyqtSlot() def showPreferencesDialog(self) -> None: @@ -780,7 +766,6 @@ class GuiMain(QMainWindow): dialog = GuiPreferences(self) dialog.newPreferencesReady.connect(self._processConfigChanges) dialog.exec() - return @pyqtSlot() @pyqtSlot(int) @@ -790,7 +775,6 @@ class GuiMain(QMainWindow): dialog = GuiProjectSettings(self, gotoPage=focusTab) dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges) dialog.exec() - return @pyqtSlot() def showNovelDetailsDialog(self) -> None: @@ -799,7 +783,6 @@ class GuiMain(QMainWindow): dialog = GuiNovelDetails(self) dialog.activateDialog() dialog.updateValues() - return @pyqtSlot() def showBuildManuscriptDialog(self) -> None: @@ -809,7 +792,6 @@ class GuiMain(QMainWindow): dialog = GuiManuscript(self) dialog.activateDialog() dialog.loadContent() - return @pyqtSlot() def showProjectWordListDialog(self) -> None: @@ -818,7 +800,6 @@ class GuiMain(QMainWindow): dialog = GuiWordList(self) dialog.newWordListReady.connect(self._processWordListChanges) dialog.exec() - return @pyqtSlot() def showWritingStatsDialog(self) -> None: @@ -828,21 +809,18 @@ class GuiMain(QMainWindow): dialog = GuiWritingStats(self) dialog.activateDialog() dialog.populateGUI() - return @pyqtSlot() def showAboutNWDialog(self) -> None: """Show the novelWriter about dialog.""" dialog = GuiAbout(self) dialog.exec() - return @pyqtSlot() def showAboutQtDialog(self) -> None: """Show the Qt about dialog.""" msgBox = QMessageBox(self) msgBox.aboutQt(self, "About Qt") - return @pyqtSlot() def showDictionariesDialog(self) -> None: @@ -852,7 +830,6 @@ class GuiMain(QMainWindow): if not dialog.initDialog(): dialog.close() SHARED.error(self.tr("Could not initialise the dialog.")) - return ## # Main Window Actions @@ -915,7 +892,6 @@ class GuiMain(QMainWindow): self.refreshThemeColors(syntax=True) self.docEditor.initEditor() self.docViewer.initViewer() - return def refreshThemeColors(self, syntax: bool = False, force: bool = False) -> None: """Refresh the GUI theme.""" @@ -937,8 +913,6 @@ class GuiMain(QMainWindow): if syntax: self.docEditor.updateSyntaxColors() - return - ## # Events ## @@ -947,14 +921,12 @@ class GuiMain(QMainWindow): """Capture application change events.""" if int(event.type()) == 210: # ThemeChange self.checkThemeUpdate() - return def closeEvent(self, event: QCloseEvent) -> None: """Capture the closing event of the GUI and call the close function to handle all the close process steps. """ event.accept() if self.closeMain() else event.ignore() - return ## # Public Slots @@ -962,30 +934,26 @@ class GuiMain(QMainWindow): @pyqtSlot() def toggleFullScreenMode(self) -> None: - """Toggle full screen mode""" + """Toggle full screen mode.""" self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen) - return @pyqtSlot() def closeDocEditor(self) -> None: """Close the document editor. This does not hide the editor.""" self.closeDocument() SHARED.project.data.setLastHandle(None, "editor") - return @pyqtSlot() def closeDocViewer(self) -> None: """Close the document viewer.""" self.closeViewerPanel() SHARED.project.data.setLastHandle(None, "viewer") - return @pyqtSlot() def toggleFocusMode(self) -> None: """Toggle focus mode.""" if self.docEditor.docHandle: SHARED.setFocusMode(not SHARED.focusMode) - return ## # Private Slots @@ -1003,7 +971,6 @@ class GuiMain(QMainWindow): docViewer = True self.docEditor.changeFocusState(docEditor) self.docViewer.changeFocusState(docViewer) - return @pyqtSlot(bool) def _focusModeChanged(self, focusMode: bool) -> None: @@ -1034,7 +1001,6 @@ class GuiMain(QMainWindow): if cursorVisible: self.docEditor.ensureCursorVisibleNoCentre() - return @pyqtSlot(nwFocus) def _switchFocus(self, paneNo: nwFocus) -> None: @@ -1082,8 +1048,6 @@ class GuiMain(QMainWindow): self._changeView(nwView.OUTLINE, exitFocus=True) self.outlineView.setTreeFocus() - return - @pyqtSlot(bool, bool, bool, bool) def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None: """Refresh GUI based on flags from the Preferences dialog.""" @@ -1115,8 +1079,6 @@ class GuiMain(QMainWindow): "Some changes will not be applied until novelWriter has been restarted." )) - return - @pyqtSlot() def _processProjectSettingsChanges(self) -> None: """Refresh data dependent on project settings.""" @@ -1124,7 +1086,6 @@ class GuiMain(QMainWindow): SHARED.updateSpellCheckLanguage() self.itemDetails.refreshDetails() self._updateWindowTitle(SHARED.project.data.name) - return @pyqtSlot() def _processWordListChanges(self) -> None: @@ -1132,7 +1093,6 @@ class GuiMain(QMainWindow): logger.debug("Reloading word list") SHARED.updateSpellCheckLanguage(reload=True) self.docEditor.spellCheckDocument() - return @pyqtSlot(str, nwDocMode) def _followTag(self, tag: str, mode: nwDocMode) -> None: @@ -1151,7 +1111,6 @@ class GuiMain(QMainWindow): self.openDocument(tHandle, sTitle=sTitle) elif mode == nwDocMode.VIEW: self.viewDocument(tHandle=tHandle, sTitle=sTitle) - return @pyqtSlot(Path) def _openProjectFromWelcome(self, path: Path) -> None: @@ -1160,7 +1119,6 @@ class GuiMain(QMainWindow): self.openProject(path) if not SHARED.hasProject: self.showWelcomeDialog() - return @pyqtSlot(str, nwDocMode, str, bool) def _openDocument(self, tHandle: str, mode: nwDocMode, sTitle: str, setFocus: bool) -> None: @@ -1170,7 +1128,6 @@ class GuiMain(QMainWindow): self.openDocument(tHandle, sTitle=sTitle, changeFocus=setFocus) elif mode == nwDocMode.VIEW: self.viewDocument(tHandle=tHandle, sTitle=sTitle) - return @pyqtSlot(str, int, int, bool) def _openDocumentSelection( @@ -1179,7 +1136,6 @@ class GuiMain(QMainWindow): """Open a document and select a section of the text.""" if self.openDocument(tHandle, changeFocus=changeFocus): self.docEditor.setCursorSelection(selStart, selLength) - return @pyqtSlot() def _reloadViewer(self) -> None: @@ -1188,7 +1144,6 @@ class GuiMain(QMainWindow): # If the two panels have the same document, save any changes in the editor self.saveDocument() self.docViewer.reloadText() - return @pyqtSlot(nwView) def _changeView(self, view: nwView, exitFocus: bool = False) -> None: @@ -1219,8 +1174,6 @@ class GuiMain(QMainWindow): isNovel = self.projStack.currentWidget() == self.novelView self.novelView.setActive(isMain and isNovel) - return - @pyqtSlot(nwDocAction) def _passDocumentAction(self, action: nwDocAction) -> None: """Pass on a document action to the editor or viewer based on @@ -1230,7 +1183,6 @@ class GuiMain(QMainWindow): self.docEditor.docAction(action) elif self.docViewer.hasFocus(): self.docViewer.docAction(action) - return @pyqtSlot(str) @pyqtSlot(nwDocInsert) @@ -1240,14 +1192,12 @@ class GuiMain(QMainWindow): """ if self.docEditor.hasFocus(): self.docEditor.insertText(content) - return @pyqtSlot() def _toggleViewerPanelVisibility(self) -> None: """Toggle the visibility of the document viewer panel.""" CONFIG.showViewerPanel = not CONFIG.showViewerPanel self.docViewerPanel.setVisible(CONFIG.showViewerPanel) - return @pyqtSlot() def _timeTick(self) -> None: @@ -1263,7 +1213,6 @@ class GuiMain(QMainWindow): self._updateStatusWordCount() if CONFIG.memInfo: # pragma: no cover self.mainStatus.memInfo() - return @pyqtSlot() def _autoSaveProject(self) -> None: @@ -1274,7 +1223,6 @@ class GuiMain(QMainWindow): if doSave: logger.debug("Auto-saving project") self.saveProject(autoSave=True) - return @pyqtSlot() def _autoSaveDocument(self) -> None: @@ -1282,7 +1230,6 @@ class GuiMain(QMainWindow): if SHARED.hasProject and self.docEditor.docChanged: logger.debug("Auto-saving document") self.saveDocument() - return @pyqtSlot() def _updateStatusWordCount(self) -> None: @@ -1312,8 +1259,6 @@ class GuiMain(QMainWindow): self.mainStatus.setProjectStats(cTotal, cTotal - iTotal) - return - @pyqtSlot() def _keyPressReturn(self) -> None: """Process a return or enter keypress in the main window.""" @@ -1321,7 +1266,6 @@ class GuiMain(QMainWindow): self.projSearch.processReturn() else: self.openSelectedItem() - return @pyqtSlot() def _keyPressEscape(self) -> None: @@ -1330,7 +1274,6 @@ class GuiMain(QMainWindow): self.docEditor.closeSearch() elif SHARED.focusMode: SHARED.setFocusMode(False) - return @pyqtSlot(int) def _mainStackChanged(self, index: int) -> None: @@ -1338,7 +1281,6 @@ class GuiMain(QMainWindow): if self.mainStack.widget(index) == self.outlineView: if SHARED.hasProject: self.outlineView.refreshTree() - return @pyqtSlot(int) def _projStackChanged(self, index: int) -> None: @@ -1350,7 +1292,6 @@ class GuiMain(QMainWindow): elif widget == self.novelView: sHandle, _ = self.novelView.getSelectedHandle() self.itemDetails.updateViewBox(sHandle) - return ## # Internal Functions @@ -1363,9 +1304,7 @@ class GuiMain(QMainWindow): width = minmax(size[0], 900, availSize.width()) height = minmax(size[1], 500, availSize.height()) self.resize(width, height) - return def _updateWindowTitle(self, projName: str | None = None) -> None: """Set the window title and add the project's name.""" self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName]))) - return diff --git a/novelwriter/shared.py b/novelwriter/shared.py index 28c8d5be..7c971341 100644 --- a/novelwriter/shared.py +++ b/novelwriter/shared.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -57,6 +57,12 @@ RX_HTML = re.compile(r"<.*?>") class SharedData(QObject): + """Shared Data Singleton. + + This is the class instantiated as the SHARED singleton. It holds + various globally needed data and pointers to important objects like + the main GUI, the current project, and the GUI theme. + """ __slots__ = ( "_gui", "_idleRefTime", "_idleTime", "_lastAlert", "_lockedBy", @@ -96,8 +102,6 @@ class SharedData(QObject): self._clock.setInterval(1000) self._clock.timeout.connect(lambda: self.mainClockTick.emit()) - return - ## # Properties ## @@ -169,7 +173,6 @@ class SharedData(QObject): if state is not self._focusMode: self._focusMode = state self.focusModeChanged.emit(state) - return ## # Methods @@ -181,7 +184,6 @@ class SharedData(QObject): """ self._theme = theme self._theme.initThemes() - return def initSharedData(self, gui: GuiMain) -> None: """Initialise the SharedData instance. This must be called as @@ -194,7 +196,6 @@ class SharedData(QObject): logger.debug("Ready: SharedData") if pool := QThreadPool.globalInstance(): logger.debug("Thread Pool Max Count: %d", pool.maxThreadCount()) - return def closeDocument(self, tHandle: str | None = None) -> None: """Close the document editor, optionally a specific document.""" @@ -202,7 +203,6 @@ class SharedData(QObject): self.mainGui.closeDocument() if tHandle is None or tHandle == self.mainGui.docViewer.docHandle: self.mainGui.closeViewerPanel() - return def saveEditor(self, tHandle: str | None = None) -> None: """Save the editor content, optionally a specific document.""" @@ -213,7 +213,6 @@ class SharedData(QObject): ): logger.debug("Saving editor document before action") docEditor.saveText() - return def openProject(self, path: str | Path, clearLock: bool = False) -> bool: """Open a project.""" @@ -246,7 +245,6 @@ class SharedData(QObject): self.project.closeProject(self._idleTime) self._resetProject() self._resetIdleTimer() - return def updateSpellCheckLanguage(self, reload: bool = False) -> None: """Update the active spell check language from settings.""" @@ -256,7 +254,6 @@ class SharedData(QObject): self.spelling.setLanguage(language) _, provider = self.spelling.describeDict() self.spellLanguageChanged.emit(language, provider) - return def updateIdleTime(self, currTime: float, userIdle: bool) -> None: """Update the idle time record. If the userIdle flag is True, @@ -267,47 +264,40 @@ class SharedData(QObject): if userIdle: self._idleTime += currTime - self._idleRefTime self._idleRefTime = currTime - return def initMainProgress(self, maximum: int, inclusive: bool = False) -> None: """Start a session for the main progress bar.""" if gui := self._gui: gui.mainProgress.setMaximum(maximum - (1 if inclusive else 0)) gui.mainProgress.setValue(0) - return def incMainProgress(self) -> None: """Increment the value for the main progress bar.""" if gui := self._gui: gui.mainProgress.setValue(gui.mainProgress.value() + 1) QApplication.processEvents() - return def clearMainProgress(self, delay: float = 1.0) -> None: """Clear the main progress bar.""" if gui := self._gui: QTimer.singleShot(int(delay*1000), gui.mainProgress.reset) - return def newStatusMessage(self, message: str) -> None: """Request a new status message. This is a callable function for core classes that cannot emit signals on their own. """ self.projectStatusMessage.emit(message) - return def setGlobalProjectState(self, state: bool) -> None: """Change the global project status. This is a callable function for core classes that cannot emit signals on their own. """ self.projectStatusChanged.emit(state) - return def runInThreadPool(self, runnable: QRunnable, priority: int = 0) -> None: """Queue a runnable in the application thread pool.""" if pool := QThreadPool.globalInstance(): pool.start(runnable, priority=priority) - return def getProjectPath( self, parent: QWidget, @@ -346,13 +336,11 @@ class SharedData(QObject): def openWebsite(self, url: str) -> None: """Open a URL in the system's default browser.""" QDesktopServices.openUrl(QUrl(url)) - return @pyqtSlot(str, nwItemClass) def createNewNote(self, tag: str, itemClass: nwItemClass) -> None: """Process new note request.""" self.project.createNewNote(tag, itemClass) - return ## # Signal Proxies @@ -364,37 +352,31 @@ class SharedData(QObject): """Emit the indexChangedTags signal.""" if self._project and self._project.data.uuid == project.data.uuid: self.indexChangedTags.emit(updated, deleted) - return def emitIndexCleared(self, project: NWProject) -> None: """Emit the indexCleared signal.""" if self._project and self._project.data.uuid == project.data.uuid: self.indexCleared.emit() - return def emitIndexAvailable(self, project: NWProject) -> None: """Emit the indexAvailable signal.""" if self._project and self._project.data.uuid == project.data.uuid: self.indexAvailable.emit() - return def emitStatusLabelsChanged(self, project: NWProject, kind: T_StatusKind) -> None: """Emit the statusLabelsChanged signal.""" if self._project and self._project.data.uuid == project.data.uuid: self.statusLabelsChanged.emit(kind) - return def emitProjectItemChanged(self, project: NWProject, handle: str, change: nwChange) -> None: """Emit the projectItemChanged signal.""" if self._project and self._project.data.uuid == project.data.uuid: self.projectItemChanged.emit(handle, change) - return def emitRootFolderChanged(self, project: NWProject, handle: str, change: nwChange) -> None: """Emit the rootFolderChanged signal.""" if self._project and self._project.data.uuid == project.data.uuid: self.rootFolderChanged.emit(handle, change) - return ## # Alert Boxes @@ -409,7 +391,6 @@ class SharedData(QObject): if log: self._logMessage(self._lastAlert, logger.info) alert.exec() - return def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None: """Open a warning alert box.""" @@ -420,7 +401,6 @@ class SharedData(QObject): if log: self._logMessage(self._lastAlert, logger.warning) alert.exec() - return def error(self, text: str, info: str = "", details: str = "", log: bool = True, exc: Exception | None = None) -> None: @@ -434,7 +414,6 @@ class SharedData(QObject): if log: self._logMessage(self._lastAlert, logger.error) alert.exec() - return def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool: """Open a question box.""" @@ -443,8 +422,7 @@ class SharedData(QObject): alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True) self._lastAlert = alert.logMessage alert.exec() - isYes = alert.result() == QMessageBox.StandardButton.Yes - return isYes + return alert.result() == QMessageBox.StandardButton.Yes ## # Internal Functions @@ -454,7 +432,6 @@ class SharedData(QObject): """Print message to log.""" for text in message.split("
"): log(RX_HTML.sub("", text), stacklevel=3) - return def _resetProject(self) -> None: """Create a new project and spell checking instance.""" @@ -467,13 +444,11 @@ class SharedData(QObject): self._spelling = NWSpellEnchant(self._project) self.updateSpellCheckLanguage() self._focusMode = False - return def _resetIdleTimer(self) -> None: """Reset the timer data for the idle timer.""" self._idleRefTime = time() self._idleTime = 0.0 - return def _closeToolDialogs(self) -> None: """Close all open tool dialogs.""" @@ -481,7 +456,6 @@ class SharedData(QObject): for widget in self.mainGui.children(): if isinstance(widget, NToolDialog): widget.close() - return class _GuiAlert(QMessageBox): @@ -496,11 +470,9 @@ class _GuiAlert(QMessageBox): self._theme = theme self._message = "" logger.debug("Ready: _GuiAlert") - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: _GuiAlert") - return @property def logMessage(self) -> str: @@ -512,14 +484,12 @@ class _GuiAlert(QMessageBox): self.setText(text) self.setInformativeText(info) self.setDetailedText(details) - return def setException(self, exception: Exception) -> None: """Add exception details.""" info = self.informativeText() text = f"{type(exception).__name__}: {exception!s}" self.setInformativeText(f"{info}
{text}" if info else text) - return def setAlertType(self, level: int, isYesNo: bool) -> None: """Set the type of alert and whether the dialog should have @@ -542,4 +512,3 @@ class _GuiAlert(QMessageBox): elif level == self.ASK: self.setIconPixmap(self._theme.getPixmap("alert_question", (pSz, pSz), "blue")) self.setWindowTitle(self.tr("Question")) - return diff --git a/novelwriter/splash.py b/novelwriter/splash.py index f1792a98..8de22019 100644 --- a/novelwriter/splash.py +++ b/novelwriter/splash.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -38,6 +38,14 @@ SPLASH_IMG = Path(__file__).parent / "assets" / "images" / "splash.png" class NSplashScreen(QSplashScreen): + """GUI: App Launch Splash Screen. + + A small splash screen that is shown as novelWriter starts up. Its + primary purpose is to provide user feedback that the app is being + initiated when there are delays in the process while Qt waits for + responses from the OS, or has to load particularly large data sets + like when the system has a lot of fonts installed. + """ __slots__ = ("_color", "_rect", "_text") @@ -52,17 +60,14 @@ class NSplashScreen(QSplashScreen): self._color = QColor(26, 52, 78) self._rect = QRect(144, 110, 440, 30) self._text = "" - return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NSplashScreen") - return def drawContents(self, painter: QPainter) -> None: """Draw the text message.""" painter.setPen(self._color) painter.drawText(self._rect, Qt.AlignmentFlag.AlignLeft, self._text) - return def showStatus(self, message: str) -> None: """Update the status message.""" @@ -71,4 +76,3 @@ class NSplashScreen(QSplashScreen): if message: logger.info("[Splash] %s", message) sleep(0.025) - return diff --git a/novelwriter/text/comments.py b/novelwriter/text/comments.py index d2a62738..5513430d 100644 --- a/novelwriter/text/comments.py +++ b/novelwriter/text/comments.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from novelwriter.enum import nwComment diff --git a/novelwriter/text/counting.py b/novelwriter/text/counting.py index fc3c749d..7b591884 100644 --- a/novelwriter/text/counting.py +++ b/novelwriter/text/counting.py @@ -22,7 +22,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import re @@ -74,9 +74,11 @@ def preProcessText(text: str, keepHeaders: bool = True) -> list[str]: def standardCounter(text: str) -> tuple[int, int, int]: - """A counter that counts paragraphs, words and characters. - This is the standard counter that includes headings in the word and - character counts. + """Return a standard count. + + A counter that counts paragraphs, words and characters. This is the + standard counter that includes headings in the word and character + counts. """ cCount = 0 wCount = 0 @@ -124,7 +126,9 @@ def standardCounter(text: str) -> tuple[int, int, int]: def bodyTextCounter(text: str) -> tuple[int, int, int]: - """A counter that counts body text words, characters, and characters + """Return a body text count. + + A counter that counts body text words, characters, and characters without white spaces. """ wCount = 0 diff --git a/novelwriter/text/patterns.py b/novelwriter/text/patterns.py index 8a1ad205..3160eb0d 100644 --- a/novelwriter/text/patterns.py +++ b/novelwriter/text/patterns.py @@ -21,7 +21,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import re @@ -32,6 +32,7 @@ from novelwriter.constants import nwRegEx, nwUnicode class RegExPatterns: + """Compiled RegEx Patterns.""" AMBIGUOUS = (nwUnicode.U_APOS, nwUnicode.U_RSQUO) @@ -132,6 +133,7 @@ REGEX_PATTERNS = RegExPatterns() class DialogParser: + """A callable parser for finding dialog regions in text.""" __slots__ = ( "_alternate", "_breakD", "_breakQ", "_dialog", "_enabled", "_mode", @@ -147,7 +149,6 @@ class DialogParser: self._breakD = None self._breakQ = None self._mode = "" - return @property def enabled(self) -> bool: @@ -174,8 +175,6 @@ class DialogParser: self._narrator = narrator self._mode = f" {narrator}" - return - def __call__(self, text: str) -> list[tuple[int, int]]: """Caller wrapper for dialogue processing.""" temp: list[int] = [] diff --git a/novelwriter/tools/dictionaries.py b/novelwriter/tools/dictionaries.py index 90f618bc..5cc97021 100644 --- a/novelwriter/tools/dictionaries.py +++ b/novelwriter/tools/dictionaries.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -45,6 +45,12 @@ logger = logging.getLogger(__name__) class GuiDictionaries(NNonBlockingDialog): + """GUI: Spell Check Dictionary Tool. + + A helper tool for downloading and extracting dictionaries to a + location where Enchant can find them. This tool is only needed on + Windows. + """ def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -123,11 +129,8 @@ class GuiDictionaries(NNonBlockingDialog): logger.debug("Ready: GuiDictionaries") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiDictionaries") - return def initDialog(self) -> bool: """Prepare and check that we can proceed.""" @@ -164,7 +167,6 @@ class GuiDictionaries(NNonBlockingDialog): """Capture the user closing the window.""" event.accept() self.softDelete() - return ## # Private Slots @@ -182,7 +184,6 @@ class GuiDictionaries(NNonBlockingDialog): if soxFile: path = Path(soxFile).absolute() self.huInput.setText(str(path)) - return @pyqtSlot() def _doImportHunspell(self) -> None: @@ -202,14 +203,12 @@ class GuiDictionaries(NNonBlockingDialog): self._appendLog(formatException(exc), err=True) else: self._appendLog(procErr, err=True) - return @pyqtSlot() def _doOpenInstallLocation(self) -> None: """Open the dictionary folder.""" if not openExternalPath(Path(self.inPath.text())): SHARED.error("Path not found.") - return ## # Internal Functions @@ -247,4 +246,3 @@ class GuiDictionaries(NNonBlockingDialog): cursor.movePosition(QTextCursor.MoveOperation.End) cursor.deleteChar() self.infoBox.setTextCursor(cursor) - return diff --git a/novelwriter/tools/lipsum.py b/novelwriter/tools/lipsum.py index 4e54d79e..d28fb845 100644 --- a/novelwriter/tools/lipsum.py +++ b/novelwriter/tools/lipsum.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -42,6 +42,7 @@ logger = logging.getLogger(__name__) class GuiLipsum(NDialog): + """GUI: Lorem Ipsum Text Tool.""" def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -111,11 +112,8 @@ class GuiLipsum(NDialog): logger.debug("Ready: GuiLipsum") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiLipsum") - return @property def lipsumText(self) -> str: @@ -145,4 +143,3 @@ class GuiLipsum(NDialog): pCount = self.paraCount.value() self._lipsumText = "\n\n".join(lipsumText[0:pCount]) + "\n\n" self.close() - return diff --git a/novelwriter/tools/manusbuild.py b/novelwriter/tools/manusbuild.py index 22628f43..19070b14 100644 --- a/novelwriter/tools/manusbuild.py +++ b/novelwriter/tools/manusbuild.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -54,7 +54,7 @@ logger = logging.getLogger(__name__) class GuiManuscriptBuild(NDialog): - """GUI Tools: Manuscript Build Dialog + """GUI Tools: Manuscript Build Dialog. This is the tool for running the build itself. It can be accessed independently of the Manuscript Build Tool. @@ -244,11 +244,8 @@ class GuiManuscriptBuild(NDialog): logger.debug("Ready: GuiManuscriptBuild") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiManuscriptBuild") - return ## # Events @@ -261,7 +258,6 @@ class GuiManuscriptBuild(NDialog): self._saveSettings() event.accept() self.softDelete() - return ## # Private Slots @@ -278,7 +274,6 @@ class GuiManuscriptBuild(NDialog): self._openOutputFolder() elif role == QtRoleReject: self.close() - return @pyqtSlot() def _doSelectPath(self) -> None: @@ -290,7 +285,6 @@ class GuiManuscriptBuild(NDialog): ) if savePath: self.buildPath.setText(savePath) - return @pyqtSlot() def _doResetBuildName(self) -> None: @@ -298,13 +292,11 @@ class GuiManuscriptBuild(NDialog): bName = f"{SHARED.project.data.name} - {self._build.name}" self.buildName.setText(bName) self._build.setLastBuildName(bName) - return @pyqtSlot() def _resetProgress(self) -> None: """Set the progress bar back to 0.""" self.buildProgress.setValue(0) - return ## # Internal Functions @@ -371,7 +363,6 @@ class GuiManuscriptBuild(NDialog): pOptions.setValue("GuiManuscriptBuild", "fmtWidth", mainSplit[0]) pOptions.setValue("GuiManuscriptBuild", "sumWidth", mainSplit[1]) pOptions.saveSettings() - return def _populateContentList(self) -> None: """Build the content list.""" @@ -396,9 +387,6 @@ class GuiManuscriptBuild(NDialog): item.setIcon(nwItem.getMainIcon()) self.listContent.addItem(item) - return - def _openOutputFolder(self) -> None: """Open the build folder in the system's file explorer.""" openExternalPath(Path(self.buildPath.text())) - return diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index f1bfb2dc..8c458db3 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -66,7 +66,7 @@ logger = logging.getLogger(__name__) class GuiManuscript(NToolDialog): - """GUI Tools: Manuscript Tool + """GUI Tools: Manuscript Tool. The dialog displays all the users build definitions, a preview panel for the manuscript, and can trigger the actual build dialog to build @@ -251,11 +251,8 @@ class GuiManuscript(NToolDialog): logger.debug("Ready: GuiManuscript") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiManuscript") - return def loadContent(self) -> None: """Load dialog content from project data.""" @@ -272,8 +269,6 @@ class GuiManuscript(NToolDialog): self.buildList.setCurrentItem(self._buildMap[selected]) QTimer.singleShot(200, self._generatePreview) - return - ## # Events ## @@ -290,7 +285,6 @@ class GuiManuscript(NToolDialog): obj.close() event.accept() self.softDelete() - return ## # Private Slots @@ -302,14 +296,12 @@ class GuiManuscript(NToolDialog): build = BuildSettings() build.setName(self.tr("My Manuscript")) self._openSettingsDialog(build) - return @pyqtSlot() def _editSelectedBuild(self) -> None: """Edit the currently selected build settings entry.""" if build := self._getSelectedBuild(): self._openSettingsDialog(build) - return @pyqtSlot() def _copySelectedBuild(self) -> None: @@ -320,14 +312,12 @@ class GuiManuscript(NToolDialog): self._updateBuildsList() if item := self._buildMap.get(new.buildID): item.setSelected(True) - return @pyqtSlot("QListWidgetItem*", "QListWidgetItem*") def _updateBuildDetails(self, current: QListWidgetItem, previous: QListWidgetItem) -> None: """Process change of build selection to update the details.""" if current and (build := self._builds.getBuild(current.data(self.D_KEY))): self.buildDetails.updateInfo(build) - return @pyqtSlot() def _deleteSelectedBuild(self) -> None: @@ -338,7 +328,6 @@ class GuiManuscript(NToolDialog): dialog.close() self._builds.removeBuild(build.buildID) self._updateBuildsList() - return @pyqtSlot(BuildSettings) def _processNewSettings(self, build: BuildSettings) -> None: @@ -347,7 +336,6 @@ class GuiManuscript(NToolDialog): self._updateBuildItem(build) if (current := self.buildList.currentItem()) and current.data(self.D_KEY) == build.buildID: self._updateBuildDetails(current, current) - return @pyqtSlot() def _generatePreview(self) -> None: @@ -399,15 +387,12 @@ class GuiManuscript(NToolDialog): if build.changed: self._builds.setBuild(build) - return - @pyqtSlot() def _printDocument(self) -> None: """Open the print preview dialog.""" preview = QPrintPreviewDialog(self) preview.paintRequested.connect(self.docPreview.printPreview) preview.exec() - return ## # Internal Functions @@ -456,8 +441,6 @@ class GuiManuscript(NToolDialog): pOptions.setValue("GuiManuscript", "showNewPage", showNewPage) pOptions.saveSettings() - return - def _openSettingsDialog(self, build: BuildSettings) -> None: """Open the build settings dialog.""" if dialog := self._findSettingsDialog(build.buildID): @@ -482,7 +465,6 @@ class GuiManuscript(NToolDialog): bItem.setData(self.D_KEY, key) self.buildList.addItem(bItem) self._buildMap[key] = bItem - return def _updateBuildItem(self, build: BuildSettings) -> None: """Update the entry of a specific build item.""" @@ -490,7 +472,6 @@ class GuiManuscript(NToolDialog): item.setText(build.name) else: # Probably a new item self._updateBuildsList() - return def _findSettingsDialog(self, buildID: str) -> GuiBuildSettings | None: """Return an open build settings dialog for a given build, if @@ -521,8 +502,6 @@ class _DetailsWidget(QWidget): self.outerBox.setContentsMargins(0, 0, 0, 0) self.setLayout(self.outerBox) - return - ## # Getters ## @@ -547,7 +526,6 @@ class _DetailsWidget(QWidget): def setColumnWidth(self, value: int) -> None: """Set the width of the first column.""" self.listView.setColumnWidth(0, value) - return def setExpandedState(self, state: list[bool]) -> None: """Set the expanded state of each top level item.""" @@ -556,7 +534,6 @@ class _DetailsWidget(QWidget): item = self.listView.topLevelItem(i) if isinstance(item, QTreeWidgetItem): item.setExpanded((state[i] if i < count else True) and item.childCount() > 0) - return ## # Methods @@ -637,8 +614,6 @@ class _DetailsWidget(QWidget): # Restore expanded state self.setExpandedState(expanded) - return - class _OutlineWidget(QWidget): @@ -663,8 +638,6 @@ class _OutlineWidget(QWidget): self.outerBox.setContentsMargins(0, 0, 0, 0) self.setLayout(self.outerBox) - return - def updateOutline(self, data: dict[str, str]) -> None: """Update the outline.""" if isinstance(data, dict) and data != self._outline: @@ -705,8 +678,6 @@ class _OutlineWidget(QWidget): self.listView.setIndentation(SHARED.theme.baseIconHeight if indent else 4) self._outline = data - return - ## # Private Slots ## @@ -714,7 +685,6 @@ class _OutlineWidget(QWidget): def _onItemClick(self, item: QTreeWidgetItem) -> None: """Process tree item click.""" self.outlineEntryClicked.emit(str(item.data(0, self.D_LINE))) - return class _PreviewWidget(QTextBrowser): @@ -787,8 +757,6 @@ class _PreviewWidget(QTextBrowser): self.ageTimer.timeout.connect(self._updateBuildAge) self.ageTimer.start() - return - ## # Setters ## @@ -797,7 +765,6 @@ class _PreviewWidget(QTextBrowser): """Set the build name for the document label.""" self._buildName = name self._updateBuildAge() - return def setTextFont(self, font: QFont) -> None: """Set the text font properties and then reset for sub-widgets. @@ -807,7 +774,6 @@ class _PreviewWidget(QTextBrowser): self.setFont(font) self.buildProgress.setFont(SHARED.theme.guiFont) self.ageLabel.setFont(SHARED.theme.guiFontSmall) - return ## # Methods @@ -823,13 +789,11 @@ class _PreviewWidget(QTextBrowser): self._scrollPos = vBar.value() self.setPlaceholderText("") self.clear() - return def buildStep(self, value: int) -> None: """Update the progress bar value.""" self.buildProgress.setValue(value) QApplication.processEvents() - return def setContent(self, document: QTextDocument) -> None: """Set the content of the preview widget.""" @@ -850,8 +814,6 @@ class _PreviewWidget(QTextBrowser): QApplication.processEvents() QTimer.singleShot(300, self._postUpdate) - return - ## # Events ## @@ -860,7 +822,6 @@ class _PreviewWidget(QTextBrowser): """Capture resize and update the document margins.""" super().resizeEvent(event) self._updateDocMargins() - return ## # Public Slots @@ -874,14 +835,12 @@ class _PreviewWidget(QTextBrowser): printer.setPageOrientation(QPageLayout.Orientation.Portrait) document.print(printer) QApplication.restoreOverrideCursor() - return @pyqtSlot(str) def navigateTo(self, anchor: str) -> None: """Go to a specific #link in the document.""" logger.debug("Moving to anchor '#%s'", anchor) self.setSource(QUrl(f"#{anchor}")) - return ## # Private Slots @@ -896,7 +855,6 @@ class _PreviewWidget(QTextBrowser): self.navigateTo(link.lstrip("#")) elif link.startswith("http"): QDesktopServices.openUrl(QUrl(url)) - return @pyqtSlot() def _updateBuildAge(self) -> None: @@ -909,7 +867,6 @@ class _PreviewWidget(QTextBrowser): )) else: self.ageLabel.setText("{0}".format(self.tr("No Preview"))) - return @pyqtSlot() def _postUpdate(self) -> None: @@ -917,7 +874,6 @@ class _PreviewWidget(QTextBrowser): self.buildProgress.setVisible(False) if vBar := self.verticalScrollBar(): vBar.setValue(self._scrollPos) - return ## # Internal Functions @@ -936,7 +892,6 @@ class _PreviewWidget(QTextBrowser): self.ageLabel.setGeometry(tB, tB, vW, tH) self.setViewportMargins(0, tH, 0, 0) self.buildProgress.move((vW-pS)//2, (vH-pS)//2) - return class _StatsWidget(QWidget): @@ -969,8 +924,6 @@ class _StatsWidget(QWidget): self._toggleView(False) - return - def updateStats(self, data: dict[str, int]) -> None: """Update the stats values from a Tokenizer stats dict.""" # Minimal @@ -992,8 +945,6 @@ class _StatsWidget(QWidget): self.maxHeadWordChars.setText(f"{data.get(nwStats.WCHARS_TITLE, 0):n}") self.maxTextWordChars.setText(f"{data.get(nwStats.WCHARS_TEXT, 0):n}") - return - ## # Private Slots ## @@ -1013,7 +964,6 @@ class _StatsWidget(QWidget): self.minWidget.adjustSize() self.mainStack.adjustSize() self.adjustSize() - return ## # Internal Functions @@ -1105,5 +1055,3 @@ class _StatsWidget(QWidget): self.minWidget.setLayout(self.minLayout) self.maxWidget.setLayout(self.maxLayout) - - return diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index 6fd4baa5..e8bf94b6 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -62,7 +62,7 @@ logger = logging.getLogger(__name__) class GuiBuildSettings(NToolDialog): - """GUI Tools: Manuscript Build Settings Dialog + """GUI Tools: Manuscript Build Settings Dialog. The main tool for configuring manuscript builds. It's a GUI tool for editing JSON build definitions, wrapped as a BuildSettings object. @@ -153,11 +153,8 @@ class GuiBuildSettings(NToolDialog): logger.debug("Ready: GuiBuildSettings") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiBuildSettings") - return def loadContent(self) -> None: """Populate the child widgets.""" @@ -165,7 +162,6 @@ class GuiBuildSettings(NToolDialog): self.optTabSelect.loadContent() self.optTabHeadings.loadContent() self.optTabFormatting.loadContent() - return ## # Properties @@ -190,7 +186,6 @@ class GuiBuildSettings(NToolDialog): self._saveSettings() event.accept() self.softDelete() - return ## # Private Slots @@ -206,7 +201,6 @@ class GuiBuildSettings(NToolDialog): elif pageId >= self.OPT_FORMATTING: self.toolStack.setCurrentWidget(self.optTabFormatting) self.optTabFormatting.scrollToSection(pageId) - return @pyqtSlot("QAbstractButton*") def _dialogButtonClicked(self, button: QAbstractButton) -> None: @@ -222,7 +216,6 @@ class GuiBuildSettings(NToolDialog): elif role == QtRoleReject: self._build.resetChangedState() self.close() - return ## # Internal Functions @@ -238,7 +231,6 @@ class GuiBuildSettings(NToolDialog): ).format(self._build.name)): self._emitBuildData() self._build.resetChangedState() - return def _saveSettings(self) -> None: """Save the various user settings.""" @@ -250,20 +242,17 @@ class GuiBuildSettings(NToolDialog): pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth) pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth) pOptions.saveSettings() - return def _applyChanges(self) -> None: """Apply all settings changes to the build object.""" self._build.setName(self.editBuildName.text()) self.optTabHeadings.saveContent() self.optTabFormatting.saveContent() - return def _emitBuildData(self) -> None: """Assemble the build data and emit the signal.""" self.newSettingsReady.emit(self._build) self._build.resetChangedState() - return class _FilterTab(NFixedPage): @@ -382,13 +371,10 @@ class _FilterTab(NFixedPage): self.setCentralWidget(self.mainSplit) - return - def loadContent(self) -> None: """Populate the widgets.""" self._populateTree() self._populateFilters() - return def mainSplitSizes(self) -> tuple[int, int]: """Extract the sizes of the main splitter.""" @@ -409,7 +395,6 @@ class _FilterTab(NFixedPage): elif key.startswith("root:"): self._build.setAllowRoot(key[5:], state) self._populateTree() - return ## # Internal Functions @@ -453,8 +438,6 @@ class _FilterTab(NFixedPage): self._setTreeItemMode() - return - def _populateFilters(self) -> None: """Populate the filter options switches.""" self.filterOpt.clear() @@ -489,8 +472,6 @@ class _FilterTab(NFixedPage): default=self._build.isRootAllowed(tHandle) ) - return - def _setSelectedMode(self, mode: int) -> None: """Set the mode for the selected items.""" items = self.optTree.selectedItems() @@ -511,8 +492,6 @@ class _FilterTab(NFixedPage): self._setTreeItemMode() - return - def _setTreeItemMode(self) -> None: """Update the filtered mode icon on all items.""" filtered = self._build.buildItemFilter(SHARED.project) @@ -529,11 +508,10 @@ class _FilterTab(NFixedPage): item.setToolTip(self.C_STATUS, self._trIncluded) else: item.setIcon(self.C_STATUS, self._statusFlags[self.F_NONE]) - return def _scanChildren(self, item: QTreeWidgetItem | None, items: list) -> list[QTreeWidgetItem]: - """This is a recursive function returning all items in a tree - starting at a given QTreeWidgetItem. + """Recursively return all items in a tree starting at a given + QTreeWidgetItem. """ if isinstance(item, QTreeWidgetItem): items.append(item) @@ -791,8 +769,6 @@ class _HeadingsTab(NScrollablePage): self.setCentralLayout(self.outerBox) - return - def loadContent(self) -> None: """Populate the widgets.""" def fmtBreak(text: str) -> str: @@ -821,7 +797,6 @@ class _HeadingsTab(NScrollablePage): self.breakPart.setChecked(self._build.getBool("headings.breakPart")) self.breakChapter.setChecked(self._build.getBool("headings.breakChapter")) self.breakScene.setChecked(self._build.getBool("headings.breakScene")) - return def saveContent(self) -> None: """Save choices back into build object.""" @@ -841,7 +816,6 @@ class _HeadingsTab(NScrollablePage): self._build.setValue("headings.breakPart", self.breakPart.isChecked()) self._build.setValue("headings.breakChapter", self.breakChapter.isChecked()) self._build.setValue("headings.breakScene", self.breakScene.isChecked()) - return ## # Internal Functions @@ -853,7 +827,6 @@ class _HeadingsTab(NScrollablePage): cursor = self.editTextBox.textCursor() cursor.insertText(text) self.editTextBox.setFocus() - return def _editHeading(self, heading: int) -> None: """Populate the form with a specific heading format.""" @@ -886,8 +859,6 @@ class _HeadingsTab(NScrollablePage): self.editTextBox.setPlainText(text.replace(nwUnicode.U_LBREAK, "\n")) self.lblEditForm.setText(self.tr("Editing: {0}").format(label)) - return - ## # Private Slots ## @@ -934,7 +905,6 @@ class _HeadingSyntaxHighlighter(QSyntaxHighlighter): self._fmtSymbol.setForeground(syntax.head) self._fmtFormat = QTextCharFormat() self._fmtFormat.setForeground(syntax.emph) - return def highlightBlock(self, text: str) -> None: """Add syntax highlighting to the text block.""" @@ -947,7 +917,6 @@ class _HeadingSyntaxHighlighter(QSyntaxHighlighter): ddots = heading.find(":") if ddots > 0: self.setFormat(pos + ddots, 1, self._fmtSymbol) - return class _FormattingTab(NScrollableForm): @@ -961,8 +930,6 @@ class _FormattingTab(NScrollableForm): self.setHelpTextStyle(SHARED.theme.helpText) self.buildForm() - return - def buildForm(self) -> None: """Build the formatting form.""" section = 10 @@ -1290,8 +1257,6 @@ class _FormattingTab(NScrollableForm): # Finalise self.finalise() - return - def loadContent(self) -> None: """Populate the widgets.""" # Text Content @@ -1391,8 +1356,6 @@ class _FormattingTab(NScrollableForm): self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles")) self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs")) - return - def saveContent(self) -> None: """Save choices back into build object.""" # Text Content @@ -1458,8 +1421,6 @@ class _FormattingTab(NScrollableForm): self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked()) self._build.setValue("html.preserveTabs", self.htmlPreserveTabs.isChecked()) - return - ## # Private Slots ## @@ -1472,11 +1433,10 @@ class _FormattingTab(NScrollableForm): self._textFont = fontMatcher(font) self.textFont.setText(describeFont(self._textFont)) self.textFont.setCursorPosition(0) - return @pyqtSlot(int) def _changeUnit(self, index: int) -> None: - """The current unit change, so recalculate sizes.""" + """Process current unit change to recalculate sizes.""" newUnit = self.pageUnit.itemData(index) newScale = nwLabels.UNIT_SCALE.get(newUnit, 1.0) reScale = self._unitScale/newScale @@ -1531,11 +1491,9 @@ class _FormattingTab(NScrollableForm): self._unitScale = newScale self._changePageSize(self.pageSize.currentIndex()) - return - @pyqtSlot(int) def _changePageSize(self, index: int) -> None: - """The page size has changed.""" + """Process page size change.""" w, h = nwLabels.PAPER_SIZE[self.pageSize.itemData(index)] if index >= 0 else (-1.0, -1.0) if w > 0.0 and h > 0.0: self.pageWidth.blockSignals(True) @@ -1544,23 +1502,20 @@ class _FormattingTab(NScrollableForm): self.pageHeight.blockSignals(True) self.pageHeight.setValue(h/self._unitScale) self.pageHeight.blockSignals(False) - return @pyqtSlot() def _pageSizeValueChanged(self) -> None: - """The user has changed the page size spin boxes, so we flip - the page size box to Custom. + """Process that the user has changed the page size spin boxes, + so we flip the page size box to Custom. """ index = self.pageSize.findData("Custom") if index >= 0: self.pageSize.setCurrentIndex(index) - return def _resetPageHeader(self) -> None: """Reset the ODT header format to default.""" self.odtPageHeader.setText(nwHeadFmt.DOC_AUTO) self.odtPageHeader.setCursorPosition(0) - return ## # Internal Functions @@ -1573,4 +1528,3 @@ class _FormattingTab(NScrollableForm): current.append(keyword) verified = set(x for x in current if x in nwKeyWords.VALID_KEYS) self.ignoredKeywords.setText(", ".join(verified)) - return diff --git a/novelwriter/tools/noveldetails.py b/novelwriter/tools/noveldetails.py index e264df62..7447b553 100644 --- a/novelwriter/tools/noveldetails.py +++ b/novelwriter/tools/noveldetails.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -52,6 +52,7 @@ logger = logging.getLogger(__name__) class GuiNovelDetails(NNonBlockingDialog): + """GUI: Novel Details Tool.""" PAGE_OVERVIEW = 1 PAGE_CONTENTS = 2 @@ -130,11 +131,8 @@ class GuiNovelDetails(NNonBlockingDialog): logger.debug("Ready: GuiNovelDetails") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiNovelDetails") - return ## # Methods @@ -146,7 +144,6 @@ class GuiNovelDetails(NNonBlockingDialog): self.overviewPage.updateProjectData() self.overviewPage.novelValueChanged(handle) self.contentsPage.novelValueChanged(handle) - return ## # Events @@ -157,7 +154,6 @@ class GuiNovelDetails(NNonBlockingDialog): self._saveSettings() event.accept() self.softDelete() - return ## # Private Slots @@ -170,7 +166,6 @@ class GuiNovelDetails(NNonBlockingDialog): self.mainStack.setCurrentWidget(self.overviewPage) elif pageId == self.PAGE_CONTENTS: self.mainStack.setCurrentWidget(self.contentsPage) - return ## # Internal Functions @@ -185,7 +180,6 @@ class GuiNovelDetails(NNonBlockingDialog): options.setValue("GuiNovelDetails", "winHeight", self.height()) options.setValue("GuiNovelDetails", "novelRoot", novelRoot) self.contentsPage.saveSettings() - return class _OverviewPage(NScrollablePage): @@ -248,8 +242,6 @@ class _OverviewPage(NScrollablePage): self.setCentralLayout(self.outerBox) - return - ## # Methods ## @@ -266,7 +258,6 @@ class _OverviewPage(NScrollablePage): self.projWords.setText(f"{wcNovel + wcNotes:n}") self.projNovels.setText(f"{wcNovel:n}") self.projNotes.setText(f"{wcNotes:n}") - return ## # Public Slots @@ -286,8 +277,6 @@ class _OverviewPage(NScrollablePage): self.novelChapters.setText(f"{hCounts[2]:n}") self.novelScenes.setText(f"{hCounts[3]:n}") - return - class _ContentsPage(NFixedPage): @@ -400,8 +389,6 @@ class _ContentsPage(NFixedPage): self.setCentralLayout(self.outerBox) - return - def saveSettings(self) -> None: """Save the user GUI settings.""" options = SHARED.project.options @@ -413,7 +400,6 @@ class _ContentsPage(NFixedPage): options.setValue("GuiNovelDetails", "wordsPerPage", self.wpValue.value()) options.setValue("GuiNovelDetails", "countFrom", self.poValue.value()) options.setValue("GuiNovelDetails", "clearDouble", self.dblValue.isChecked()) - return ## # Public Slots @@ -426,7 +412,6 @@ class _ContentsPage(NFixedPage): self._prepareData(tHandle) self._populateTree() self._currentRoot = tHandle - return ## # Private Slots @@ -496,8 +481,6 @@ class _ContentsPage(NFixedPage): self.tocTree.addTopLevelItem(newItem) - return - ## # Internal Functions ## @@ -507,4 +490,3 @@ class _ContentsPage(NFixedPage): logger.debug("Populating ToC from handle '%s'", rootHandle) self._data = SHARED.project.index.getTableOfContents(rootHandle, 2) self._data.append(("", 0, self.tr("END"), 0)) - return diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py index c462b9af..69d2e090 100644 --- a/novelwriter/tools/welcome.py +++ b/novelwriter/tools/welcome.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import logging @@ -56,6 +56,11 @@ PANEL_ALPHA = 178 class GuiWelcome(NDialog): + """GUI: Welcome Dialog. + + This is the main dialog shown when novelWriter launches or when the + user wants to create or open another project. + """ openProjectRequest = pyqtSignal(Path) @@ -161,11 +166,8 @@ class GuiWelcome(NDialog): logger.debug("Ready: GuiWelcome") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiWelcome") - return ## # Events @@ -180,14 +182,12 @@ class GuiWelcome(NDialog): painter.drawPixmap(0, hWin - hPix, self.bgImage.scaledToHeight(hPix, tMode)) painter.end() super().paintEvent(event) - return def closeEvent(self, event: QCloseEvent) -> None: """Capture the user closing the window and save settings.""" self._saveSettings() event.accept() self.softDelete() - return ## # Private Slots @@ -199,28 +199,24 @@ class GuiWelcome(NDialog): self.mainStack.setCurrentWidget(self.tabNew) self._setButtonVisibility() self.tabNew.enterForm() - return @pyqtSlot() def _showOpenProjectPage(self) -> None: """Show the open exiting project page.""" self.mainStack.setCurrentWidget(self.tabOpen) self._setButtonVisibility() - return @pyqtSlot() def _browseForProject(self) -> None: """Browse for a project to open.""" if path := SHARED.getProjectPath(self, path=CONFIG.homePath(), allowZip=False): self._openProjectPath(path) - return @pyqtSlot() def _openSelectedItem(self) -> None: """Open the currently selected project item.""" if self.mainStack.currentWidget() == self.tabOpen: self.tabOpen.openSelectedItem() - return @pyqtSlot(Path) def _openProjectPath(self, path: Path) -> None: @@ -231,7 +227,6 @@ class GuiWelcome(NDialog): self.hide() self.openProjectRequest.emit(path) self.close() - return ## # Internal Functions @@ -241,7 +236,6 @@ class GuiWelcome(NDialog): """Save the user GUI settings.""" logger.debug("Saving State: GuiWelcome") CONFIG.setWelcomeWinSize(self.width(), self.height()) - return def _setButtonVisibility(self) -> None: """Change the visibility of the dialog buttons.""" @@ -255,7 +249,6 @@ class GuiWelcome(NDialog): self.btnOpen.setFocus() else: self.btnCreate.setFocus() - return class _OpenProjectPage(QWidget): @@ -308,8 +301,6 @@ class _OpenProjectPage(QWidget): f"QLineEdit {{border: none; background: {baseCol}; padding: 4px;}} " ) - return - ## # Public Slots ## @@ -319,7 +310,6 @@ class _OpenProjectPage(QWidget): """Open the currently selected project item.""" if (selection := self.listWidget.selectedIndexes()) and (index := selection[0]).isValid(): self.openProjectRequest.emit(Path(str(index.data()[1]))) - return ## # Private Slots @@ -335,14 +325,12 @@ class _OpenProjectPage(QWidget): self.selectedPath.setToolTip(text) self.selectedPath.setCursorPosition(0) self.aMissing.setVisible(not (Path(value) / nwFiles.PROJ_FILE).is_file()) - return @pyqtSlot(QModelIndex) def _projectDoubleClicked(self, index: QModelIndex) -> None: """Process double click on project item.""" if index.isValid(): self.openProjectRequest.emit(Path(str(index.data()[1]))) - return @pyqtSlot() def _deleteSelectedItem(self) -> None: @@ -355,7 +343,6 @@ class _OpenProjectPage(QWidget): if SHARED.question(text): self.listModel.removeEntry(index) self._selectFirstItem() - return @pyqtSlot("QPoint") def _openContextMenu(self, pos: QPoint) -> None: @@ -368,7 +355,6 @@ class _OpenProjectPage(QWidget): action.triggered.connect(self._deleteSelectedItem) ctxMenu.exec(self.mapToGlobal(pos)) ctxMenu.setParent(None) - return ## # Internal Functions @@ -379,7 +365,6 @@ class _OpenProjectPage(QWidget): index = self.listModel.index(0) self.listWidget.setCurrentIndex(index) self._projectClicked(index) - return class _ProjectListItem(QStyledItemDelegate): @@ -407,8 +392,6 @@ class _ProjectListItem(QStyledItemDelegate): self._icon = SHARED.theme.getPixmap("proj_nwx", (iPx, iPx)) - return - def paint(self, painter: QPainter, opt: QStyleOptionViewItem, index: QModelIndex) -> None: """Paint a project entry on the canvas.""" rect = opt.rect @@ -430,8 +413,6 @@ class _ProjectListItem(QStyledItemDelegate): painter.drawText(rect.adjusted(x, y, 0, 0), tFlag, details) painter.restore() - return - def sizeHint(self, opt: QStyleOptionViewItem, index: QModelIndex) -> QSize: """Set the size hint to fixed height.""" return QSize(opt.rect.width(), self._hPx) @@ -449,7 +430,6 @@ class _ProjectListModel(QAbstractListModel): when = CONFIG.localDate(datetime.fromtimestamp(time)) data.append((title, path, f"{opened}: {when}, {words}: {formatInt(count)}")) self._data = data - return def rowCount(self, parent: QModelIndex | None = None) -> int: """Return the size of the model.""" @@ -516,8 +496,6 @@ class _NewProjectPage(QWidget): f"_NewProjectForm {{border: none; background: {baseCol};}} " ) - return - ## # Public Slots ## @@ -689,13 +667,10 @@ class _NewProjectForm(QWidget): self._updateProjPath() self._updateFillInfo() - return - def enterForm(self) -> None: """Focus the project name field when entering the form.""" self.projName.setFocus() self.projName.selectAll() - return def getProjectData(self) -> dict: """Collect form data and return it as a dictionary.""" @@ -733,14 +708,12 @@ class _NewProjectForm(QWidget): self._basePath = Path(path) self._updateProjPath() CONFIG.setLastPath("project", path) - return @pyqtSlot() def _updateProjPath(self) -> None: """Update the path box to show the full project path.""" projName = makeFileNameSafe(self.projName.text().strip()) self.projPath.setText(str(self._basePath / projName)) - return @pyqtSlot() def _syncSwitches(self) -> None: @@ -750,21 +723,18 @@ class _NewProjectForm(QWidget): addWorld = self.addWorld.isChecked() if not (addPlot or addChar or addWorld): self.addNotes.setChecked(False) - return @pyqtSlot() def _setFillBlank(self) -> None: """Set fill mode to blank project.""" self._fillMode = self.FILL_BLANK self._updateFillInfo() - return @pyqtSlot() def _setFillSample(self) -> None: """Set fill mode to sample project.""" self._fillMode = self.FILL_SAMPLE self._updateFillInfo() - return @pyqtSlot() def _setFillCopy(self) -> None: @@ -773,7 +743,6 @@ class _NewProjectForm(QWidget): self._fillMode = self.FILL_COPY self._copyPath = copyPath self._updateFillInfo() - return ## # Internal Functions @@ -793,5 +762,3 @@ class _NewProjectForm(QWidget): self.projFill.setToolTip(text) self.projFill.setCursorPosition(0) self.extraWidget.setVisible(self._fillMode == self.FILL_BLANK) - - return diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py index 0cbb9124..4494da8f 100644 --- a/novelwriter/tools/writingstats.py +++ b/novelwriter/tools/writingstats.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations import json @@ -54,7 +54,7 @@ logger = logging.getLogger(__name__) class GuiWritingStats(NToolDialog): - """GUI Tools: Writing Statistics + """GUI Tools: Writing Statistics. Displays data from the NWSessionLog object. """ @@ -311,11 +311,8 @@ class GuiWritingStats(NToolDialog): logger.debug("Ready: GuiWritingStats") - return - def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiWritingStats") - return def populateGUI(self) -> None: """Populate list box with data from the log file.""" @@ -323,7 +320,6 @@ class GuiWritingStats(NToolDialog): self._loadLogFile() self._updateListBox() QApplication.restoreOverrideCursor() - return ## # Events @@ -333,7 +329,6 @@ class GuiWritingStats(NToolDialog): """Capture the user closing the window.""" event.accept() self.softDelete() - return ## # Private Slots @@ -377,8 +372,6 @@ class GuiWritingStats(NToolDialog): self.close() - return - def _saveData(self, dataFmt: int) -> bool: """Save the content of the list box to a file.""" fileExt = "" @@ -498,8 +491,6 @@ class GuiWritingStats(NToolDialog): self.notesWords.setText(f"{ttNotes:n}") self.totalWords.setText(f"{ttWords:n}") - return - ## # Private Slots ## @@ -622,5 +613,3 @@ class GuiWritingStats(NToolDialog): self.timeFilter += sDiff self.labelFilter.setText(formatTime(round(self.timeFilter))) - - return diff --git a/novelwriter/types.py b/novelwriter/types.py index 05b7f7f3..ba803715 100644 --- a/novelwriter/types.py +++ b/novelwriter/types.py @@ -20,7 +20,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -""" +""" # noqa from __future__ import annotations from PyQt6.QtCore import Qt diff --git a/pyproject.toml b/pyproject.toml index 956d2724..920d7d6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,19 +76,24 @@ select = [ "PERF", # Perflint (PERF) "PLC", # Pylint Convention (PLC) "PLE", # Pylint Error (PLE) + "PLR17", # Refactor (PLR) - Only PLR17xx "PLW", # Pylint Warning (PLW) "Q", # flake8-quotes (Q) + "RET", # flake8-return (RET) "RUF", # Ruff-specific rules (RUF) "SLF", # flake8-self (SLF) "SLOT", # flake8-slots (SLOT) "TC", # flake8-type-checking (TC) "UP", # pyupgrade (UP) "W", # pycodestyle (W) - "RET", # flake8-return (RET) - "PLR17", # Refactor (PLR) - Only PLR17xx ] ignore = [ "ANN401", # any-type + "D105", # undocumented-magic-method + "D107", # undocumented-public-init + "D203", # incorrect-blank-line-before-class + "D205", # missing-blank-line-after-summary + "D213", # multi-line-summary-second-line "E221", # multiple-spaces-before-operator "E226", # missing-whitespace-around-arithmetic-operator "E228", # missing-whitespace-around-modulo-operator @@ -98,15 +103,12 @@ ignore = [ "PLC1901", # compare-to-empty-string "PLW0108", # unnecessary-lambda "PLW2901", # redefined-loop-name + "RET505", # superfluous-else-return "RUF001", # ambiguous-unicode-character-string "RUF002", # ambiguous-unicode-character-docstring "RUF015", # unnecessary-iterable-allocation-for-first-element "UP015", # redundant-open-modes "UP030", # format-literals - "D107", # undocumented-public-init - "D205", # missing-blank-line-after-summary - "D105", # undocumented-magic-method - "RET505", # superfluous-else-return ] [tool.ruff.lint.per-file-ignores]