如何从Model内部选择QTableView索引或行

时间:2015-01-21 03:43:51

标签: python pyqt qtableview qabstracttablemodel

发布的代码会创建一个单Model/Proxy QTableView。已启用多选功能。

enter image description here

总共有四个项目。其中两个包括一个字符“A”。其他两个在“项目”名称中包含字符“B”。

QPushButton按下时调用clicked()方法。 调用此方法时,首先查询与Proxy Model相关联的QTableView

proxyModel=self.tableview.model()

然后该方法要求proxyModel返回总行数:

rows=proxyModel.rowCount()

知道QTabelView模型中有多少行迭代每一行。首先是查询行索引:

index=proxyModel.index(row, 0)

知道index它继续通过调用self.items方法请求存储在data()变量中的值,并在前一步骤{i}中查询QModelIndex这里index)和Role标志。

item=proxyModel.data(index, Qt.DisplayRole).toPyObject()

'toPyObject()'用于将从.data()方法接收的数据转换为“常规”Python变量。

最后,它会检查收到的字符串中的字符“ B ”。如果是这样,它使用:

选择QTableView行
self.tableview.selectRow(row)

现在我想要的是在可能的情况下从代理模型的filterAcceptsRow()范围内获得相同的选择功能。

如果不可能,我想知道是否还有其他办法...我应该使用QItemSelectionModel吗?那怎么样?

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = ['Item_A_001','Item_A_002','Item_B_001','Item_B_002']

    def rowCount(self, parent=QModelIndex()):
        return len(self.items)       
    def columnCount(self, parent=QModelIndex()):
        return 1

    def data(self, index, role):
        if not index.isValid(): return QVariant()
        elif role != Qt.DisplayRole:
            return QVariant()

        row=index.row()
        if row<len(self.items):
            return QVariant(self.items[row])
        else:
            return QVariant()

class Proxy(QSortFilterProxyModel):
    def __init__(self):
        super(Proxy, self).__init__()

    def filterAcceptsRow(self, row, parent):
        return True

class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)

        tableModel=Model(self)               

        proxyModel=Proxy()
        proxyModel.setSourceModel(tableModel)

        self.tableview=QTableView(self) 
        self.tableview.setModel(proxyModel)
        self.tableview.horizontalHeader().setStretchLastSection(True)
        self.tableview.setSelectionMode(QAbstractItemView.MultiSelection)

        button=QPushButton(self)
        button.setText('Select Items with B')
        button.clicked.connect(self.clicked)

        layout = QVBoxLayout(self)
        layout.addWidget(self.tableview)
        layout.addWidget(button)
        self.setLayout(layout)

    def clicked(self, arg):
        proxyModel=self.tableview.model()

        self.tableview.clearSelection()
        rows=proxyModel.rowCount()
        for row in range(rows):
            index=proxyModel.index(row, 0)
            item=proxyModel.data(index, Qt.DisplayRole).toPyObject()
            if '_B_' in item:
                self.tableview.selectRow(row)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

1 个答案:

答案 0 :(得分:2)

您可以在代理模型的filterAcceptsRow()方法中进行选择,但这样做需要以下内容:

  1. 您的代理模型(或源模型)包含对QTableView实例的引用。
  2. 您的代理模型包含一个属性,指示它是否处于活动状态。这是因为您只想在单击按钮时选择表行,但代理模型会自动调用filterAcceptsRow()。因此,您需要避免在单击按钮之前调用视图的selectRow()方法。
  3. 要实现#1,您可以在代理模型类中定义一个简单的setter方法:

    def setView(self, view):
        self._view = view
    

    您当然还需要在MyWindow类的构造函数中调用该setter:

    proxyModel.setView(self.tableview)
    

    实现#2是在代理模型类的构造函数

    中创建此属性的简单问题
    self.filterActive = False
    

    现在您的课程已准备就绪,您可以实现所需的行为。在filterAcceptsRow()重新实施中,您只想选择包含'_B_'的行并且过滤器处于活动状态(即点击该按钮):

    def filterAcceptsRow(self, row, parent):
        if self.filterActive and '_B_' in self.sourceModel().data(self.sourceModel().index(row, 0), Qt.DisplayRole).toPyObject():
            self._view.selectRow(row)
        return True
    

    最后,您希望确保在单击按钮后满足这些条件,因此在clicked()方法中,您需要将proxyModel的{​​{1}}属性设置为True并且您需要调用filterActive类的QSortFilterProxyModel方法来指示现有过滤器无效,因此应再次调用invalidateFilter()

    filterAcceptsRow()

    所以新代码完全是:

    def clicked(self, arg):
        proxyModel=self.tableview.model()
        self.tableview.clearSelection()
        proxyModel.filterActive = True
        proxyModel.invalidateFilter()
    

    说完所有这些之后,from PyQt4.QtCore import * from PyQt4.QtGui import * import sys class Model(QAbstractTableModel): def __init__(self, parent=None, *args): QAbstractTableModel.__init__(self, parent, *args) self.items = ['Item_A_001','Item_A_002','Item_B_001','Item_B_002'] def rowCount(self, parent=QModelIndex()): return len(self.items) def columnCount(self, parent=QModelIndex()): return 1 def data(self, index, role): if not index.isValid(): return QVariant() elif role != Qt.DisplayRole: return QVariant() row=index.row() if row<len(self.items): return QVariant(self.items[row]) else: return QVariant() class Proxy(QSortFilterProxyModel): def __init__(self): super(Proxy, self).__init__() self.filterActive = False def setView(self, view): self._view = view def filterAcceptsRow(self, row, parent): if self.filterActive and '_B_' in self.sourceModel().data(self.sourceModel().index(row, 0), Qt.DisplayRole).toPyObject(): self._view.selectRow(row) return True class MyWindow(QWidget): def __init__(self, *args): QWidget.__init__(self, *args) tableModel=Model(self) proxyModel=Proxy() proxyModel.setSourceModel(tableModel) self.tableview=QTableView(self) self.tableview.setModel(proxyModel) self.tableview.horizontalHeader().setStretchLastSection(True) self.tableview.setSelectionMode(QAbstractItemView.MultiSelection) proxyModel.setView(self.tableview) button=QPushButton(self) button.setText('Select Items with B') button.clicked.connect(self.clicked) layout = QVBoxLayout(self) layout.addWidget(self.tableview) layout.addWidget(button) self.setLayout(layout) def clicked(self, arg): proxyModel=self.tableview.model() self.tableview.clearSelection() proxyModel.filterActive = True proxyModel.invalidateFilter() if __name__ == "__main__": app = QApplication(sys.argv) w = MyWindow() w.show() sys.exit(app.exec_()) 的目的是让您可以在filterAcceptsRow()的子类中实现自己的自定义过滤。因此,更典型的实现(遵循您期望的规则)将是:

    QSortFilterProxyModel

    即便如此,因为可以使用正则表达式进行过滤,所以甚至不需要重新实现def filterAcceptsRow(self, row, parent): if not self.filterActive or '_B_' in self.sourceModel().data(self.sourceModel().index(row, 0), Qt.DisplayRole).toPyObject(): return True return False 。您可以调用filterAcceptsRow()proxyModel.setFilterRegExp(QRegExp("_B_", Qt.CaseInsensitive, QRegExp.FixedString))来实现相同的操作,过滤方式。

    希望有所帮助!