如何从模型内部 select QTableView 索引或行
How to select QTableView index or row from inside of Model
发布的代码创建了一个 Model/Proxy
QTableView
。 multi-selection 功能已为其启用。
共有四项。其中两个包含一个字符"A"。其他两个在其 "item" 名称中包含字符 "B"。
QPushButton
按下时调用 clicked()
方法。
调用此方法时,首先查询连接到 QTableView
:
的 Proxy Model
proxyModel=self.tableview.model()
然后该方法要求proxyModel
到return总行数:
rows=proxyModel.rowCount()
知道 QTabelView
的模型中有多少行,它会迭代每一行。首先它查询行索引:
index=proxyModel.index(row, 0)
知道 index
它继续通过调用 data()
方法来请求存储在 self.items
变量中的值,并为其提供在上一步中查询的 QModelIndex
(一个变量 index
这里)和一个 Role
标志。
item=proxyModel.data(index, Qt.DisplayRole).toPyObject()
'toPyObject()'用于将从.data()
方法接收到的数据转换为"regular"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_())
您可以在代理模型的 filterAcceptsRow()
方法中实现 selection,但这样做需要满足以下条件:
- 您的代理模型(或源模型)包含对
QTableView
实例的引用。
- 您的代理模型包含一个指示它是否处于活动状态的属性。这是因为当单击按钮时,您只想 select table 行,但代理模型会自动调用
filterAcceptsRow()
。因此,您可能希望在单击按钮之前避免调用视图的 selectRow()
方法。
要实现#1,您可以在代理模型中定义一个简单的 setter 方法 class:
def setView(self, view):
self._view = view
您当然还需要在 MyWindow
class 的构造函数中调用 setter:
proxyModel.setView(self.tableview)
实现 #2 是在代理模型 class 的构造函数中创建此属性的简单问题
self.filterActive = False
现在您的 classes 已准备就绪,您可以实施所需的行为。在您的 filterAcceptsRow()
重新实现中,如果行包含 '_B_'
并且过滤器处于活动状态(即单击按钮),您只想 select 行:
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
的 filterActive
属性设置为 True并且您需要调用 QSortFilterProxyModel
class 的 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)
来实现同样的事情,filter-wise。
希望对您有所帮助!
发布的代码创建了一个 Model/Proxy
QTableView
。 multi-selection 功能已为其启用。
共有四项。其中两个包含一个字符"A"。其他两个在其 "item" 名称中包含字符 "B"。
QPushButton
按下时调用 clicked()
方法。
调用此方法时,首先查询连接到 QTableView
:
Proxy Model
proxyModel=self.tableview.model()
然后该方法要求proxyModel
到return总行数:
rows=proxyModel.rowCount()
知道 QTabelView
的模型中有多少行,它会迭代每一行。首先它查询行索引:
index=proxyModel.index(row, 0)
知道 index
它继续通过调用 data()
方法来请求存储在 self.items
变量中的值,并为其提供在上一步中查询的 QModelIndex
(一个变量 index
这里)和一个 Role
标志。
item=proxyModel.data(index, Qt.DisplayRole).toPyObject()
'toPyObject()'用于将从.data()
方法接收到的数据转换为"regular"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_())
您可以在代理模型的 filterAcceptsRow()
方法中实现 selection,但这样做需要满足以下条件:
- 您的代理模型(或源模型)包含对
QTableView
实例的引用。 - 您的代理模型包含一个指示它是否处于活动状态的属性。这是因为当单击按钮时,您只想 select table 行,但代理模型会自动调用
filterAcceptsRow()
。因此,您可能希望在单击按钮之前避免调用视图的selectRow()
方法。
要实现#1,您可以在代理模型中定义一个简单的 setter 方法 class:
def setView(self, view):
self._view = view
您当然还需要在 MyWindow
class 的构造函数中调用 setter:
proxyModel.setView(self.tableview)
实现 #2 是在代理模型 class 的构造函数中创建此属性的简单问题
self.filterActive = False
现在您的 classes 已准备就绪,您可以实施所需的行为。在您的 filterAcceptsRow()
重新实现中,如果行包含 '_B_'
并且过滤器处于活动状态(即单击按钮),您只想 select 行:
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
的 filterActive
属性设置为 True并且您需要调用 QSortFilterProxyModel
class 的 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)
来实现同样的事情,filter-wise。
希望对您有所帮助!