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 logging
import re import re
import nw import nw
@@ -41,6 +42,25 @@ class Tokenizer():
self.theTokens = None self.theTokens = None
self.theResult = 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 return
def setText(self, theHandle, theText=None): def setText(self, theHandle, theText=None):
@@ -129,4 +149,61 @@ class Tokenizer():
return 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 # END Class Tokenizer
+93 -27
View File
@@ -11,6 +11,7 @@
""" """
import logging import logging
import time
import json import json
import nw import nw
@@ -19,13 +20,16 @@ from os import path
from PyQt5.QtCore import Qt, QSize from PyQt5.QtCore import Qt, QSize
from PyQt5.QtSvg import QSvgWidget from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QDialogButtonBox, QGridLayout, QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QGridLayout, QGroupBox, QCheckBox,
QGroupBox, QCheckBox, QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar
) )
from nw.tools.translate import numberToWord from nw.project.document import NWDoc
from nw.common import checkString, checkBool, checkInt from nw.tools.translate import numberToWord
from nw.constants import nwFiles 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__) logger = logging.getLogger(__name__)
@@ -59,13 +63,27 @@ class GuiExport(QDialog):
self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop) self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop)
self.outerBox.addLayout(self.innerBox) self.outerBox.addLayout(self.innerBox)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel) self.doExportForm = QGridLayout()
self.buttonBox.accepted.connect(self._doSave) self.doExportForm.setContentsMargins(10,5,0,10)
self.buttonBox.rejected.connect(self._doClose)
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.tabWidget)
self.innerBox.addWidget(self.buttonBox) self.innerBox.addLayout(self.doExportForm)
self.rejected.connect(self._doClose)
self.show() self.show()
logger.debug("GuiExport initialisation complete") logger.debug("GuiExport initialisation complete")
@@ -76,9 +94,56 @@ class GuiExport(QDialog):
# Buttons # 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() wNovel = self.tabMain.expNovel.isChecked()
wNotes = self.tabMain.expNotes.isChecked() wNotes = self.tabMain.expNotes.isChecked()
@@ -103,21 +168,21 @@ class GuiExport(QDialog):
return return
def _doClose(self):
logger.verbose("GuiExport close button clicked")
self.close()
return
# END Class GuiExport # END Class GuiExport
class GuiExportMain(QWidget): class GuiExportMain(QWidget):
FMT_MD = 1 FMT_TXT = 1
FMT_HTML = 2 FMT_MD = 2
FMT_EBOOK = 3 FMT_HTML = 3
FMT_ODT = 4 FMT_EBOOK = 4
FMT_TEX = 5 FMT_ODT = 5
FMT_TEX = 6
FMT_HELP = { FMT_HELP = {
FMT_TXT : (
"Exports a plain text file. "
"All formatting is stripped and comments are in square brackets."
),
FMT_MD : ( FMT_MD : (
"Exports a standard markdown file. " "Exports a standard markdown file. "
"Comments are converted to preformatted text blocks." "Comments are converted to preformatted text blocks."
@@ -198,11 +263,12 @@ class GuiExportMain(QWidget):
self.outputHelp.setAlignment(Qt.AlignTop) self.outputHelp.setAlignment(Qt.AlignTop)
self.outputFormat = QComboBox(self) self.outputFormat = QComboBox(self)
self.outputFormat.addItem("Markdown", self.FMT_MD) self.outputFormat.addItem("Plain Text", self.FMT_TXT)
self.outputFormat.addItem("HTML5 (Plain)", self.FMT_HTML) # self.outputFormat.addItem("Markdown", self.FMT_MD)
self.outputFormat.addItem("HTML5 (eBook)", self.FMT_EBOOK) # self.outputFormat.addItem("HTML5 (Plain)", self.FMT_HTML)
self.outputFormat.addItem("Open Document", self.FMT_ODT) # self.outputFormat.addItem("HTML5 (eBook)", self.FMT_EBOOK)
self.outputFormat.addItem("LaTeX (PDF)", self.FMT_TEX) # self.outputFormat.addItem("Open Document", self.FMT_ODT)
# self.outputFormat.addItem("LaTeX (PDF)", self.FMT_TEX)
self.outputFormat.currentIndexChanged.connect(self._updateFormatHelp) self.outputFormat.currentIndexChanged.connect(self._updateFormatHelp)
optIdx = self.outputFormat.findData(self.optState.eFormat()) optIdx = self.outputFormat.findData(self.optState.eFormat())
@@ -300,7 +366,7 @@ class ExportLastState():
"wNovel" : True, "wNovel" : True,
"wNotes" : False, "wNotes" : False,
"wTOC" : True, "wTOC" : True,
"eFormat" : 1, "eFormat" : 2,
"wComments" : False, "wComments" : False,
"chFormat" : "Chapter %numword%", "chFormat" : "Chapter %numword%",
"scFormat" : "* * *", "scFormat" : "* * *",