#!/usr/bin/env python ############################################################################ ## ## Copyright (C) 2005-2005 Trolltech AS. All rights reserved. ## ## This file is part of the example classes of the Qt Toolkit. ## ## This file may be used under the terms of the GNU General Public ## License version 2.0 as published by the Free Software Foundation ## and appearing in the file LICENSE.GPL included in the packaging of ## this file. Please review the following information to ensure GNU ## General Public Licensing requirements will be met: ## http://www.trolltech.com/products/qt/opensource.html ## ## If you are unsure which license is appropriate for your use, please ## review the following information: ## http://www.trolltech.com/products/qt/licensing.html or contact the ## sales department at sales@trolltech.com. ## ## This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ## WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ## ############################################################################ # This is only needed for Python v2 but is harmless for Python v3. import sip sip.setapi('QVariant', 2) from PyQt4 import QtCore, QtGui import editabletreemodel_rc from ui_mainwindow import Ui_MainWindow class TreeItem(object): def __init__(self, data, parent=None): self.parentItem = parent self.itemData = data self.childItems = [] def child(self, row): return self.childItems[row] def childCount(self): return len(self.childItems) def childNumber(self): if self.parentItem != None: return self.parentItem.childItems.index(self) return 0 def columnCount(self): return len(self.itemData) def data(self, column): return self.itemData[column] def insertChildren(self, position, count, columns): if position < 0 or position > len(self.childItems): return False for row in range(count): data = [None for v in range(columns)] item = TreeItem(data, self) self.childItems.insert(position, item) return True def insertColumns(self, position, columns): if position < 0 or position > len(self.itemData): return False for column in range(columns): self.itemData.insert(position, None) for child in self.childItems: child.insertColumns(position, columns) return True def parent(self): return self.parentItem def removeChildren(self, position, count): if position < 0 or position + count > len(self.childItems): return False for row in range(count): self.childItems.pop(position) return True def removeColumns(self, position, columns): if position < 0 or position + columns > len(self.itemData): return False for column in range(columns): self.itemData.pop(position) for child in self.childItems: child.removeColumns(position, columns) return True def setData(self, column, value): if column < 0 or column >= len(self.itemData): return False self.itemData[column] = value return True class TreeModel(QtCore.QAbstractItemModel): def __init__(self, headers, data, parent=None): super(TreeModel, self).__init__(parent) rootData = [header for header in headers] self.rootItem = TreeItem(rootData) self.setupModelData(data.split("\n"), self.rootItem) def columnCount(self, parent=QtCore.QModelIndex()): return self.rootItem.columnCount() def data(self, index, role): if not index.isValid(): return None if role != QtCore.Qt.DisplayRole and role != QtCore.Qt.EditRole: return None item = self.getItem(index) return item.data(index.column()) def flags(self, index): if not index.isValid(): return 0 return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def getItem(self, index): if index.isValid(): item = index.internalPointer() if item: return item return self.rootItem def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: return self.rootItem.data(section) return None def index(self, row, column, parent=QtCore.QModelIndex()): if parent.isValid() and parent.column() != 0: return QtCore.QModelIndex() parentItem = self.getItem(parent) childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QtCore.QModelIndex() def insertColumns(self, position, columns, parent=QtCore.QModelIndex()): self.beginInsertColumns(parent, position, position + columns - 1) success = self.rootItem.insertColumns(position, columns) self.endInsertColumns() return success def insertRows(self, position, rows, parent=QtCore.QModelIndex()): parentItem = self.getItem(parent) self.beginInsertRows(parent, position, position + rows - 1) success = parentItem.insertChildren(position, rows, self.rootItem.columnCount()) self.endInsertRows() return success def parent(self, index): if not index.isValid(): return QtCore.QModelIndex() childItem = self.getItem(index) parentItem = childItem.parent() if parentItem == self.rootItem: return QtCore.QModelIndex() return self.createIndex(parentItem.childNumber(), 0, parentItem) def removeColumns(self, position, columns, parent=QtCore.QModelIndex()): self.beginRemoveColumns(parent, position, position + columns - 1) success = self.rootItem.removeColumns(position, columns) self.endRemoveColumns() if self.rootItem.columnCount() == 0: self.removeRows(0, rowCount()) return success def removeRows(self, position, rows, parent=QtCore.QModelIndex()): parentItem = self.getItem(parent) self.beginRemoveRows(parent, position, position + rows - 1) success = parentItem.removeChildren(position, rows) self.endRemoveRows() return success def rowCount(self, parent=QtCore.QModelIndex()): parentItem = self.getItem(parent) return parentItem.childCount() def setData(self, index, value, role=QtCore.Qt.EditRole): if role != QtCore.Qt.EditRole: return False item = self.getItem(index) result = item.setData(index.column(), value) if result: self.dataChanged.emit(index, index) return result def setHeaderData(self, section, orientation, value, role=QtCore.Qt.EditRole): if role != QtCore.Qt.EditRole or orientation != QtCore.Qt.Horizontal: return False result = self.rootItem.setData(section, value) if result: self.headerDataChanged.emit(orientation, section, section) return result def setupModelData(self, lines, parent): parents = [parent] indentations = [0] number = 0 while number < len(lines): position = 0 while position < len(lines[number]): if lines[number][position] != " ": break position += 1 lineData = lines[number][position:].trimmed() if lineData: # Read the column data from the rest of the line. columnData = [s for s in lineData.split('\t') if s] if position > indentations[-1]: # The last child of the current parent is now the new # parent unless the current parent has no children. if parents[-1].childCount() > 0: parents.append(parents[-1].child(parents[-1].childCount() - 1)) indentations.append(position) else: while position < indentations[-1] and len(parents) > 0: parents.pop() indentations.pop() # Append a new item to the current parent's list of children. parent = parents[-1] parent.insertChildren(parent.childCount(), 1, self.rootItem.columnCount()) for column in range(len(columnData)): parent.child(parent.childCount() -1).setData(column, columnData[column]) number += 1 class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) headers = ("Title", "Description") file = QtCore.QFile(':/default.txt') file.open(QtCore.QIODevice.ReadOnly) model = TreeModel(headers, file.readAll()) file.close() self.view.setModel(model) for column in range(model.columnCount(QtCore.QModelIndex())): self.view.resizeColumnToContents(column) self.exitAction.triggered.connect(QtGui.qApp.quit) self.view.selectionModel().selectionChanged.connect(self.updateActions) self.actionsMenu.aboutToShow.connect(self.updateActions) self.insertRowAction.triggered.connect(self.insertRow) self.insertColumnAction.triggered.connect(self.insertColumn) self.removeRowAction.triggered.connect(self.removeRow) self.removeColumnAction.triggered.connect(self.removeColumn) self.insertChildAction.triggered.connect(self.insertChild) self.updateActions() def insertChild(self): index = self.view.selectionModel().currentIndex() model = self.view.model() if model.columnCount(index) == 0: if not model.insertColumn(0, index): return if not model.insertRow(0, index): return for column in range(model.columnCount(index)): child = model.index(0, column, index) model.setData(child, "[No data]", QtCore.Qt.EditRole) if not model.headerData(column, QtCore.Qt.Horizontal).isValid(): model.setHeaderData(column, QtCore.Qt.Horizontal, "[No header]", QtCore.Qt.EditRole) self.view.selectionModel().setCurrentIndex(model.index(0, 0, index), QtGui.QItemSelectionModel.ClearAndSelect) self.updateActions() def insertColumn(self, parent=QtCore.QModelIndex()): model = self.view.model() column = self.view.selectionModel().currentIndex().column() # Insert a column in the parent item. changed = model.insertColumn(column + 1, parent) if changed: model.setHeaderData(column + 1, QtCore.Qt.Horizontal, "[No header]", QtCore.Qt.EditRole) self.updateActions() return changed def insertRow(self): index = self.view.selectionModel().currentIndex() model = self.view.model() if not model.insertRow(index.row()+1, index.parent()): return self.updateActions() for column in range(model.columnCount(index.parent())): child = model.index(index.row()+1, column, index.parent()) model.setData(child, "[No data]", QtCore.Qt.EditRole) def removeColumn(self, parent=QtCore.QModelIndex()): model = self.view.model() column = self.view.selectionModel().currentIndex().column() # Insert columns in each child of the parent item. changed = model.removeColumn(column, parent) if not parent.isValid() and changed: self.updateActions() return changed def removeRow(self): index = self.view.selectionModel().currentIndex() model = self.view.model() if (model.removeRow(index.row(), index.parent())): self.updateActions() def updateActions(self): hasSelection = not self.view.selectionModel().selection().isEmpty() self.removeRowAction.setEnabled(hasSelection) self.removeColumnAction.setEnabled(hasSelection) hasCurrent = self.view.selectionModel().currentIndex().isValid() self.insertRowAction.setEnabled(hasCurrent) self.insertColumnAction.setEnabled(hasCurrent) if hasCurrent: self.view.closePersistentEditor(self.view.selectionModel().currentIndex()) row = self.view.selectionModel().currentIndex().row() column = self.view.selectionModel().currentIndex().column() if self.view.selectionModel().currentIndex().parent().isValid(): self.statusBar().showMessage("Position: (%d,%d)" % (row, column)) else: self.statusBar().showMessage("Position: (%d,%d) in top level" % (row, column)) if __name__ == '__main__': import sys app = QtGui.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())