Add import/export for status labels
This commit is contained in:
@@ -174,6 +174,20 @@ class NWStatus:
|
|||||||
"""Yield entries from the status icons."""
|
"""Yield entries from the status icons."""
|
||||||
yield from self._store.items()
|
yield from self._store.items()
|
||||||
|
|
||||||
|
def fromRaw(self, data: list[str]) -> StatusEntry | None:
|
||||||
|
"""Create a StatusEntry from a list of three strings consisting
|
||||||
|
of shape, colour, and name. This entry is not automatically
|
||||||
|
added to the list of entries.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
shape = nwStatusShape[str(data[0])]
|
||||||
|
color = QColor(str(data[1]))
|
||||||
|
icon = NWStatus.createIcon(self._height, color, shape)
|
||||||
|
return StatusEntry(simplified(data[2]), color, shape, icon)
|
||||||
|
except Exception:
|
||||||
|
logger.error("Could not parse entry %s", str(data))
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
|
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
|
||||||
"""Generate an icon for a status label."""
|
"""Generate an icon for a status label."""
|
||||||
|
|||||||
@@ -24,18 +24,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt5.QtGui import QCloseEvent, QColor
|
from PyQt5.QtGui import QCloseEvent, QColor
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
|
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
|
||||||
QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QTreeWidget,
|
QFileDialog, QGridLayout, QHBoxLayout, QLineEdit, QMenu, QStackedWidget,
|
||||||
QTreeWidgetItem, QVBoxLayout, QWidget
|
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import qtLambda, simplified
|
from novelwriter.common import formatFileFilter, qtLambda, simplified
|
||||||
from novelwriter.constants import nwLabels, trConst
|
from novelwriter.constants import nwLabels, trConst
|
||||||
from novelwriter.core.status import NWStatus, StatusEntry
|
from novelwriter.core.status import NWStatus, StatusEntry
|
||||||
from novelwriter.enum import nwStatusShape
|
from novelwriter.enum import nwStatusShape
|
||||||
@@ -310,11 +313,13 @@ class _StatusPage(NFixedPage):
|
|||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
if isStatus:
|
if isStatus:
|
||||||
status = SHARED.project.data.itemStatus
|
self._kind = self.tr("Status")
|
||||||
|
self._store = SHARED.project.data.itemStatus
|
||||||
pageLabel = self.tr("Novel Document Status Levels")
|
pageLabel = self.tr("Novel Document Status Levels")
|
||||||
colSetting = "statusColW"
|
colSetting = "statusColW"
|
||||||
else:
|
else:
|
||||||
status = SHARED.project.data.itemImport
|
self._kind = self.tr("Importance")
|
||||||
|
self._store = SHARED.project.data.itemImport
|
||||||
pageLabel = self.tr("Project Note Importance Levels")
|
pageLabel = self.tr("Project Note Importance Levels")
|
||||||
colSetting = "importColW"
|
colSetting = "importColW"
|
||||||
|
|
||||||
@@ -354,21 +359,33 @@ class _StatusPage(NFixedPage):
|
|||||||
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
|
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
|
||||||
|
|
||||||
for key, entry in status.iterItems():
|
for key, entry in self._store.iterItems():
|
||||||
self._addItem(key, StatusEntry.duplicate(entry))
|
self._addItem(key, StatusEntry.duplicate(entry))
|
||||||
|
|
||||||
# List Controls
|
# List Controls
|
||||||
self.addButton = NIconToolButton(self, iSz, "add")
|
self.addButton = NIconToolButton(self, iSz, "add")
|
||||||
|
self.addButton.setToolTip(self.tr("Add Label"))
|
||||||
self.addButton.clicked.connect(self._onItemCreate)
|
self.addButton.clicked.connect(self._onItemCreate)
|
||||||
|
|
||||||
self.delButton = NIconToolButton(self, iSz, "remove")
|
self.delButton = NIconToolButton(self, iSz, "remove")
|
||||||
|
self.delButton.setToolTip(self.tr("Delete Label"))
|
||||||
self.delButton.clicked.connect(self._onItemDelete)
|
self.delButton.clicked.connect(self._onItemDelete)
|
||||||
|
|
||||||
self.upButton = NIconToolButton(self, iSz, "up")
|
self.upButton = NIconToolButton(self, iSz, "up")
|
||||||
|
self.upButton.setToolTip(self.tr("Move Up"))
|
||||||
self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
|
self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
|
||||||
|
|
||||||
self.dnButton = NIconToolButton(self, iSz, "down")
|
self.downButton = NIconToolButton(self, iSz, "down")
|
||||||
self.dnButton.clicked.connect(qtLambda(self._moveItem, 1))
|
self.downButton.setToolTip(self.tr("Move Down"))
|
||||||
|
self.downButton.clicked.connect(qtLambda(self._moveItem, 1))
|
||||||
|
|
||||||
|
self.importButton = NIconToolButton(self, iSz, "import")
|
||||||
|
self.importButton.setToolTip(self.tr("Import Labels"))
|
||||||
|
self.importButton.clicked.connect(self._importLabels)
|
||||||
|
|
||||||
|
self.exportButton = NIconToolButton(self, iSz, "export")
|
||||||
|
self.exportButton.setToolTip(self.tr("Export Labels"))
|
||||||
|
self.exportButton.clicked.connect(self._exportLabels)
|
||||||
|
|
||||||
# Edit Form
|
# Edit Form
|
||||||
self.labelText = QLineEdit(self)
|
self.labelText = QLineEdit(self)
|
||||||
@@ -414,21 +431,22 @@ class _StatusPage(NFixedPage):
|
|||||||
self.listControls.addWidget(self.addButton)
|
self.listControls.addWidget(self.addButton)
|
||||||
self.listControls.addWidget(self.delButton)
|
self.listControls.addWidget(self.delButton)
|
||||||
self.listControls.addWidget(self.upButton)
|
self.listControls.addWidget(self.upButton)
|
||||||
self.listControls.addWidget(self.dnButton)
|
self.listControls.addWidget(self.downButton)
|
||||||
self.listControls.addStretch(1)
|
self.listControls.addStretch(1)
|
||||||
|
self.listControls.addWidget(self.importButton)
|
||||||
|
self.listControls.addWidget(self.exportButton)
|
||||||
|
|
||||||
self.editBox = QHBoxLayout()
|
self.editBox = QHBoxLayout()
|
||||||
self.editBox.addWidget(self.labelText, 1)
|
self.editBox.addWidget(self.labelText, 1)
|
||||||
self.editBox.addWidget(self.colorButton, 0)
|
self.editBox.addWidget(self.colorButton, 0)
|
||||||
self.editBox.addWidget(self.shapeButton, 0)
|
self.editBox.addWidget(self.shapeButton, 0)
|
||||||
|
|
||||||
self.mainBox = QVBoxLayout()
|
self.innerBox = QGridLayout()
|
||||||
self.mainBox.addWidget(self.listBox, 1)
|
self.innerBox.addWidget(self.listBox, 0, 0)
|
||||||
self.mainBox.addLayout(self.editBox, 0)
|
self.innerBox.addLayout(self.listControls, 0, 1)
|
||||||
|
self.innerBox.addLayout(self.editBox, 1, 0)
|
||||||
self.innerBox = QHBoxLayout()
|
self.innerBox.setRowStretch(0, 1)
|
||||||
self.innerBox.addLayout(self.mainBox, 1)
|
self.innerBox.setColumnStretch(1, 0)
|
||||||
self.innerBox.addLayout(self.listControls, 0)
|
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.addWidget(self.pageTitle, 0)
|
self.outerBox.addWidget(self.pageTitle, 0)
|
||||||
@@ -540,6 +558,42 @@ class _StatusPage(NFixedPage):
|
|||||||
self.shapeButton.setEnabled(False)
|
self.shapeButton.setEnabled(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _importLabels(self) -> None:
|
||||||
|
"""Import labels from file."""
|
||||||
|
if path := QFileDialog.getOpenFileName(
|
||||||
|
self, self.tr("Import File"),
|
||||||
|
str(CONFIG.homePath()), filter=formatFileFilter(["*.csv", "*"]),
|
||||||
|
)[0]:
|
||||||
|
try:
|
||||||
|
with open(path, mode="r", encoding="utf-8") as fo:
|
||||||
|
for row in csv.reader(fo):
|
||||||
|
if entry := self._store.fromRaw(row):
|
||||||
|
self._addItem(None, entry)
|
||||||
|
except Exception as exc:
|
||||||
|
SHARED.error("Could not read file.", exc=exc)
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _exportLabels(self) -> None:
|
||||||
|
"""Export labels to file."""
|
||||||
|
name = f"{SHARED.project.data.fileSafeName} - {self._kind}.csv"
|
||||||
|
if path := QFileDialog.getSaveFileName(
|
||||||
|
self, self.tr("Export File"), str(CONFIG.homePath() / name),
|
||||||
|
)[0]:
|
||||||
|
try:
|
||||||
|
path = Path(path).with_suffix(".csv")
|
||||||
|
with open(path, mode="w", encoding="utf-8") as fo:
|
||||||
|
writer = csv.writer(fo)
|
||||||
|
for n in range(self.listBox.topLevelItemCount()):
|
||||||
|
if item := self.listBox.topLevelItem(n):
|
||||||
|
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
||||||
|
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
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|||||||
Reference in New Issue
Block a user