Basic export to plain text now works, but many options not connected yet

This commit is contained in:
Veronica K. B. Olsen
2019-10-18 22:09:01 +02:00
parent a5ad2335de
commit f8878535ba
3 changed files with 296 additions and 27 deletions
+126
View File
@@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
"""novelWriter Text File
novelWriter Text File
=========================
Writes the project to a plain text file
File History:
Created: 2019-10-18 [0.2.3]
"""
import logging
import nw
from os import path
from PyQt5.QtWidgets import QMessageBox
from nw.convert.tokenizer import Tokenizer
from nw.enum import nwAlert
logger = logging.getLogger(__name__)
class TextFile():
def __init__(self, theProject, theParent):
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
self.fileExt = "txt"
self.outFile = None
self.fileName = ""
self.theText = ""
self.doComments = False
self.doMeta = False
self.wordWrap = 80
self.winEnding = False
self.makeAlert = self.theParent.makeAlert
return
##
# Setters
##
def setComments(self, doComments):
self.doComments = doComments
return
def setMeta(self, doMeta):
self.doMeta = doMeta
return
##
# Core Methods
##
def openFile(self, saveTo, baseName):
fileName = "%s.%s" % (baseName.strip(),self.fileExt)
filePath = path.join(saveTo,fileName)
self.fileName = fileName
if path.isfile(filePath)and self.mainConf.showGUI:
msgBox = QMessageBox()
msgRes = msgBox.question(
self.theParent, "Overwrite", ("File '%s' already exists.<br>Do you want to overwrite it?" % fileName)
)
if msgRes != QMessageBox.Yes:
return False
self._doOpenFile(filePath)
return True
def closeFile(self):
self._doCloseFile()
return True
def addText(self, tHandle):
logger.verbose("Parsing content of item '%s'" % tHandle)
aDoc = Tokenizer(self.theProject, self.theParent)
aDoc.setText(tHandle)
aDoc.doAutoReplace()
aDoc.tokenizeText()
aDoc.setComments(self.doComments)
aDoc.setCommands(self.doMeta)
aDoc.setWordWrap(self.wordWrap)
aDoc.doConvert()
theText = ""
if aDoc.theResult is not None:
theText = aDoc.theResult
if self.winEnding:
theText = theText.replace("\n","\r\n")
self.outFile.write(theText)
return True
##
# Internal Functions
##
def _doOpenFile(self, filePath):
try:
self.outFile = open(filePath,mode="w+")
except Exception as e:
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
return False
return True
def _doCloseFile(self):
self.outFile.close()
return True
# END Class OutFile
+77
View File
@@ -10,6 +10,7 @@
"""
import textwrap
import logging
import re
import nw
@@ -41,6 +42,25 @@ class Tokenizer():
self.theTokens = None
self.theResult = None
self.wordWrap = 80
self.doComments = False
self.doCommands = False
return
def setComments(self, doComments):
self.doComments = doComments
return
def setCommands(self, doCommands):
self.doCommands = doCommands
return
def setWordWrap(self, wordWrap):
if wordWrap >= 0:
self.wordWrap = wordWrap
else:
self.wordWrap = 0
return
def setText(self, theHandle, theText=None):
@@ -129,4 +149,61 @@ class Tokenizer():
return
def doConvert(self):
"""Converts the tokenized text into plain text.
"""
self.theResult = ""
thisPar = []
for tType, tText, tFormat in self.theTokens:
# First check if we have a comment or plain text, as they need some
# extra replacing before we proceed
if tType == "comment":
tText = "[%s]" % tText
elif tType == "text":
tTemp = tText
for xPos, xLen, xFmt in reversed(tFormat):
tTemp = tTemp[:xPos]+tTemp[xPos+xLen:]
tText = tTemp
# The text can now be word wrapped, if we have requested this
if self.wordWrap > 0:
tText = textwrap.fill(tText, width=self.wordWrap)
# Then the text can receive final formatting before we append it
# to the results. We store text bits in a buffer and merge them only
# when we find an empty line, indicating a new paragraph
if tType == "empty":
if len(thisPar) > 0:
self.theResult += "%s\n\n" % " ".join(thisPar)
thisPar = []
elif tType == "header1":
uLine = "="*min(len(tText),self.wordWrap)
self.theResult += "%s\n%s\n\n" % (tText,uLine)
elif tType == "header2":
uLine = "~"*min(len(tText),self.wordWrap)
self.theResult += "%s\n%s\n\n" % (tText,uLine)
elif tType == "header3":
uLine = "-"*min(len(tText),self.wordWrap)
self.theResult += "%s\n%s\n\n" % (tText,uLine)
elif tType == "header4":
self.theResult += "%s\n\n" % tText
elif tType == "text":
thisPar.append(tText)
elif tType == "comment" and self.doComments:
self.theResult += "%s\n\n" % tText
elif tType == "command" and self.doCommands:
self.theResult += "%s\n\n" % tText
return
# END Class Tokenizer
+93 -27
View File
@@ -11,6 +11,7 @@
"""
import logging
import time
import json
import nw
@@ -19,13 +20,16 @@ from os import path
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QDialogButtonBox, QGridLayout,
QGroupBox, QCheckBox, QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog
QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QGridLayout, QGroupBox, QCheckBox,
QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar
)
from nw.tools.translate import numberToWord
from nw.common import checkString, checkBool, checkInt
from nw.constants import nwFiles
from nw.project.document import NWDoc
from nw.tools.translate import numberToWord
from nw.convert.textfile import TextFile
from nw.common import checkString, checkBool, checkInt
from nw.constants import nwFiles
from nw.enum import nwItemType
logger = logging.getLogger(__name__)
@@ -59,13 +63,27 @@ class GuiExport(QDialog):
self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop)
self.outerBox.addLayout(self.innerBox)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
self.doExportForm = QGridLayout()
self.doExportForm.setContentsMargins(10,5,0,10)
self.exportButton = QPushButton("Export")
self.exportButton.clicked.connect(self._doExport)
self.closeButton = QPushButton("Close")
self.closeButton.clicked.connect(self._doClose)
self.exportStatus = QLabel("Ready ...")
self.exportProgress = QProgressBar(self)
self.doExportForm.addWidget(self.exportStatus, 0, 0, 1, 3)
self.doExportForm.addWidget(self.exportProgress, 1, 0)
self.doExportForm.addWidget(self.exportButton, 1, 1)
self.doExportForm.addWidget(self.closeButton, 1, 2)
self.innerBox.addWidget(self.tabWidget)
self.innerBox.addWidget(self.buttonBox)
self.innerBox.addLayout(self.doExportForm)
self.rejected.connect(self._doClose)
self.show()
logger.debug("GuiExport initialisation complete")
@@ -76,9 +94,56 @@ class GuiExport(QDialog):
# Buttons
##
def _doSave(self):
def _doExport(self):
logger.verbose("GuiExport save button clicked")
logger.verbose("GuiExport export button clicked")
eFormat = self.tabMain.outputFormat.currentData()
wComments = self.tabMain.outputComments.isChecked()
saveTo = self.tabMain.exportPath.text()
outFile = None
if eFormat == GuiExportMain.FMT_TXT:
outFile = TextFile(self.theProject, self.theParent)
if outFile is None:
return False
outFile.openFile(saveTo,"testfile")
outFile.setComments(wComments)
nItems = len(self.theProject.treeOrder)
self.exportProgress.setMinimum(0)
self.exportProgress.setMaximum(nItems)
self.exportProgress.setValue(0)
nDone = 0
for tHandle in self.theProject.treeOrder:
time.sleep(0.1)
self.exportProgress.setValue(nDone)
tItem = self.theProject.getItem(tHandle)
self.exportStatus.setText("Exporting: %s" % tItem.itemName)
logger.verbose("Exporting: %s" % tItem.itemName)
if tItem is not None and tItem.itemType == nwItemType.FILE:
outFile.addText(tHandle)
nDone += 1
outFile.closeFile()
self.exportProgress.setValue(nDone)
self.exportStatus.setText("Export to %s complete" % outFile.fileName)
logger.verbose("Export to %s complete" % outFile.fileName)
return
def _doClose(self):
logger.verbose("GuiExport close button clicked")
wNovel = self.tabMain.expNovel.isChecked()
wNotes = self.tabMain.expNotes.isChecked()
@@ -103,21 +168,21 @@ class GuiExport(QDialog):
return
def _doClose(self):
logger.verbose("GuiExport close button clicked")
self.close()
return
# END Class GuiExport
class GuiExportMain(QWidget):
FMT_MD = 1
FMT_HTML = 2
FMT_EBOOK = 3
FMT_ODT = 4
FMT_TEX = 5
FMT_TXT = 1
FMT_MD = 2
FMT_HTML = 3
FMT_EBOOK = 4
FMT_ODT = 5
FMT_TEX = 6
FMT_HELP = {
FMT_TXT : (
"Exports a plain text file. "
"All formatting is stripped and comments are in square brackets."
),
FMT_MD : (
"Exports a standard markdown file. "
"Comments are converted to preformatted text blocks."
@@ -198,11 +263,12 @@ class GuiExportMain(QWidget):
self.outputHelp.setAlignment(Qt.AlignTop)
self.outputFormat = QComboBox(self)
self.outputFormat.addItem("Markdown", self.FMT_MD)
self.outputFormat.addItem("HTML5 (Plain)", self.FMT_HTML)
self.outputFormat.addItem("HTML5 (eBook)", self.FMT_EBOOK)
self.outputFormat.addItem("Open Document", self.FMT_ODT)
self.outputFormat.addItem("LaTeX (PDF)", self.FMT_TEX)
self.outputFormat.addItem("Plain Text", self.FMT_TXT)
# self.outputFormat.addItem("Markdown", self.FMT_MD)
# self.outputFormat.addItem("HTML5 (Plain)", self.FMT_HTML)
# self.outputFormat.addItem("HTML5 (eBook)", self.FMT_EBOOK)
# self.outputFormat.addItem("Open Document", self.FMT_ODT)
# self.outputFormat.addItem("LaTeX (PDF)", self.FMT_TEX)
self.outputFormat.currentIndexChanged.connect(self._updateFormatHelp)
optIdx = self.outputFormat.findData(self.optState.eFormat())
@@ -300,7 +366,7 @@ class ExportLastState():
"wNovel" : True,
"wNotes" : False,
"wTOC" : True,
"eFormat" : 1,
"eFormat" : 2,
"wComments" : False,
"chFormat" : "Chapter %numword%",
"scFormat" : "* * *",