Hi! I've been trying to fight my way though the model-view-delegate paradigm in pyqt and must admit that I'm a bit confused. Tables weren't that bad, but now I've reached trees....So, I have some questions. My first question is what are the reasons to use internalptr as compared to internalid.
One strategy that I've seen people adopt is to subclass QAbstractItemModel and to create a dictionary in it. They then use id() on the python side to create a dictionary where the keys are the ids of objects. As far as I can tell, internalid() also returns an address, so in data methods, etc. they can get at the actual item that they want to. When they use createindex, they store the id() of the object of interest for later retrieval, but it seems to hardly matter, because they use internalid() to get the address and then their dictionary to retrieve the object. Here is an example I found using this approach: """*************************************************************************** ** ** 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 [email protected]. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ***************************************************************************""" import sys from PyQt4 import QtCore, QtGui from PyQt4.examples.itemviews.simpletreemodel import simpletreemodel_rc class TreeItem(object): def __init__(self, data, parent=None): self.parentItem = parent self.itemData = data self.childItems = [] def appendChild(self, item): self.childItems.append(item) def child(self, row): return self.childItems[row] def childCount(self): return len(self.childItems) def columnCount(self): return len(self.itemData) def data(self, column): return self.itemData[column] def parent(self): return self.parentItem def row(self): if self.parentItem: return self.parentItem.childItems.index(self) return 0 class TreeModel(QtCore.QAbstractItemModel): def __init__(self, data, parent=None): QtCore.QAbstractItemModel.__init__(self, parent) self.idMap = {} rootData = [] rootData.append(QtCore.QVariant("Title")) rootData.append(QtCore.QVariant("Summary")) self.rootItem = TreeItem(rootData) self.idMap[id(self.rootItem)] = self.rootItem self.setupModelData(data.split("\n"), self.rootItem) def columnCount(self, parent): if parent.isValid(): return self.idMap[parent.internalId()].columnCount() else: return self.rootItem.columnCount() def data(self, index, role): if not index.isValid(): return QtCore.QVariant() if role != QtCore.Qt.DisplayRole: return QtCore.QVariant() try: item = self.idMap[index.internalId()] return QtCore.QVariant(item.data(index.column())) except KeyError: return QtCore.QVariant() def flags(self, index): if not index.isValid(): return QtCore.Qt.ItemIsEnabled return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def headerData(self, section, orientation, role): if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: return self.rootItem.data(section) return QtCore.QVariant() def index(self, row, column, parent): if row < 0 or column < 0 or row >= self.rowCount(parent) or column >= self.columnCount(parent): return QtCore.QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = self.idMap[parent.internalId()] childItem = parentItem.child(row) if childItem: index = self.createIndex(row, column, id(childItem)) self.idMap.setdefault(index.internalId(), childItem) return index else: return QtCore.QModelIndex() def parent(self, index): if not index.isValid(): return QtCore.QModelIndex() try: childItem = self.idMap[index.internalId()] parentItem = childItem.parent() if parentItem == self.rootItem: return QtCore.QModelIndex() return self.createIndex(parentItem.row(), 0, id(parentItem)) except KeyError: return QtCore.QModelIndex() def rowCount(self, parent): if parent.column() > 0: return 0 try: if not parent.isValid(): parentItem = self.rootItem else: parentItem = self.idMap[parent.internalId()] return parentItem.childCount() except: return 0 def setupModelData(self, lines, parent): parents = [] indentations = [] parents.append(parent) indentations.append(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 not lineData.isEmpty(): # Read the column data from the rest of the line. columnStrings = lineData.split("\t", QtCore.QString.SkipEmptyParts) columnData = [] for column in range(0, len(columnStrings)): columnData.append(columnStrings[column]) 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. item = TreeItem(columnData, parents[-1]) self.idMap[id(item)] = item parents[-1].appendChild(item) number += 1 if __name__ == "__main__": app = QtGui.QApplication(sys.argv) f = QtCore.QFile(":/default.txt") f.open(QtCore.QIODevice.ReadOnly) model = TreeModel(QtCore.QString(f.readAll())) f.close() view = QtGui.QTreeView() view.setModel(model) view.setWindowTitle("Simple Tree Model") view.show() sys.exit(app.exec_()) ####################################################################### ####################################################################### The other strategy I've seen is for people to simply use internalptr() to retrieve the item that they want. Here is an example I found of this approach: import sys from PyQt4.QtCore import * from PyQt4.QtGui import * from copy import deepcopy from cPickle import dumps, load, loads from cStringIO import StringIO class PyMimeData(QMimeData): """ The PyMimeData wraps a Python instance as MIME data. """ # The MIME type for instances. MIME_TYPE = QString('application/x-ets-qt4-instance') def __init__(self, data=None): """ Initialise the instance. """ QMimeData.__init__(self) # Keep a local reference to be returned if possible. self._local_instance = data if data is not None: # We may not be able to pickle the data. try: pdata = dumps(data) except: return # This format (as opposed to using a single sequence)allows the # type to be extracted without unpickling the data itself. self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata) @classmethod def coerce(cls, md): """ Coerce a QMimeData instance to a PyMimeData instance if possible. """ # See if the data is already of the right type. If it is thenwe know # we are in the same process. if isinstance(md, cls): return md # See if the data type is supported. if not md.hasFormat(cls.MIME_TYPE): return None nmd = cls() nmd.setData(cls.MIME_TYPE, md.data()) return nmd def instance(self): """ Return the instance. """ if self._local_instance is not None: return self._local_instance io = StringIO(str(self.data(self.MIME_TYPE))) try: # Skip the type. load(io) # Recreate the instance. return load(io) except: pass return None def instanceType(self): """ Return the type of the instance. """ if self._local_instance is not None: return self._local_instance.__class__ try: return loads(str(self.data(self.MIME_TYPE))) except: pass return None class myNode(object): def __init__(self, name, state, description, parent=None): self.name = QString(name) self.state = QString(state) self.description = QString(description) self.parent = parent self.children = [] self.setParent(parent) def setParent(self, parent): if parent != None: self.parent = parent self.parent.appendChild(self) else: self.parent = None def appendChild(self, child): self.children.append(child) def childAtRow(self, row): return self.children[row] def rowOfChild(self, child): for i, item in enumerate(self.children): if item == child: return i return -1 def removeChild(self, row): value = self.children[row] self.children.remove(value) return True def __len__(self): return len(self.children) class myModel(QAbstractItemModel): def __init__(self, parent=None): super(myModel, self).__init__(parent) self.treeView = parent self.headers = ['Item','State','Description'] self.columns = 3 # Create items self.root = myNode('root', 'on', 'this is root', None) itemA = myNode('itemA', 'on', 'this is item A',self.root) #itemA.setCheckState(0, Qt.Unchecked) # 0 is the column number itemA1 = myNode('itemA1', 'on', 'this is item A1', itemA) itemB = myNode('itemB', 'on', 'this is item B', self.root) itemB1 = myNode('itemB1', 'on', 'this is item B1', itemB) itemC = myNode('itemC', 'on', 'this is item C',self.root) itemC1 = myNode('itemC1', 'on', 'this is item C1', itemC) def supportedDropActions(self): return Qt.CopyAction | Qt.MoveAction def flags(self, index): defaultFlags = QAbstractItemModel.flags(self, index) if index.isValid(): return Qt.ItemIsEditable | Qt.ItemIsDragEnabled | \ Qt.ItemIsDropEnabled | defaultFlags |Qt.ItemIsUserCheckable else: return Qt.ItemIsDropEnabled | defaultFlags | Qt.ItemIsUserCheckable def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return QVariant(self.headers[section]) return QVariant() def mimeTypes(self): types = QStringList() types.append('application/x-ets-qt4-instance') return types def mimeData(self, index): node = self.nodeFromIndex(index[0]) mimeData =PyMimeData(node) return mimeData def dropMimeData(self, mimedata, action, row, column, parentIndex): if action == Qt.IgnoreAction: return True dragNode = mimedata.instance() parentNode = self.nodeFromIndex(parentIndex) # make an copy of the node being moved newNode = deepcopy(dragNode) newNode.setParent(parentNode) self.insertRow(len(parentNode)-1, parentIndex) self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),parentIndex, parentIndex) return True def insertRow(self, row, parent): return self.insertRows(row, 1, parent) def insertRows(self, row, count, parent): self.beginInsertRows(parent, row, (row + (count - 1))) self.endInsertRows() return True def removeRow(self, row, parentIndex): return self.removeRows(row, 1, parentIndex) def removeRows(self, row, count, parentIndex): self.beginRemoveRows(parentIndex, row, row) node = self.nodeFromIndex(parentIndex) node.removeChild(row) self.endRemoveRows() return True def index(self, row, column, parent): node = self.nodeFromIndex(parent) return self.createIndex(row, column, node.childAtRow(row)) def data(self, index, role): if role == Qt.DecorationRole: return QVariant() if role == Qt.TextAlignmentRole: return QVariant(int(Qt.AlignTop | Qt.AlignLeft)) if role != Qt.DisplayRole: return QVariant() node = self.nodeFromIndex(index) if index.column() == 0: return QVariant(node.name) elif index.column() == 1: return QVariant(node.state) elif index.column() == 2: return QVariant(node.description) else: return QVariant() def columnCount(self, parent): return self.columns def rowCount(self, parent): node = self.nodeFromIndex(parent) if node is None: return 0 return len(node) def parent(self, child): if not child.isValid(): return QModelIndex() node = self.nodeFromIndex(child) if node is None: return QModelIndex() parent = node.parent if parent is None: return QModelIndex() grandparent = parent.parent if grandparent is None: return QModelIndex() row = grandparent.rowOfChild(parent) assert row != - 1 return self.createIndex(row, 0, parent) def nodeFromIndex(self, index): return index.internalPointer() if index.isValid() else self.root class myTreeView(QTreeView): def __init__(self, parent=None): super(myTreeView, self).__init__(parent) self.myModel = myModel() self.setModel(self.myModel) #item=self.currentItem() #item.setCheckState(0, Qt.Unchecked) # 0 is the column number self.dragEnabled() self.acceptDrops() self.showDropIndicator() self.setDragDropMode(QAbstractItemView.InternalMove) self.connect(self.model(), SIGNAL("dataChanged(QModelIndex,QModelIndex)"), self.change) self.expandAll() def change(self, topLeftIndex, bottomRightIndex): self.update(topLeftIndex) self.expandAll() self.expanded() def expanded(self): for column in range(self.model().columnCount(QModelIndex())): self.resizeColumnToContents(column) class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(600, 400) self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") self.treeView = myTreeView(self.centralwidget) self.treeView.setObjectName("treeView") self.horizontalLayout.addWidget(self.treeView) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setGeometry(QRect(0, 0, 600, 22)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QApplication.translate("MainWindow","MainWindow", None, QApplication.UnicodeUTF8)) if __name__ == "__main__": app = QApplication(sys.argv) MainWindow = QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_()) Is there a reason to employ one approach as compared to the other? __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
_______________________________________________ PyQt mailing list [email protected] http://www.riverbankcomputing.com/mailman/listinfo/pyqt
