Hello,
I had a hard time figuring out why my QAbstractItemModel subclass wasn't
working properly. I added debug statements everywhere, which showed that
my subclass was behaving fine. The only symptoms were:
1. The associated QTreeView() had an empty display.
2. My model was only queried for the columnCount() of the root item
(twice) and for part of the headerData(), and that's all. No
rowCount(), no index(), no data(), nothing.
I reduced the problem to a minimal example, and eventually found that it
was caused by the model (subclass of QAbstractItemModel) falling off the
scope, and therefore presumably gargabe-collected by Python. If I keep
an explicit reference to the model, everything works fine.
My question is: is this behavior normal?
I mean, I have something like:
view.setModel(model)
return view
followed by:
view = [...]
app.exec_()
so that the view is surely not garbage-collected when the app is
running. It seems to me that 'view.setModel(model)' should have added a
reference from 'view' to 'model', so that 'model' doesn't get
garbage-collected either.
But clearly, this isn't the case, because everything works fine as long
as I keep an explicit reference to 'model' when running app.exec_().
To make this clear, I'm attaching my minimal example. There you'll find
the following function:
def test():
model = FooBarModel(files)
view = QtGui.QTreeView()
view.setModel(model)
return view, model
If my main() function calls:
view, model = test()
then everything works fine, because we keep a reference to the model as
long as app.exec_() runs.
But if I change this line into:
view = test()[0]
then everything fails in a very hard-to-debug way, the reason being
presumably that the model is garbage-collected by Python during the
exection of app.exec_().
The tools I'm using are:
* Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
(from Debian etch)
* Qt 4.2.1
(from Debian etch; version 4.2.1-2+etch1 of the qt4 packages)
* PyQt 4.2
(from Debian unstable; python-qt4 packages version 4.2-1 backported
by myself)
Thanks in advance for your comments.
#! /usr/bin/env python
import sys
from PyQt4 import QtCore, QtGui
def debug(s):
sys.stderr.write(s + '\n')
files = [("foo", 12),
("bar", 13),
("baz", 72)]
class FooBarModel(QtCore.QAbstractItemModel):
def __init__(self, files, parent=None):
QtCore.QAbstractItemModel.__init__(self, parent)
self.files = files
self.num_columns = 2
def rowCount(self, parent):
debug("*** rowCount(self, parent=%s)" % (parent,))
debug("parent.internalPointer() = %s" % (parent.internalPointer(),))
debug("parent.isValid(): %s" % parent.isValid())
if parent.isValid():
res = 0
else:
res = len(self.files)
debug("-> return %s" % res)
return res
def columnCount(self, parent):
debug("*** columnCount(self, parent=%s)" % (parent,))
debug("parent.internalPointer() = %s" % (parent.internalPointer(),))
if parent.isValid():
res = 0
else:
res = self.num_columns
debug("-> return %s" % res)
return res
def index(self, row, column, parent):
debug("*** index(self, row=%s, column=%s, parent=%s)" % \
(row, column, parent))
debug("parent.internalPointer() = %s" % (parent.internalPointer(),))
# Only the root element has childs
if parent.isValid():
return QtCore.QModelIndex()
if row < 0 or column < 0 \
or row >= len(self.files) or column >= self.num_columns:
return QtCore.QModelIndex()
return self.createIndex(row, column, self.files[row])
def data(self, index, role):
debug("*** data(self, index=%s, role=%s)" % (index, role))
debug("index.internalPointer() = %s" % (index.internalPointer(),))
if not index.isValid():
return QtCore.QVariant()
if role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
item = index.internalPointer()
if index.column() == 0:
data = item[0]
elif index.column() == 1:
data = item[1]
else:
assert False, index.column()
return QtCore.QVariant(data)
def flags(self, index):
debug("*** flags(self, index=%s)" % (index,))
if not index.isValid():
return QtCore.Qt.ItemIsEnabled
else:
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
debug("*** headerData(self, section=%s, orientation=%s, role=%s)" \
% (section, orientation, role))
data = map(QtCore.QVariant, [self.tr("File"), self.tr("Size")])
if orientation == QtCore.Qt.Horizontal \
and role == QtCore.Qt.DisplayRole:
res = data[section]
else:
res = QtCore.QVariant()
debug("-> return %s (str: '%s')" % (repr(res), res.toString()))
return res
def parent(self, index):
debug("*** parent(self, index=%s)" % (index,))
return QtCore.QModelIndex()
def test():
model = FooBarModel(files)
view = QtGui.QTreeView()
view.setModel(model)
return view, model
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
# With this line, everything works
# view, model = test()
# But with this one, no luck
view = test()[0]
view.show()
sys.exit(app.exec_())
--
Florent
_______________________________________________
PyQt mailing list [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt