2014-09-19 260 views
2

下面的代码创建了一个QListView,数据和代理模型“附加”。 单击其中一个单选按钮可调用buttonClicked()函数。如何访问存储在QModelIndex中的数据

该函数调用模型的.data(index,role)方法来获取存储在当前索引中的数据。

对于DisplayRole该模型的.data()方法正确地返回索引的名称(在创建时分配给它)。但是,当使用
'ItemDataRole' 我正在一个错误:

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

-

Question 1: How to fix this error? 

如果你看一看addItems()方法有一行有:

self.setData(index, 'keyword') 

显然我试图将'关键字'设置为我自己的“自定义数据”(它是什么ProxyModel将用于过滤索引?)。

问题2:如何查询我用self.setData(index, 'keyword')设置的字符串“keyword”? 该数据是可访问的还是“保留”且无法查询?

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_()) 

回答

3

下面是关于如何使用QTableViewQStandardItemModel填充QStandardItem个工作示例。

使用QtGui.QStandardItemModel超过QtCore.QAbstractListModel的一个可能的优点是,QtGui.QStandardItemModel不必是子类,以便被分配给QTableView。 你先走一步,并宣布它:

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

要创建视图项先声明它:

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

分配宣称QStandardItem使用查看:

model.appendRow(item) 

model.appendRow(item)方法“创建”尽可能多的QModelIndex es,因为在表视图中有列。因此,无需为每行中的每个列手动创建QModelIndex es:该项目分配给的行。使用item.setData(value,role)方法

接着可以存储任何类型的使用的一个数据的预定义QModelIndex作用,如:

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) 

所有三种分配线以上用于分配三个不同的值,以相同的“项目“:第一个值是字符串'< Column 0 My Display Value as "DisplayRole" >',它存储在Qt.DisplayRole角色下(使用此角色将其恢复)。

字符串值:'< Column 0 My Custom Value as "UserRole" >'存储在Qt.UserRole下。使用此角色将其恢复。等

虽然item.setData(value,role)方法是非常有用的,它使用简单的语法,它也是非常有限的,因为它只处理存储在项目分配到的行的零列中的单个QModelIndex。为了能够存储自定义数据(或修改现有的数据)存储在该项目的行非零列,我们获得该项目的行数第一:

itemRow=item.row() 

了解项目的行数,我们可以从在“手动”模式下使用 每列得到所有的项目的索引:

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

(其中的第二整数参数如12是列数)。

或者在“全自动”模式:

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

知道我们可以使用它们的项目的QModelIndexes查询或存储数据,就像我们使用item.setData()方法(即只分配/方法也retreives数据从/到零列QModelIndex)。

为了存储和retreive数据中的每列〜QModelIndex我们必须使用model.setData(index,value,role)model.data(index, role)方法(我们似乎不能得到/设置直接使用QModelIndex实例的数据......这样的:

myIndex.setData(value,role) - 这是无效的代码,因为QModelIndex不附带.setData()方法

为了获取每QModelIndex存储我们使用model.data(index,role)数据:

0123。

对于处理提供的QStandardItem方法的单列视图(例如.QListView)可能就足够了。但对于多列视图(如QTabLeView有机会单独列的QModelIndexe s就必须访问。

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_()) 
+1

的“限制”请您谈谈并不真正存在。该你的例子的局限性在那里,因为你只在行中添加一个项目,你应该为每一列创建一个'QStandardItem',将它们放入一个列表中,并添加列表,例如'model.appendRow([item_for_col_1,item_for_col_2 ,item_for_col_3])'然后你可以在每个项目上调用'item_for_col_x.setData',如果你以后需要访问这些项目,你可以自己存储对它们的引用,或者使用像'i ndexToItem()'或'itemToIndex()'(如果需要,可能构造你自己的QModelIndex)。 – 2014-09-27 02:47:09

+0

我要补充的最后一件事是,尽管'QModelIndex'变得无效(例如当或从模型中添加/删除),但项目不会。因此,只要您不想在第一次后续调用以任何方式修改模型后继续使用索引,就可以使用该项目随时查找索引。 – 2014-09-27 02:48:45