Make new code pass docstring linting

This commit is contained in:
Veronica Berglyd Olsen
2023-06-03 15:46:51 +02:00
parent 7ff5ffaebd
commit 3a2733eee5
6 changed files with 169 additions and 178 deletions
+40 -53
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Build Settings Class novelWriter Build Settings
================================== ============================
A class to hold build settings for the build tool
File History: File History:
Created: 2023-02-14 [2.1b1] BuildSettings Created: 2023-02-14 [2.1b1] BuildSettings
@@ -117,6 +116,7 @@ SETTINGS_LABELS = {
class FilterMode(Enum): class FilterMode(Enum):
"""The decision reason for an item in a filtered project."""
UNKNOWN = 0 UNKNOWN = 0
FILTERED = 1 FILTERED = 1
@@ -128,6 +128,11 @@ class FilterMode(Enum):
class BuildSettings: class BuildSettings:
"""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.
"""
def __init__(self): def __init__(self):
self._name = "" self._name = ""
@@ -145,14 +150,17 @@ class BuildSettings:
@property @property
def name(self) -> str: def name(self) -> str:
"""Return the build name."""
return self._name return self._name
@property @property
def buildID(self) -> str: def buildID(self) -> str:
"""Return the build ID."""
return self._uuid return self._uuid
@property @property
def changed(self) -> bool: def changed(self) -> bool:
"""Return the changed status of the build."""
return self._changed return self._changed
## ##
@@ -161,33 +169,28 @@ class BuildSettings:
@staticmethod @staticmethod
def getLabel(key: str) -> str: def getLabel(key: str) -> str:
"""Extract the label for a specific item. """Extract the GUI label for a specific setting."""
"""
return SETTINGS_LABELS.get(key, "ERROR") return SETTINGS_LABELS.get(key, "ERROR")
def getStr(self, key: str) -> str: def getStr(self, key: str) -> str:
"""Type safe value access for strings. """Type safe value access for strings."""
"""
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1])) value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
return str(value) return str(value)
def getBool(self, key: str) -> bool: def getBool(self, key: str) -> bool:
"""Type safe value access for bools. """Type safe value access for bools."""
"""
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1])) value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
return bool(value) return bool(value)
def getInt(self, key: str) -> int: def getInt(self, key: str) -> int:
"""Type safe value access for integers. """Type safe value access for integers."""
"""
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1])) value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
if isinstance(value, int): if isinstance(value, int):
return value return value
return 0 return 0
def getFloat(self, key: str) -> float: def getFloat(self, key: str) -> float:
"""Type safe value access for float. """Type safe value access for float."""
"""
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1])) value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
if isinstance(value, float): if isinstance(value, float):
return value return value
@@ -198,14 +201,12 @@ class BuildSettings:
## ##
def setName(self, name: str): def setName(self, name: str):
"""Set the build setting display name. """Set the build setting display name."""
"""
self._name = str(name) self._name = str(name)
return return
def setBuildID(self, value: str | uuid.UUID): def setBuildID(self, value: str | uuid.UUID):
"""Set a UUID build ID. """Set a UUID build ID."""
"""
value = checkUuid(value, "") value = checkUuid(value, "")
if not value: if not value:
self._uuid = str(uuid.uuid4()) self._uuid = str(uuid.uuid4())
@@ -214,32 +215,28 @@ class BuildSettings:
return return
def setFiltered(self, tHandle: str): def setFiltered(self, tHandle: str):
"""Set an item as filtered. """Set an item as filtered."""
"""
self._excluded.discard(tHandle) self._excluded.discard(tHandle)
self._included.discard(tHandle) self._included.discard(tHandle)
self._changed = True self._changed = True
return return
def setIncluded(self, tHandle: str): def setIncluded(self, tHandle: str):
"""Set an item as explicitly included. """Set an item as explicitly included."""
"""
self._excluded.discard(tHandle) self._excluded.discard(tHandle)
self._included.add(tHandle) self._included.add(tHandle)
self._changed = True self._changed = True
return return
def setExcluded(self, tHandle: str): def setExcluded(self, tHandle: str):
"""Set an item as explicitly excluded. """Set an item as explicitly excluded."""
"""
self._excluded.add(tHandle) self._excluded.add(tHandle)
self._included.discard(tHandle) self._included.discard(tHandle)
self._changed = True self._changed = True
return return
def setSkipRoot(self, tHandle: str, state: bool): def setSkipRoot(self, tHandle: str, state: bool):
"""Set a specific root folder as skipped or not. """Set a specific root folder as skipped or not."""
"""
if state is True: if state is True:
self._skipRoot.discard(tHandle) self._skipRoot.discard(tHandle)
self._changed = True self._changed = True
@@ -249,8 +246,7 @@ class BuildSettings:
return return
def setValue(self, key: str, value: str | int | bool | float) -> bool: def setValue(self, key: str, value: str | int | bool | float) -> bool:
"""Set a specific value for a build setting. """Set a specific value for a build setting."""
"""
if key not in SETTINGS_TEMPLATE: if key not in SETTINGS_TEMPLATE:
return False return False
definition = SETTINGS_TEMPLATE[key] definition = SETTINGS_TEMPLATE[key]
@@ -267,19 +263,11 @@ class BuildSettings:
# Methods # Methods
## ##
def isFiltered(self, tHandle: str) -> bool:
return tHandle not in self._included and tHandle not in self._excluded
def isIncluded(self, tHandle: str) -> bool:
return tHandle in self._included
def isExcluded(self, tHandle: str) -> bool:
return tHandle in self._excluded
def isRootAllowed(self, tHandle: str) -> bool: def isRootAllowed(self, tHandle: str) -> bool:
"""Check if a root handle is allowed in the build."""
return tHandle not in self._skipRoot return tHandle not in self._skipRoot
def buildItemFilter(self, project: NWProject) -> dict: def buildItemFilter(self, project: NWProject) -> dict[str, tuple[bool, FilterMode]]:
"""Return a dictionary of item handles with filter decissions """Return a dictionary of item handles with filter decissions
applied. applied.
""" """
@@ -325,15 +313,14 @@ class BuildSettings:
return result return result
def resetChangedState(self): def resetChangedState(self):
"""This must be called when the changes to this class has been """Reset the changed status of the settings object. This must be
safely saved to file or passed on. called when the changes have been safely saved or passed on.
""" """
self._changed = False self._changed = False
return return
def pack(self) -> dict: def pack(self) -> dict:
"""Pack all content into a JSON compatible dictionary. """Pack all content into a JSON compatible dictionary."""
"""
logger.debug("Collecting build setting for '%s'", self._name) logger.debug("Collecting build setting for '%s'", self._name)
return { return {
"name": self._name, "name": self._name,
@@ -347,8 +334,7 @@ class BuildSettings:
} }
def unpack(self, data: dict): def unpack(self, data: dict):
"""Unpack a dictionary and populate the class. """Unpack a dictionary and populate the class."""
"""
settings = data.get("settings", {}) settings = data.get("settings", {})
content = data.get("content", {}) content = data.get("content", {})
included = content.get("included", []) included = content.get("included", [])
@@ -377,6 +363,12 @@ class BuildSettings:
class BuildCollection: class BuildCollection:
"""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
project folder.
"""
def __init__(self, project: NWProject): def __init__(self, project: NWProject):
self._project = project self._project = project
@@ -384,8 +376,7 @@ class BuildCollection:
return return
def getBuild(self, buildID: str) -> BuildSettings | None: def getBuild(self, buildID: str) -> BuildSettings | None:
"""Get a specific build settings object. """Get a specific build settings object."""
"""
if buildID not in self._builds: if buildID not in self._builds:
return None return None
build = BuildSettings() build = BuildSettings()
@@ -393,8 +384,7 @@ class BuildCollection:
return build return build
def setBuild(self, build: BuildSettings) -> bool: def setBuild(self, build: BuildSettings) -> bool:
"""Set build settings data in the collection. """Set build settings data in the collection."""
"""
if not isinstance(build, BuildSettings): if not isinstance(build, BuildSettings):
return False return False
buildID = build.buildID buildID = build.buildID
@@ -402,15 +392,13 @@ class BuildCollection:
return True return True
def builds(self) -> Iterable[tuple[str, str]]: def builds(self) -> Iterable[tuple[str, str]]:
"""Iterate over all avaiable builds. """Iterate over all avaiable builds."""
"""
for buildID in self._builds: for buildID in self._builds:
yield buildID, self._builds[buildID].get("name", "") yield buildID, self._builds[buildID].get("name", "")
return return
def loadCollection(self) -> bool: def loadCollection(self) -> bool:
"""Load build collections file. """Load build collections file."""
"""
buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE) buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE)
if not isinstance(buildsFile, Path): if not isinstance(buildsFile, Path):
return False return False
@@ -442,8 +430,7 @@ class BuildCollection:
return True return True
def saveCollection(self) -> bool: def saveCollection(self) -> bool:
"""Save build collections file. """Save build collections file."""
"""
buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE) buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE)
if not isinstance(buildsFile, Path): if not isinstance(buildsFile, Path):
return False return False
+22 -16
View File
@@ -1,10 +1,9 @@
""" """
novelWriter Build Document Tool novelWriter Manuscript Document Builder
================================= =========================================
A class to build one or more novelWriter files to a single document
File History: File History:
Created: 2022-12-01 [2.1b1] Created: 2022-12-01 [2.1b1] NWBuildDocument
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -44,6 +43,11 @@ logger = logging.getLogger(__name__)
class NWBuildDocument: class NWBuildDocument:
"""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).
"""
__slots__ = ("_project", "_build", "_queue", "_error", "_cache") __slots__ = ("_project", "_build", "_queue", "_error", "_cache")
@@ -61,10 +65,15 @@ class NWBuildDocument:
@property @property
def error(self) -> str | None: def error(self) -> str | None:
"""Return the last error, if any."""
return self._error return self._error
@property @property
def lastBuild(self) -> Tokenizer | None: def lastBuild(self) -> Tokenizer | None:
"""Return the build object of the last build process, if any.
This is useful for accessing build details and data after the
build job is completed.
"""
return self._cache return self._cache
## ##
@@ -72,6 +81,7 @@ class NWBuildDocument:
## ##
def __len__(self) -> int: def __len__(self) -> int:
"""Return the length of the build queue."""
return len(self._queue) return len(self._queue)
## ##
@@ -79,17 +89,17 @@ class NWBuildDocument:
## ##
def addDocument(self, tHandle: str): def addDocument(self, tHandle: str):
"""Add a document to the build queue manually. """Add a document to the build queue manually."""
"""
self._queue.append(tHandle) self._queue.append(tHandle)
return return
def queueAll(self): def queueAll(self):
"""Queue all document as defined by the build setup. """Queue all document as defined by the build settings."""
"""
filtered = self._build.buildItemFilter(self._project) filtered = self._build.buildItemFilter(self._project)
noteTitles = self._build.getBool("text.addNoteHeadings") noteTitles = self._build.getBool("text.addNoteHeadings")
for item in self._project.tree: for item in self._project.tree:
if not item.itemHandle:
continue
if filtered.get(item.itemHandle, False): if filtered.get(item.itemHandle, False):
self._queue.append(item.itemHandle) self._queue.append(item.itemHandle)
elif item.isRootType() and noteTitles: elif item.isRootType() and noteTitles:
@@ -97,8 +107,7 @@ class NWBuildDocument:
return return
def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]: def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]:
"""Build an Open Document file. """Build an Open Document file."""
"""
makeOdt = ToOdt(self._project, isFlat=isFlat) makeOdt = ToOdt(self._project, isFlat=isFlat)
self._setupBuild(makeOdt) self._setupBuild(makeOdt)
makeOdt.initDocument() makeOdt.initDocument()
@@ -146,8 +155,7 @@ class NWBuildDocument:
return return
def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, bool]]: def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, bool]]:
"""Build a Markdown file. """Build a Markdown file."""
"""
makeMd = ToMarkdown(self._project) makeMd = ToMarkdown(self._project)
self._setupBuild(makeMd) self._setupBuild(makeMd)
@@ -177,8 +185,7 @@ class NWBuildDocument:
## ##
def _setupBuild(self, bldObj: Tokenizer): def _setupBuild(self, bldObj: Tokenizer):
"""Configure the build object. """Configure the build object."""
"""
# Get Settings # Get Settings
fmtTitle = self._build.getStr("headings.fmtTitle") fmtTitle = self._build.getStr("headings.fmtTitle")
fmtChapter = self._build.getStr("headings.fmtChapter") fmtChapter = self._build.getStr("headings.fmtChapter")
@@ -240,8 +247,7 @@ class NWBuildDocument:
return return
def _doBuild(self, bldObj: Tokenizer, tHandle: str) -> bool: def _doBuild(self, bldObj: Tokenizer, tHandle: str) -> bool:
"""Build a single document and add it to the build object. """Build a single document and add it to the build object."""
"""
self._error = None self._error = None
tItem = self._project.tree[tHandle] tItem = self._project.tree[tHandle]
if tItem is None: if tItem is None:
+19 -2
View File
@@ -1,10 +1,9 @@
""" """
novelWriter GUI Main Window novelWriter GUI Main Window
============================= =============================
The main application window
File History: File History:
Created: 2018-09-22 [0.0.1] Created: 2018-09-22 [0.0.1] GuiMain
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -74,6 +73,24 @@ logger = logging.getLogger(__name__)
class GuiMain(QMainWindow): class GuiMain(QMainWindow):
"""Main GUI Window
The Main GUI window class. It is the entry point of the
application, and holds all runtime objects aside from the main
Config instance, which is created before the Main GUI.
The Main GUI is split up into GUI components, assembled in the init
function. Also, the project instance and theme instance are created
here. These should be passed around to all other objects who need
them and new instances of them should generally not be created.
* All other GUI classes that depend on any components from the
main GUI should be passed a reference to the instance of this
class.
* All non-GUI classes can be passed a reference to the NWProject
instance if the Main GUI is not needed (which it generally
shouldn't need).
"""
def __init__(self): def __init__(self):
super().__init__() super().__init__()
+7 -2
View File
@@ -1,10 +1,9 @@
""" """
novelWriter GUI Build Manuscript novelWriter GUI Build Manuscript
================================== ==================================
GUI classes for the Manuscript Build Tool
File History: File History:
Created: 2023-05-24 [2.1b1] Created: 2023-05-24 [2.1b1] GuiManuscriptBuild
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -32,6 +31,11 @@ logger = logging.getLogger(__name__)
class GuiManuscriptBuild(QDialog): class GuiManuscriptBuild(QDialog):
"""GUI Tools: Manucript Builder Dialog
This is the tool for running the build itself. It can be accessed
independently of the Manuscript Build Tool.
"""
def __init__(self, parent: QWidget): def __init__(self, parent: QWidget):
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -46,6 +50,7 @@ class GuiManuscriptBuild(QDialog):
return return
def __del__(self): def __del__(self):
"""For debug use only."""
logger.debug("Delete: GuiManuscriptBuild") logger.debug("Delete: GuiManuscriptBuild")
return return
+27 -32
View File
@@ -1,11 +1,9 @@
""" """
novelWriter GUI Build Manuscript novelWriter GUI Manuscript Tool
================================== =================================
GUI classes for the Manuscript Build Tool
File History: File History:
Created: 2023-05-13 [2.1b1] GuiManuscript Created: 2023-05-13 [2.1b1] GuiManuscript
Created: 2023-05-13 [2.1b1] GuiManuscriptPreview
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -52,6 +50,12 @@ logger = logging.getLogger(__name__)
class GuiManuscript(QDialog): class GuiManuscript(QDialog):
"""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
a document directly to disk.
"""
def __init__(self, mainGui: GuiMain): def __init__(self, mainGui: GuiMain):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -98,7 +102,7 @@ class GuiManuscript(QDialog):
self.buildProgress = QProgressBar() self.buildProgress = QProgressBar()
self.btnPreview = QPushButton(self.tr("Build Preview")) self.btnPreview = QPushButton(self.tr("Build Preview"))
self.btnPreview.clicked.connect(self._generatePreview) self.btnPreview.clicked.connect(self._generatePreview)
self.manPreview = GuiManuscriptPreview(self.mainGui) self.manPreview = _PreviewWidget(self.mainGui)
self.menuPrint = QMenu(self) self.menuPrint = QMenu(self)
self.aPrintSend = self.menuPrint.addAction(self.tr("Print Preview")) self.aPrintSend = self.menuPrint.addAction(self.tr("Print Preview"))
@@ -166,12 +170,12 @@ class GuiManuscript(QDialog):
return return
def __del__(self): def __del__(self):
"""For debug use only."""
logger.debug("Delete: GuiManuscript") logger.debug("Delete: GuiManuscript")
return return
def loadContent(self): def loadContent(self):
"""Load dialog content from project data. """Load dialog content from project data."""
"""
self._builds.loadCollection() self._builds.loadCollection()
self._updateBuildsList() self._updateBuildsList()
return return
@@ -181,7 +185,9 @@ class GuiManuscript(QDialog):
## ##
def closeEvent(self, event): def closeEvent(self, event):
"""Capture the user closing the window so we can save settings. """Capture the user closing the window so we can save GUI
settings. We also check that we don't have a build settings
diralog open.
""" """
self._saveSettings() self._saveSettings()
for obj in self.children(): for obj in self.children():
@@ -198,8 +204,7 @@ class GuiManuscript(QDialog):
@pyqtSlot() @pyqtSlot()
def _createNewBuild(self): def _createNewBuild(self):
"""Open the build settings dialog for a new build. """Open the build settings dialog for a new build."""
"""
build = BuildSettings() build = BuildSettings()
build.setName(self.tr("My Manuscript")) build.setName(self.tr("My Manuscript"))
self._openSettingsDialog(build) self._openSettingsDialog(build)
@@ -207,8 +212,7 @@ class GuiManuscript(QDialog):
@pyqtSlot() @pyqtSlot()
def _editSelectedBuild(self): def _editSelectedBuild(self):
"""Edit the currently selected build settings entry. """Edit the currently selected build settings entry."""
"""
build = self._getSelectedBuild() build = self._getSelectedBuild()
if build is not None: if build is not None:
self._openSettingsDialog(build) self._openSettingsDialog(build)
@@ -216,8 +220,7 @@ class GuiManuscript(QDialog):
@pyqtSlot(BuildSettings) @pyqtSlot(BuildSettings)
def _processNewSettings(self, build: BuildSettings): def _processNewSettings(self, build: BuildSettings):
"""Process new build settings from the settings dialog. """Process new build settings from the settings dialog."""
"""
self._builds.setBuild(build) self._builds.setBuild(build)
self._builds.saveCollection() self._builds.saveCollection()
self._updateBuildItem(build) self._updateBuildItem(build)
@@ -263,8 +266,7 @@ class GuiManuscript(QDialog):
@pyqtSlot() @pyqtSlot()
def _doClose(self): def _doClose(self):
"""The close button has been clicked. """Forward the close button to the default close method."""
"""
self.close() self.close()
return return
@@ -273,8 +275,7 @@ class GuiManuscript(QDialog):
## ##
def _getSelectedBuild(self) -> BuildSettings | None: def _getSelectedBuild(self) -> BuildSettings | None:
"""Get the currently selected build. """Get the currently selected build."""
"""
bItems = self.buildList.selectedItems() bItems = self.buildList.selectedItems()
if bItems: if bItems:
build = self._builds.getBuild(bItems[0].data(Qt.UserRole)) build = self._builds.getBuild(bItems[0].data(Qt.UserRole))
@@ -283,13 +284,11 @@ class GuiManuscript(QDialog):
return None return None
def _saveManuscript(self, outFormat: int): def _saveManuscript(self, outFormat: int):
"""Save the manuscript file or files. """Save the manuscript file or files."""
"""
return return
def _saveSettings(self): def _saveSettings(self):
"""Save the various user settings. """Save the user GUI settings."""
"""
logger.debug("Saving GuiManuscript settings") logger.debug("Saving GuiManuscript settings")
winWidth = CONFIG.rpxInt(self.width()) winWidth = CONFIG.rpxInt(self.width())
@@ -309,8 +308,7 @@ class GuiManuscript(QDialog):
return return
def _openSettingsDialog(self, build: BuildSettings): def _openSettingsDialog(self, build: BuildSettings):
"""Open the build settings dialog. """Open the build settings dialog."""
"""
dlgSettings = GuiBuildSettings(self, self.mainGui, build) dlgSettings = GuiBuildSettings(self, self.mainGui, build)
dlgSettings.setModal(False) dlgSettings.setModal(False)
dlgSettings.show() dlgSettings.show()
@@ -321,8 +319,7 @@ class GuiManuscript(QDialog):
return return
def _updateBuildsList(self): def _updateBuildsList(self):
"""Update the list of available builds. """Update the list of available builds."""
"""
self.buildList.clear() self.buildList.clear()
for key, name in self._builds.builds(): for key, name in self._builds.builds():
bItem = QListWidgetItem() bItem = QListWidgetItem()
@@ -333,8 +330,7 @@ class GuiManuscript(QDialog):
return return
def _updateBuildItem(self, build: BuildSettings): def _updateBuildItem(self, build: BuildSettings):
"""Update the entry of a specific build item. """Update the entry of a specific build item."""
"""
bItem = self._buildMap.get(build.buildID, None) bItem = self._buildMap.get(build.buildID, None)
if isinstance(bItem, QListWidgetItem): if isinstance(bItem, QListWidgetItem):
bItem.setText(build.name) bItem.setText(build.name)
@@ -345,7 +341,7 @@ class GuiManuscript(QDialog):
# END Class GuiManuscript # END Class GuiManuscript
class GuiManuscriptPreview(QTextBrowser): class _PreviewWidget(QTextBrowser):
def __init__(self, mainGui: GuiMain): def __init__(self, mainGui: GuiMain):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -357,8 +353,7 @@ class GuiManuscriptPreview(QTextBrowser):
return return
def setContent(self, data: dict): def setContent(self, data: dict):
""" """Set the content of the preview widget."""
"""
sPos = self.verticalScrollBar().value() sPos = self.verticalScrollBar().value()
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -377,4 +372,4 @@ class GuiManuscriptPreview(QTextBrowser):
return return
# END Class GuiManuscriptPreview # END Class _PreviewWidget
+54 -73
View File
@@ -1,10 +1,9 @@
""" """
novelWriter GUI Build Settings novelWriter GUI Build Settings
================================ ================================
GUI classes for editing Manuscript Build Settings
File History: File History:
Created: 2023-02-13 [2.1b1] Created: 2023-02-13 [2.1b1] GuiBuildSettings
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -34,8 +33,8 @@ from PyQt5.QtGui import (
from PyQt5.QtCore import QEvent, QSize, Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import QEvent, QSize, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractButton, QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QAbstractButton, QAbstractItemView, QComboBox, QDialog, QDialogButtonBox,
QDoubleSpinBox, QFontDialog, QFrame, QGridLayout, QHBoxLayout, QHeaderView, QLabel, QDoubleSpinBox, QFontDialog, QFrame, QGridLayout, QHBoxLayout, QHeaderView,
QLineEdit, QMenu, QPlainTextEdit, QPushButton, QSpinBox, QSplitter, QLabel, QLineEdit, QMenu, QPlainTextEdit, QPushButton, QSpinBox, QSplitter,
QStackedWidget, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QStackedWidget, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QWidget QWidget
) )
@@ -56,6 +55,11 @@ logger = logging.getLogger(__name__)
class GuiBuildSettings(QDialog): class GuiBuildSettings(QDialog):
"""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.
"""
OPT_FILTERS = 1 OPT_FILTERS = 1
OPT_HEADINGS = 2 OPT_HEADINGS = 2
@@ -112,11 +116,11 @@ class GuiBuildSettings(QDialog):
# ============ # ============
# Create Tabs # Create Tabs
self.optTabSelect = GuiBuildFilterTab(self, self._build) self.optTabSelect = _FilterTab(self, self._build)
self.optTabHeadings = GuiBuildHeadingsTab(self, self._build) self.optTabHeadings = _HeadingsTab(self, self._build)
self.optTabFormat = GuiBuildFormatTab(self, self._build) self.optTabFormat = _FormatTab(self, self._build)
self.optTabContent = GuiBuildContentTab(self, self._build) self.optTabContent = _ContentTab(self, self._build)
self.optTabOutput = GuiBuildOutputTab(self, self._build) self.optTabOutput = _OutputTab(self, self._build)
# Add Tabs # Add Tabs
self.toolStack = QStackedWidget(self) self.toolStack = QStackedWidget(self)
@@ -164,11 +168,11 @@ class GuiBuildSettings(QDialog):
return return
def __del__(self): def __del__(self):
"""For debug use only."""
logger.debug("Delete: GuiBuildSettings") logger.debug("Delete: GuiBuildSettings")
def loadContent(self): def loadContent(self):
"""Populate the child widgets. """Populate the child widgets."""
"""
self.editBuildName.setText(self._build.name) self.editBuildName.setText(self._build.name)
self.optTabSelect.loadContent() self.optTabSelect.loadContent()
self.optTabHeadings.loadContent() self.optTabHeadings.loadContent()
@@ -183,8 +187,7 @@ class GuiBuildSettings(QDialog):
@pyqtSlot(int) @pyqtSlot(int)
def _stackPageSelected(self, pageId: int): def _stackPageSelected(self, pageId: int):
"""Process a user request to switch page. """Process a user request to switch page."""
"""
if pageId == self.OPT_FILTERS: if pageId == self.OPT_FILTERS:
self.toolStack.setCurrentWidget(self.optTabSelect) self.toolStack.setCurrentWidget(self.optTabSelect)
elif pageId == self.OPT_HEADINGS: elif pageId == self.OPT_HEADINGS:
@@ -199,8 +202,7 @@ class GuiBuildSettings(QDialog):
@pyqtSlot("QAbstractButton*") @pyqtSlot("QAbstractButton*")
def _dialogButtonClicked(self, button: QAbstractButton): def _dialogButtonClicked(self, button: QAbstractButton):
"""Handle button clicks from the dialog button box. """Handle button clicks from the dialog button box."""
"""
role = self.dlgButtons.buttonRole(button) role = self.dlgButtons.buttonRole(button)
if role == QDialogButtonBox.ApplyRole: if role == QDialogButtonBox.ApplyRole:
self._emitBuildData() self._emitBuildData()
@@ -216,7 +218,8 @@ class GuiBuildSettings(QDialog):
## ##
def closeEvent(self, event: QEvent): def closeEvent(self, event: QEvent):
"""Capture the user closing the window so we can save settings. """Capture the user closing the window so we can save
settings.
""" """
logger.debug("Closing: GuiBuildSettings") logger.debug("Closing: GuiBuildSettings")
self._askToSaveBuild() self._askToSaveBuild()
@@ -244,8 +247,7 @@ class GuiBuildSettings(QDialog):
return return
def _saveSettings(self): def _saveSettings(self):
"""Save the various user settings. """Save the various user settings."""
"""
logger.debug("Saving GuiBuildSettings settings") logger.debug("Saving GuiBuildSettings settings")
winWidth = CONFIG.rpxInt(self.width()) winWidth = CONFIG.rpxInt(self.width())
@@ -263,8 +265,7 @@ class GuiBuildSettings(QDialog):
return return
def _emitBuildData(self): def _emitBuildData(self):
"""Assemble the build data and emit the signal. """Assemble the build data and emit the signal."""
"""
self._build.setName(self.editBuildName.text()) self._build.setName(self.editBuildName.text())
self.optTabHeadings.saveContent() self.optTabHeadings.saveContent()
self.optTabContent.saveContent() self.optTabContent.saveContent()
@@ -277,7 +278,7 @@ class GuiBuildSettings(QDialog):
# END Class GuiBuildSettings # END Class GuiBuildSettings
class GuiBuildFilterTab(QWidget): class _FilterTab(QWidget):
C_DATA = 0 C_DATA = 0
C_NAME = 0 C_NAME = 0
@@ -398,15 +399,13 @@ class GuiBuildFilterTab(QWidget):
return return
def loadContent(self): def loadContent(self):
"""Populate the widgets. """Populate the widgets."""
"""
self._populateTree() self._populateTree()
self._populateFilters() self._populateFilters()
return return
def mainSplitSizes(self) -> tuple[int, int]: def mainSplitSizes(self) -> tuple[int, int]:
"""Extract the sizes of the main splitter. """Extract the sizes of the main splitter."""
"""
sizes = self.mainSplit.sizes() sizes = self.mainSplit.sizes()
if len(sizes) < 2: if len(sizes) < 2:
return 0, 0 return 0, 0
@@ -418,8 +417,7 @@ class GuiBuildFilterTab(QWidget):
@pyqtSlot(str, bool) @pyqtSlot(str, bool)
def _applyFilterSwitch(self, key: str, state: bool): def _applyFilterSwitch(self, key: str, state: bool):
"""A filter switch has been toggled, so update the settings. """Apply filter switch and update the settings."""
"""
if key.startswith("doc:"): if key.startswith("doc:"):
self._build.setValue(key[4:], state) self._build.setValue(key[4:], state)
self._setTreeItemMode() self._setTreeItemMode()
@@ -433,8 +431,7 @@ class GuiBuildFilterTab(QWidget):
## ##
def _populateTree(self): def _populateTree(self):
"""Build the tree of project items. """Build the tree of project items."""
"""
logger.debug("Building project tree") logger.debug("Building project tree")
self._treeMap = {} self._treeMap = {}
self.optTree.clear() self.optTree.clear()
@@ -490,8 +487,7 @@ class GuiBuildFilterTab(QWidget):
return return
def _populateFilters(self): def _populateFilters(self):
"""Populate the filter options switches. """Populate the filter options switches."""
"""
self.filterOpt.clear() self.filterOpt.clear()
self.filterOpt.addLabel(self._build.getLabel("filter")) self.filterOpt.addLabel(self._build.getLabel("filter"))
self.filterOpt.addItem( self.filterOpt.addItem(
@@ -530,8 +526,7 @@ class GuiBuildFilterTab(QWidget):
return return
def _setSelectedMode(self, mode: int): def _setSelectedMode(self, mode: int):
"""Set the mode for the selected items. """Set the mode for the selected items."""
"""
for item in self.optTree.selectedItems(): for item in self.optTree.selectedItems():
if not isinstance(item, QTreeWidgetItem): if not isinstance(item, QTreeWidgetItem):
continue continue
@@ -551,8 +546,7 @@ class GuiBuildFilterTab(QWidget):
return return
def _setTreeItemMode(self): def _setTreeItemMode(self):
"""Update the filtered mode icon on all items. """Update the filtered mode icon on all items."""
"""
filtered = self._build.buildItemFilter(self.theProject) filtered = self._build.buildItemFilter(self.theProject)
for tHandle, item in self._treeMap.items(): for tHandle, item in self._treeMap.items():
allow, mode = filtered.get(tHandle, (False, FilterMode.UNKNOWN)) allow, mode = filtered.get(tHandle, (False, FilterMode.UNKNOWN))
@@ -566,10 +560,10 @@ class GuiBuildFilterTab(QWidget):
item.setIcon(self.C_STATUS, self._statusFlags[self.F_NONE][1]) item.setIcon(self.C_STATUS, self._statusFlags[self.F_NONE][1])
return return
# END Class GuiBuildFilterTab # END Class _FilterTab
class GuiBuildHeadingsTab(QWidget): class _HeadingsTab(QWidget):
EDIT_TITLE = 1 EDIT_TITLE = 1
EDIT_CHAPTER = 2 EDIT_CHAPTER = 2
@@ -707,7 +701,7 @@ class GuiBuildHeadingsTab(QWidget):
self.editTextBox.setFixedHeight(5*iPx) self.editTextBox.setFixedHeight(5*iPx)
self.editTextBox.setEnabled(False) self.editTextBox.setEnabled(False)
self.formSyntax = GuiHeadingSyntax(self.editTextBox.document(), self.mainTheme) self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document(), self.mainTheme)
self.menuInsert = QMenu() self.menuInsert = QMenu()
self.aInsTitle = self.menuInsert.addAction(self.tr("Title")) self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
@@ -756,8 +750,7 @@ class GuiBuildHeadingsTab(QWidget):
return return
def loadContent(self): def loadContent(self):
"""Populate the widgets. """Populate the widgets."""
"""
self.fmtTitle.setText(self._build.getStr("headings.fmtTitle")) self.fmtTitle.setText(self._build.getStr("headings.fmtTitle"))
self.fmtChapter.setText(self._build.getStr("headings.fmtChapter")) self.fmtChapter.setText(self._build.getStr("headings.fmtChapter"))
self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered")) self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered"))
@@ -768,8 +761,7 @@ class GuiBuildHeadingsTab(QWidget):
return return
def saveContent(self): def saveContent(self):
"""Save choices back into build object. """Save choices back into build object."""
"""
self._build.setValue("headings.hideScene", self.swtScene.isChecked()) self._build.setValue("headings.hideScene", self.swtScene.isChecked())
self._build.setValue("headings.hideSection", self.swtSection.isChecked()) self._build.setValue("headings.hideSection", self.swtSection.isChecked())
return return
@@ -779,8 +771,7 @@ class GuiBuildHeadingsTab(QWidget):
## ##
def _insertIntoForm(self, text: str): def _insertIntoForm(self, text: str):
"""Insert formatting text from the dropdown menu. """Insert formatting text from the dropdown menu."""
"""
if self._editing > 0: if self._editing > 0:
cursor = self.editTextBox.textCursor() cursor = self.editTextBox.textCursor()
cursor.insertText(text) cursor.insertText(text)
@@ -788,8 +779,7 @@ class GuiBuildHeadingsTab(QWidget):
return return
def _editHeading(self, heading: int): def _editHeading(self, heading: int):
"""Populate the form with a specific heading format. """Populate the form with a specific heading format."""
"""
self._editing = heading self._editing = heading
self.editTextBox.setEnabled(True) self.editTextBox.setEnabled(True)
if heading == self.EDIT_TITLE: if heading == self.EDIT_TITLE:
@@ -823,8 +813,7 @@ class GuiBuildHeadingsTab(QWidget):
## ##
def _saveFormat(self): def _saveFormat(self):
"""Save the format from the edit text box. """Save the format from the edit text box."""
"""
heading = self._editing heading = self._editing
text = self.editTextBox.toPlainText().replace("\n", "//") text = self.editTextBox.toPlainText().replace("\n", "//")
if heading == self.EDIT_TITLE: if heading == self.EDIT_TITLE:
@@ -850,10 +839,10 @@ class GuiBuildHeadingsTab(QWidget):
return return
# END Class GuiBuildHeadingsTab # END Class _HeadingsTab
class GuiHeadingSyntax(QSyntaxHighlighter): class _HeadingSyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, document: QTextDocument, mainTheme: GuiTheme): def __init__(self, document: QTextDocument, mainTheme: GuiTheme):
super().__init__(document) super().__init__(document)
@@ -864,8 +853,7 @@ class GuiHeadingSyntax(QSyntaxHighlighter):
return return
def highlightBlock(self, text: str): def highlightBlock(self, text: str):
"""Add syntax highlighting to the text block. """Add syntax highlighting to the text block."""
"""
for heading in nwHeadFmt.ALL: for heading in nwHeadFmt.ALL:
pos = text.find(heading) pos = text.find(heading)
if pos >= 0: if pos >= 0:
@@ -877,10 +865,10 @@ class GuiHeadingSyntax(QSyntaxHighlighter):
self.setFormat(pos + ddots, 1, self._fmtSymbol) self.setFormat(pos + ddots, 1, self._fmtSymbol)
return return
# END Class GuiHeadingSyntax # END Class _HeadingSyntaxHighlighter
class GuiBuildContentTab(QWidget): class _ContentTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings):
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
@@ -932,8 +920,7 @@ class GuiBuildContentTab(QWidget):
return return
def loadContent(self): def loadContent(self):
"""Populate the widgets. """Populate the widgets."""
"""
self.incSynopsis.setChecked(self._build.getBool("text.includeSynopsis")) self.incSynopsis.setChecked(self._build.getBool("text.includeSynopsis"))
self.incComments.setChecked(self._build.getBool("text.includeComments")) self.incComments.setChecked(self._build.getBool("text.includeComments"))
self.incKeywords.setChecked(self._build.getBool("text.includeKeywords")) self.incKeywords.setChecked(self._build.getBool("text.includeKeywords"))
@@ -942,8 +929,7 @@ class GuiBuildContentTab(QWidget):
return return
def saveContent(self): def saveContent(self):
"""Save choices back into build object. """Save choices back into build object."""
"""
self._build.setValue("text.includeSynopsis", self.incSynopsis.isChecked()) self._build.setValue("text.includeSynopsis", self.incSynopsis.isChecked())
self._build.setValue("text.includeComments", self.incComments.isChecked()) self._build.setValue("text.includeComments", self.incComments.isChecked())
self._build.setValue("text.includeKeywords", self.incKeywords.isChecked()) self._build.setValue("text.includeKeywords", self.incKeywords.isChecked())
@@ -951,10 +937,10 @@ class GuiBuildContentTab(QWidget):
self._build.setValue("text.addNoteHeadings", self.addNoteHead.isChecked()) self._build.setValue("text.addNoteHeadings", self.addNoteHead.isChecked())
return return
# END Class GuiBuildContentTab # END Class _ContentTab
class GuiBuildFormatTab(QWidget): class _FormatTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings):
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
@@ -1035,8 +1021,7 @@ class GuiBuildFormatTab(QWidget):
return return
def loadContent(self): def loadContent(self):
"""Populate the widgets. """Populate the widgets."""
"""
langIdx = self.buildLang.findData(self._build.getStr("format.buildLang")) langIdx = self.buildLang.findData(self._build.getStr("format.buildLang"))
if langIdx != -1: if langIdx != -1:
self.buildLang.setCurrentIndex(langIdx) self.buildLang.setCurrentIndex(langIdx)
@@ -1056,8 +1041,7 @@ class GuiBuildFormatTab(QWidget):
return return
def saveContent(self): def saveContent(self):
"""Save choices back into build object. """Save choices back into build object."""
"""
self._build.setValue("format.buildLang", str(self.buildLang.currentData())) self._build.setValue("format.buildLang", str(self.buildLang.currentData()))
self._build.setValue("format.textFont", self.textFont.text()) self._build.setValue("format.textFont", self.textFont.text())
self._build.setValue("format.textSize", self.textSize.value()) self._build.setValue("format.textSize", self.textSize.value())
@@ -1073,8 +1057,7 @@ class GuiBuildFormatTab(QWidget):
@pyqtSlot() @pyqtSlot()
def _selectFont(self): def _selectFont(self):
"""Open the QFontDialog and set a font for the font style. """Open the QFontDialog and set a font for the font style."""
"""
currFont = QFont() currFont = QFont()
currFont.setFamily(self.textFont.text()) currFont.setFamily(self.textFont.text())
currFont.setPointSize(self.textSize.value()) currFont.setPointSize(self.textSize.value())
@@ -1084,10 +1067,10 @@ class GuiBuildFormatTab(QWidget):
self.textSize.setValue(theFont.pointSize()) self.textSize.setValue(theFont.pointSize())
return return
# END Class GuiBuildFormatTab # END Class _FormatTab
class GuiBuildOutputTab(QWidget): class _OutputTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings):
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
@@ -1133,17 +1116,15 @@ class GuiBuildOutputTab(QWidget):
return return
def loadContent(self): def loadContent(self):
"""Populate the widgets. """Populate the widgets."""
"""
self.odtAddColours.setChecked(self._build.getBool("odt.addColours")) self.odtAddColours.setChecked(self._build.getBool("odt.addColours"))
self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles")) self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles"))
return return
def saveContent(self): def saveContent(self):
"""Save choices back into build object. """Save choices back into build object."""
"""
self._build.setValue("odt.addColours", self.odtAddColours.isChecked()) self._build.setValue("odt.addColours", self.odtAddColours.isChecked())
self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked()) self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked())
return return
# END Class GuiBuildOutputTab # END Class _OutputTab