Add list of builds to manuscript build dialog

This commit is contained in:
Veronica Berglyd Olsen
2023-05-22 18:38:58 +02:00
parent 0a2355488e
commit 854cc77220
4 changed files with 184 additions and 31 deletions
+1
View File
@@ -85,6 +85,7 @@ class nwFiles:
OPTS_FILE = "guiOptions.json" OPTS_FILE = "guiOptions.json"
RECENT_FILE = "recentProjects.json" RECENT_FILE = "recentProjects.json"
BUILD_CACHE = "prevBuild.json" BUILD_CACHE = "prevBuild.json"
BUILDS_FILE = "builds.json"
# END Class nwFiles # END Class nwFiles
+104 -9
View File
@@ -4,7 +4,8 @@ novelWriter Build Settings Class
A class to hold build settings for the build tool A class to hold build settings for the build tool
File History: File History:
Created: 2023-02-14 [2.1b1] Created: 2023-02-14 [2.1b1] BuildSettings
Created: 2023-05-22 [2.1b1] BuildCollection
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -24,17 +25,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations from __future__ import annotations
import json
import uuid import uuid
import logging import logging
from enum import Enum from enum import Enum
from typing import Iterable
from pathlib import Path
from PyQt5.QtCore import QT_TRANSLATE_NOOP from PyQt5.QtCore import QT_TRANSLATE_NOOP
from novelwriter.common import checkUuid, isHandle from novelwriter.common import checkUuid, isHandle, jsonEncode
from novelwriter.constants import nwHeadingFormats from novelwriter.constants import nwFiles, nwHeadingFormats
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.error import logException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -305,18 +310,21 @@ class BuildSettings:
"name": self._name, "name": self._name,
"uuid": self._uuid, "uuid": self._uuid,
"settings": self._settings.copy(), "settings": self._settings.copy(),
"included": list(self._included), "content": {
"excluded": list(self._excluded), "included": list(self._included),
"skipRoot": list(self._skipRoot), "excluded": list(self._excluded),
"skipRoot": list(self._skipRoot),
}
} }
def unpack(self, data: dict): def unpack(self, data: dict):
"""Unpack a dictionary and populate the class. """Unpack a dictionary and populate the class.
""" """
included = data.get("included", [])
excluded = data.get("excluded", [])
skipRoot = data.get("skipRoot", [])
settings = data.get("settings", {}) settings = data.get("settings", {})
content = data.get("content", {})
included = content.get("included", [])
excluded = content.get("excluded", [])
skipRoot = content.get("skipRoot", [])
self.setName(data.get("name", "")) self.setName(data.get("name", ""))
self.setBuildID(data.get("uuid", "")) self.setBuildID(data.get("uuid", ""))
@@ -337,3 +345,90 @@ class BuildSettings:
return return
# END Class BuildSettings # END Class BuildSettings
class BuildCollection:
def __init__(self, project: NWProject):
self._project = project
self._builds = {}
return
def getBuild(self, buildID: str) -> BuildSettings | None:
"""
"""
if buildID not in self._builds:
return None
build = BuildSettings()
build.unpack(self._builds[buildID])
return build
def setBuild(self, build: BuildSettings) -> bool:
"""
"""
if not isinstance(build, BuildSettings):
return False
buildID = build.buildID
self._builds[buildID] = build.pack()
return True
def builds(self) -> Iterable[tuple[str, str]]:
"""
"""
for buildID in self._builds:
yield buildID, self._builds[buildID].get("name", "")
return
def loadCollection(self) -> bool:
"""
"""
buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE)
if not isinstance(buildsFile, Path):
return False
data = {}
if buildsFile.exists():
logger.debug("Loading builds file")
try:
with open(buildsFile, mode="r", encoding="utf-8") as inFile:
data = json.load(inFile)
except Exception:
logger.error("Failed to load builds file")
logException()
return False
if not isinstance(data, dict):
logger.error("Builds file is not a JSON object")
return False
builds = data.get("novelWriter.builds", None)
if not isinstance(builds, dict):
logger.error("No novelWriter.builds in the builds file")
return False
for key, entry in builds.items():
if isinstance(entry, dict):
self._builds[key] = entry
return True
def saveCollection(self) -> bool:
"""
"""
buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE)
if not isinstance(buildsFile, Path):
return False
logger.debug("Saving builds file")
try:
data = {"novelWriter.builds": self._builds}
with open(buildsFile, mode="w+", encoding="utf-8") as outFile:
outFile.write(jsonEncode(data, nmax=4))
except Exception:
logger.error("Failed to save builds file")
logException()
return False
return True
# END Class BuildCollection
+77 -20
View File
@@ -28,14 +28,14 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PyQt5.QtCore import pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QGridLayout, QPushButton, QSplitter, QTextBrowser, QVBoxLayout, QDialog, QHBoxLayout, QListWidget, QListWidgetItem, QPushButton, QSplitter,
QWidget, qApp QTextBrowser, QVBoxLayout, QWidget, qApp
) )
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.core.buildsettings import BuildSettings from novelwriter.core.buildsettings import BuildCollection, BuildSettings
from novelwriter.tools.manussettings import GuiBuildSettings from novelwriter.tools.manussettings import GuiBuildSettings
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -53,6 +53,9 @@ class GuiBuildManuscript(QDialog):
self.mainTheme = mainGui.mainTheme self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject self.theProject = mainGui.theProject
self._builds = BuildCollection(self.theProject)
self._buildMap = {}
self.setWindowTitle(self.tr("Build Manuscript")) self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(600)) self.setMinimumWidth(CONFIG.pxInt(600))
self.setMinimumHeight(CONFIG.pxInt(500)) self.setMinimumHeight(CONFIG.pxInt(500))
@@ -69,19 +72,32 @@ class GuiBuildManuscript(QDialog):
# Controls # Controls
# ======== # ========
self.btnNew = QPushButton(self.tr("New Build")) self.buildList = QListWidget()
self.btnNew = QPushButton(self.tr("New"))
self.btnNew.clicked.connect(self._createNewBuild) self.btnNew.clicked.connect(self._createNewBuild)
self.optsGrid = QGridLayout() self.btnEdit = QPushButton(self.tr("Edit"))
self.optsGrid.addWidget(self.btnNew, 0, 0) self.btnEdit.clicked.connect(self._editSelectedBuild)
self.btnDel = QPushButton(self.tr("Delete"))
self.buttonBox = QHBoxLayout()
self.buttonBox.addWidget(self.btnNew)
self.buttonBox.addWidget(self.btnEdit)
self.buttonBox.addWidget(self.btnDel)
self.manPreview = GuiManuscriptPreview(self) self.manPreview = GuiManuscriptPreview(self)
# Assemble GUI # Assemble GUI
# ============ # ============
self.controlBox = QVBoxLayout()
self.controlBox.addWidget(self.buildList)
self.controlBox.addLayout(self.buttonBox)
self.optsWidget = QWidget() self.optsWidget = QWidget()
self.optsWidget.setLayout(self.optsGrid) self.optsWidget.setLayout(self.controlBox)
self.mainSplit = QSplitter() self.mainSplit = QSplitter()
self.mainSplit.addWidget(self.optsWidget) self.mainSplit.addWidget(self.optsWidget)
@@ -101,6 +117,8 @@ class GuiBuildManuscript(QDialog):
def loadContent(self): def loadContent(self):
""" """
""" """
self._builds.loadCollection()
self._updateBuildsList()
return return
## ##
@@ -124,22 +142,27 @@ class GuiBuildManuscript(QDialog):
""" """
build = BuildSettings() build = BuildSettings()
build.setName(self.tr("My Manuscript")) build.setName(self.tr("My Manuscript"))
self._openSettingsDialog(build)
dlgSettings = GuiBuildSettings(self.mainGui, build)
dlgSettings.setModal(False)
dlgSettings.show()
dlgSettings.raise_()
qApp.processEvents()
dlgSettings.loadContent()
dlgSettings.newSettingsReady.connect(self._processNewSettings)
return return
@pyqtSlot(dict) @pyqtSlot()
def _processNewSettings(self, data: dict): def _editSelectedBuild(self):
"""Edit the currently selected build settings entry.
""" """
bItems = self.buildList.selectedItems()
if bItems:
build = self._builds.getBuild(bItems[0].data(Qt.UserRole))
if isinstance(build, BuildSettings):
self._openSettingsDialog(build)
return
@pyqtSlot(BuildSettings)
def _processNewSettings(self, build: BuildSettings):
"""Process new build settings from the settings dialog.
""" """
print(data) self._builds.setBuild(build)
self._builds.saveCollection()
self._updateBuildItem(build)
return return
## ##
@@ -167,6 +190,40 @@ class GuiBuildManuscript(QDialog):
return return
def _openSettingsDialog(self, build: BuildSettings):
"""Open a new build settings dialog.
"""
dlgSettings = GuiBuildSettings(self.mainGui, build)
dlgSettings.setModal(False)
dlgSettings.show()
dlgSettings.raise_()
qApp.processEvents()
dlgSettings.loadContent()
dlgSettings.newSettingsReady.connect(self._processNewSettings)
return
def _updateBuildsList(self):
"""Update the list of available builds.
"""
self.buildList.clear()
for key, name in self._builds.builds():
bItem = QListWidgetItem()
bItem.setText(name)
bItem.setData(Qt.UserRole, key)
self.buildList.addItem(bItem)
self._buildMap[key] = bItem
return
def _updateBuildItem(self, build: BuildSettings):
"""Update the entry of a specific build item.
"""
bItem = self._buildMap.get(build.buildID, None)
if isinstance(bItem, QListWidgetItem):
bItem.setText(build.name)
else: # Propbably a new item
self._updateBuildsList()
return
# END Class GuiBuildManuscript # END Class GuiBuildManuscript
+2 -2
View File
@@ -59,7 +59,7 @@ class GuiBuildSettings(QDialog):
OPT_CONTENT = 4 OPT_CONTENT = 4
OPT_OUTPUT = 5 OPT_OUTPUT = 5
newSettingsReady = pyqtSignal(dict) newSettingsReady = pyqtSignal(BuildSettings)
def __init__(self, mainGui: GuiMain, build: BuildSettings): def __init__(self, mainGui: GuiMain, build: BuildSettings):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -194,7 +194,7 @@ class GuiBuildSettings(QDialog):
role = self.dlgButtons.buttonRole(button) role = self.dlgButtons.buttonRole(button)
if role in (QDialogButtonBox.ApplyRole, QDialogButtonBox.AcceptRole): if role in (QDialogButtonBox.ApplyRole, QDialogButtonBox.AcceptRole):
self._build.setName(self.editBuildName.text()) self._build.setName(self.editBuildName.text())
self.newSettingsReady.emit(self._build.pack()) self.newSettingsReady.emit(self._build)
self._saveSettings() self._saveSettings()
if role == QDialogButtonBox.AcceptRole: if role == QDialogButtonBox.AcceptRole: