Add CSV export to outline

This commit is contained in:
Veronica Berglyd Olsen
2024-02-15 21:09:41 +01:00
parent 1a635f60a9
commit 25b6d1c001
2 changed files with 57 additions and 21 deletions
+1
View File
@@ -274,6 +274,7 @@ class nwLabels:
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"), "*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"), "*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"), "*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
"*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
"*": QT_TRANSLATE_NOOP("Constant", "All files"), "*": QT_TRANSLATE_NOOP("Constant", "All files"),
} }
UNIT_NAME = { UNIT_NAME = {
+56 -21
View File
@@ -27,18 +27,17 @@ 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 time import time from time import time
from enum import Enum from enum import Enum
from PyQt5.QtCore import ( from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, QSize, QT_TRANSLATE_NOOP
Qt, pyqtSignal, pyqtSlot, QSize, QT_TRANSLATE_NOOP
)
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QAction, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QAbstractItemView, QAction, QFileDialog, QFrame, QGridLayout, QGroupBox,
QLabel, QMenu, QScrollArea, QSizePolicy, QSplitter, QToolBar, QToolButton, QHBoxLayout, QLabel, QMenu, QScrollArea, QSizePolicy, QSplitter, QToolBar,
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -46,7 +45,7 @@ from novelwriter.enum import (
nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
) )
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.common import checkInt from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
from novelwriter.extensions.novelselector import NovelSelector from novelwriter.extensions.novelselector import NovelSelector
@@ -88,6 +87,7 @@ class GuiOutlineView(QWidget):
self.outlineData.itemTagClicked.connect(self._tagClicked) self.outlineData.itemTagClicked.connect(self._tagClicked)
self.outlineBar.loadNovelRootRequest.connect(self._rootItemChanged) self.outlineBar.loadNovelRootRequest.connect(self._rootItemChanged)
self.outlineBar.viewColumnToggled.connect(self.outlineTree.menuColumnToggled) self.outlineBar.viewColumnToggled.connect(self.outlineTree.menuColumnToggled)
self.outlineBar.outlineExportRequest.connect(self.outlineTree.exportOutline)
# Function Mappings # Function Mappings
self.getSelectedHandle = self.outlineTree.getSelectedHandle self.getSelectedHandle = self.outlineTree.getSelectedHandle
@@ -198,6 +198,7 @@ class GuiOutlineView(QWidget):
class GuiOutlineToolBar(QToolBar): class GuiOutlineToolBar(QToolBar):
loadNovelRootRequest = pyqtSignal(str) loadNovelRootRequest = pyqtSignal(str)
outlineExportRequest = pyqtSignal()
viewColumnToggled = pyqtSignal(bool, Enum) viewColumnToggled = pyqtSignal(bool, Enum)
def __init__(self, outlineView: GuiOutlineView) -> None: def __init__(self, outlineView: GuiOutlineView) -> None:
@@ -228,6 +229,9 @@ class GuiOutlineToolBar(QToolBar):
self.aRefresh = QAction(self.tr("Refresh"), self) self.aRefresh = QAction(self.tr("Refresh"), self)
self.aRefresh.triggered.connect(self._refreshRequested) self.aRefresh.triggered.connect(self._refreshRequested)
self.aExport = QAction(self.tr("Export CSV"), self)
self.aExport.triggered.connect(self._exportRequested)
# Column Menu # Column Menu
self.mColumns = GuiOutlineHeaderMenu(self) self.mColumns = GuiOutlineHeaderMenu(self)
self.mColumns.columnToggled.connect( self.mColumns.columnToggled.connect(
@@ -243,6 +247,7 @@ class GuiOutlineToolBar(QToolBar):
self.addWidget(self.novelValue) self.addWidget(self.novelValue)
self.addSeparator() self.addSeparator()
self.addAction(self.aRefresh) self.addAction(self.aRefresh)
self.addAction(self.aExport)
self.addWidget(self.tbColumns) self.addWidget(self.tbColumns)
self.addWidget(stretch) self.addWidget(stretch)
@@ -261,6 +266,7 @@ class GuiOutlineToolBar(QToolBar):
self.setStyleSheet("QToolBar {border: 0px;}") self.setStyleSheet("QToolBar {border: 0px;}")
self.novelValue.refreshNovelList() self.novelValue.refreshNovelList()
self.aRefresh.setIcon(SHARED.theme.getIcon("refresh")) self.aRefresh.setIcon(SHARED.theme.getIcon("refresh"))
self.aExport.setIcon(SHARED.theme.getIcon("export"))
self.tbColumns.setIcon(SHARED.theme.getIcon("menu")) self.tbColumns.setIcon(SHARED.theme.getIcon("menu"))
self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}") self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}")
return return
@@ -296,6 +302,12 @@ class GuiOutlineToolBar(QToolBar):
self.loadNovelRootRequest.emit(self.novelValue.handle) self.loadNovelRootRequest.emit(self.novelValue.handle)
return return
@pyqtSlot()
def _exportRequested(self) -> None:
"""Emit a signal that an export of the outline was requested."""
self.outlineExportRequest.emit()
return
# END Class GuiOutlineToolBar # END Class GuiOutlineToolBar
@@ -500,7 +512,41 @@ class GuiOutlineTree(QTreeWidget):
return None, None return None, None
## ##
# Slots # Public Slots
##
@pyqtSlot(bool, Enum)
def menuColumnToggled(self, isChecked: bool, hItem: nwOutline) -> None:
"""Receive the changes to column visibility forwarded by the
column selection menu.
"""
if hItem in self._colIdx:
self.setColumnHidden(self._colIdx[hItem], not isChecked)
self._saveHeaderState()
return
@pyqtSlot()
def exportOutline(self) -> None:
"""Export the outline as a CSV file."""
path = CONFIG.lastPath() / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
path, _ = QFileDialog.getSaveFileName(
self, self.tr("Save Outline As"), str(path), formatFileFilter(["*.csv", "*"])
)
if path:
CONFIG.setLastPath(path)
logger.info("Writing CSV file: %s", path)
cols = [col for col in self._treeOrder if not self._colHidden[col]]
order = [self._colIdx[col] for col in cols]
with open(path, mode="w") as csvFile:
writer = csv.writer(csvFile, dialect="excel", quoting=csv.QUOTE_ALL)
writer.writerow([trConst(nwLabels.OUTLINE_COLS[col]) for col in cols])
for i in range(self.topLevelItemCount()):
if item := self.topLevelItem(i):
writer.writerow(item.text(i) for i in order)
return
##
# Private Slots
## ##
@pyqtSlot("QTreeWidgetItem*", int) @pyqtSlot("QTreeWidgetItem*", int)
@@ -510,9 +556,8 @@ class GuiOutlineTree(QTreeWidget):
document editor. document editor.
""" """
tHandle, sTitle = self.getSelectedHandle() tHandle, sTitle = self.getSelectedHandle()
if tHandle is None: if tHandle:
return self.outlineView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle or "", True)
self.outlineView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle or "", True)
return return
@pyqtSlot() @pyqtSlot()
@@ -536,16 +581,6 @@ class GuiOutlineTree(QTreeWidget):
self._saveHeaderState() self._saveHeaderState()
return return
@pyqtSlot(bool, Enum)
def menuColumnToggled(self, isChecked: bool, hItem: nwOutline) -> None:
"""Receive the changes to column visibility forwarded by the
column selection menu.
"""
if hItem in self._colIdx:
self.setColumnHidden(self._colIdx[hItem], not isChecked)
self._saveHeaderState()
return
## ##
# Internal Functions # Internal Functions
## ##