Assemble most of the Manuscript Build tool

This commit is contained in:
Veronica Berglyd Olsen
2023-06-04 20:58:50 +02:00
parent 5f6335d8f9
commit 3996ff8798
18 changed files with 429 additions and 106 deletions
+238 -2
View File
@@ -25,7 +25,23 @@ from __future__ import annotations
import logging
from PyQt5.QtWidgets import QDialog, QWidget
from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import (
QAbstractButton, QDialog, QDialogButtonBox, QFrame, QGridLayout,
QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QProgressBar,
QPushButton, QSplitter, QVBoxLayout, QWidget
)
from novelwriter import CONFIG
from novelwriter.common import makeFileNameSafe
from novelwriter.constants import nwLabels
from novelwriter.core.buildsettings import BuildSettings
from novelwriter.extensions.switchbox import NSwitchBox
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
@@ -37,13 +53,146 @@ class GuiManuscriptBuild(QDialog):
independently of the Manuscript Build Tool.
"""
def __init__(self, parent: QWidget):
def __init__(self, parent: QWidget, mainGui: GuiMain, build: BuildSettings):
super().__init__(parent=parent)
logger.debug("Create: GuiManuscriptBuild")
self.setObjectName("GuiManuscriptBuild")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._parent = parent
self._build = build
self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(500))
self.setMinimumHeight(CONFIG.pxInt(250))
wWin = CONFIG.pxInt(660)
hWin = CONFIG.pxInt(350)
pOptions = self.theProject.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin))
)
# Formats
# =======
self.lblFormat = QLabel("<b>{0}</b>".format(self.tr("Build Format")))
self.listFormats = QListWidget()
current = None
for key, (_, label) in nwLabels.BUILD_FORMATS.items():
item = QListWidgetItem()
item.setText(label)
item.setData(Qt.UserRole, key)
self.listFormats.addItem(item)
if key == self._build.lastFormat:
current = item
if current:
self.listFormats.setCurrentItem(current)
self.formatBox = QVBoxLayout()
self.formatBox.addWidget(self.lblFormat, 0)
self.formatBox.addWidget(self.listFormats, 1)
self.formatBox.setContentsMargins(0, 0, 0, 0)
self.formatWidget = QWidget()
self.formatWidget.setLayout(self.formatBox)
self.formatWidget.setContentsMargins(0, 0, 0, 0)
# Build Controls
# ==============
# Build Options
self.swtOptions = NSwitchBox(self, self.mainTheme.baseIconSize)
self.swtOptions.switchToggled.connect(self._applyBuildOptions)
self.swtOptions.setFrameStyle(QFrame.NoFrame)
self.swtOptions.setInnerContentsMargins(0, 0, 0, 0)
self.swtOptions.addLabel(self._build.getLabel("build"))
self.swtOptions.addItem(
self.mainTheme.getIcon("cls_novel"), self._build.getLabel("build.splitNovel"),
"build.splitNovel", default=self._build.getBool("build.splitNovel")
)
self.swtOptions.addItem(
self.mainTheme.getIcon("cls_custom"), self._build.getLabel("build.splitNotes"),
"build.splitNotes", default=self._build.getBool("build.splitNotes")
)
self.swtOptions.addItem(
self.mainTheme.getIcon("proj_chapter"), self._build.getLabel("build.splitChapters"),
"build.splitChapters", default=self._build.getBool("build.splitChapters")
)
# Dialog Controls
# ===============
# Build Path
self.lblPath = QLabel(self.tr("Build Folder"))
self.buildPath = QLineEdit()
self.buildPath.setText(str(self._build.lastPath))
self.btnBrowse = QPushButton(self.mainTheme.getIcon("browse"), "")
self.pathBox = QHBoxLayout()
self.pathBox.addWidget(self.buildPath)
self.pathBox.addWidget(self.btnBrowse)
# Build Name
self.lblName = QLabel(self.tr("Build Name"))
self.buildName = QLineEdit()
self.btnReset = QPushButton(self.mainTheme.getIcon("revert"), "")
self.btnReset.setToolTip(self.tr("Reset Build Name to default"))
self.btnReset.clicked.connect(self._doResetBuildName)
self.nameBox = QHBoxLayout()
self.nameBox.addWidget(self.buildName)
self.nameBox.addWidget(self.btnReset)
# Build Progress
self.lblProgress = QLabel(self.tr("Build Progress"))
self.buildProgress = QProgressBar()
# Dialog Buttons
self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close)
self.dlgButtons.addButton(
QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build")),
QDialogButtonBox.ActionRole
)
self.dlgButtons.clicked.connect(self._dialogButtonClicked)
# Assemble GUI
# ============
self.mainSplit = QSplitter()
self.mainSplit.addWidget(self.formatWidget)
self.mainSplit.addWidget(self.swtOptions)
self.mainSplit.setHandleWidth(CONFIG.pxInt(16))
self.mainSplit.setCollapsible(0, False)
self.mainSplit.setCollapsible(1, False)
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "fmtWidth", int(0.45*wWin))),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "optsWidth", int(0.55*wWin))),
])
self.outerBox = QGridLayout()
self.outerBox.addWidget(self.mainSplit, 0, 0, 1, 2)
self.outerBox.addWidget(self.lblPath, 1, 0, 1, 1)
self.outerBox.addLayout(self.pathBox, 1, 1, 1, 1)
self.outerBox.addWidget(self.lblName, 2, 0, 1, 1)
self.outerBox.addLayout(self.nameBox, 2, 1, 1, 1)
self.outerBox.addWidget(self.lblProgress, 3, 0, 1, 1)
self.outerBox.addWidget(self.buildProgress, 3, 1, 1, 1)
self.outerBox.addWidget(self.dlgButtons, 4, 0, 1, 2)
self.setLayout(self.outerBox)
if self._build.lastBuildName:
self.buildName.setText(makeFileNameSafe(self._build.lastBuildName))
else:
self._doResetBuildName()
logger.debug("Ready: GuiManuscriptBuild")
@@ -54,4 +203,91 @@ class GuiManuscriptBuild(QDialog):
logger.debug("Delete: GuiManuscriptBuild")
return
##
# Events
##
def closeEvent(self, event):
"""Capture the user closing the window so we can save GUI
settings.
"""
self._saveSettings()
event.accept()
self.deleteLater()
return
##
# Private Slots
##
@pyqtSlot(str, bool)
def _applyBuildOptions(self, key: str, state: bool):
"""Set the build options for the build."""
self._build.setValue(key, state)
return
@pyqtSlot("QAbstractButton*")
def _dialogButtonClicked(self, button: QAbstractButton):
"""Handle button clicks from the dialog button box."""
role = self.dlgButtons.buttonRole(button)
if role == QDialogButtonBox.ActionRole:
self._runBuild()
elif role == QDialogButtonBox.RejectRole:
self.close()
return
@pyqtSlot()
def _doResetBuildName(self):
"""Generate a default build name."""
bName = makeFileNameSafe(f"{self.theProject.data.name} - {self._build.name}")
self.buildName.setText(bName)
self._build.setLastBuildName(bName)
return
##
# Internal Functions
##
def _runBuild(self) -> bool:
"""Run the currently selected build."""
bFormat = self._getSelectedFormat()
if not bFormat:
return False
bPath = self.buildPath.text()
bName = self.buildName.text()
self._build.setLastFormat(bFormat)
self._build.setLastPath(bPath)
self._build.setLastBuildName(bName)
return True
def _getSelectedFormat(self) -> str | None:
"""Get the currently selected format."""
items = self.listFormats.selectedItems()
if items and isinstance(items[0], QListWidgetItem):
return str(items[0].data(Qt.UserRole))
return None
def _saveSettings(self):
"""Save the user GUI settings."""
logger.debug("Saving GuiManuscriptBuild settings")
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
mainSplit = self.mainSplit.sizes()
fmtWidth = CONFIG.rpxInt(mainSplit[0])
optsWidth = CONFIG.rpxInt(mainSplit[1])
pOptions = self.theProject.options
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth)
pOptions.setValue("GuiManuscriptBuild", "optsWidth", optsWidth)
pOptions.saveSettings()
return
# END Class GuiManuscriptBuild
+23 -15
View File
@@ -44,6 +44,7 @@ from novelwriter.common import checkInt, fuzzyTime
from novelwriter.core.tohtml import ToHtml
from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.buildsettings import BuildCollection, BuildSettings
from novelwriter.tools.manusbuild import GuiManuscriptBuild
from novelwriter.tools.manussettings import GuiBuildSettings
if TYPE_CHECKING: # pragma: no cover
@@ -107,26 +108,16 @@ class GuiManuscript(QDialog):
self.btnPreview.clicked.connect(self._generatePreview)
self.manPreview = _PreviewWidget(self.mainGui)
self.btnBuild = QPushButton(self.tr("Build"))
self.btnBuild.clicked.connect(self._buildManuscript)
self.menuPrint = QMenu(self)
self.aPrintSend = self.menuPrint.addAction(self.tr("Print Preview"))
self.aPrintFile = self.menuPrint.addAction(self.tr("Print to PDF"))
self.menuSave = QMenu(self)
self.aSaveODT = self.menuSave.addAction(self.tr("Open Document (.odt)"))
self.aSaveFODT = self.menuSave.addAction(self.tr("Flat Open Document (.fodt)"))
self.aSaveHTM = self.menuSave.addAction(self.tr("novelWriter HTML (.htm)"))
self.aSaveNWD = self.menuSave.addAction(self.tr("novelWriter Markdown (.nwd)"))
self.aSaveMD = self.menuSave.addAction(self.tr("Standard Markdown (.md)"))
self.aSaveGH = self.menuSave.addAction(self.tr("GitHub Markdown (.md)"))
self.aSaveJsonH = self.menuSave.addAction(self.tr("JSON + novelWriter HTML (.json)"))
self.aSaveJsonM = self.menuSave.addAction(self.tr("JSON + novelWriter Markdown (.json)"))
self.btnPrint = QPushButton(self.tr("Print"))
self.btnPrint.setMenu(self.menuPrint)
self.btnSave = QPushButton(self.tr("Save As"))
self.btnSave.setMenu(self.menuSave)
self.btnClose = QPushButton(self.tr("Close"))
self.btnClose.clicked.connect(self._doClose)
@@ -139,7 +130,7 @@ class GuiManuscript(QDialog):
self.buildBox.addWidget(self.btnDelete)
self.processBox = QHBoxLayout()
self.processBox.addWidget(self.btnSave)
self.processBox.addWidget(self.btnBuild)
self.processBox.addWidget(self.btnPrint)
self.processBox.addWidget(self.btnClose)
@@ -283,6 +274,23 @@ class GuiManuscript(QDialog):
return
@pyqtSlot()
def _buildManuscript(self):
"""Open the build dialog and build the manuscript."""
build = self._getSelectedBuild()
if build is None:
return
dlgBuild = GuiManuscriptBuild(self, self.mainGui, build)
dlgBuild.exec_()
# After the build is done, save build settings changes
if build.changed:
self._builds.setBuild(build)
self._builds.saveCollection()
return
@pyqtSlot()
def _doClose(self):
"""Forward the close button to the default close method."""
@@ -514,7 +522,7 @@ class _PreviewWidget(QTextBrowser):
if self._docTime > 0:
strBuildTime = "%s (%s)" % (
datetime.fromtimestamp(self._docTime).strftime("%x %X"),
fuzzyTime(time() - self._docTime)
fuzzyTime(int(time()) - self._docTime)
)
else:
strBuildTime = self.tr("Unknown")