【问题标题】:How to select QTableView index or row from inside of Model如何从模型内部选择 QTableView 索引或行
【发布时间】:2015-01-21 03:43:51
【问题描述】:

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

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

QPushButton 按下时调用clicked() 方法。 调用此方法时首先查询连接到QTableViewProxy Model

proxyModel=self.tableview.model()

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

rows=proxyModel.rowCount()

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

index=proxyModel.index(row, 0)

知道index,它继续通过调用data()方法来请求存储在self.items变量中的值,并为它提供上一步中查询的QModelIndex(此处为变量index)和@ 987654338@ 标志。

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

【问题讨论】:

    标签: python pyqt qtableview qabstracttablemodel


    【解决方案1】:

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

    1. 您的代理模型(或源模型)包含对QTableView 实例的引用。
    2. 您的代理模型包含指示它是否处于活动状态的属性。这是因为您只想在单击按钮时选择表行,但代理模型会自动调用filterAcceptsRow()。因此,在单击按钮之前,您应该避免调用视图的 selectRow() 方法。

    要实现 #1,您可以在代理模型类中定义一个简单的 setter 方法:

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

    当然,您还需要在 MyWindow 类的构造函数中调用该设置器:

    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() 方法中,您需要将 proxyModelfilterActive 属性设置为 True 并且您需要调用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))proxyModel.setFilterKeyColumn(0) 来实现相同的效果,过滤方式。

    希望有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-15
      • 2011-04-21
      • 2015-05-01
      • 2014-04-29
      • 1970-01-01
      • 1970-01-01
      • 2019-04-25
      • 1970-01-01
      相关资源
      最近更新 更多