How to save new data in tree model view
Question: I'm trying to modify the excellent example of pyqt4 called "Editabletreemodel"
but I have a problem I can't manage: after I add a new row in the model, how
can I save or update the data I've inserted to a text file? Or more in
general, how is it possible to save data from the model/view into a file?

Thank you for your help.

    
    
    # 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
    from ui_mainwindow import Ui_MainWindow
    import sys, os, time
    import paramiko
    import threading
    
    
    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 = ("Sendor Name", "Address", "Comments")
    
            file = QtCore.QFile('./default.txt')
            file.open(QtCore.QIODevice.ReadOnly)
            model = TreeModel(headers, file.readAll())
            file.close()
    
            print model.invisibleRootItem()
    
    
            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.callSensorsButton.clicked.connect(self.call_sensors)
    
            self.updateActions()
    
            self.view.expandAll()
            self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            self.view.customContextMenuRequested.connect(self.openMenu)
    
            self.connection = connection_thread()
            self.connect(self.connection, QtCore.SIGNAL("started()"), self.start_progressBar)
            self.connect(self.connection, QtCore.SIGNAL("finished()"), self.stop_progressBar)
            self.connect(self.connection, QtCore.SIGNAL("terminated()"), self.stop_progressBar)
    
            self.SaveListButton.clicked.connect(self.save_sensor_list)
            self.pushButton.clicked.connect(self.prova)
    
    
        def save_sensor_list(self):
            index = self.view.selectionModel().currentIndex()
            model = self.view.model()
    
            print model.rootItem
    
            for i in range(0, model.rootItem.rowCount()):
                print model.child(i)
    
    
    
        def prova(self):
            index = self.view.selectionModel().currentIndex()
            model = self.view.model()
    
            print model.data(index,0)
    
        def openMenu(self, position):
            indexes = self.view.selectedIndexes()
            model = self.view.model()
            if len(indexes) > 0:
    
                level = 0
                index = indexes[0]
                while index.parent().isValid():
                    index = index.parent()
                    level += 1
    
            menu = QtGui.QMenu()
            if level == 0:
                menu.addAction(self.tr("Call all %ss" % (model.data(index,0))))
                menu.addSeparator()
                menu.addAction(self.tr("Add new sensor family"),self.insertRow)
                menu.addAction(self.tr("Add new sensor"),self.insertChild)
            elif level == 1:
                menu.addAction(self.tr("Call this sensor"))
                menu.addSeparator()
                menu.addAction(self.tr("Add new sensor"),self.insertRow)
            elif level == 2:
                menu.addAction(self.tr("Edit object"))
    
            menu.exec_(self.view.viewport().mapToGlobal(position))
    
        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))
    
        def start_progressBar(self):
            self.progressBar.setRange(0,0)
            self.progressBar.setValue(0)
    
        def stop_progressBar(self):
            self.progressBar.setRange(0,1)
            self.progressBar.setValue(1)
    
        def call_sensors(self):
            self.textEdit.insertPlainText("Connecting to Fox...\n")
            self.connection.start_thread(self.textEdit)
    
    
    class connection_thread(QtCore.QThread):
        def __init__(self, parent = None):
            QtCore.QThread.__init__(self, parent)
    
        def start_thread(self,textEdit):
            self.textEdit = textEdit
            self.start()
    
        def run(self):
            print "Dentro il thread"
            time.sleep(10)
            try:
                ssh = paramiko.SSHClient()
                ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                ssh.connect('192.168.0.90', username='root', password='netusg20')
                self.textEdit.insertPlainText("Calling sensor list...\n")
                app.processEvents()
                stdin, stdout, stderr = ssh.exec_command('python g20.py c')
                self.textEdit.insertPlainText(stdout.read())
                self.textEdit.insertPlainText(stderr.read())
                self.textEdit.insertPlainText("Connection closed\n")
                ssh.close()
                app.processEvents()
            except:
                self.textEdit.insertPlainText(str(sys.exc_info()[1]))
                ssh.close()
                app.processEvents()
    
    if __name__ == '__main__':
    
        app = QtGui.QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec_())
    


Answer: I guess the easies way would be to iterate though model items and save save to
file via [QDataStream](http://doc.qt.nokia.com/latest/qdatastream.html).
QDataStream supports reading\writing QVariant's and you can get\set the model
item's data as [QVariant](http://doc.qt.nokia.com/4.7-snapshot/qvariant.html).
Below is a small example, I'm using
[QStandardItemModel](http://doc.qt.nokia.com/4.7.1/qstandarditemmodel.html)
for simplicity:

    
    
    import sys
    from PyQt4 import QtGui, QtCore
    
    class MainForm(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainForm, self).__init__(parent)
            self.setMinimumSize(400, 400)
    
            # create model
            self.model = QtGui.QStandardItemModel()        
            for k in range(0, 4):
                parentItem = self.model.invisibleRootItem()
                for i in range(0, 4):
                    item = QtGui.QStandardItem(QtCore.QString("item %0 %1").arg(k).arg(i))
                    parentItem.appendRow(item)
                    parentItem = item
    
            # create treeview
            self.view = QtGui.QTreeView(self)
            self.view.setModel(self.model)
            self.view.setMinimumSize(300, 400)
    
            self.saveButton = QtGui.QPushButton("save", self)
            self.saveButton.move(300, 1)
            self.saveButton.clicked.connect(self.on_save_button_clicked)
    
            self.layout = QtGui.QVBoxLayout(self.centralWidget())        
            self.layout.addWidget(self.view)
            self.layout.addWidget(self.saveButton)
    
        def on_save_button_clicked(self):
            # create text file
            file = QtCore.QFile("save.txt")
            file.open(QtCore.QIODevice.WriteOnly)
            # open data stream
            out = QtCore.QDataStream(file)
            # recursively write model item into the datastream 
            self.save_item(self.model.invisibleRootItem(), out)
    
        def save_item(self, item, out):
            for i in range(0, item.rowCount()):
                child = item.child(i)
                child.write(out)
                self.save_item(child, out)
    
    def main():
        app = QtGui.QApplication(sys.argv)
        form = MainForm()
        form.show()
        app.exec_()
    
    if __name__ == '__main__':
        main() 
    

hope this helps, regards

