Remove timeline view so we can clean up the indexing class

This commit is contained in:
Veronica K. B. Olsen
2019-11-17 13:09:21 +01:00
parent 19ebedd8b3
commit 816c708853
8 changed files with 20 additions and 388 deletions
-2
View File
@@ -12,7 +12,6 @@ from nw.gui.dialogs.export import GuiExport
from nw.gui.dialogs.itemeditor import GuiItemEditor from nw.gui.dialogs.itemeditor import GuiItemEditor
from nw.gui.dialogs.projecteditor import GuiProjectEditor from nw.gui.dialogs.projecteditor import GuiProjectEditor
from nw.gui.dialogs.sessionlog import GuiSessionLogView from nw.gui.dialogs.sessionlog import GuiSessionLogView
from nw.gui.dialogs.timelineview import GuiTimeLineView
# GUI Elements # GUI Elements
from nw.gui.elements.docdetails import GuiDocDetails from nw.gui.elements.docdetails import GuiDocDetails
@@ -38,7 +37,6 @@ __all__ = [
"GuiItemEditor", "GuiItemEditor",
"GuiProjectEditor", "GuiProjectEditor",
"GuiSessionLogView", "GuiSessionLogView",
"GuiTimeLineView",
"GuiDocDetails", "GuiDocDetails",
"GuiDocEditor", "GuiDocEditor",
"GuiDocTree", "GuiDocTree",
-2
View File
@@ -5,7 +5,6 @@ from nw.gui.dialogs.export import GuiExport
from nw.gui.dialogs.itemeditor import GuiItemEditor from nw.gui.dialogs.itemeditor import GuiItemEditor
from nw.gui.dialogs.projecteditor import GuiProjectEditor from nw.gui.dialogs.projecteditor import GuiProjectEditor
from nw.gui.dialogs.sessionlog import GuiSessionLogView from nw.gui.dialogs.sessionlog import GuiSessionLogView
from nw.gui.dialogs.timelineview import GuiTimeLineView
__all__ = [ __all__ = [
"GuiConfigEditor", "GuiConfigEditor",
@@ -13,5 +12,4 @@ __all__ = [
"GuiItemEditor", "GuiItemEditor",
"GuiProjectEditor", "GuiProjectEditor",
"GuiSessionLogView", "GuiSessionLogView",
"GuiTimeLineView",
] ]
-273
View File
@@ -1,273 +0,0 @@
# -*- coding: utf-8 -*-
"""novelWriter GUI Timeline View
novelWriter GUI Timeline View
=================================
Class holding the timeline view window
File History:
Created: 2019-05-30 [0.1.4]
"""
import logging
import nw
from os import path
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor, QPixmap
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QLabel,
QDialogButtonBox, QPushButton, QHeaderView, QGridLayout, QGroupBox,
QCheckBox
)
from nw.constants import nwFiles, nwItemClass
from nw.tools import OptLastState
logger = logging.getLogger(__name__)
class GuiTimeLineView(QDialog):
def __init__(self, theParent, theProject, theIndex):
QDialog.__init__(self, theParent)
logger.debug("Initialising TimeLineView ...")
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
self.theIndex = theIndex
self.optState = TimeLineLastState(self.theProject,nwFiles.TLINE_OPT)
self.optState.loadSettings()
self.theMatrix = {}
self.numRows = 0
self.numCols = 0
self.outerBox = QVBoxLayout()
self.filterBox = QVBoxLayout()
self.centreBox = QHBoxLayout()
self.bottomBox = QHBoxLayout()
self.setWindowTitle("Timeline View")
self.setMinimumWidth(700)
self.setMinimumHeight(400)
winWidth = self.optState.validIntRange(
self.optState.getSetting("winWidth"), 700, 10000, 700
)
winHeight = self.optState.validIntRange(
self.optState.getSetting("winHeight"), 400, 10000, 400
)
self.resize(winWidth,winHeight)
# TimeLine Table
self.mainTable = QTableWidget()
self.mainTable.setGridStyle(Qt.NoPen)
self.hHeader = self.mainTable.horizontalHeader()
self.hHeader.setSectionResizeMode(QHeaderView.ResizeToContents)
self.mainTable.setHorizontalHeader(self.hHeader)
self.vHeader = self.mainTable.verticalHeader()
self.vHeader.setSectionResizeMode(QHeaderView.ResizeToContents)
self.mainTable.setVerticalHeader(self.vHeader)
# Option Box
self.optFilter = QGroupBox("Include Tags", self)
self.optFilterGrid = QGridLayout(self)
self.optFilter.setLayout(self.optFilterGrid)
self.filterPlot = QCheckBox("Plot tags", self)
self.filterPlot.setChecked(self.optState.getSetting("fPlot"))
self.filterPlot.stateChanged.connect(self._filterChange)
self.filterChar = QCheckBox("Character tags", self)
self.filterChar.setChecked(self.optState.getSetting("fChar"))
self.filterChar.stateChanged.connect(self._filterChange)
self.filterWorld = QCheckBox("Location tags", self)
self.filterWorld.setChecked(self.optState.getSetting("fWorld"))
self.filterWorld.stateChanged.connect(self._filterChange)
self.filterTime = QCheckBox("Timeline tags", self)
self.filterTime.setChecked(self.optState.getSetting("fTime"))
self.filterTime.stateChanged.connect(self._filterChange)
self.filterObject = QCheckBox("Object tags", self)
self.filterObject.setChecked(self.optState.getSetting("fObject"))
self.filterObject.stateChanged.connect(self._filterChange)
self.filterCustom = QCheckBox("Custom tags", self)
self.filterCustom.setChecked(self.optState.getSetting("fCustom"))
self.filterCustom.stateChanged.connect(self._filterChange)
self.optFilterGrid.addWidget(self.filterPlot, 0, 1)
self.optFilterGrid.addWidget(self.filterChar, 1, 1)
self.optFilterGrid.addWidget(self.filterWorld, 2, 1)
self.optFilterGrid.addWidget(self.filterTime, 3, 1)
self.optFilterGrid.addWidget(self.filterObject, 4, 1)
self.optFilterGrid.addWidget(self.filterCustom, 5, 1)
self.optHide = QGroupBox("Filters", self)
self.optHideGrid = QGridLayout(self)
self.optHide.setLayout(self.optHideGrid)
self.hideUnused = QCheckBox("Hide unused", self)
self.hideUnused.setChecked(self.optState.getSetting("hUnused"))
self.hideUnused.stateChanged.connect(self._filterChange)
self.optHideGrid.addWidget(self.hideUnused, 0, 1)
# Button Box
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
self.buttonBox.rejected.connect(self._doClose)
self.btnRebuild = QPushButton("Rebuild Index")
self.btnRebuild.clicked.connect(self.theParent.rebuildIndex)
self.btnRefresh = QPushButton("Refresh Table")
self.btnRefresh.clicked.connect(self._buildNovelList)
self.bottomBox.addWidget(self.btnRebuild)
self.bottomBox.addWidget(self.btnRefresh)
self.bottomBox.addStretch()
self.bottomBox.addWidget(self.buttonBox)
# Assemble
self.filterBox.addWidget(self.optFilter)
self.filterBox.addWidget(self.optHide)
self.filterBox.addStretch()
self.centreBox.addWidget(self.mainTable)
self.centreBox.addLayout(self.filterBox)
self.outerBox.addLayout(self.centreBox)
self.outerBox.addLayout(self.bottomBox)
self.setLayout(self.outerBox)
self._buildNovelList()
self.buttonBox.setFocus()
self.show()
logger.debug("TimeLineView initialisation complete")
return
def _buildNovelList(self):
self.mainTable.clear()
self.theIndex.buildNovelList()
self.numRows = len(self.theIndex.novelList)
self.mainTable.setRowCount(self.numRows)
theFilters = {}
theFilters["exClass"] = []
theFilters["hUnused"] = self.hideUnused.isChecked()
if not self.filterPlot.isChecked():
theFilters["exClass"].append(nwItemClass.PLOT)
if not self.filterChar.isChecked():
theFilters["exClass"].append(nwItemClass.CHARACTER)
if not self.filterWorld.isChecked():
theFilters["exClass"].append(nwItemClass.WORLD)
if not self.filterTime.isChecked():
theFilters["exClass"].append(nwItemClass.TIMELINE)
if not self.filterObject.isChecked():
theFilters["exClass"].append(nwItemClass.OBJECT)
if not self.filterCustom.isChecked():
theFilters["exClass"].append(nwItemClass.CUSTOM)
for n in range(len(self.theIndex.novelList)):
iDepth = self.theIndex.novelList[n][1]
iTitle = self.theIndex.novelList[n][2]
newItem = QTableWidgetItem("%s%s " % (" "*iDepth,iTitle))
self.mainTable.setVerticalHeaderItem(n, newItem)
theMap = self.theIndex.buildTagNovelMap(self.theIndex.tagIndex.keys(), theFilters)
self.numCols = len(theMap.keys())
self.mainTable.setColumnCount(self.numCols)
nCol = 0
for theTag, theCols in theMap.items():
newItem = QTableWidgetItem(" %s " % theTag)
self.mainTable.setHorizontalHeaderItem(nCol, newItem)
for n in range(len(theCols)):
if theCols[n] == 1:
pxNew = QPixmap(10,10)
pxNew.fill(QColor(0,120,0))
lblNew = QLabel()
lblNew.setPixmap(pxNew)
lblNew.setAlignment(Qt.AlignCenter)
lblNew.setAttribute(Qt.WA_TranslucentBackground)
self.mainTable.setCellWidget(n, nCol, lblNew)
elif theCols[n] == 2:
pxNew = QPixmap(10,10)
pxNew.fill(QColor(0,0,120))
lblNew = QLabel()
lblNew.setPixmap(pxNew)
lblNew.setAlignment(Qt.AlignCenter)
lblNew.setAttribute(Qt.WA_TranslucentBackground)
self.mainTable.setCellWidget(n, nCol, lblNew)
nCol += 1
return
def _doClose(self):
logger.verbose("GuiTimeLineView close button clicked")
winWidth = self.width()
winHeight = self.height()
fPlot = self.filterPlot.isChecked()
fChar = self.filterChar.isChecked()
fWorld = self.filterWorld.isChecked()
fTime = self.filterTime.isChecked()
fObject = self.filterObject.isChecked()
fCustom = self.filterCustom.isChecked()
hUnused = self.hideUnused.isChecked()
self.optState.setSetting("winWidth", winWidth)
self.optState.setSetting("winHeight",winHeight)
self.optState.setSetting("fPlot", fPlot)
self.optState.setSetting("fChar", fChar)
self.optState.setSetting("fWorld", fWorld)
self.optState.setSetting("fTime", fTime)
self.optState.setSetting("fObject", fObject)
self.optState.setSetting("fCustom", fCustom)
self.optState.setSetting("hUnused", hUnused)
self.optState.saveSettings()
self.close()
return
def _filterChange(self, checkState):
self._buildNovelList()
return
# END Class GuiTimeLineView
class TimeLineLastState(OptLastState):
def __init__(self, theProject, theFile):
OptLastState.__init__(self, theProject, theFile)
self.theState = {
"winWidth" : 700,
"winHeight" : 400,
"fPlot" : True,
"fChar" : True,
"fWorld" : True,
"fTime" : True,
"fObject" : True,
"fCustom" : True,
"hUnused" : True,
}
self.stringOpt = ()
self.boolOpt = ("fPlot","fChar","fWorld","fTime","fObject","fCustom","hUnused")
self.intOpt = ("winWidth","winHeight")
return
# END Class TimeLineLastState
+1 -1
View File
@@ -87,7 +87,7 @@ class GuiDocViewDetails(QWidget):
if self.isSticky.isChecked(): if self.isSticky.isChecked():
return return
theRefs = self.theParent.theIndex.buildReferenceList(tHandle) theRefs = self.theParent.theIndex.getBackReferenceList(tHandle)
theList = [] theList = []
for tHandle in theRefs: for tHandle in theRefs:
tItem = self.theProject.getItem(tHandle) tItem = self.theProject.getItem(tHandle)
-10
View File
@@ -416,16 +416,6 @@ class GuiMainMenu(QMenuBar):
self.aFullScreen.triggered.connect(self.theParent.toggleFullScreenMode) self.aFullScreen.triggered.connect(self.theParent.toggleFullScreenMode)
self.viewMenu.addAction(self.aFullScreen) self.viewMenu.addAction(self.aFullScreen)
# View > Separator
self.viewMenu.addSeparator()
# View > Project Timeline
self.aViewTimeLine = QAction("Show Project Timeline", self)
self.aViewTimeLine.setStatusTip("Open the project timeline window")
self.aViewTimeLine.setShortcut("Ctrl+T")
self.aViewTimeLine.triggered.connect(self.theParent.showTimeLineDialog)
self.viewMenu.addAction(self.aViewTimeLine)
return return
def _buildEditMenu(self): def _buildEditMenu(self):
+1 -8
View File
@@ -27,7 +27,7 @@ from nw.gui import (
GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor,
GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar,
GuiDocViewDetails, GuiConfigEditor, GuiProjectEditor, GuiExport, GuiDocViewDetails, GuiConfigEditor, GuiProjectEditor, GuiExport,
GuiItemEditor, GuiTimeLineView, GuiSessionLogView, GuiProjectOutline GuiItemEditor, GuiSessionLogView, GuiProjectOutline
) )
from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup
from nw.tools import countWords from nw.tools import countWords
@@ -619,12 +619,6 @@ class GuiMain(QMainWindow):
dlgExport.exec_() dlgExport.exec_()
return True return True
def showTimeLineDialog(self):
if self.hasProject:
dlgTLine = GuiTimeLineView(self, self.theProject, self.theIndex)
dlgTLine.exec_()
return True
def showSessionLogDialog(self): def showSessionLogDialog(self):
if self.hasProject: if self.hasProject:
dlgTLine = GuiSessionLogView(self, self.theProject) dlgTLine = GuiSessionLogView(self, self.theProject)
@@ -778,7 +772,6 @@ class GuiMain(QMainWindow):
self.addAction(self.mainMenu.aFileDetails) self.addAction(self.mainMenu.aFileDetails)
self.addAction(self.mainMenu.aZenMode) self.addAction(self.mainMenu.aZenMode)
self.addAction(self.mainMenu.aFullScreen) self.addAction(self.mainMenu.aFullScreen)
self.addAction(self.mainMenu.aViewTimeLine)
self.addAction(self.mainMenu.aEditUndo) self.addAction(self.mainMenu.aEditUndo)
self.addAction(self.mainMenu.aEditRedo) self.addAction(self.mainMenu.aEditRedo)
self.addAction(self.mainMenu.aEditCut) self.addAction(self.mainMenu.aEditCut)
+18 -70
View File
@@ -17,7 +17,7 @@ import nw
from os import path from os import path
from nw.constants import ( from nw.constants import (
nwFiles, nwKeyWords, nwItemType, nwItemClass, nwAlert nwFiles, nwKeyWords, nwItemType, nwItemClass, nwItemLayout, nwAlert
) )
from nw.tools import countWords from nw.tools import countWords
@@ -66,9 +66,6 @@ class NWIndex():
self.textCounts = {} self.textCounts = {}
self.textSynopsis = {} self.textSynopsis = {}
# Lists
self.novelList = []
return return
## ##
@@ -261,23 +258,28 @@ class NWIndex():
""" """
theItem = self.theProject.getItem(tHandle) theItem = self.theProject.getItem(tHandle)
if theItem is None: return False if theItem is None:
if theItem.itemType != nwItemType.FILE: return False return False
if theItem.parHandle == self.theProject.trashRoot: return False if theItem.itemType != nwItemType.FILE:
return False
if theItem.parHandle == self.theProject.trashRoot:
return False
if theItem.itemLayout == nwItemLayout.NO_LAYOUT:
return False
itemClass = theItem.itemClass itemClass = theItem.itemClass
itemLayout = theItem.itemLayout itemLayout = theItem.itemLayout
logger.debug("Indexing item with handle %s" % tHandle) logger.debug("Indexing item with handle %s" % tHandle)
# Check file type, and reset its old index # Check file type, and reset its old index
if itemClass == nwItemClass.NOVEL: self.refIndex[tHandle] = []
self.novelIndex[tHandle] = [] if itemLayout == nwItemLayout.NOTE:
self.refIndex[tHandle] = []
isNovel = True
else:
self.noteIndex[tHandle] = [] self.noteIndex[tHandle] = []
self.refIndex[tHandle] = []
isNovel = False isNovel = False
else:
self.novelIndex[tHandle] = []
isNovel = True
# Also clear references to file in tag index # Also clear references to file in tag index
clearTags = [] clearTags = []
@@ -296,7 +298,8 @@ class NWIndex():
aLine = aLine aLine = aLine
nLine += 1 nLine += 1
nChar = len(aLine.strip()) nChar = len(aLine.strip())
if nChar == 0: continue if nChar == 0:
continue
if aLine.startswith(r"#"): if aLine.startswith(r"#"):
isTitle = self.indexTitle(tHandle, isNovel, aLine, nLine, itemLayout) isTitle = self.indexTitle(tHandle, isNovel, aLine, nLine, itemLayout)
@@ -513,20 +516,7 @@ class NWIndex():
return theRefs return theRefs
def buildNovelList(self): def getBackReferenceList(self, tHandle):
"""Build a list of the content of the novel.
"""
self.novelList = []
self.novelOrder = []
for tHandle in self.theProject.treeOrder:
if tHandle not in self.novelIndex:
continue
for tEntry in self.novelIndex[tHandle]:
self.novelList.append(tEntry)
self.novelOrder.append("%s:%d" % (tHandle,tEntry[0]))
return True
def buildReferenceList(self, tHandle):
"""Build a list of files referring back to our file, specified """Build a list of files referring back to our file, specified
by tHandle. by tHandle.
""" """
@@ -560,46 +550,4 @@ class NWIndex():
return theRef[1], theRef[0] return theRef[1], theRef[0]
return None, 0 return None, 0
def buildTagNovelMap(self, theTags, theFilters=None):
"""Build a two-dimensional map of all titles of the novel and
which tags they link to from the various meta tags. This map is
used to display the timeline view.
"""
tagMap = {}
tagClass = {}
exClass = []
if theFilters is not None:
if "exClass" in theFilters.keys():
exClass = theFilters["exClass"]
for theTag in theTags:
try:
tagClass[theTag] = nwItemClass[self.tagIndex[theTag][2]]
except:
logger.error("Could not map '%s' to nwItemClass" % self.tagIndex[theTag][2])
tagClass[theTag] = None
if tagClass[theTag] not in exClass:
tagMap[theTag] = [0]*len(self.novelOrder)
for tHandle in self.refIndex:
for nLine, tKey, tTag, nTitle in self.refIndex[tHandle]:
if tTag in tagMap.keys() and tKey in self.TAG_CLASS:
try:
nPos = self.novelOrder.index("%s:%d" % (tHandle, nTitle))
if self.TAG_CLASS[tKey][0] == tagClass[tTag]:
tagMap[tTag][nPos] = self.TAG_CLASS[tKey][1]
except:
logger.error("Could not find '%s:%d' in novelOrder" % (tHandle, nTitle))
if theFilters["hUnused"]:
tagMapFiltered = {}
for theTag in tagMap.keys():
if sum(tagMap[theTag]) > 0:
tagMapFiltered[theTag] = tagMap[theTag]
return tagMapFiltered
return tagMap
# END Class NWIndex # END Class NWIndex
-22
View File
@@ -95,25 +95,3 @@ def testIndexScanThis(nwTempProj):
assert isValid assert isValid
assert str(theBits) == "['@tag', 'this', 'and this']" assert str(theBits) == "['@tag', 'this', 'and this']"
assert str(thePos) == "[0, 6, 12]" assert str(thePos) == "[0, 6, 12]"
@pytest.mark.project
def testBuildIndex(nwTempProj):
projFile = path.join(nwTempProj,"nwProject.nwx")
assert theProject.openProject(projFile)
theIndex = NWIndex(theProject,theMain)
tHandle = "31489056e0916"
theIndex.scanText(tHandle, (
"# Novel\n\n"
"## Chapter\n\n"
"### Scene\n\n"
"#### Section\n\n"
"@pov: John\n"
"@char: Jane\n"
"@location: Somewhere\n"
))
assert theIndex.buildNovelList()
assert str(theIndex.novelList) == "[[1, 1, 'Novel', 'SCENE'], [3, 2, 'Chapter', 'SCENE'], [5, 3, 'Scene', 'SCENE'], [7, 4, 'Section', 'SCENE']]"
assert str(theIndex.novelOrder) == "['31489056e0916:1', '31489056e0916:3', '31489056e0916:5', '31489056e0916:7']"