Added some the tree building code, with the help of 3 G&Ts.
This commit is contained in:
+11
-2
@@ -52,7 +52,8 @@ class Config:
|
||||
self.confChanged = False
|
||||
|
||||
## General
|
||||
self.winGeometry = [1100, 650]
|
||||
self.winGeometry = [1100, 650]
|
||||
self.treeColWidth = [120, 30, 50]
|
||||
|
||||
# Check if config file exists
|
||||
if path.isfile(path.join(self.confPath,self.confFile)):
|
||||
@@ -83,6 +84,10 @@ class Config:
|
||||
self.winGeometry = self.unpackList(
|
||||
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
|
||||
cnfSec = "Path"
|
||||
@@ -105,6 +110,7 @@ class Config:
|
||||
confParser.add_section(cnfSec)
|
||||
confParser.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
confParser.set(cnfSec,"geometry", self.packList(self.winGeometry))
|
||||
confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth))
|
||||
|
||||
## Path
|
||||
cnfSec = "Path"
|
||||
@@ -142,7 +148,6 @@ class Config:
|
||||
self.recentList.insert(0,recentPath)
|
||||
return
|
||||
|
||||
|
||||
def setConfPath(self, newPath):
|
||||
if newPath is None: return
|
||||
if not path.isfile(newPath):
|
||||
@@ -161,4 +166,8 @@ class Config:
|
||||
self.confChanged = True
|
||||
return
|
||||
|
||||
def setTreeColWidths(self, colWidths):
|
||||
self.treeColWidth = colWidths
|
||||
return
|
||||
|
||||
# End Class Config
|
||||
|
||||
+61
-11
@@ -15,28 +15,78 @@ import nw
|
||||
|
||||
from os import path
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QTreeView
|
||||
from PyQt5.QtGui import QStandardItemModel
|
||||
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QTreeWidgetItemIterator, QAbstractItemView
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiDocTree(QTreeView):
|
||||
class GuiDocTree(QTreeWidget):
|
||||
|
||||
def __init__(self):
|
||||
QTreeView.__init__(self)
|
||||
def __init__(self, theProject):
|
||||
QTreeWidget.__init__(self)
|
||||
|
||||
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.treeModel.setHeaderData(0, Qt.Horizontal, "Name")
|
||||
self.treeModel.setHeaderData(1, Qt.Horizontal, "Words")
|
||||
self.treeModel.setHeaderData(2, Qt.Horizontal, "Status")
|
||||
self.setColumnCount(4)
|
||||
self.setHeaderLabels(["Name","","","Handle"])
|
||||
if not self.debugGUI:
|
||||
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")
|
||||
|
||||
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
|
||||
|
||||
+7
-5
@@ -46,8 +46,8 @@ class GuiMain(QMainWindow):
|
||||
self.splitMain.addWidget(self.treePane)
|
||||
self.splitMain.addWidget(self.docEditor)
|
||||
|
||||
self.treeBox = QVBoxLayout()
|
||||
self.treeView = GuiDocTree()
|
||||
self.treeBox = QVBoxLayout()
|
||||
self.treeView = GuiDocTree(self.theProject)
|
||||
self.treeBox.addWidget(self.treeView)
|
||||
self.treePane.setLayout(self.treeBox)
|
||||
|
||||
@@ -58,8 +58,7 @@ class GuiMain(QMainWindow):
|
||||
self.setCentralWidget(self.splitMain)
|
||||
|
||||
self._buildMenu()
|
||||
|
||||
# self.docTabs.createTab()
|
||||
self.treeView.buildTree()
|
||||
|
||||
self.show()
|
||||
|
||||
@@ -76,7 +75,8 @@ class GuiMain(QMainWindow):
|
||||
|
||||
def newProject(self):
|
||||
logger.info("Creating new project")
|
||||
self.docTabs.createTab(tabType=nw.DOCTYPE_PROJECT)
|
||||
self.theProject.newProject()
|
||||
self.treeView.buildTree()
|
||||
return
|
||||
|
||||
def openProject(self):
|
||||
@@ -101,6 +101,7 @@ class GuiMain(QMainWindow):
|
||||
self.theProject.setProjectPath(projPath)
|
||||
else:
|
||||
return False
|
||||
self.treeView.saveTreeOrder()
|
||||
self.theProject.saveProject()
|
||||
return True
|
||||
|
||||
@@ -130,6 +131,7 @@ class GuiMain(QMainWindow):
|
||||
def _closeMain(self):
|
||||
logger.info("Exiting %s" % nw.__package__)
|
||||
self.mainConf.setWinSize(self.width(), self.height())
|
||||
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
|
||||
self.mainConf.saveConfig()
|
||||
return
|
||||
|
||||
|
||||
@@ -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
@@ -13,9 +13,12 @@
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from os import path, mkdir
|
||||
from lxml import etree
|
||||
from datetime import datetime
|
||||
from os import path, mkdir
|
||||
from lxml import etree
|
||||
from hashlib import sha256
|
||||
from datetime import datetime, time
|
||||
|
||||
from nw.project.item import NWItem
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -23,10 +26,13 @@ class NWProject():
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.mainConf = nw.CONFIG
|
||||
self.projTree = []
|
||||
# Internal
|
||||
self.mainConf = nw.CONFIG
|
||||
self.seedCount = 0
|
||||
|
||||
# Project Settings
|
||||
self.projTree = {}
|
||||
self.treeOrder = []
|
||||
self.projPath = None
|
||||
self.projFile = "nwProject.nwx"
|
||||
self.projName = ""
|
||||
@@ -36,9 +42,47 @@ class NWProject():
|
||||
return
|
||||
|
||||
def buildProjectTree(self):
|
||||
|
||||
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):
|
||||
|
||||
if not path.isfile(fileName):
|
||||
@@ -108,6 +152,24 @@ class NWProject():
|
||||
xBookAuthor = etree.SubElement(xProject,"author")
|
||||
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
|
||||
with open(path.join(self.projPath,self.projFile),"wb") as outFile:
|
||||
outFile.write(etree.tostring(
|
||||
@@ -146,15 +208,49 @@ class NWProject():
|
||||
self.bookAuthors.append(bookAuthor)
|
||||
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
|
||||
#
|
||||
|
||||
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]
|
||||
# if itemHandle in self.treeLookup.keys():
|
||||
# logger.warning("BookTree: Duplicate handle encountered! Retrying ...")
|
||||
# itemHandle = self.makeHandle(seed+"!")
|
||||
if itemHandle in self.projTree.keys():
|
||||
logger.warning("Duplicate handle encountered! Retrying ...")
|
||||
itemHandle = self._makeHandle(seed+"!")
|
||||
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
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
<?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>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user