diff --git a/src/enums.py b/src/enums.py index f226aba9..40f5166b 100644 --- a/src/enums.py +++ b/src/enums.py @@ -39,3 +39,5 @@ class Outline(Enum): wordCount = 10 goal = 11 goalPercentage = 12 + setGoal = 13 # The goal set by the user, if any. Can be different from goal which can be computed + # (sum of all sub-items' goals) diff --git a/src/functions.py b/src/functions.py index 498d45f6..54e35811 100644 --- a/src/functions.py +++ b/src/functions.py @@ -5,4 +5,16 @@ from __future__ import print_function from __future__ import unicode_literals def wordCount(text): - return len(text.strip().split(" ")) if text else 0 \ No newline at end of file + return len(text.strip().split(" ")) if text else 0 + +def toInt(text): + if text: + return int(text) + else: + return 0 + +def toFloat(text): + if text: + return float(text) + else: + return 0. \ No newline at end of file diff --git a/src/mainWindow.py b/src/mainWindow.py index 863464b1..1325e65a 100644 --- a/src/mainWindow.py +++ b/src/mainWindow.py @@ -120,7 +120,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.mprPersos.addMapping(w, i) self.mprPersos.addMapping(self.sldPersoImportance, Perso.importance.value, "importance") self.sldPersoImportance.importanceChanged.connect(self.mprPersos.submit) - + self.tabMain.currentChanged.connect(self.mprPersos.submit) + self.mprPersos.setCurrentIndex(0) self.lstPersos.selectionModel().currentChanged.connect(self.changeCurrentPerso) self.tabPersos.currentChanged.connect(self.resizePersosInfos) @@ -135,6 +136,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.treePlanOutline.setItemDelegateForColumn(Outline.compile.value, self.treePlanOutlineCompileDelegate) self.treePlanOutlineStatusDelegate = treeOutlineStatusDelegate() self.treePlanOutline.setItemDelegateForColumn(Outline.status.value, self.treePlanOutlineStatusDelegate) + self.treePlanOutlineGoalPercentageDelegate = treeOutlineGoalPercentageDelegate() + self.treePlanOutline.setItemDelegateForColumn(Outline.goalPercentage.value, self.treePlanOutlineGoalPercentageDelegate) self.cmbPlanPOV.setModels(self.mdlPersos, self.mdlOutline) self.treePlanOutline.header().setSectionResizeMode(QHeaderView.ResizeToContents) @@ -153,7 +156,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.treePlanOutline.selectionModel().currentChanged.connect(lambda idx: self.mprPlan.setRootIndex(idx.parent())) self.treePlanOutline.selectionModel().currentChanged.connect(self.mprPlan.setCurrentModelIndex) self.treePlanOutline.selectionModel().currentChanged.connect(self.cmbPlanPOV.setCurrentModelIndex) - + self.tabMain.currentChanged.connect(self.mprPlan.submit) self.treeRedacOutline.setSelectionModel(self.treePlanOutline.selectionModel()) @@ -182,7 +185,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): (self.txtRedacSummaryFull, Outline.summaryFull.value), (self.txtRedacNotes, Outline.notes.value), (self.txtRedacTitle, Outline.title.value), - (self.txtRedacGoal, Outline.goal.value) + (self.txtRedacGoal, Outline.setGoal.value) ] for w, i in mapping: self.mprOutline.addMapping(w, i) @@ -192,6 +195,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.treeRedacOutline.selectionModel().currentChanged.connect(self.cmbRedacPOV.setCurrentModelIndex) self.treeRedacOutline.selectionModel().currentChanged.connect(self.cmbRedacStatus.setCurrentModelIndex) self.treeRedacOutline.selectionModel().currentChanged.connect(self.chkRedacCompile.setCurrentModelIndex) + self.tabMain.currentChanged.connect(self.mprOutline.submit) self.treeRedacOutline.selectionModel().currentChanged.connect(lambda idx: self.lblRedacPOV.setHidden(idx.internalPointer().isFolder())) self.treeRedacOutline.selectionModel().currentChanged.connect(lambda idx: self.cmbRedacPOV.setHidden(idx.internalPointer().isFolder())) @@ -218,7 +222,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): else: parent = self.treeRedacOutline.currentIndex() - item = outlineItem("Nouveau", type) + item = outlineItem(title="Nouveau", type=type) self.mdlOutline.appendItem(item, parent) def outlineRemoveItems(self): diff --git a/src/models/outlineModel.py b/src/models/outlineModel.py index d9cd01d4..2f931a6c 100644 --- a/src/models/outlineModel.py +++ b/src/models/outlineModel.py @@ -19,7 +19,7 @@ class outlineModel(QAbstractItemModel): def __init__(self): QAbstractItemModel.__init__(self) - self.rootItem = outlineItem("root", "folder") + self.rootItem = outlineItem(self, title="root") self.generateStatuses() def index(self, row, column, parent): @@ -38,6 +38,23 @@ class outlineModel(QAbstractItemModel): else: return QModelIndex() + def indexFromItem(self, item, column=0): + if item == self.rootItem: + return None + + parent = item.parent() + if not parent: + parent = self.rootItem + + if len(parent.children()) == 0: + return None + + #print(item.title(), [i.title() for i in parent.children()]) + + row = parent.children().index(item) + col = column + return self.createIndex(row, col, item) + def parent(self, index=QModelIndex()): if not index.isValid(): return QModelIndex() @@ -112,6 +129,9 @@ class outlineModel(QAbstractItemModel): if index.isValid() and index.column() == Outline.compile.value: flags |= Qt.ItemIsUserCheckable + if index.column() in [i.value for i in [Outline.wordCount, Outline.goalPercentage]]: + flags &= ~ Qt.ItemIsEditable + return flags def mimeTypes(self): @@ -195,6 +215,9 @@ class outlineModel(QAbstractItemModel): else: parentItem = parent.internalPointer() + if parent.column() <> 0: + parent = parentItem.index() + # Insert only if parent is folder if parentItem.isFolder(): self.beginInsertRows(parent, row, row + len(items) - 1) @@ -212,6 +235,9 @@ class outlineModel(QAbstractItemModel): else: parentItem = parent.internalPointer() + if parent.column() <> 0: + parent = parentItem.index() + # If parent is folder, write into if parentItem.isFolder(): self.insertItem(item, self.rowCount(parent), parent) @@ -260,13 +286,13 @@ class outlineModel(QAbstractItemModel): ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True) def loadFromXML(self, xml): - try: + #try: root = ET.parse(xml) - self.rootItem = outlineItem(xml=ET.tostring(root)) + self.rootItem = outlineItem(self, xml=ET.tostring(root)) self.generateStatuses() - except: - print("N'arrive pas à ouvrir {}".format(xml)) - return + #except: + #print("N'arrive pas à ouvrir {}".format(xml)) + #return ################# DIVERS ################# @@ -293,14 +319,16 @@ class outlineModel(QAbstractItemModel): class outlineItem(): - def __init__(self, title="", type="folder", xml=None): + def __init__(self, model=None, title="", type="folder", xml=None): self._data = {} self.childItems = [] + self._parent = None + self._model = model if title: self._data[Outline.title] = title self._data[Outline.type] = type - + self._data[Outline.compile] = Qt.Checked if xml is not None: self.setFromXML(xml) @@ -327,6 +355,7 @@ class outlineItem(): elif Outline(column) in self._data: return self._data[Outline(column)] + else: return None @@ -341,29 +370,70 @@ class outlineItem(): return QBrush(Qt.gray) elif role == Qt.CheckStateRole and column == Outline.compile.value: - if Outline(column) in self._data and self._data[Outline(column)]: - return Qt.Checked - else: - return Qt.Unchecked + return self._data[Outline(column)] def setData(self, column, data, role=Qt.DisplayRole): if role not in [Qt.DisplayRole, Qt.EditRole, Qt.CheckStateRole]: print(column, column == Outline.text.value, data, role) return + if column == Outline.text.value and self.isFolder(): + # Folder have no text + return + if column == Outline.text.value: wc = wordCount(data) self.setData(Outline.wordCount.value, wc) - if column in [Outline.wordCount.value, Outline.goal.value]: - wc = self.data(Outline.wordCount.value) - goal = self.data(Outline.goal.value) - if goal and wc: - self.setData(Outline.goalPercentage.value, int(wc) / float(goal)) - else: - self.setData(Outline.goalPercentage.value, "0") + if column == Outline.goal.value: + self._data[Outline.setGoal] = toInt(data) if toInt(data) > 0 else "" + + + updateWordCount = False + if column in [Outline.wordCount.value, Outline.goal.value, Outline.setGoal.value]: + updateWordCount = not Outline(column) in self._data or self._data[Outline(column)] <> data self._data[Outline(column)] = data + + if updateWordCount: + self.updateWordCount() + + def updateWordCount(self): + if not self.isFolder(): + setGoal = toInt(self.data(Outline.setGoal.value)) + goal = toInt(self.data(Outline.goal.value)) + + if goal <> setGoal: + self._data[Outline.goal] = setGoal + if setGoal: + wc = toInt(self.data(Outline.wordCount.value)) + self.setData(Outline.goalPercentage.value, wc / float(setGoal)) + + else: + wc = 0 + for c in self.children(): + wc += toInt(c.data(Outline.wordCount.value)) + self._data[Outline.wordCount] = wc + + setGoal = toInt(self.data(Outline.setGoal.value)) + goal = toInt(self.data(Outline.goal.value)) + + if setGoal: + if goal <> setGoal: + self._data[Outline.goal] = setGoal + else: + goal = 0 + for c in self.children(): + goal += toInt(c.data(Outline.goal.value)) + self._data[Outline.goal] = goal + + if goal: + self.setData(Outline.goalPercentage.value, wc / float(goal)) + + self.emitDataChanged() + + if self.parent(): + self.parent().updateWordCount() def row(self): if self.parent: @@ -375,6 +445,16 @@ class outlineItem(): def insertChild(self, row, child): self.childItems.insert(row, child) child._parent = self + child._model = self._model + self.updateWordCount() + + def index(self, column=0): + return self._model.indexFromItem(self, column) + + def emitDataChanged(self): + idx = self.index() + if idx: + self._model.dataChanged.emit(idx, self.index(len(Outline))) def removeChild(self, row): self.childItems.pop(row) @@ -388,13 +468,28 @@ class outlineItem(): def isCompile(self): return Outline.compile in self._data and self._data[Outline.compile] + def title(self): + if Outline.title in self._data: + return self._data[Outline.title] + else: + return "" + def isScene(self): return self._data[Outline.type] == "scene" + def level(self): + if self.parent(): + return self.parent().level() + 1 + else: + return -1 + def toXML(self): item = ET.Element("outlineItem") + exclude = [Outline.goal, Outline.goalPercentage] + for attrib in Outline: + if attrib in exclude: continue val = self.data(attrib.value) if val: item.set(attrib.name, unicode(val)) @@ -412,5 +507,5 @@ class outlineItem(): self.setData(Outline.__members__[k].value, unicode(root.attrib[k])) for child in root: - item = outlineItem(xml=ET.tostring(child)) + item = outlineItem(self._model, xml=ET.tostring(child)) self.appendChild(item) \ No newline at end of file diff --git a/src/ui/treeOutlineDelegates.py b/src/ui/treeOutlineDelegates.py index c295954b..2e1e888a 100644 --- a/src/ui/treeOutlineDelegates.py +++ b/src/ui/treeOutlineDelegates.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals from qt import * from enums import * +from functions import * class treeOutlinePersoDelegate(QStyledItemDelegate): @@ -48,8 +49,64 @@ class treeOutlineCompileDelegate(QStyledItemDelegate): def __init__(self, parent=None): QStyledItemDelegate.__init__(self, parent) - #def displayText(self, value, locale): - #return "" + def displayText(self, value, locale): + return "" + +class treeOutlineGoalPercentageDelegate(QStyledItemDelegate): + def __init__(self, parent=None): + QStyledItemDelegate.__init__(self, parent) + + def paint(self, painter, option, index): + if not index.isValid(): + return QStyledItemDelegate.paint(self, painter, option, index) + + QStyledItemDelegate.paint(self, painter, option, index) + + item = index.internalPointer() + + if not item.data(Outline.goal.value): + return + + p = toFloat(item.data(Outline.goalPercentage.value)) + if p > 1: p = 1. + + typ = item.data(Outline.type.value) + + level = item.level() + + margin = 7 + #height = 5 if typ == "folder" else 3 + height = max(12 - 2 * level, 6) + painter.save() + + rect = option.rect.adjusted(margin, margin, -margin, -margin) + #rect.setWidth(rect.width() - level * rect.width() / 10) + rect.setHeight(height) + rect.setTop(option.rect.top() + (option.rect.height() - height) / 2) + painter.drawRect(rect) + + c1 = QColor(Qt.red) + c2 = QColor(Qt.blue) + c3 = QColor(Qt.darkGreen) + if p < 0.3: + painter.setBrush(QBrush(c1)) + elif p < 0.8: + painter.setBrush(QBrush(c2)) + else: + painter.setBrush(QBrush(c3)) + + #if typ == "folder": + #painter.setBrush(QBrush(Qt.blue)) + #else: + #painter.setBrush(QBrush(Qt.green)) + + r2 = QRect(rect) + r2.setWidth(r2.width() * p) + painter.drawRect(r2) + painter.restore() + + def displayText(self, value, locale): + return "" class treeOutlineStatusDelegate(QStyledItemDelegate): diff --git a/test_project/outline.xml b/test_project/outline.xml index f7beed6c..b4c2801a 100644 --- a/test_project/outline.xml +++ b/test_project/outline.xml @@ -1,11 +1,39 @@ - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +