Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access data stored in QModelIndex

Tags:

python

qt

pyqt

The code below create a single QListView with the data and proxy models "attached". Clicking one of the radio buttons calls for buttonClicked() function.

This function calls model's .data(index,role) method to get the data stored in a current index.

For the DisplayRole the model's .data() method properly returns the name of index (assigned to it by the time it was created). But when
'ItemDataRole' is used I am getting an error:

TypeError: QSortFilterProxyModel.data(QModelIndex, int role=Qt.DisplayRole): argument 2 has unexpected type 'sip.enumtype'

--

Question 1: How to fix this error?

If you take a look at addItems() method there is a line there:

self.setData(index, 'keyword')

Apparently I am trying to set 'keyword' as my own "custom data" (is it what ProxyModel is going to be use while filtering the indexes?).

Question 2: How to query the string "keyword" I've set with self.setData(index, 'keyword')? Is this data accessible or it is "reserved" and can't be queried?

enter image description here

from PyQt4 import QtCore, QtGui
app=QtGui.QApplication(sys.argv)
elements={'Animals':{1:'Bison',2:'Panther',3:'Elephant'},'Birds':{1:'Duck',2:'Hawk',3:'Pigeon'},'Fish':{1:'Shark',2:'Salmon',3:'Piranha'}}

class ProxyModel(QtGui.QSortFilterProxyModel):
    def __init__(self, parent=None):
        super(ProxyModel, self).__init__(parent)

class DataModel(QtCore.QAbstractListModel):
    def __init__(self):
        QtCore.QAbstractListModel.__init__(self)
        self.items=[] 
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items)
    def data(self, index, role):
        if not index.isValid() or not (0<=index.row()<len(self.items)):  return QtCore.QVariant()

        if role==QtCore.Qt.DisplayRole:
            return self.items[index.row()]

        elif role==QtCore.Qt.ItemDataRole:
            return index.data()

    def addItems(self):
        for key in elements:
            index=QtCore.QModelIndex()
            self.setData(index, 'keyword')
            self.beginInsertRows(index, 0, 0)
            self.items.append(key)           

        self.endInsertRows()        

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        layout=QtGui.QVBoxLayout()
        self.setLayout(layout)        

        self.view=QtGui.QListView()

        self.dataModel=DataModel()        
        self.dataModel.addItems()

        self.proxyModel=ProxyModel()
        self.proxyModel.setSourceModel(self.dataModel)
        self.view.setModel(self.proxyModel)
        buttonsLayout=QtGui.QHBoxLayout()
        animalsButton=QtGui.QRadioButton('Show Animals')
        birdsButton=QtGui.QRadioButton('Show Birds')
        fishButton=QtGui.QRadioButton('Show Fish')
        self.buttons=[animalsButton,birdsButton,fishButton]
        for button in self.buttons:
            button.toggled.connect(self.buttonClicked)
            buttonsLayout.addWidget(button)      
        layout.addWidget(self.view)
        layout.insertLayout(1,buttonsLayout)
        self.show()

    def buttonClicked(self,arg=None):
        for button in self.buttons:
            if button.isChecked(): break

        index=self.view.currentIndex()
        print 'Index DisplayRole: %s'%self.view.model().data(index, QtCore.Qt.DisplayRole).toString() 
        # print 'ItemDataRole', self.view.model().data(index, QtCore.Qt.ItemDataRole)    

window=Window()
sys.exit(app.exec_())
like image 608
alphanumeric Avatar asked Sep 19 '14 22:09

alphanumeric


1 Answers

Here is a working example on how to use QTableView with QStandardItemModel populated with QStandardItems.

A possible advantage of using QtGui.QStandardItemModel over QtCore.QAbstractListModel is that QtGui.QStandardItemModel doesn't have to be sub-classed in order to be assigned to QTableView. You just go ahead and declare it:

view=QtGui.QTableView() # declare table view
model=QtGui.QStandardItemModel() # declare model
view.setModel(model) # assign model to table view

To create a view-item declare it first:

item=QtGui.QStandardItem('My Item Name') # stored 'My Item Name' can be queried using `Qt.DisplayRole` flag

Assign a declared QStandardItem to view using:

model.appendRow(item)

model.appendRow(item) method "creates" as many QModelIndexes as there are columns in a table-view. So there is no need to create QModelIndexes manually for each column in a row : the row the item was assigned to.

Next using item.setData(value,role) method you can store any type of data using one of pre-defined QModelIndex roles such as:

item.setData('< Column 0 My Display Value as "DisplayRole" >', QtCore.Qt.DisplayRole)
item.setData('< Column 0 My Custom Value as "UserRole" >', QtCore.Qt.UserRole)
item.setData('< Column 0 My Custom Value as "UserRole+1" >', QtCore.Qt.UserRole+1)

All three assignment-lines above are used to assign three different values to the same "item": first value is a string '< Column 0 My Display Value as "DisplayRole" >' which is stored under Qt.DisplayRole role (use this role to retrieve it back).

A string value: '< Column 0 My Custom Value as "UserRole" >' is stored under Qt.UserRole. Use this role to retrieve it back. And etc.

While item.setData(value,role) method is quite useful and it does use a simple syntax it is also very limited since it only deals with a single QModelIndex stored in a zero column of the row the item is assigned to. In order to be able to store a custom data (or modify an existing data) stored in non-zero columns of the item's row we get the item's row number first:

itemRow=item.row()

Knowing the item's row-number we can get all the item indexes from every column using in a "manual" mode:

indexOfColumn1=model.index(itemRow, 1)
indexOfColumn2=model.index(itemRow, 2)

(where a second integer argument such as 1 or 2 are the column numbers).

Or in a "fully automatic" mode:

for i in range(model.columnCount()):
    eachColumnIndex=model.index(itemRow, i)

Knowing the item's QModelIndexes we can use them to query or store data just like we did using item.setData() method (a method that only assigns/retreives a data from/to a zero-column QModelIndex).

In order to store and retreive the data in each-column-QModelIndex we have to use model.setData(index,value,role) and model.data(index, role) methods (it appears we can't get/set the data using QModelIndex instance directly... like this:

myIndex.setData(value,role) - this is invalid code since QModelIndex doesn't come with .setData() method.

In order to retrieve the data stored per QModelIndex we use model.data(index,role):

model=view.model()
print model.data(index, QtCore.Qt.UserRole+1).toPyObject() 

For a single column views (such as .QListView) dealing with supplied QStandardItem methods would be probably sufficient. But for multi-column views (such as QTabLeView there is a chance the individual column's QModelIndexes would have to be accessed.

enter image description here

import os,sys
home=os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
sys.path.append(os.path.join(home,'site-packages'))
from PyQt4 import QtCore, QtGui
app=QtGui.QApplication(sys.argv)
class Window(QtGui.QTableView):
    def __init__(self):
        super(Window, self).__init__() 
        model=QtGui.QStandardItemModel()

        model.setHorizontalHeaderLabels(['Column 0','Column 1','Column 3'])
        for i in range(3):
            item=QtGui.QStandardItem('Column 0 Item %s'%i)
            item.setData('< Column 0 Custom Value as "UserRole" >', QtCore.Qt.UserRole)
            item.setData('< Column 0 Custom Value as "UserRole+1" >', QtCore.Qt.UserRole+1)
            model.appendRow(item)

            itemRow=item.row()
            indexOfColumn1=model.index(itemRow, 1)
            indexOfColumn2=model.index(itemRow, 2)

            model.setData(indexOfColumn1, 'Column 1 Item', QtCore.Qt.DisplayRole)
            model.setData(indexOfColumn1, '< Column 1 Custom Value as "UserRole" >', QtCore.Qt.UserRole)

            model.setData(indexOfColumn2, 'Column 2 Item', QtCore.Qt.DisplayRole)
            model.setData(indexOfColumn2, '< Column 2 Custom Value as "UserRole" >', QtCore.Qt.UserRole)

        self.setModel(model)
        self.clicked.connect(self.onClick)  
        self.show()

    def onClick(self,index=None):
        row=index.row()
        column=index.column()

        model=self.model()
        indexOfColumn0=model.index(row, 0)
        indexOfColumn1=model.index(row, 1)
        indexOfColumn2=model.index(row, 2)
        print indexOfColumn0==index, indexOfColumn1==index, indexOfColumn2==index


        print  'ROW: %s  COLUMN: %s'%(row,column)
        print 'DispayRoleData via self.model().data(index,role): "%s"'%self.model().data(index, QtCore.Qt.DisplayRole).toString()
        print 'UserRoleData via self.model().data(index,role): "%s"'%self.model().data(index, QtCore.Qt.UserRole).toPyObject()
        print 'UserRoleData via self.model().data(index,role): "%s"'%self.model().data(index, QtCore.Qt.UserRole+1).toPyObject()      

        for key in self.model().itemData(index):
            print 'self.model.itemData(index):    key: %s  value: %s'%(key, self.model().itemData(index)[key].toString())

        item=self.model().itemFromIndex(index)


window=Window()
sys.exit(app.exec_())
like image 110
alphanumeric Avatar answered Sep 19 '22 17:09

alphanumeric