From 215a29d325bd23eb342592b67899b18723990307 Mon Sep 17 00:00:00 2001 From: Olivier Keshavjee Date: Tue, 2 Jun 2015 14:40:48 +0200 Subject: [PATCH] Adds: drag and drop --- src/loadSave.py | 2 + src/mainWindow.py | 14 ++- src/models/outlineModel.py | 222 ++++++++++++++++++++++++++++++------- src/ui/mainWindow.py | 10 +- src/ui/mainWindow.ui | 16 +++ 5 files changed, 214 insertions(+), 50 deletions(-) diff --git a/src/loadSave.py b/src/loadSave.py index 0a809890..e10ae030 100644 --- a/src/loadSave.py +++ b/src/loadSave.py @@ -64,6 +64,8 @@ def loadStandardItemModelXML(mdl, xml): for l in root.find("header").find("vertical").findall("label"): vLabels.append(l.attrib["text"]) + #print(root.find("header").find("vertical").text) + mdl.setVerticalHeaderLabels(vLabels) mdl.setHorizontalHeaderLabels(hLabels) diff --git a/src/mainWindow.py b/src/mainWindow.py index 0a3aa384..74d1af04 100644 --- a/src/mainWindow.py +++ b/src/mainWindow.py @@ -143,14 +143,15 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.treePlanOutline.hideColumn(c) self.btnRedacAddFolder.clicked.connect(lambda: self.outlineAddItem("folder")) self.btnRedacAddScene.clicked.connect(lambda: self.outlineAddItem("scene")) - self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItem) + self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItems) + #Debug self.mdlFlatData.setVerticalHeaderLabels(["Infos générales", "Summary"]) self.tblDebugFlatData.setModel(self.mdlFlatData) self.tblDebugPersos.setModel(self.mdlPersos) self.tblDebugPersosInfos.setModel(self.mdlPersosInfos) self.treeDebugOutline.setModel(self.mdlOutline) - + self.btnRedacPreview.clicked.connect(self.mdlOutline.saveToXML) self.loadProject("test_project") @@ -161,11 +162,12 @@ class MainWindow(QMainWindow, Ui_MainWindow): def outlineAddItem(self, type="folder"): currentIndex = self.treeRedacOutline.currentIndex() item = outlineItem("Nouveau", type) - self.mdlOutline.appendRow(item, currentIndex) + self.mdlOutline.appendItem(item, currentIndex) - def outlineRemoveItem(self): - currentIndex = self.treeRedacOutline.currentIndex() - self.mdlOutline.removeIndex(currentIndex) + def outlineRemoveItems(self): + for idx in self.treeRedacOutline.selectedIndexes(): + if idx.isValid(): + self.mdlOutline.removeIndex(idx) #################################################################################################### diff --git a/src/models/outlineModel.py b/src/models/outlineModel.py index 0b637d8d..dc7c65c2 100644 --- a/src/models/outlineModel.py +++ b/src/models/outlineModel.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals from PyQt4.QtCore import * from PyQt4.QtGui import * from enum import Enum +from lxml import etree as ET class Outline(Enum): title = 0 @@ -24,7 +25,7 @@ class outlineModel(QAbstractItemModel): def __init__(self): QAbstractItemModel.__init__(self) - self.rootItem = outlineItem() + self.rootItem = outlineItem("root", "folder") def index(self, row, column, parent): @@ -84,14 +85,6 @@ class outlineModel(QAbstractItemModel): item.setData(index.column(), value) self.dataChanged.emit(index, index) return True - - def flags(self, index): - flags = Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled - - if index.isValid() and index.internalPointer().isFolder(): - flags |= Qt.ItemIsDropEnabled - - return flags def headerData(self, section, orientation, role=Qt.DisplayRole): @@ -99,6 +92,141 @@ class outlineModel(QAbstractItemModel): return [i.name for i in Outline][section] else: return QVariant() + return True + + #################### DRAG AND DROP ######################## + # http://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views + + def flags(self, index): + flags = QAbstractItemModel.flags(self, index) | Qt.ItemIsEditable + + if index.isValid() and index.internalPointer().isFolder(): + flags |= Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled + + elif index.isValid(): + flags |= Qt.ItemIsDragEnabled + + else: + flags |= Qt.ItemIsDropEnabled + + return flags + + def mimeTypes(self): + return ["application/xml"] + + def mimeData(self, indexes): + mimeData = QMimeData() + #encodedData = QByteArray() + #stream = QDataStream(encodedData, QIODevice.WriteOnly) + encodedData = "" + + root = ET.Element("outlineItems") + for index in indexes: + if index.isValid(): + item = ET.XML(index.internalPointer().toXML()) + root.append(item) + + encodedData = ET.tostring(root) + + mimeData.setData("application/xml", encodedData) + return mimeData + + def supportedDropActions(self): + + return Qt.MoveAction # Qt.CopyAction | + return Qt.CopyAction | Qt.MoveAction + + def dropMimeData(self, data, action, row, column, parent): + + if action == Qt.IgnoreAction: + return True # What is that? + + if not data.hasFormat("application/xml"): + return False + + if column > 0: + column = 0 + + if row <> -1: + beginRow = row + elif parent.isValid(): + beginRow = self.rowCount(parent) + 1 + else: + beginRow = self.rowCount() + 1 + + encodedData = str(data.data("application/xml")) + root = ET.XML(encodedData) + + if root.tag <> "outlineItems": + return False + + items = [] + for child in root: + if child.tag == "outlineItem": + items.append(outlineItem(xml=ET.tostring(child))) + + if not items: + return False + + self.insertItems(items, beginRow, parent) + + return True + + ################# ADDING AND REMOVING ################# + + def insertItem(self, item, row, parent=QModelIndex()): + return self.insertItems([item], row, parent) + + def insertItems(self, items, row, parent=QModelIndex()): + if not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + # Insert only if parent is folder + if parentItem.isFolder(): + self.beginInsertRows(parent, row, row + len(items) - 1) + + for i in items: + parentItem.insertChild(row + items.index(i), i) + + self.endInsertRows() + + return True + + def appendItem(self, item, parent=QModelIndex()): + if not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + # If parent is folder, write into + if parentItem.isFolder(): + self.insertItem(item, self.rowCount(parent), parent) + + # If parent is not folder, write next to + else: + self.insertItem(item, parent.row()+1, parent.parent()) + + def removeIndex(self, index): + item = index.internalPointer() + self.removeRow(item.row(), index.parent()) + + def removeRow(self, row, parent=QModelIndex()): + return self.removeRows(row, 1, parent) + + def removeRows(self, row, count, parent=QModelIndex()): + if not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + self.beginRemoveRows(parent, row, row + count - 1) + for i in range(count): + parentItem.removeChild(row) + + self.endRemoveRows() + return True #def insertRow(self, row, item, parent=QModelIndex()): #self.beginInsertRows(parent, row, row) @@ -112,44 +240,28 @@ class outlineModel(QAbstractItemModel): #self.endInsertRows() - def appendRow(self, item, parent=QModelIndex()): - if not parent.isValid(): - parentItem = self.rootItem - else: - parentItem = parent.internalPointer() - if parentItem.isFolder(): - self.beginInsertRows(parent, parentItem.childCount(), parentItem.childCount()) - parentItem.appendChild(item) - self.endInsertRows() - - def removeIndex(self, index): - item = index.internalPointer() - self.removeRow(item.row(), 1, index.parent()) - - - def removeRow(self, row, count, parent=QModelIndex()): - if not parent.isValid(): - parentItem = self.rootItem - else: - parentItem = parent.internalPointer() - - self.beginRemoveRows(parent, row, row) - parentItem.removeChild(row) - self.endRemoveRows() - return True + ################# XML ################# + + def saveToXML(self): + root = ET.XML(self.rootItem.toXML()) + print(ET.tostring(root, pretty_print=True)) + # FIXME + class outlineItem(): - def __init__(self, title="", type="folder", parent=None): - - self._parent = parent + + def __init__(self, title="", type="folder", xml=""): self._data = {} self.childItems = [] - self._data[Outline.title] = title + if title: self._data[Outline.title] = title self._data[Outline.type] = type + if xml: + self.setFromXML(xml) + def child(self, row): return self.childItems[row] @@ -165,7 +277,7 @@ class outlineItem(): if Outline(column) in self._data: return self._data[Outline(column)] else: - return QVariant() + return None elif role == Qt.DecorationRole and column == Outline.title.value: if self.isFolder(): return QIcon.fromTheme("folder") @@ -173,18 +285,18 @@ class outlineItem(): return QIcon.fromTheme("document-new") def setData(self, column, data): - self._data[Outline(column)] = data + self._data[Outline(column)] = str(data.toString()) def row(self): if self.parent: return self.parent().childItems.index(self) def appendChild(self, child): - self.childItems.append(child) - child._parent = self + self.insertChild(self.childCount(), child) def insertChild(self, row, child): self.childItems.insert(row, child) + child._parent = self def removeChild(self, row): self.childItems.pop(row) @@ -196,4 +308,28 @@ class outlineItem(): return self._data[Outline.type] == "folder" def isScene(self): - return self._data[Outline.type] == "scene" \ No newline at end of file + return self._data[Outline.type] == "scene" + + def toXML(self): + item = ET.Element("outlineItem") + + for attrib in Outline: + val = self.data(attrib) + if val: + item.set(attrib.name, val) + + for i in self.childItems: + item.append(ET.XML(i.toXML())) + + return ET.tostring(item) + + def setFromXML(self, xml): + root = ET.XML(xml) + + for k in root.attrib: + if k in Outline.__members__: + self._data[Outline.__members__[k]] = root.attrib[k] + + for child in root: + item = outlineItem(xml=ET.tostring(child)) + self.appendChild(item) \ No newline at end of file diff --git a/src/ui/mainWindow.py b/src/ui/mainWindow.py index 50baa280..ad56e5a3 100644 --- a/src/ui/mainWindow.py +++ b/src/ui/mainWindow.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'src/ui/mainWindow.ui' # -# Created: Mon Jun 1 23:58:06 2015 +# Created: Tue Jun 2 13:54:35 2015 # by: PyQt4 UI code generator 4.11.3 # # WARNING! All changes made in this file will be lost! @@ -774,6 +774,8 @@ class Ui_MainWindow(object): self.verticalLayout_19.setObjectName(_fromUtf8("verticalLayout_19")) self.treeRedacOutline = QtGui.QTreeView(self.layoutWidget1) self.treeRedacOutline.setDragDropMode(QtGui.QAbstractItemView.DragDrop) + self.treeRedacOutline.setDefaultDropAction(QtCore.Qt.MoveAction) + self.treeRedacOutline.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.treeRedacOutline.setObjectName(_fromUtf8("treeRedacOutline")) self.verticalLayout_19.addWidget(self.treeRedacOutline) self.horizontalLayout_31 = QtGui.QHBoxLayout() @@ -798,6 +800,12 @@ class Ui_MainWindow(object): self.horizontalLayout_31.addWidget(self.btnRedacRemoveItem) spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.horizontalLayout_31.addItem(spacerItem9) + self.btnRedacPreview = QtGui.QPushButton(self.layoutWidget1) + self.btnRedacPreview.setText(_fromUtf8("")) + icon = QtGui.QIcon.fromTheme(_fromUtf8("document-print")) + self.btnRedacPreview.setIcon(icon) + self.btnRedacPreview.setObjectName(_fromUtf8("btnRedacPreview")) + self.horizontalLayout_31.addWidget(self.btnRedacPreview) self.verticalLayout_19.addLayout(self.horizontalLayout_31) self.layoutWidget2 = QtGui.QWidget(self.splitterRedac) self.layoutWidget2.setObjectName(_fromUtf8("layoutWidget2")) diff --git a/src/ui/mainWindow.ui b/src/ui/mainWindow.ui index 350951b8..9c8f8be4 100644 --- a/src/ui/mainWindow.ui +++ b/src/ui/mainWindow.ui @@ -1452,6 +1452,12 @@ QAbstractItemView::DragDrop + + Qt::MoveAction + + + QAbstractItemView::ExtendedSelection + @@ -1505,6 +1511,16 @@ + + + + + + + + + +