From 06031ab7d52467f9107b5cba3a6caf6b4cd1d483 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 1 Dec 2019 16:26:37 +0100 Subject: [PATCH 01/14] Started adding split amd merge functions --- nw/project/project.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/nw/project/project.py b/nw/project/project.py index 041029a1..2a88333b 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -369,6 +369,39 @@ class NWProject(): self.clearProject() return True + ## + # Document Methods + ## + + def splitDocument(self, tHandle, headerLevel, folderLevel): + """Split a document into multiple documents under the same + header. Header level threshold determines at what level the + splitting should occur, and folders can also be created at a + certain structure level. + """ + + return True + + def mergeDocuments(self, handleList): + """Merge a list of document handles into a single document. + """ + + fileList = [] + for tHandle in handleList: + tItem = self.getItem(tHandle) + if tItem is None: + continue + if tItem.itemType == nwItemType.FILE: + fileList.append(tHandle) + else: + pass + + mergeText = "" + for tHandle in fileList: + pass + + return True + ## # Set Functions ## From a111247a3c3a73572916c5b95e9744e4673fc09b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 21 Jan 2020 11:51:49 +0100 Subject: [PATCH 02/14] Added split and merge icons --- .../graphics/{export.txt => license.txt} | 5 +++ nw/assets/graphics/merge.svg | 31 +++++++++++++++++++ nw/assets/graphics/split.svg | 31 +++++++++++++++++++ 3 files changed, 67 insertions(+) rename nw/assets/graphics/{export.txt => license.txt} (75%) create mode 100644 nw/assets/graphics/merge.svg create mode 100644 nw/assets/graphics/split.svg diff --git a/nw/assets/graphics/export.txt b/nw/assets/graphics/license.txt similarity index 75% rename from nw/assets/graphics/export.txt rename to nw/assets/graphics/license.txt index 1a65e8da..8a822fd2 100644 --- a/nw/assets/graphics/export.txt +++ b/nw/assets/graphics/license.txt @@ -1,3 +1,8 @@ FROM ICON SET: Typicons LICENSE: Creative Commons (Attribution-Share Alike 3.0 Unported) https://creativecommons.org/licenses/by-sa/3.0/ + +Apllies to: +export.svg +merge.svg +split.svg diff --git a/nw/assets/graphics/merge.svg b/nw/assets/graphics/merge.svg new file mode 100644 index 00000000..1b4c6ae9 --- /dev/null +++ b/nw/assets/graphics/merge.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/graphics/split.svg b/nw/assets/graphics/split.svg new file mode 100644 index 00000000..c427eea6 --- /dev/null +++ b/nw/assets/graphics/split.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + From 6cac679458ecdab38873cd379589bdf5f1b77457 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 23 Jan 2020 15:52:27 +0100 Subject: [PATCH 03/14] Added merge documents dialog and related code --- nw/constants/constants.py | 1 + nw/gui/__init__.py | 2 + nw/gui/dialogs/__init__.py | 2 + nw/gui/dialogs/docmerge.py | 108 +++++++++++++++++++++++++++++++++++++ nw/gui/icons.py | 2 + nw/gui/mainmenu.py | 7 +++ nw/guimain.py | 13 ++++- 7 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 nw/gui/dialogs/docmerge.py diff --git a/nw/constants/constants.py b/nw/constants/constants.py index a0325482..d3ccd1c7 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -29,6 +29,7 @@ class nwFiles(): EXPORT_OPT = "exportOptions.json" TLINE_OPT = "timelineOptions.json" SLOG_OPT = "sessionLogOptions.json" + MERGE_OPT = "docMergeOptions.json" # END Class nwFiles diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py index b8196cc2..fad7b148 100644 --- a/nw/gui/__init__.py +++ b/nw/gui/__init__.py @@ -8,6 +8,7 @@ from nw.gui.theme import GuiTheme # Dialogs from nw.gui.dialogs.configeditor import GuiConfigEditor +from nw.gui.dialogs.docmerge import GuiDocMerge from nw.gui.dialogs.export import GuiExport from nw.gui.dialogs.itemeditor import GuiItemEditor from nw.gui.dialogs.projecteditor import GuiProjectEditor @@ -33,6 +34,7 @@ __all__ = [ "GuiMainStatus", "GuiTheme", "GuiConfigEditor", + "GuiDocMerge", "GuiExport", "GuiItemEditor", "GuiProjectEditor", diff --git a/nw/gui/dialogs/__init__.py b/nw/gui/dialogs/__init__.py index fac299ba..0c6b842e 100644 --- a/nw/gui/dialogs/__init__.py +++ b/nw/gui/dialogs/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from nw.gui.dialogs.configeditor import GuiConfigEditor +from nw.gui.dialogs.docmerge import GuiDocMerge from nw.gui.dialogs.export import GuiExport from nw.gui.dialogs.itemeditor import GuiItemEditor from nw.gui.dialogs.projecteditor import GuiProjectEditor @@ -9,6 +10,7 @@ from nw.gui.dialogs.timelineview import GuiTimeLineView __all__ = [ "GuiConfigEditor", + "GuiDocMerge", "GuiExport", "GuiItemEditor", "GuiProjectEditor", diff --git a/nw/gui/dialogs/docmerge.py b/nw/gui/dialogs/docmerge.py new file mode 100644 index 00000000..264fc703 --- /dev/null +++ b/nw/gui/dialogs/docmerge.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +"""novelWriter GUI Doc Merge + + novelWriter – GUI Doc Merge +============================= + Tool for merging multiple documents to one + + File History: + Created: 2020-01-23 [0.4.3] + +""" + +import logging +import nw + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import ( + QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QLabel, + QProgressBar +) + +from nw.tools import OptLastState +from nw.constants import nwFiles + +logger = logging.getLogger(__name__) + +class GuiDocMerge(QDialog): + + def __init__(self, theParent, theProject): + QDialog.__init__(self, theParent) + + logger.debug("Initialising GuiDocMerge ...") + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theProject + self.optState = DocMergeLastState(self.theProject,nwFiles.MERGE_OPT) + self.optState.loadSettings() + + self.outerBox = QHBoxLayout() + self.innerBox = QVBoxLayout() + self.setWindowTitle("Merge Documents") + self.setLayout(self.outerBox) + + self.guiDeco = self.theParent.theTheme.loadDecoration("merge",(64,64)) + + self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) + self.outerBox.addLayout(self.innerBox) + + self.doMergeForm = QGridLayout() + self.doMergeForm.setContentsMargins(10,5,0,10) + + self.mergeButton = QPushButton("Merge") + self.mergeButton.clicked.connect(self._doMerge) + + self.closeButton = QPushButton("Close") + self.closeButton.clicked.connect(self._doClose) + + self.mergeStatus = QLabel("Ready ...") + self.mergeProgress = QProgressBar(self) + + self.doMergeForm.addWidget(self.mergeStatus, 0, 0, 1, 3) + self.doMergeForm.addWidget(self.mergeProgress, 1, 0) + self.doMergeForm.addWidget(self.mergeButton, 1, 1) + self.doMergeForm.addWidget(self.closeButton, 1, 2) + + self.innerBox.addLayout(self.doMergeForm) + + self.rejected.connect(self._doClose) + self.show() + + logger.debug("GuiDocMerge initialisation complete") + + return + + ## + # Buttons + ## + + def _doMerge(self): + + logger.verbose("GuiDocMerge merge button clicked") + + return + + def _doClose(self): + + logger.verbose("GuiDocMerge close button clicked") + + self.optState.saveSettings() + self.close() + + return + +# END Class GuiDocMerge + +class DocMergeLastState(OptLastState): + + def __init__(self, theProject, theFile): + OptLastState.__init__(self, theProject, theFile) + self.theState = { + } + self.stringOpt = () + self.boolOpt = () + self.intOpt = () + return + +# END Class DocMergeLastState diff --git a/nw/gui/icons.py b/nw/gui/icons.py index 88149b6e..facb84d7 100644 --- a/nw/gui/icons.py +++ b/nw/gui/icons.py @@ -46,7 +46,9 @@ class GuiIcons: DECO_MAP = { "export" : "export.svg", + "merge" : "merge.svg", "settings" : "gear.svg", + "split" : "split.svg", } def __init__(self, theParent): diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index ae5df846..061b5e2d 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -369,6 +369,13 @@ class GuiMainMenu(QMenuBar): self.aImportFile.triggered.connect(self.theParent.importDocument) self.docuMenu.addAction(self.aImportFile) + # Document > Merge Documents + self.aMergeDocs = QAction("Merge Documents", self) + self.aMergeDocs.setStatusTip("Merge multiple documents") + # self.aMergeDocs.setShortcut("Ctrl+Shift+I") + self.aMergeDocs.triggered.connect(self.theParent.mergeDocuments) + self.docuMenu.addAction(self.aMergeDocs) + return def _buildViewMenu(self): diff --git a/nw/guimain.py b/nw/guimain.py index 63a31795..4572ee01 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -27,7 +27,7 @@ from nw.gui import ( GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails, GuiConfigEditor, GuiProjectEditor, GuiExport, - GuiItemEditor, GuiTimeLineView, GuiSessionLogView + GuiItemEditor, GuiTimeLineView, GuiSessionLogView, GuiDocMerge ) from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup from nw.tools import countWords @@ -458,6 +458,17 @@ class GuiMain(QMainWindow): return True + def mergeDocuments(self): + """Merge multiple documents to one single new document. + """ + + if self.mainConf.showGUI: + dlgMerge = GuiDocMerge(self, self.theProject) + if dlgMerge.exec_(): + pass + + return True + def passDocumentAction(self, theAction): """Pass on document action theAction to whatever document has the focus. If no document has focus, the action is discarded. From 6b79b7a42395c53e465e15cdf629665bc834317f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 31 Jan 2020 17:17:07 +0100 Subject: [PATCH 04/14] Merging a folder of documents into one file now works --- nw/gui/dialogs/docmerge.py | 78 +++++++++++++++++++++++++++++++++----- nw/gui/elements/doctree.py | 25 ++++++++++-- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/nw/gui/dialogs/docmerge.py b/nw/gui/dialogs/docmerge.py index 264fc703..503eba44 100644 --- a/nw/gui/dialogs/docmerge.py +++ b/nw/gui/dialogs/docmerge.py @@ -16,11 +16,11 @@ import nw from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QLabel, - QProgressBar + QProgressBar, QListWidget, QAbstractItemView, QListWidgetItem ) - +from nw.constants import nwFiles, nwAlert, nwItemClass, nwItemType +from nw.project import NWDoc from nw.tools import OptLastState -from nw.constants import nwFiles logger = logging.getLogger(__name__) @@ -34,6 +34,7 @@ class GuiDocMerge(QDialog): self.mainConf = nw.CONFIG self.theParent = theParent self.theProject = theProject + self.sourceItem = None self.optState = DocMergeLastState(self.theProject,nwFiles.MERGE_OPT) self.optState.loadSettings() @@ -50,25 +51,26 @@ class GuiDocMerge(QDialog): self.doMergeForm = QGridLayout() self.doMergeForm.setContentsMargins(10,5,0,10) + self.listBox = QListWidget() + self.listBox.setDragDropMode(QAbstractItemView.InternalMove) + self.mergeButton = QPushButton("Merge") self.mergeButton.clicked.connect(self._doMerge) self.closeButton = QPushButton("Close") self.closeButton.clicked.connect(self._doClose) - self.mergeStatus = QLabel("Ready ...") - self.mergeProgress = QProgressBar(self) - - self.doMergeForm.addWidget(self.mergeStatus, 0, 0, 1, 3) - self.doMergeForm.addWidget(self.mergeProgress, 1, 0) - self.doMergeForm.addWidget(self.mergeButton, 1, 1) - self.doMergeForm.addWidget(self.closeButton, 1, 2) + self.doMergeForm.addWidget(self.listBox, 0, 0, 1, 3) + self.doMergeForm.addWidget(self.mergeButton, 1, 1) + self.doMergeForm.addWidget(self.closeButton, 1, 2) self.innerBox.addLayout(self.doMergeForm) self.rejected.connect(self._doClose) self.show() + self._populateList() + logger.debug("GuiDocMerge initialisation complete") return @@ -81,6 +83,31 @@ class GuiDocMerge(QDialog): logger.verbose("GuiDocMerge merge button clicked") + finalOrder = [] + for i in range(self.listBox.count()): + finalOrder.append(self.listBox.item(i).data(Qt.UserRole)) + + theDoc = NWDoc(self.theProject, self.theParent) + theText = "" + for tHandle in finalOrder: + theText += theDoc.openDocument(tHandle, False).rstrip() + theText += "\n\n" + + if self.sourceItem is None: + self.theParent.makeAlert(( + "Cannot parse source item." + ), nwAlert.ERROR) + return + + srcItem = self.theProject.getItem(self.sourceItem) + nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.parHandle) + self.theParent.treeView.revealTreeItem(nHandle) + theDoc.openDocument(nHandle, False) + theDoc.saveDocument(theText) + self.theParent.openDocument(nHandle) + + self.close() + return def _doClose(self): @@ -92,6 +119,37 @@ class GuiDocMerge(QDialog): return + ## + # Internal Functions + ## + + def _populateList(self): + + tHandle = self.theParent.treeView.getSelectedHandle() + self.sourceItem = tHandle + if tHandle is None: + return + + nwItem = self.theProject.getItem(tHandle) + if nwItem is None: + return + if nwItem.itemType is not nwItemType.FOLDER: + self.theParent.makeAlert(( + "Element selected in the project tree must be a folder." + ), nwAlert.ERROR) + return + + for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle): + newItem = QListWidgetItem() + nwItem = self.theProject.getItem(sHandle) + if nwItem.itemType is not nwItemType.FILE: + continue + newItem.setText(nwItem.itemName) + newItem.setData(Qt.UserRole, sHandle) + self.listBox.addItem(newItem) + + return + # END Class GuiDocMerge class DocMergeLastState(OptLastState): diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index d5b9fb4f..63a998dd 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -171,14 +171,21 @@ class GuiDocTree(QTreeWidget): return False # Add the new item to the tree - nwItem = self.theProject.getItem(tHandle) - trItem = self._addTreeItem(nwItem) + self.revealTreeItem(tHandle) + self.theParent.editItem() + + return True + + def revealTreeItem(self, tHandle): + """Reveal a newly added project item in the project tree. + """ + nwItem = self.theProject.getItem(tHandle) + trItem = self._addTreeItem(nwItem) + pHandle = nwItem.parHandle if pHandle is not None and pHandle in self.theMap.keys(): self.theMap[pHandle].setExpanded(True) self.clearSelection() trItem.setSelected(True) - self.theParent.editItem() - return True def moveTreeItem(self, nStep): @@ -221,6 +228,16 @@ class GuiDocTree(QTreeWidget): self.theProject.setTreeOrder(theList) return True + def getTreeFromHandle(self, tHandle): + """Recursively return all the children items starting from a + given item handle. + """ + theList = [] + theItem = self._getTreeItem(tHandle) + if theItem is not None: + theList = self._scanChildren(theList, theItem, 0) + return theList + def getColumnSizes(self): retVals = [ self.columnWidth(0), From 605541c87dc440cfbc0fa0885f5653ff10472e3f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 1 Feb 2020 12:49:45 +0100 Subject: [PATCH 05/14] Some cleanup of the merge document dialog --- nw/gui/dialogs/docmerge.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/nw/gui/dialogs/docmerge.py b/nw/gui/dialogs/docmerge.py index 503eba44..2b46eba5 100644 --- a/nw/gui/dialogs/docmerge.py +++ b/nw/gui/dialogs/docmerge.py @@ -15,12 +15,11 @@ import nw from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( - QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QLabel, - QProgressBar, QListWidget, QAbstractItemView, QListWidgetItem + QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, + QListWidget, QAbstractItemView, QListWidgetItem ) -from nw.constants import nwFiles, nwAlert, nwItemClass, nwItemType +from nw.constants import nwAlert, nwItemType from nw.project import NWDoc -from nw.tools import OptLastState logger = logging.getLogger(__name__) @@ -35,8 +34,6 @@ class GuiDocMerge(QDialog): self.theParent = theParent self.theProject = theProject self.sourceItem = None - self.optState = DocMergeLastState(self.theProject,nwFiles.MERGE_OPT) - self.optState.loadSettings() self.outerBox = QHBoxLayout() self.innerBox = QVBoxLayout() @@ -80,6 +77,10 @@ class GuiDocMerge(QDialog): ## def _doMerge(self): + """Perform the merge of the files in the selected folder, and + create a new file in the same parent folder. The old files are + not removed in the merge process, and must be deleted manually. + """ logger.verbose("GuiDocMerge merge button clicked") @@ -111,12 +112,10 @@ class GuiDocMerge(QDialog): return def _doClose(self): - + """Close the dialog window without doing anything. + """ logger.verbose("GuiDocMerge close button clicked") - - self.optState.saveSettings() self.close() - return ## @@ -124,6 +123,11 @@ class GuiDocMerge(QDialog): ## def _populateList(self): + """Get the item selected in the tree, check that it is a folder, + and try to find all files associated with it. The valid files + are then added to the list view in order. The list itself can be + reordered by the user. + """ tHandle = self.theParent.treeView.getSelectedHandle() self.sourceItem = tHandle @@ -151,16 +155,3 @@ class GuiDocMerge(QDialog): return # END Class GuiDocMerge - -class DocMergeLastState(OptLastState): - - def __init__(self, theProject, theFile): - OptLastState.__init__(self, theProject, theFile) - self.theState = { - } - self.stringOpt = () - self.boolOpt = () - self.intOpt = () - return - -# END Class DocMergeLastState From f937ebc396e956a3b251e2d7bf6f9f42599cbccb Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 1 Feb 2020 14:10:48 +0100 Subject: [PATCH 06/14] Added code to permanently delete project documents --- nw/gui/elements/doctree.py | 69 ++++++++++++++++++++++++++++++++------ nw/gui/mainmenu.py | 4 +-- nw/project/document.py | 35 ++++++++++++++----- nw/project/project.py | 50 ++++++--------------------- 4 files changed, 96 insertions(+), 62 deletions(-) diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index 63a998dd..30a369e1 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -16,10 +16,10 @@ import nw from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QFont, QColor from PyQt5.QtWidgets import ( - QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication + QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication, QMessageBox ) -from nw.project import NWItem +from nw.project import NWItem, NWDoc from nw.constants import ( nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert ) @@ -263,21 +263,65 @@ class GuiDocTree(QTreeWidget): trItemS = self._getTreeItem(tHandle) nwItemS = self.theProject.getItem(tHandle) + if nwItemS is None: + return False + if nwItemS.itemType == nwItemType.FILE: logger.debug("User requested file %s moved to trash" % tHandle) trItemP = trItemS.parent() trItemT = self._addTrashRoot() if trItemP is None or trItemT is None: - logger.error("Could not move item to trash") + logger.error("Could not delete item") return False - tIndex = trItemP.indexOfChild(trItemS) - trItemC = trItemP.takeChild(tIndex) - trItemT.addChild(trItemC) - nwItemS.setParent(self.theProject.trashRoot) - self.clearSelection() - trItemP.setSelected(True) - self.theProject.setProjectChanged(True) - self.theParent.theIndex.deleteHandle(tHandle) + + pHandle = nwItemS.parHandle + if pHandle is not None and pHandle == self.theProject.trashRoot: + # If the file is in the trash folder already, as the + # user if they want to permanently delete the file. + + doPermanent = False + if self.mainConf.showGUI: + msgBox = QMessageBox() + msgRes = msgBox.question( + self, "Delete File", "Permanently delete file '%s'?" % nwItemS.itemName + ) + if msgRes == QMessageBox.Yes: + doPermanent = True + else: + doPermanent = True + + if doPermanent: + logger.debug("Permanently deleting file with handle %s" % tHandle) + + tIndex = trItemP.indexOfChild(trItemS) + trItemC = trItemP.takeChild(tIndex) + self.clearSelection() + trItemP.setSelected(True) + + if self.theParent.docEditor.theHandle == tHandle: + self.theParent.closeDocument() + + theDoc = NWDoc(self.theProject, self.theParent) + theDoc.deleteDocument(tHandle) + self.theProject.deleteItem(tHandle) + self.theParent.theIndex.deleteHandle(tHandle) + + else: + # The file is not already in the trash folder, so we + # move it there. + + if pHandle is None: + logger.warning("File has no parent item") + + tIndex = trItemP.indexOfChild(trItemS) + trItemC = trItemP.takeChild(tIndex) + trItemT.addChild(trItemC) + nwItemS.setParent(self.theProject.trashRoot) + self.clearSelection() + trItemP.setSelected(True) + + self.theProject.setProjectChanged(True) + self.theParent.theIndex.deleteHandle(tHandle) elif nwItemS.itemType == nwItemType.FOLDER: logger.debug("User requested folder %s deleted" % tHandle) @@ -445,6 +489,9 @@ class GuiDocTree(QTreeWidget): return newItem def _addTrashRoot(self): + """Adds the trash root folder if it doesn't already exist in the + project tree. + """ if self.theProject.trashRoot is None: self.theProject.addTrash() trItem = self._addTreeItem( diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 061b5e2d..580aacaf 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -370,8 +370,8 @@ class GuiMainMenu(QMenuBar): self.docuMenu.addAction(self.aImportFile) # Document > Merge Documents - self.aMergeDocs = QAction("Merge Documents", self) - self.aMergeDocs.setStatusTip("Merge multiple documents") + self.aMergeDocs = QAction("Merge Folder to Document", self) + self.aMergeDocs.setStatusTip("Merge a folder of documents to a single document") # self.aMergeDocs.setShortcut("Ctrl+Shift+I") self.aMergeDocs.triggered.connect(self.theParent.mergeDocuments) self.docuMenu.addAction(self.aMergeDocs) diff --git a/nw/project/document.py b/nw/project/document.py index 0cc0fa66..e2a46d65 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -59,7 +59,7 @@ class NWDoc(): if self.theItem.parHandle == self.theProject.trashRoot: self.docEditable = False - docDir, docFile = self._assemblePath(self.FILE_MN) + docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN) self.fileLoc = path.join(docDir,docFile) logger.debug("Opening document %s" % self.fileLoc) dataDir = path.join(self.theProject.projPath, docDir) @@ -92,7 +92,7 @@ class NWDoc(): if self.docHandle is None or not self.docEditable: return False - docDir, docFile = self._assemblePath(self.FILE_MN) + docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN) logger.debug("Saving document %s" % path.join(docDir,docFile)) dataPath = path.join(self.theProject.projPath, docDir) docPath = path.join(dataPath, docFile) @@ -124,15 +124,32 @@ class NWDoc(): return True - ## - # Internal Functions - ## + def deleteDocument(self, tHandle): + """Permanently delete a document source file and its backups + from the project data folder. + """ + docDir, docFile = self.assemblePath(tHandle, self.FILE_MN) + dataPath = path.join(self.theProject.projPath, docDir) + chkList = [] + chkList.append(path.join(dataPath, docFile)) + chkList.append(path.join(dataPath,docFile[:-3]+"tmp")) + chkList.append(path.join(dataPath,docFile[:-3]+"bak")) + for chkFile in chkList: + if path.isfile(chkFile): + try: + unlink(chkFile) + logger.debug("Deleted: %s" % chkFile) + except Exception as e: + self.makeAlert(["Could not delete document file.",str(e)], nwAlert.ERROR) + return False + return True - def _assemblePath(self, docExt): - if self.docHandle is None: + @staticmethod + def assemblePath(tHandle, docExt): + if tHandle is None: return None - docDir = "data_"+self.docHandle[0] - docFile = self.docHandle[1:13]+"_"+docExt + docDir = "data_"+tHandle[0] + docFile = tHandle[1:13]+"_"+docExt return docDir, docFile # END Class NWDoc diff --git a/nw/project/project.py b/nw/project/project.py index 2a88333b..a52a83f3 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -13,7 +13,7 @@ import logging import nw -from os import path, mkdir, listdir +from os import path, mkdir, listdir, unlink from shutil import copyfile from lxml import etree from hashlib import sha256 @@ -369,39 +369,6 @@ class NWProject(): self.clearProject() return True - ## - # Document Methods - ## - - def splitDocument(self, tHandle, headerLevel, folderLevel): - """Split a document into multiple documents under the same - header. Header level threshold determines at what level the - splitting should occur, and folders can also be created at a - certain structure level. - """ - - return True - - def mergeDocuments(self, handleList): - """Merge a list of document handles into a single document. - """ - - fileList = [] - for tHandle in handleList: - tItem = self.getItem(tHandle) - if tItem is None: - continue - if tItem.itemType == nwItemType.FILE: - fileList.append(tHandle) - else: - pass - - mergeText = "" - for tHandle in fileList: - pass - - return True - ## # Set Functions ## @@ -485,9 +452,6 @@ class NWProject(): self.setProjectChanged(True) return True - def getSessionWordCount(self): - return self.currWCount - self.lastWCount - def setStatusColours(self, newCols): replaceMap = self.statusItems.setNewEntries(newCols) if self.projTree is not None: @@ -530,6 +494,9 @@ class NWProject(): logger.error("No tree item with handle %s" % str(tHandle)) return None + def getSessionWordCount(self): + return self.currWCount - self.lastWCount + def getRootItem(self, tHandle): """Iterate upwards in the tree until we find the item with parent None, the root item. We do this with a for loop with a @@ -538,10 +505,13 @@ class NWProject(): tItem = self.getItem(tHandle) if tItem is not None: for i in range(200): - tHandle = tItem.parHandle - tItem = self.getItem(tHandle) - if tItem is None: + if tItem.parHandle is None: return tHandle + else: + tHandle = tItem.parHandle + tItem = self.getItem(tHandle) + if tItem is None: + return tHandle return None def getProjectItems(self): From 068674e63dc05cfe19b093d67095f04e348b9c78 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 4 Feb 2020 23:57:29 +0100 Subject: [PATCH 07/14] Remove the change of selected item when deleting, and let the tree widget handle it --- nw/gui/elements/doctree.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index 30a369e1..5ade6c5b 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -295,8 +295,6 @@ class GuiDocTree(QTreeWidget): tIndex = trItemP.indexOfChild(trItemS) trItemC = trItemP.takeChild(tIndex) - self.clearSelection() - trItemP.setSelected(True) if self.theParent.docEditor.theHandle == tHandle: self.theParent.closeDocument() @@ -317,8 +315,6 @@ class GuiDocTree(QTreeWidget): trItemC = trItemP.takeChild(tIndex) trItemT.addChild(trItemC) nwItemS.setParent(self.theProject.trashRoot) - self.clearSelection() - trItemP.setSelected(True) self.theProject.setProjectChanged(True) self.theParent.theIndex.deleteHandle(tHandle) @@ -332,8 +328,6 @@ class GuiDocTree(QTreeWidget): tIndex = trItemP.indexOfChild(trItemS) if trItemS.childCount() == 0: trItemP.takeChild(tIndex) - self.clearSelection() - trItemP.setSelected(True) self.theProject.deleteItem(tHandle) else: self.makeAlert(["Cannot delete folder.","It is not empty."], nwAlert.ERROR) From c43e121389a23931f7cb80c55edcd12ab809012a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 4 Feb 2020 23:59:00 +0100 Subject: [PATCH 08/14] Added document split dialog and associated code --- nw/gui/__init__.py | 2 + nw/gui/dialogs/__init__.py | 2 + nw/gui/dialogs/docsplit.py | 247 +++++++++++++++++++++++++++++++++++++ nw/gui/mainmenu.py | 7 +- nw/guimain.py | 21 ++-- nw/project/project.py | 5 + 6 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 nw/gui/dialogs/docsplit.py diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py index fad7b148..d8842bcc 100644 --- a/nw/gui/__init__.py +++ b/nw/gui/__init__.py @@ -9,6 +9,7 @@ from nw.gui.theme import GuiTheme # Dialogs from nw.gui.dialogs.configeditor import GuiConfigEditor from nw.gui.dialogs.docmerge import GuiDocMerge +from nw.gui.dialogs.docsplit import GuiDocSplit from nw.gui.dialogs.export import GuiExport from nw.gui.dialogs.itemeditor import GuiItemEditor from nw.gui.dialogs.projecteditor import GuiProjectEditor @@ -35,6 +36,7 @@ __all__ = [ "GuiTheme", "GuiConfigEditor", "GuiDocMerge", + "GuiDocSplit", "GuiExport", "GuiItemEditor", "GuiProjectEditor", diff --git a/nw/gui/dialogs/__init__.py b/nw/gui/dialogs/__init__.py index 0c6b842e..a58b4982 100644 --- a/nw/gui/dialogs/__init__.py +++ b/nw/gui/dialogs/__init__.py @@ -2,6 +2,7 @@ from nw.gui.dialogs.configeditor import GuiConfigEditor from nw.gui.dialogs.docmerge import GuiDocMerge +from nw.gui.dialogs.docsplit import GuiDocSplit from nw.gui.dialogs.export import GuiExport from nw.gui.dialogs.itemeditor import GuiItemEditor from nw.gui.dialogs.projecteditor import GuiProjectEditor @@ -11,6 +12,7 @@ from nw.gui.dialogs.timelineview import GuiTimeLineView __all__ = [ "GuiConfigEditor", "GuiDocMerge", + "GuiDocSplit", "GuiExport", "GuiItemEditor", "GuiProjectEditor", diff --git a/nw/gui/dialogs/docsplit.py b/nw/gui/dialogs/docsplit.py new file mode 100644 index 00000000..3e8502b5 --- /dev/null +++ b/nw/gui/dialogs/docsplit.py @@ -0,0 +1,247 @@ +# -*- coding: utf-8 -*- +"""novelWriter GUI Doc Split + + novelWriter – GUI Doc Split +============================= + Tool for splitting a single document into multiple documents + + File History: + Created: 2020-02-01 [0.4.3] + +""" + +import logging +import nw + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import ( + QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QComboBox, + QListWidget, QAbstractItemView, QListWidgetItem +) +from nw.constants import nwAlert, nwItemType, nwItemClass, nwItemLayout +from nw.project import NWDoc + +logger = logging.getLogger(__name__) + +class GuiDocSplit(QDialog): + + def __init__(self, theParent, theProject): + QDialog.__init__(self, theParent) + + logger.debug("Initialising GuiDocSplit ...") + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theProject + self.sourceItem = None + + self.outerBox = QHBoxLayout() + self.innerBox = QVBoxLayout() + self.setWindowTitle("Split Document") + self.setLayout(self.outerBox) + + self.guiDeco = self.theParent.theTheme.loadDecoration("split",(64,64)) + + self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) + self.outerBox.addLayout(self.innerBox) + + self.doMergeForm = QGridLayout() + self.doMergeForm.setContentsMargins(10,5,0,10) + + self.listBox = QListWidget() + self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop) + + self.splitLevel = QComboBox(self) + self.splitLevel.addItem("Split on Title (Level 1)", 1) + self.splitLevel.addItem("Split on Chapter (Level 2)", 2) + self.splitLevel.addItem("Split on Scene (Level 3)", 3) + self.splitLevel.addItem("Split on Section (Level 4)", 4) + self.splitLevel.setCurrentIndex(2) + self.splitLevel.currentIndexChanged.connect(self._populateList) + + self.splitButton = QPushButton("Split") + self.splitButton.clicked.connect(self._doSplit) + + self.closeButton = QPushButton("Close") + self.closeButton.clicked.connect(self._doClose) + + self.doMergeForm.addWidget(self.listBox, 0, 0, 1, 3) + self.doMergeForm.addWidget(self.splitLevel, 1, 0, 1, 2) + self.doMergeForm.addWidget(self.splitButton, 2, 1) + self.doMergeForm.addWidget(self.closeButton, 2, 2) + + self.innerBox.addLayout(self.doMergeForm) + + self.rejected.connect(self._doClose) + self.show() + + self._populateList() + + logger.debug("GuiDocSplit initialisation complete") + + return + + ## + # Buttons + ## + + def _doSplit(self): + """Perform the split of the file, create a new folder in the + same parent folder, and multiple files depending on split level + settings. The old file is not removed in the merge process, and + must be deleted manually. + """ + + logger.verbose("GuiDocSplit split button clicked") + + if self.sourceItem is None: + self.theParent.makeAlert(( + "No source document selected. Nothing to do." + ), nwAlert.ERROR) + return + + srcItem = self.theProject.getItem(self.sourceItem) + if srcItem is None: + self.theParent.makeAlert(( + "Could not parse source document." + ), nwAlert.ERROR) + return + + theDoc = NWDoc(self.theProject, self.theParent) + theText = theDoc.openDocument(self.sourceItem, False) + theLines = theText.splitlines() + nLines = len(theLines) + theLines.insert(0, "%Split Doc") + + finalOrder = [] + for i in range(self.listBox.count()): + listItem = self.listBox.item(i) + wTitle = listItem.text() + lineNo = listItem.data(Qt.UserRole) + finalOrder.append([wTitle, lineNo, nLines]) + if i > 0: + finalOrder[i-1][2] = lineNo + + if len(finalOrder) == 0: + self.theParent.makeAlert(( + "No headers found. Nothing to do." + ), nwAlert.ERROR) + return + + fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemClass, srcItem.parHandle) + self.theParent.treeView.revealTreeItem(fHandle) + + for wTitle, iStart, iEnd in finalOrder: + + itemLayout = nwItemLayout.NOTE + if srcItem.itemClass == nwItemClass.NOVEL: + if wTitle.startswith("# "): + itemLayout = nwItemLayout.PARTITION + elif wTitle.startswith("## "): + itemLayout = nwItemLayout.CHAPTER + elif wTitle.startswith("### "): + itemLayout = nwItemLayout.SCENE + elif wTitle.startswith("#### "): + itemLayout = nwItemLayout.PAGE + + wTitle = wTitle.lstrip("#") + wTitle = wTitle.strip() + print(wTitle, iStart, iEnd) + + nHandle = self.theProject.newFile(wTitle, srcItem.itemClass, fHandle) + newItem = self.theProject.getItem(nHandle) + newItem.setLayout(itemLayout) + + theText = "\n".join(theLines[iStart:iEnd]) + theDoc.openDocument(nHandle, False) + theDoc.saveDocument(theText) + theDoc.clearDocument() + self.theParent.treeView.revealTreeItem(nHandle) + + # theDoc = NWDoc(self.theProject, self.theParent) + # theText = "" + # for tHandle in finalOrder: + # theText += theDoc.openDocument(tHandle, False).rstrip() + # theText += "\n\n" + + # if self.sourceItem is None: + # self.theParent.makeAlert(( + # "Cannot parse source item." + # ), nwAlert.ERROR) + # return + + # srcItem = self.theProject.getItem(self.sourceItem) + # nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.parHandle) + # self.theParent.treeView.revealTreeItem(nHandle) + # theDoc.openDocument(nHandle, False) + # theDoc.saveDocument(theText) + # self.theParent.openDocument(nHandle) + + self.close() + + return + + def _doClose(self): + """Close the dialog window without doing anything. + """ + logger.verbose("GuiDocSplit close button clicked") + self.close() + return + + ## + # Internal Functions + ## + + def _populateList(self): + """Get the item selected in the tree, check that it is a folder, + and try to find all files associated with it. The valid files + are then added to the list view in order. The list itself can be + reordered by the user. + """ + + if self.sourceItem is None: + self.sourceItem = self.theParent.treeView.getSelectedHandle() + + if self.sourceItem is None: + return + + nwItem = self.theProject.getItem(self.sourceItem) + if nwItem is None: + return + if nwItem.itemType is not nwItemType.FILE: + self.theParent.makeAlert(( + "Element selected in the project tree must be a file." + ), nwAlert.ERROR) + return + + self.listBox.clear() + theDoc = NWDoc(self.theProject, self.theParent) + theText = theDoc.openDocument(self.sourceItem, False) + + spLevel = self.splitLevel.currentData() + logger.debug("Scanning document %s for headings level <= %d" % (self.sourceItem, spLevel)) + + lineNo = 0 + for aLine in theText.splitlines(): + + lineNo += 1 + onLine = 0 + + if aLine.startswith("# ") and spLevel >= 1: + onLine = lineNo + elif aLine.startswith("## ") and spLevel >= 2: + onLine = lineNo + elif aLine.startswith("### ") and spLevel >= 3: + onLine = lineNo + elif aLine.startswith("#### ") and spLevel >= 4: + onLine = lineNo + + if onLine > 0: + newItem = QListWidgetItem() + newItem.setText(aLine.strip()) + newItem.setData(Qt.UserRole, onLine) + self.listBox.addItem(newItem) + + return + +# END Class GuiDocSplit diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 580aacaf..ba75e779 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -372,10 +372,15 @@ class GuiMainMenu(QMenuBar): # Document > Merge Documents self.aMergeDocs = QAction("Merge Folder to Document", self) self.aMergeDocs.setStatusTip("Merge a folder of documents to a single document") - # self.aMergeDocs.setShortcut("Ctrl+Shift+I") self.aMergeDocs.triggered.connect(self.theParent.mergeDocuments) self.docuMenu.addAction(self.aMergeDocs) + # Document > Split Document + self.aSplitDoc = QAction("Split Document to Folder", self) + self.aSplitDoc.setStatusTip("Split a document into a folder of multiple documents") + self.aSplitDoc.triggered.connect(self.theParent.splitDocument) + self.docuMenu.addAction(self.aSplitDoc) + return def _buildViewMenu(self): diff --git a/nw/guimain.py b/nw/guimain.py index 4572ee01..bfc67115 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -24,10 +24,10 @@ from PyQt5.QtWidgets import ( ) from nw.gui import ( - GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, - GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, - GuiDocViewDetails, GuiConfigEditor, GuiProjectEditor, GuiExport, - GuiItemEditor, GuiTimeLineView, GuiSessionLogView, GuiDocMerge + GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiExport, + GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails, + GuiConfigEditor, GuiProjectEditor, GuiItemEditor, GuiTimeLineView, + GuiSessionLogView, GuiDocMerge, GuiDocSplit ) from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup from nw.tools import countWords @@ -83,7 +83,7 @@ class GuiMain(QMainWindow): # Assemble Main Window self.treePane = QFrame() - self.treeBox = QVBoxLayout() + self.treeBox = QVBoxLayout() self.treeBox.setContentsMargins(0,0,0,0) self.treeBox.addWidget(self.treeView) self.treeBox.addWidget(self.treeMeta) @@ -461,12 +461,17 @@ class GuiMain(QMainWindow): def mergeDocuments(self): """Merge multiple documents to one single new document. """ - if self.mainConf.showGUI: dlgMerge = GuiDocMerge(self, self.theProject) - if dlgMerge.exec_(): - pass + dlgMerge.exec_() + return True + def splitDocument(self): + """Split a single document into multiple documents. + """ + if self.mainConf.showGUI: + dlgSplit = GuiDocSplit(self, self.theProject) + dlgSplit.exec_() return True def passDocumentAction(self, theAction): diff --git a/nw/project/project.py b/nw/project/project.py index a52a83f3..e6167eb9 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -563,6 +563,11 @@ class NWProject(): """This only removes the item from the order list, but not from the project tree. """ + if tHandle not in self.treeOrder: + logger.warning( + "Could not remove item %s from treeOrder as it does not exist" % tHandle + ) + return False self.treeOrder.remove(tHandle) self.setProjectChanged(True) return True From 643a301e2d1b82c74ad125699a30fa8df2cdb7fc Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 5 Feb 2020 00:10:29 +0100 Subject: [PATCH 09/14] Some cleanup in the new doc split dialog --- nw/gui/dialogs/docsplit.py | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/nw/gui/dialogs/docsplit.py b/nw/gui/dialogs/docsplit.py index 3e8502b5..739bb14e 100644 --- a/nw/gui/dialogs/docsplit.py +++ b/nw/gui/dialogs/docsplit.py @@ -66,7 +66,7 @@ class GuiDocSplit(QDialog): self.closeButton.clicked.connect(self._doClose) self.doMergeForm.addWidget(self.listBox, 0, 0, 1, 3) - self.doMergeForm.addWidget(self.splitLevel, 1, 0, 1, 2) + self.doMergeForm.addWidget(self.splitLevel, 1, 0, 1, 3) self.doMergeForm.addWidget(self.splitButton, 2, 1) self.doMergeForm.addWidget(self.closeButton, 2, 2) @@ -112,6 +112,9 @@ class GuiDocSplit(QDialog): theLines = theText.splitlines() nLines = len(theLines) theLines.insert(0, "%Split Doc") + logger.debug( + "Splitting document %s with %d lines" % (self.sourceItem,nLines) + ) finalOrder = [] for i in range(self.listBox.count()): @@ -130,6 +133,7 @@ class GuiDocSplit(QDialog): fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemClass, srcItem.parHandle) self.theParent.treeView.revealTreeItem(fHandle) + logger.verbose("Creating folder %s" % fHandle) for wTitle, iStart, iEnd in finalOrder: @@ -146,11 +150,13 @@ class GuiDocSplit(QDialog): wTitle = wTitle.lstrip("#") wTitle = wTitle.strip() - print(wTitle, iStart, iEnd) nHandle = self.theProject.newFile(wTitle, srcItem.itemClass, fHandle) newItem = self.theProject.getItem(nHandle) newItem.setLayout(itemLayout) + logger.verbose( + "Creating new document %s with text from line %d to %d" % (nHandle, iStart, iEnd-1) + ) theText = "\n".join(theLines[iStart:iEnd]) theDoc.openDocument(nHandle, False) @@ -158,25 +164,6 @@ class GuiDocSplit(QDialog): theDoc.clearDocument() self.theParent.treeView.revealTreeItem(nHandle) - # theDoc = NWDoc(self.theProject, self.theParent) - # theText = "" - # for tHandle in finalOrder: - # theText += theDoc.openDocument(tHandle, False).rstrip() - # theText += "\n\n" - - # if self.sourceItem is None: - # self.theParent.makeAlert(( - # "Cannot parse source item." - # ), nwAlert.ERROR) - # return - - # srcItem = self.theProject.getItem(self.sourceItem) - # nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.parHandle) - # self.theParent.treeView.revealTreeItem(nHandle) - # theDoc.openDocument(nHandle, False) - # theDoc.saveDocument(theText) - # self.theParent.openDocument(nHandle) - self.close() return From 6cfa2853fe1d71c424612c2d81408dc3d5e3ae34 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 5 Feb 2020 01:02:18 +0100 Subject: [PATCH 10/14] trying to make travis run again --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d40b1c68..bba7e64e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,17 @@ addons: - libenchant-dev - python3-pyqt5 - python3-pyqt5.qtsvg + - python3-appdirs + - python3-lxml + - python3-enchant python: - "3.5" - "3.7" install: - - pip install -r requirements.txt +# - pip install -r requirements.txt # - pip install pytest-faulthandler + - pip install latexcodec + - pip install pypandoc - pip install pytest-xvfb - pip install pytest-cov - pip install pytest-qt From f58b7afd43431a7672d135ef261cb94b221a7c9b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 5 Feb 2020 01:10:01 +0100 Subject: [PATCH 11/14] trying to make travis run again --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index bba7e64e..82955733 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,19 +9,19 @@ addons: apt: packages: - libenchant-dev - - python3-pyqt5 - - python3-pyqt5.qtsvg - - python3-appdirs - - python3-lxml - - python3-enchant +# - python3-pyqt5 +# - python3-pyqt5.qtsvg +# - python3-appdirs +# - python3-lxml +# - python3-enchant python: - "3.5" - "3.7" install: -# - pip install -r requirements.txt + - pip install -r requirements.txt # - pip install pytest-faulthandler - - pip install latexcodec - - pip install pypandoc +# - pip install latexcodec +# - pip install pypandoc - pip install pytest-xvfb - pip install pytest-cov - pip install pytest-qt From 5d971508426e973361430b5b186bc0f748a830bd Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 5 Feb 2020 01:13:07 +0100 Subject: [PATCH 12/14] trying to make travis run again --- .travis.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82955733..8097a051 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,19 +9,15 @@ addons: apt: packages: - libenchant-dev -# - python3-pyqt5 -# - python3-pyqt5.qtsvg -# - python3-appdirs -# - python3-lxml -# - python3-enchant + - python3-pyqt5 + - python3-pyqt5.qtsvg python: - "3.5" - "3.7" install: + - pip install --upgrade pip - pip install -r requirements.txt # - pip install pytest-faulthandler -# - pip install latexcodec -# - pip install pypandoc - pip install pytest-xvfb - pip install pytest-cov - pip install pytest-qt From cac75abcafdeed688a2e69f5faa930137b139e99 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 5 Feb 2020 01:22:19 +0100 Subject: [PATCH 13/14] Switch ubutu distro on travis builds --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8097a051..5958aeec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ os: linux -dist: xenial +dist: bionic services: - xvfb language: python cache: bundler -sudo: required addons: apt: packages: @@ -13,7 +12,7 @@ addons: - python3-pyqt5.qtsvg python: - "3.5" - - "3.7" + - "3.8" install: - pip install --upgrade pip - pip install -r requirements.txt From 123d64bba93a2da5f49bd02e3317120fe1bd5a4e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 5 Feb 2020 01:27:17 +0100 Subject: [PATCH 14/14] Dropping Python 3.5 support --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5958aeec..de4a683a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,8 @@ addons: - python3-pyqt5 - python3-pyqt5.qtsvg python: - - "3.5" + - "3.6" + - "3.7" - "3.8" install: - pip install --upgrade pip