PDA

View Full Version : Help with Model View



weepdoo
10th October 2007, 11:09
Hye all,

I'm developing an application with PyQt (I do not think it's the problem), and I've got some little problem with my model and my views.

I've got 1 model with 3 view on QSortFilterProxyModel that point on my model.
Some point on a specific node of the global model ("setRootIndex").

My problem is that when i want to add a new row in my model (I use "beginInsertRows" and "endInsertRows") it work not exactly well; row are properly inserted, but all my windows lost there RootIndex.

Does any of you know how to manage it ?

wysota
10th October 2007, 11:16
Could you show us your implementation of insertRow()?

weepdoo
11th October 2007, 10:29
As I said, I'm codinf with PyQt, so it's python:


def newFromIndex(self, index):
"""
@param index: the index wanted
@type index: QModelIndex
"""
self.beginInsertRows(index, self.rowCount(index), self.rowCount(index))

node = self.getNodeFromIndex(index)
newNode = node.new()
newIndex = self.getIndexFromNode(newNode)

self.endInsertRows()
return newIndex


I do not use "insertRow" because I have two different case where I have to inser a new row (new and duplicate).

wysota
11th October 2007, 10:40
This code doesn't say much... What does node.new() exactly do? Does it insert the new node into some internal structure? You are inserting the new node as a child of the old node, correct?

weepdoo
11th October 2007, 10:59
Yes :)
Ok, I going to give a bit more explanation :P

My data struct is has this:


root
|-A
|-S
|-E


The 'new' method applied on the node 'A' and produce the result:


root
|-A
|-S
| |-E
|-New S


So, the method "newFromIndex" get the index of 'A' create a 'New S' and return.
The new node is added as the last child (that why I use "rowCount(index)").

The two method "getNodeFromIndex" and "getIndexFromNode" are just two method to make the link from a QModelIndex and my data. But they do not modifiy anything in my data.

wysota
11th October 2007, 11:31
You could get rid of those two methods you know... Each model index can contain an internalPointer() or internalId() that you can tie to your datastructure. Then you can just query for that value and access your node directly.

Honestly I can't see anything wrong with your code. Could you show the part of code responsible for calling that method?

weepdoo
11th October 2007, 11:53
Ok...
It's going to be a bit long here, but I'll try.

I've got a main window that can manipulate many sort of data (so many different models), and if we change the current type of data, it change the model, mainwindow menu etc... well quiet classic in fact.
There is a qWorkSpace, and different windows in it

Here is the init off the QAction button for the 'new':


self._dActions["actionDocNew"] = QtGui.QAction(self._parent)
self._dActions["actionDocNew"].setObjectName("actionDocNew")
self._dActions["actionDocNew"].setText(_(u"New"))
self._dActions["actionDocNew"].setEnabled(False)
QtCore.QObject.connect(self._dActions["actionDocNew"],
QtCore.SIGNAL('activated()'),
self._newDoc)

All actions are stored in a dictionnary (=hasTable) called "dActions" (_ before 'means' protected in python)

Here is the code for the 'new' action


def _newDoc(self):
index = self._mainWindow.currentIndex
# If the index is not correct we do nothing
if not index:
return
#This is my call to the model (as I showed it to you befiore)
newIndex = self._mainWindow.currentModel.newFromIndex(index)

# Now we edit the properties of the new index
# Even If I unable this call, I've got my problem
self._properties(newIndex)


In my child Windows (that display different part of the model as the node 'E') I link with the model as this.
They all inherit from QWidget (not from QAbstractItemView because some part of my widget are not linked to the model).
self.ui <- subobject where all view / widget are
self.ui.treeView <- a simple QTreeView


def setModel(self, model, globalRootIndex=None):
# We use a proxy, and not the model
self._proxy = QtGui.QSortFilterProxyModel()
self._proxy.setSourceModel(model)

#We link the treeView (used in this widget) and the proxy model
self.ui.treeView.setModel(self._proxy)

# We set the correct tree root
proxyRootIndex = self._proxy.mapFromSource(globalRootIndex)
self.ui.treeView.setRootIndex(proxyRootIndex)

# We add the delegate, to manage event
self.ui.treeView.setItemDelegate(self._delegate)

wysota
11th October 2007, 12:05
Could you create an additional small object that would connect to signals layoutChanged, modelReset and rowsInserted of the model and report when they are emited? Then you can trace what exactly happens when you insert an item to the model.

weepdoo
11th October 2007, 14:25
Ok, I've changed a bit my model:

Here is how I init it


class Model(QtCore.QAbstractItemModel):
def __init__(self, qtgDoc, parent=None):
super(QtgModel, self).__init__(parent)
#======= Attributes Declaration =========
[...] # <- We do not care this

self.modelLoger = modelLoger(self) # <- the "listener" of the model
#========================================

#We test our listener :)
self.emit(QtCore.SIGNAL("layoutChanged()"))


Here is the code of our listener


class modelLoger(object):
def __init__(self, model):
connect = QtCore.QObject.connect #We create a shortcut to the function (easiest :P)

connect(model,
QtCore.SIGNAL('layoutChanged()'),
self.layoutChanged)

connect(model,
QtCore.SIGNAL('modelReset()'),
self.modelReset)

connect(model,
QtCore.SIGNAL('rowsInserted(const QModelIndex &, int , int)'),
self.rowsInserted)

def layoutChanged(self):
print time.strftime("%H:%M:%S"), "layout Changed"

def modelReset(self):
print time.strftime("%H:%M:%S"), "modelReset"

def rowsInserted(self):
print time.strftime("%H:%M:%S"), "rowsInserted"


Here is the output I have (I only launch the application, make a new 'S' under 'A' and quit) :


14:19:35 layout Changed # <- the test in the __init__ of the model
14:19:39 rowsInserted # <- we create the new 's'
#<- we quit :)


Thanks for your time ;)

weepdoo
11th October 2007, 14:44
You could get rid of those two methods you know... Each model index can contain an internalPointer() or internalId() that you can tie to your datastructure. Then you can just query for that value and access your node directly.

I really doubted it could work.
Because, when we use "createIndex" we do it like this:


self.createIndex(row, column, id(node))


Where 'id' is a built in function, that return a unique number of the object (quiet like a pointer in C++). From the python documentation: This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime

But in Python, we always use reference on object, and there is (as far as I know) no way to get the object when you have his address in memory (nor his 'id').

That why I have to create my own methods.

But I've tested your method, and it work just great :)
Thanks to you :D

wysota
11th October 2007, 17:45
Here is the output I have (I only launch the application, make a new 'S' under 'A' and quit) :


14:19:35 layout Changed # <- the test in the __init__ of the model
14:19:39 rowsInserted # <- we create the new 's'
#<- we quit :)



That's not good. I was hoping to receive a faulty modelReset() or similar signal that might have caused the views to malfunction, but nothing like that happens. Could you check what happens if you remove the proxies from the model? Does the root index still malfunction?

weepdoo
12th October 2007, 10:21
I've change my windows code to do not use a proxy, and I do not have the problem :(

Must I "reset" the rootIndex in the proxy ?

weepdoo
12th October 2007, 10:49
I've use the same object logger as before on my proxy, and when I inser a new row, with 3 view opened I've got this:


10:45:30 rowsInserted # <- I inser the row
10:45:30 layout Changed # <-|
10:45:30 layout Changed # <-|- The three view have their layout changed
10:45:30 layout Changed # <-|


Should I use this signal to reset the root index to the view ?

weepdoo
12th October 2007, 11:32
I've catch the signal (layoutChanged) and reset the root Index to the correct value, and I must confess that it work well :)

Thank for your help wysota ;)