TreeView Example

from DevMachines import __pyside2__, __pyside6__

if __pyside2__:
    from PySide2 import QtCore
    from PySide2.QtCore import Qt, QAbstractItemModel, QModelIndex

if __pyside6__:
    from PySide6 import QtCore
    from PySide6.QtCore import Qt, QAbstractItemModel, QModelIndex

class TreeItem():
    def __init__(self, data, parent = None):
        self.childItems = list()
        self.itemData = data
        self.parentItem = parent

    def child(self, number):
        if number < 0 or number >= len(self.childItems):
            return None;
        return self.childItems[number]

    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):
        if column < 0 or column >= len(self.itemData):
            return None
        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] * (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 i in range(len(self.childItems)):
            self.childItems[i].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 i in range(len(self.childItems)):
            self.childItems[i].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(QAbstractItemModel):
    def __init__(self, headers, data, parent = None):
        QAbstractItemModel.__init__(self, parent)
        rootData = list()
        for header in headers:
            rootData.append(header)

        self.rootItem = TreeItem(rootData)
        self.setupModelData(data.split('\n'), self.rootItem)

    def columnCount(self, parent):
        return self.rootItem.columnCount()

    def data(self, index, role):
        if not index.isValid():
            return None
        if role != Qt.DisplayRole and role != Qt.EditRole:
            return None
        item = self.getItem(index)
        return item.data(index.column())

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags
        return Qt.ItemIsEditable ^ QAbstractItemModel.flags(self, index)

    def getItem(self, index):
        if index.isValid():
            item = index.internalPointer()
            if item != None:
                return item
        return self.rootItem

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.rootItem.data(section)
        return None

    def index(self, row, column, parent):
        if parent.isValid() and parent.column() != 0:
            return QModelIndex()

        parentItem = self.getItem(parent)
        if parentItem == None:
            return QModelIndex();

        childItem = parentItem.child(row)
        if childItem != None:
            return self.createIndex(row, column, childItem)
        return QModelIndex()

    def insertColumns(self, position, columns, parent):
        self.beginInsertColumns(parent, position, position + columns - 1)
        success = self.rootItem.insertColumns(position, columns)
        self.endInsertColumns()
        return success

    def insertRows(self, position, rows, parent):
        parentItem = getItem(parent)
        if parentItem == None:
            return False
        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 QModelIndex()
        childItem = self.getItem(index);
        parentItem = None
        if childItem != None:
            parentItem = childItem.parent()
        if parentItem == self.rootItem or parentItem == None:
            return QModelIndex()
        return self.createIndex(parentItem.childNumber(), 0, parentItem)

    def removeColumns(self, position, columns, parent):
        self.beginRemoveColumns(parent, position, position + columns - 1)
        success = self.rootItem.removeColumns(position, columns)
        self.endRemoveColumns()
        if self.rootItem.columnCount() == 0:
            self.removeRows(0, self.rowCount())
        return success

    def removeRows(self, position, rows, parent):
        parentItem = self.getItem(parent)
        if parentItem == None:
            return False
        self.beginRemoveRows(parent, position, position + rows - 1)
        success = parentItem.removeChildren(position, rows)
        self.endRemoveRows()
        return success

    def rowCount(self, parent):
        parentItem = self.getItem(parent)
        if parentItem == None:
            return 0
        return parentItem.childCount()

    def setData(self, index, value, role):
        if role != Qt.EditRole:
            return False
        item = self.getItem(index)
        result = item.setData(index.column(), value)
        if result:
            self.dataChanged(index, index, [Qt.DisplayRole, Qt.EditRole])
        return result

    def setHeaderData(self, section, orientation, value, role):
        if role != Qt.EditRole or orientation != Qt.Horizontal:
            return False
        result = self.rootItem.setData(section, value)
        if result:
            self.headerDataChanged(orientation, section, section)
        return result

    def setupModelData(self, lines, parent):
        parents = list();
        indentations = list();
        parents.append(parent)
        indentations.append(0)

        number = 0
        for number in range(len(lines)):
            position = 0
            while position < len(lines[number]):
                if str(lines[number][position], 'utf-8') != ' ':
                    break
                position += 1

            lineData = str(lines[number][position:].trimmed(), 'utf-8')

            if lineData:
                # Read the column data from the rest of the line.
                columnStrings = lineData.split('\t')
                columnData = list()
                for columnString in columnStrings:
                    if columnString:
                        columnData.append(columnString)

                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.
                p = parents[-1]
                p.insertChildren(p.childCount(), 1, self.rootItem.columnCount())
                for column in range(len(columnData)):
                    p.child(p.childCount() - 1).setData(column, columnData[column])