Assemble most of the Manuscript Build tool
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user