[PySide] Bug: widget doesn't update when model change?

1,435 views
Skip to first unread message

Jun Koi

unread,
Feb 7, 2012, 5:24:52 AM2/7/12
to pys...@lists.pyside.org
hi,

i am creating a treeview (QTreeView), and i set a model
(QAbstractItemModel) for it (using setModel() method).

now i update the model (with new data), and call treeview.update()
with the hope that my treeview will be redrawn to reflect the new
data.

however, that doesnt update the view. i looked closer, and confirm
that even after treeview.update() is called, the rowCount() method of
the model is not executed at all. which means the update() doesnt do
anything with the new model data.

i must missed something. anybody please tell me how to fix this problem?

thanks,
Jun
_______________________________________________
PySide mailing list
PyS...@lists.pyside.org
http://lists.pyside.org/listinfo/pyside

Daniele Esposti

unread,
Feb 7, 2012, 6:08:03 AM2/7/12
to Jun Koi, pys...@lists.pyside.org
Hi Jun,

the slot QAbstractItemView.update() wants a QModelIndex as a parameter
http://www.pyside.org/docs/pyside/PySide/QtGui/QAbstractItemView.html#PySide.QtGui.PySide.QtGui.QAbstractItemView.update

This means you need to pass the QModelIndex of the data you added to the model.

--
Daniele Esposti

My Blog http://www.expobrain.net
LinkedIn http://www.linkedin.com/in/danieleesposti
Twitter http://www.twitter.com/#!/expobrain

Jun Koi

unread,
Feb 7, 2012, 6:23:16 AM2/7/12
to Daniele Esposti, pys...@lists.pyside.org
On Tue, Feb 7, 2012 at 7:08 PM, Daniele Esposti <ex...@expobrain.net> wrote:
> Hi Jun,
>
> the slot QAbstractItemView.update() wants a QModelIndex as a parameter
> http://www.pyside.org/docs/pyside/PySide/QtGui/QAbstractItemView.html#PySide.QtGui.PySide.QtGui.QAbstractItemView.update
>
> This means you need to pass the QModelIndex of the data you added to the model.

this means i need to call update() method of the model, right?

how about the update() method of the TreeView widget? do i need to call it?

thanks a lot,

Joel B. Mohler

unread,
Feb 7, 2012, 7:23:14 AM2/7/12
to pys...@lists.pyside.org
On 02/07/2012 05:24 AM, Jun Koi wrote:
> hi,
>
> i am creating a treeview (QTreeView), and i set a model
> (QAbstractItemModel) for it (using setModel() method).
>
> now i update the model (with new data), and call treeview.update()
> with the hope that my treeview will be redrawn to reflect the new
> data.
>
> however, that doesnt update the view. i looked closer, and confirm
> that even after treeview.update() is called, the rowCount() method of
> the model is not executed at all. which means the update() doesnt do
> anything with the new model data.
>
> i must missed something. anybody please tell me how to fix this problem?

I think you might fix this by emitting one of the row change signals
from your model. For example, rowsInserted --
http://developer.qt.nokia.com/doc/qt-4.8/qabstractitemmodel.html#rowsInserted
.

Joel

Daniele Esposti

unread,
Feb 7, 2012, 7:47:19 AM2/7/12
to Jun Koi, pys...@lists.pyside.org
Hi Jun,

update(QModelIndex) is a slot in QAbstractItemView (and which is the
superclass of QTreeView); QAbstractItemModel doesn't have any update()
method at all.

What you can do (as far as I know about your code) is to emit a
rowsAboutToBeInserted() before adding the data to your model and
rowsInserted() after the data is added to your model. The view
connected to your model will show your data automatically.

On 7 February 2012 11:23, Jun Koi <junko...@gmail.com> wrote:
> On Tue, Feb 7, 2012 at 7:08 PM, Daniele Esposti <ex...@expobrain.net> wrote:
>> Hi Jun,
>>
>> the slot QAbstractItemView.update() wants a QModelIndex as a parameter
>> http://www.pyside.org/docs/pyside/PySide/QtGui/QAbstractItemView.html#PySide.QtGui.PySide.QtGui.QAbstractItemView.update
>>
>> This means you need to pass the QModelIndex of the data you added to the model.
>
> this means i need to call update() method of the model, right?
>
> how about the update() method of the TreeView widget? do i need to call it?
>
> thanks a lot,
> Jun

--
Daniele Esposti

Aaron Richiger

unread,
Feb 7, 2012, 9:22:29 AM2/7/12
to pys...@lists.pyside.org
Hello Jun!

As other suggested, model.rowsInserted(parent, start, end).emit() is the
signal you need. To show you the way one could do this, I shortly
changed the Simple Tree View example of PySide (quick and dirty, I know,
but it shows enough)... You may add new items filling in the LineEdits
and then clicking the button. The only magic line is the last one of the
method TreeModel.addData().

Hope this helps (and for the future, it would be probably a good idea to
change the treeview example of PySide to make it ready for use with
dynamic models...).
Code:


# -*- coding: utf-8 -*-

'''
@author: Aaron Richiger
@contact: a.r...@bluewin.ch
@date: 08.02.2011
'''

from PySide.QtGui import *
from PySide.QtCore import *
import sys

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):
try:
return self.itemData[column]
except IndexError:
return None

def parent(self):
return self.parentItem

def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)

return 0


class TreeModel(QAbstractItemModel):
def __init__(self, rootItem, parent=None):
super(TreeModel, self).__init__(parent)

self.rootItem = rootItem

def columnCount(self, parent = QModelIndex()):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()

def data(self, index, role):
if not index.isValid():
return None

if role != Qt.DisplayRole:
return None

item = index.internalPointer()

return item.data(index.column())

def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags

return Qt.ItemIsEnabled | Qt.ItemIsSelectable

def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.rootItem.data(section)

return None

def index(self, row, column, parent = QModelIndex()):
if not self.hasIndex(row, column, parent):
return QModelIndex()

if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()

childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QModelIndex()

def parent(self, index):
if not index.isValid():
return QModelIndex()

childItem = index.internalPointer()
parentItem = childItem.parent()

if parentItem == self.rootItem:
return QModelIndex()

return self.createIndex(parentItem.row(), 0, parentItem)

def rowCount(self, parent = QModelIndex()):
if parent.column() > 0:
return 0

if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()

return parentItem.childCount()

def addData(self, data, parent):
lines = data.split('\n')
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:].strip()

if lineData:
# Read the column data from the rest of the line.
columnData = [s.strip() for s in lineData.split(';') 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.
parents[-1].appendChild(TreeItem(columnData, parents[-1]))

number += 1
self.rowsInserted.emit(self, self.rowCount() - 1,
self.rowCount() - 1)

if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QWidget()

data = '''
Getting Started; How to familiarize yourself with Qt Designer
Launching Designer; Running the Qt Designer application
The User Interface; How to interact with Qt Designer

Designing a Component; Creating a GUI for your application
Creating a Dialog; How to create a dialog
Composing the Dialog; Putting widgets into the dialog example'''

rootItem = TreeItem(("Title", "Summary"))
model = TreeModel(rootItem)
model.addData(data, rootItem)

treeView = QTreeView(widget)
treeView.setModel(model)

lbTitle = QLabel('Title:', widget)
lbSummary = QLabel('Summary:', widget)
leTitle = QLineEdit(widget)
leSummary = QLineEdit(widget)
pbAddItem = QPushButton('Add this item', widget)
pbAddItem.clicked.connect(lambda: model.addData(leTitle.text() +
';' + leSummary.text(), model.rootItem))

grid = QGridLayout(widget)
grid.addWidget(treeView, 0, 0, 1, 2)
grid.addWidget(lbTitle, 1, 0, 1, 1)
grid.addWidget(lbSummary, 2, 0, 1, 1)
grid.addWidget(leTitle, 1, 1, 1, 1)
grid.addWidget(leSummary, 2, 1, 1, 1)
grid.addWidget(pbAddItem, 3, 1, 1, 1)
widget.show()

sys.exit(app.exec_())

Am 07.02.2012 13:47, schrieb Daniele Esposti:
> Hi Jun,
>
> update(QModelIndex) is a slot in QAbstractItemView (and which is the
> superclass of QTreeView); QAbstractItemModel doesn't have any update()
> method at all.
>
> What you can do (as far as I know about your code) is to emit a
> rowsAboutToBeInserted() before adding the data to your model and
> rowsInserted() after the data is added to your model. The view
> connected to your model will show your data automatically.
>
> On 7 February 2012 11:23, Jun Koi<junko...@gmail.com> wrote:
>> On Tue, Feb 7, 2012 at 7:08 PM, Daniele Esposti<ex...@expobrain.net> wrote:
>>> Hi Jun,
>>>
>>> the slot QAbstractItemView.update() wants a QModelIndex as a parameter
>>> http://www.pyside.org/docs/pyside/PySide/QtGui/QAbstractItemView.html#PySide.QtGui.PySide.QtGui.QAbstractItemView.update
>>>
>>> This means you need to pass the QModelIndex of the data you added to the model.
>> this means i need to call update() method of the model, right?
>>
>> how about the update() method of the TreeView widget? do i need to call it?
>>
>> thanks a lot,
>> Jun
>
>

_______________________________________________

Jun Koi

unread,
Feb 7, 2012, 10:16:14 AM2/7/12
to Aaron Richiger, pys...@lists.pyside.org
On Tue, Feb 7, 2012 at 10:22 PM, Aaron Richiger <a.r...@bluewin.ch> wrote:
> Hello Jun!
>
> As other suggested, model.rowsInserted(parent, start, end).emit() is the
> signal you need. To show you the way one could do this, I shortly changed
> the Simple Tree View example of PySide (quick and dirty, I know, but it
> shows enough)... You may add new items filling in the LineEdits and then
> clicking the button. The only magic line is the last one of the method
> TreeModel.addData().

this is awesome, thanks!!!

however, inserting new data is not the only thing i want to do. i also
want to modify some data of the model.
so what i really want is to ask the view to redraw a particular row
after i update the data of that row.
what is the best way to do that?

(for the record, i also use the model similar to what Aaron posted
above, as i also reused the SimpleTreeModel demo)

thanks again for all of your generous help. this community is amazing!

best,

Daniele Esposti

unread,
Feb 7, 2012, 10:35:21 AM2/7/12
to pys...@lists.pyside.org

Jun Koi

unread,
Feb 7, 2012, 10:53:56 PM2/7/12
to Aaron Richiger, pys...@lists.pyside.org
On Tue, Feb 7, 2012 at 10:22 PM, Aaron Richiger <a.r...@bluewin.ch> wrote:
> Hello Jun!
>
> As other suggested, model.rowsInserted(parent, start, end).emit() is the
> signal you need. To show you the way one could do this, I shortly changed
> the Simple Tree View example of PySide (quick and dirty, I know, but it
> shows enough)... You may add new items filling in the LineEdits and then
> clicking the button. The only magic line is the last one of the method
> TreeModel.addData().
>
.....

>    model = TreeModel(rootItem)
>    model.addData(data, rootItem)

with this awesome demo, i can add a new data to the view with
TreeModel.addData(), like in the code you wrote above.

for example, i created a new tree node like this:

....
NewData


now, if i want to create a subnode for NewData, so it will be like this:

....
NewData
|____ MyChildData


i guess to do that, again i need to use TreeModel.addData(), but the
difference is that this time i need the model of NewData, so it will
create a subnode, but not the model of the TreeView (the root) like
when i created NewData

so the question is: how can i get the model of the NewData i created above?
(i suppose that i need to modify the addData() function, so it returns
the model of the newly created node, but i have no idea how to do
that)

Daniele Esposti

unread,
Feb 8, 2012, 4:04:02 AM2/8/12
to Jun Koi, pys...@lists.pyside.org
Hi Jun,

to be consistent the addData() method should accept your data and the
parent QModelIndex where the data will be added, so the method
signature will be something like addData(data, parent=QModelIndex()).
By convention an invalid QModelIndex represent the root item of a tree
view.

The return value of addData should be the QModelIndex instance which
points to your data, so you can call again the addData() method
passing the new data as the parent.

Talking about code:

...
index1 = model.addData(data1) # Add data to the root item and return
the QModelIndex instance
index2 = model.addData(data2, parent=index1) # Add data as child of
the previously added data

Read carefully the docs about the QModelIndex here
http://www.pyside.org/docs/pyside/PySide/QtCore/QModelIndex.html
because it's very important when working with models and views. And
QAbstarctItemModel too
http://www.pyside.org/docs/pyside/PySide/QtCore/QAbstractItemModel.html
.

--
Daniele Esposti

Reply all
Reply to author
Forward
0 new messages