1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import QAbstractItemModel, QFile, QIODevice, QModelIndex, Qt
46from PyQt5.QtWidgets import QApplication, QTreeView
47
48import simpletreemodel_rc
49
50
51class TreeItem(object):
52    def __init__(self, data, parent=None):
53        self.parentItem = parent
54        self.itemData = data
55        self.childItems = []
56
57    def appendChild(self, item):
58        self.childItems.append(item)
59
60    def child(self, row):
61        return self.childItems[row]
62
63    def childCount(self):
64        return len(self.childItems)
65
66    def columnCount(self):
67        return len(self.itemData)
68
69    def data(self, column):
70        try:
71            return self.itemData[column]
72        except IndexError:
73            return None
74
75    def parent(self):
76        return self.parentItem
77
78    def row(self):
79        if self.parentItem:
80            return self.parentItem.childItems.index(self)
81
82        return 0
83
84
85class TreeModel(QAbstractItemModel):
86    def __init__(self, data, parent=None):
87        super(TreeModel, self).__init__(parent)
88
89        self.rootItem = TreeItem(("Title", "Summary"))
90        self.setupModelData(data.split('\n'), self.rootItem)
91
92    def columnCount(self, parent):
93        if parent.isValid():
94            return parent.internalPointer().columnCount()
95        else:
96            return self.rootItem.columnCount()
97
98    def data(self, index, role):
99        if not index.isValid():
100            return None
101
102        if role != Qt.DisplayRole:
103            return None
104
105        item = index.internalPointer()
106
107        return item.data(index.column())
108
109    def flags(self, index):
110        if not index.isValid():
111            return Qt.NoItemFlags
112
113        return Qt.ItemIsEnabled | Qt.ItemIsSelectable
114
115    def headerData(self, section, orientation, role):
116        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
117            return self.rootItem.data(section)
118
119        return None
120
121    def index(self, row, column, parent):
122        if not self.hasIndex(row, column, parent):
123            return QModelIndex()
124
125        if not parent.isValid():
126            parentItem = self.rootItem
127        else:
128            parentItem = parent.internalPointer()
129
130        childItem = parentItem.child(row)
131        if childItem:
132            return self.createIndex(row, column, childItem)
133        else:
134            return QModelIndex()
135
136    def parent(self, index):
137        if not index.isValid():
138            return QModelIndex()
139
140        childItem = index.internalPointer()
141        parentItem = childItem.parent()
142
143        if parentItem == self.rootItem:
144            return QModelIndex()
145
146        return self.createIndex(parentItem.row(), 0, parentItem)
147
148    def rowCount(self, parent):
149        if parent.column() > 0:
150            return 0
151
152        if not parent.isValid():
153            parentItem = self.rootItem
154        else:
155            parentItem = parent.internalPointer()
156
157        return parentItem.childCount()
158
159    def setupModelData(self, lines, parent):
160        parents = [parent]
161        indentations = [0]
162
163        number = 0
164
165        while number < len(lines):
166            position = 0
167            while position < len(lines[number]):
168                if lines[number][position] != ' ':
169                    break
170                position += 1
171
172            lineData = lines[number][position:].trimmed()
173
174            if lineData:
175                # Read the column data from the rest of the line.
176                columnData = [s for s in lineData.split('\t') if s]
177
178                if position > indentations[-1]:
179                    # The last child of the current parent is now the new
180                    # parent unless the current parent has no children.
181
182                    if parents[-1].childCount() > 0:
183                        parents.append(parents[-1].child(parents[-1].childCount() - 1))
184                        indentations.append(position)
185
186                else:
187                    while position < indentations[-1] and len(parents) > 0:
188                        parents.pop()
189                        indentations.pop()
190
191                # Append a new item to the current parent's list of children.
192                parents[-1].appendChild(TreeItem(columnData, parents[-1]))
193
194            number += 1
195
196
197if __name__ == '__main__':
198
199    import sys
200
201    app = QApplication(sys.argv)
202
203    f = QFile(':/default.txt')
204    f.open(QIODevice.ReadOnly)
205    model = TreeModel(f.readAll())
206    f.close()
207
208    view = QTreeView()
209    view.setModel(model)
210    view.setWindowTitle("Simple Tree Model")
211    view.show()
212    sys.exit(app.exec_())
213