Added some the tree building code, with the help of 3 G&Ts.

This commit is contained in:
Veronica K. B. Olsen
2018-10-27 23:54:34 +02:00
parent c8dab032b9
commit 0f8090601b
6 changed files with 314 additions and 29 deletions
+11 -2
View File
@@ -52,7 +52,8 @@ class Config:
self.confChanged = False self.confChanged = False
## General ## General
self.winGeometry = [1100, 650] self.winGeometry = [1100, 650]
self.treeColWidth = [120, 30, 50]
# Check if config file exists # Check if config file exists
if path.isfile(path.join(self.confPath,self.confFile)): if path.isfile(path.join(self.confPath,self.confFile)):
@@ -83,6 +84,10 @@ class Config:
self.winGeometry = self.unpackList( self.winGeometry = self.unpackList(
confParser.get(cnfSec,"geometry"), 2, self.winGeometry confParser.get(cnfSec,"geometry"), 2, self.winGeometry
) )
if confParser.has_option(cnfSec,"treecols"):
self.treeColWidth = self.unpackList(
confParser.get(cnfSec,"treecols"), 3, self.treeColWidth
)
## Path ## Path
cnfSec = "Path" cnfSec = "Path"
@@ -105,6 +110,7 @@ class Config:
confParser.add_section(cnfSec) confParser.add_section(cnfSec)
confParser.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) confParser.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
confParser.set(cnfSec,"geometry", self.packList(self.winGeometry)) confParser.set(cnfSec,"geometry", self.packList(self.winGeometry))
confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth))
## Path ## Path
cnfSec = "Path" cnfSec = "Path"
@@ -142,7 +148,6 @@ class Config:
self.recentList.insert(0,recentPath) self.recentList.insert(0,recentPath)
return return
def setConfPath(self, newPath): def setConfPath(self, newPath):
if newPath is None: return if newPath is None: return
if not path.isfile(newPath): if not path.isfile(newPath):
@@ -161,4 +166,8 @@ class Config:
self.confChanged = True self.confChanged = True
return return
def setTreeColWidths(self, colWidths):
self.treeColWidth = colWidths
return
# End Class Config # End Class Config
+61 -11
View File
@@ -15,28 +15,78 @@ import nw
from os import path from os import path
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QTreeView from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QTreeWidgetItemIterator, QAbstractItemView
from PyQt5.QtGui import QStandardItemModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocTree(QTreeView): class GuiDocTree(QTreeWidget):
def __init__(self): def __init__(self, theProject):
QTreeView.__init__(self) QTreeWidget.__init__(self)
logger.debug("Initialising DocTree ...") logger.debug("Initialising DocTree ...")
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.debugGUI = self.mainConf.debugGUI
self.theProject = theProject
self.theMap = {}
self.treeModel = QStandardItemModel(0, 3, self) self.setColumnCount(4)
self.treeModel.setHeaderData(0, Qt.Horizontal, "Name") self.setHeaderLabels(["Name","","","Handle"])
self.treeModel.setHeaderData(1, Qt.Horizontal, "Words") if not self.debugGUI:
self.treeModel.setHeaderData(2, Qt.Horizontal, "Status") self.hideColumn(3)
self.setModel(self.treeModel) # Allow Move by Drag & Drop
self.setDragEnabled(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
for colN in range(len(self.mainConf.treeColWidth)):
self.setColumnWidth(colN,self.mainConf.treeColWidth[colN])
logger.debug("DocTree initialisation complete") logger.debug("DocTree initialisation complete")
return return
def saveTreeOrder(self):
theList = []
for i in range(self.topLevelItemCount()):
theList = self._scanChildren(theList, self.topLevelItem(i))
print(theList)
return True
def _scanChildren(self, theList, theItem):
theList.append(theItem.text(3))
for i in range(theItem.childCount()):
self._scanChildren(theList, theItem.child(i))
return theList
def buildTree(self):
for tHandle in self.theProject.projTree:
nwItem = self.theProject.projTree[tHandle]
tName = nwItem.itemName
tStatus = 0
wCount = 0
pHandle = nwItem.parHandle
newItem = QTreeWidgetItem([
tName, str(tStatus), str(wCount), tHandle
])
self.theMap[tHandle] = newItem
if pHandle is None:
self.addTopLevelItem(newItem)
else:
self.theMap[pHandle].addChild(newItem)
return True
def getColumnSizes(self):
retVals = [
self.columnWidth(0),
self.columnWidth(1),
self.columnWidth(2),
]
return retVals
# END Class GuiDocTree # END Class GuiDocTree
+7 -5
View File
@@ -46,8 +46,8 @@ class GuiMain(QMainWindow):
self.splitMain.addWidget(self.treePane) self.splitMain.addWidget(self.treePane)
self.splitMain.addWidget(self.docEditor) self.splitMain.addWidget(self.docEditor)
self.treeBox = QVBoxLayout() self.treeBox = QVBoxLayout()
self.treeView = GuiDocTree() self.treeView = GuiDocTree(self.theProject)
self.treeBox.addWidget(self.treeView) self.treeBox.addWidget(self.treeView)
self.treePane.setLayout(self.treeBox) self.treePane.setLayout(self.treeBox)
@@ -58,8 +58,7 @@ class GuiMain(QMainWindow):
self.setCentralWidget(self.splitMain) self.setCentralWidget(self.splitMain)
self._buildMenu() self._buildMenu()
self.treeView.buildTree()
# self.docTabs.createTab()
self.show() self.show()
@@ -76,7 +75,8 @@ class GuiMain(QMainWindow):
def newProject(self): def newProject(self):
logger.info("Creating new project") logger.info("Creating new project")
self.docTabs.createTab(tabType=nw.DOCTYPE_PROJECT) self.theProject.newProject()
self.treeView.buildTree()
return return
def openProject(self): def openProject(self):
@@ -101,6 +101,7 @@ class GuiMain(QMainWindow):
self.theProject.setProjectPath(projPath) self.theProject.setProjectPath(projPath)
else: else:
return False return False
self.treeView.saveTreeOrder()
self.theProject.saveProject() self.theProject.saveProject()
return True return True
@@ -130,6 +131,7 @@ class GuiMain(QMainWindow):
def _closeMain(self): def _closeMain(self):
logger.info("Exiting %s" % nw.__package__) logger.info("Exiting %s" % nw.__package__)
self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setWinSize(self.width(), self.height())
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
self.mainConf.saveConfig() self.mainConf.saveConfig()
return return
+103
View File
@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*
"""novelWriter Project Item
novelWriter Project Item
============================
Class holding a project item
File History:
Created: 2018-10-27 [0.1.0]
"""
import logging
import nw
from os import path, mkdir
from lxml import etree
from datetime import datetime
logger = logging.getLogger(__name__)
class NWItem():
TYPE_ROOT = 0
TYPE_FOLDER = 1
TYPE_FILE = 2
CLASS_NONE = 0
CLASS_NOVEL = 1
CLASS_CHARACTER = 2
CLASS_WORLD = 3
def __init__(self):
self.itemName = ""
self.itemHandle = None
self.parHandle = None
self.itemOrder = None
self.itemType = None
self.itemClass = None
return
def setName(self, theName):
self.itemName = theName.strip()
return
def setHandle(self, theHandle):
self.itemHandle = theHandle
return
def setParent(self, theParent):
self.parHandle = theParent
return
def setOrder(self, theOrder):
self.itemOrder = theOrder
return
def setType(self, theType):
if isinstance(theType, int):
self.itemType = theType
elif theType == "ROOT":
self.itemType = self.TYPE_ROOT
elif theType == "FOLDER":
self.itemType = self.TYPE_FOLDER
elif theType == "FILE":
self.itemType = self.TYPE_FILE
else:
logger.error("Unrecognised item type '%s'" % theType)
self.itemType = None
return
def setClass(self, theClass):
if isinstance(theClass, int):
self.itemClass = theClass
elif theClass == "NONE":
self.itemClass = self.CLASS_NONE
elif theClass == "NOVEL":
self.itemClass = self.CLASS_NOVEL
elif theClass == "CHARACTER":
self.itemClass = self.CLASS_CHARACTER
elif theClass == "WORLD":
self.itemClass = self.CLASS_WORLD
else:
logger.error("Unrecognised root item '%s'" % theClass)
self.itemClass = None
return
def getType(self):
if self.itemType == self.TYPE_ROOT: return "ROOT"
if self.itemType == self.TYPE_FOLDER: return "FOLDER"
if self.itemType == self.TYPE_FILE: return "FILE"
return "NONE"
def getClass(self):
if self.itemClass == self.CLASS_NONE: return "NONE"
if self.itemClass == self.CLASS_NOVEL: return "NOVEL"
if self.itemClass == self.CLASS_CHARACTER: return "CHARACTER"
if self.itemClass == self.CLASS_WORLD: return "WORLD"
return "NONE"
# END Class NWItem
+106 -10
View File
@@ -13,9 +13,12 @@
import logging import logging
import nw import nw
from os import path, mkdir from os import path, mkdir
from lxml import etree from lxml import etree
from datetime import datetime from hashlib import sha256
from datetime import datetime, time
from nw.project.item import NWItem
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -23,10 +26,13 @@ class NWProject():
def __init__(self): def __init__(self):
self.mainConf = nw.CONFIG # Internal
self.projTree = [] self.mainConf = nw.CONFIG
self.seedCount = 0
# Project Settings # Project Settings
self.projTree = {}
self.treeOrder = []
self.projPath = None self.projPath = None
self.projFile = "nwProject.nwx" self.projFile = "nwProject.nwx"
self.projName = "" self.projName = ""
@@ -36,9 +42,47 @@ class NWProject():
return return
def buildProjectTree(self): def buildProjectTree(self):
return True return True
def newRoot(self, rootName, rootType):
newItem = NWItem()
newItem.setName(rootName)
newItem.setType(NWItem.TYPE_ROOT)
newItem.setClass(rootType)
self._appendItem(None,None,newItem)
return newItem.itemHandle
def newFolder(self, folderName, pHandle):
newItem = NWItem()
newItem.setName(folderName)
newItem.setType(NWItem.TYPE_FOLDER)
newItem.setClass(NWItem.CLASS_NONE)
self._appendItem(None,pHandle,newItem)
return newItem.itemHandle
def newFile(self, fileName, pHandle):
newItem = NWItem()
newItem.setName(fileName)
newItem.setType(NWItem.TYPE_FILE)
newItem.setClass(NWItem.CLASS_NONE)
self._appendItem(None,pHandle,newItem)
return newItem.itemHandle
def newProject(self):
self.projTree = {}
self.treeOrder - []
self.projPath = None
self.projFile = "nwProject.nwx"
self.projName = ""
self.bookTitle = ""
self.bookAuthors = []
hNovel = self.newRoot("Novel", NWItem.CLASS_NOVEL)
hChars = self.newRoot("Characters",NWItem.CLASS_CHARACTER)
hWorld = self.newRoot("World", NWItem.CLASS_WORLD)
hChapt = self.newFolder("New Chapter", hNovel)
hScene = self.newFile("New Scene", hChapt)
return
def openProject(self, fileName): def openProject(self, fileName):
if not path.isfile(fileName): if not path.isfile(fileName):
@@ -108,6 +152,24 @@ class NWProject():
xBookAuthor = etree.SubElement(xProject,"author") xBookAuthor = etree.SubElement(xProject,"author")
xBookAuthor.text = bookAuthor xBookAuthor.text = bookAuthor
# Save Tree Content
xContent = etree.SubElement(nwXML,"content",attrib={"count":str(len(self.projTree))})
itemIdx = 0
for tHandle in self.projTree.keys():
nwItem = self.projTree[tHandle]
xItem = etree.SubElement(xContent,"item",attrib={
"handle" : str(tHandle),
"parent" : str(nwItem.parHandle),
"order" : str(nwItem.itemOrder),
})
xItemName = etree.SubElement(xItem,"name")
xItemName.text = str(nwItem.itemName)
xItemType = etree.SubElement(xItem,"type")
xItemType.text = str(nwItem.getType())
if nwItem.getClass() is not "NONE":
xItemClass = etree.SubElement(xItem,"class")
xItemClass.text = str(nwItem.getClass())
# Write the xml tree to file # Write the xml tree to file
with open(path.join(self.projPath,self.projFile),"wb") as outFile: with open(path.join(self.projPath,self.projFile),"wb") as outFile:
outFile.write(etree.tostring( outFile.write(etree.tostring(
@@ -146,15 +208,49 @@ class NWProject():
self.bookAuthors.append(bookAuthor) self.bookAuthors.append(bookAuthor)
return True return True
def setTreeOrder(self, newOrder):
if len(self.treeOrder) != len(newOrder):
logger.warning("Size of new and old tree order does not match")
self.treeOrder - newOrder
return True
# #
# Internal Functions # Internal Functions
# #
def _makeHandle(self,seed=""): def _appendItem(self, tHandle, pHandle, nwItem):
tHandle = self._checkString(tHandle,self._makeHandle(),False)
pHandle = self._checkString(pHandle,None,True)
logger.verbose("Adding entry %s with parent %s" % (str(tHandle),str(pHandle)))
nwItem.setHandle(tHandle)
nwItem.setParent(pHandle)
self.projTree[tHandle] = nwItem
self.treeOrder.append(tHandle)
return
def _makeHandle(self, seed=""):
seed += str(self.seedCount)
self.seedCount += 1
itemHandle = sha256((str(time())+seed).encode()).hexdigest()[0:13] itemHandle = sha256((str(time())+seed).encode()).hexdigest()[0:13]
# if itemHandle in self.treeLookup.keys(): if itemHandle in self.projTree.keys():
# logger.warning("BookTree: Duplicate handle encountered! Retrying ...") logger.warning("Duplicate handle encountered! Retrying ...")
# itemHandle = self.makeHandle(seed+"!") itemHandle = self._makeHandle(seed+"!")
return itemHandle return itemHandle
def _checkString(self,checkValue,defaultValue,allowNone=False):
if allowNone:
if checkValue == None: return None
if checkValue == "None": return None
if isinstance(checkValue,str): return str(checkValue)
return defaultValue
def _checkInt(self,checkValue,defaultValue,allowNone=False):
if allowNone:
if checkValue == None: return None
if checkValue == "None": return None
try:
return int(checkValue)
except:
return defaultValue
# END Class NWProject # END Class NWProject
+26 -1
View File
@@ -1,9 +1,34 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.1.0" fileVersion="1.0" timeStamp="2018-10-25 22:17:01"> <novelWriterXML appVersion="0.1.0" fileVersion="1.0" timeStamp="2018-10-27 23:46:46">
<project> <project>
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
<author>Jane Smith</author> <author>Jane Smith</author>
<author>Jay Doh</author> <author>Jay Doh</author>
</project> </project>
<content count="5">
<item handle="7031beac91f75" order="None" parent="None">
<name>Novel</name>
<type>ROOT</type>
<class>NOVEL</class>
</item>
<item handle="f6622b4617424" order="None" parent="None">
<name>Characters</name>
<type>ROOT</type>
<class>CHARACTER</class>
</item>
<item handle="73eee34351f85" order="None" parent="None">
<name>World</name>
<type>ROOT</type>
<class>WORLD</class>
</item>
<item handle="e7ded148d6e4a" order="None" parent="7031beac91f75">
<name>New Chapter</name>
<type>FOLDER</type>
</item>
<item handle="96b68994dfa3d" order="None" parent="e7ded148d6e4a">
<name>New Scene</name>
<type>FILE</type>
</item>
</content>
</novelWriterXML> </novelWriterXML>