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?
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(): breakindex=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_())
Here is a working example on how to use QTableView
with QStandardItemModel
populated with QStandardItem
s.
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 QModelIndex
es as there are columns in a table-view. So there is no need to create QModelIndex
es 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 QModelIndexe
s would have to be accessed.
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==indexprint '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_())