如何在pyqt中仅列出QTableView中的可见项目

时间:2013-01-03 06:00:06

标签: pyqt4 qtableview qsortfilterproxymodel

我有以下代码来获取QTableView的过滤器。但我无法一次过滤多个列。

即,如果使用row 0 col 0过滤第2列并尝试过滤第2列,则应仅显示column 2的可见唯一值(可能仅显示row 0 col 1)但现在它显示column 2的所有元素(row 0 col 1row 1 col 1row 2 col 1

#-*- coding:utf-8 -*-

from PyQt4 import QtCore, QtGui

class myWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(myWindow, self).__init__(parent)
        self.centralwidget  = QtGui.QWidget(self)
        self.lineEdit       = QtGui.QLineEdit(self.centralwidget)
        self.view           = QtGui.QTableView(self.centralwidget)
        self.comboBox       = QtGui.QComboBox(self.centralwidget)
        self.label          = QtGui.QLabel(self.centralwidget)

        self.gridLayout = QtGui.QGridLayout(self.centralwidget)
        self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
        self.gridLayout.addWidget(self.view, 1, 0, 1, 3)
        self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)

        self.setCentralWidget(self.centralwidget)
        self.label.setText("Regex Filter")

        self.model = QtGui.QStandardItemModel(self)

        for rowName in range(3) * 5:
            self.model.invisibleRootItem().appendRow(
                [   QtGui.QStandardItem("row {0} col {1}".format(rowName, column))    
                    for column in range(3)
                    ]
                )

        self.proxy = QtGui.QSortFilterProxyModel(self)
        self.proxy.setSourceModel(self.model)

        self.view.setModel(self.proxy)
        self.comboBox.addItems(["Column {0}".format(x) for x in     range(self.model.columnCount())])

        self.lineEdit.textChanged.connect(self.on_lineEdit_textChanged)
        self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged)

        self.horizontalHeader = self.view.horizontalHeader()
        self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)

    @QtCore.pyqtSlot(int)
    def on_view_horizontalHeader_sectionClicked(self, logicalIndex):
        self.logicalIndex   = logicalIndex
        self.menuValues     = QtGui.QMenu(self)
        self.signalMapper   = QtCore.QSignalMapper(self)  

        self.comboBox.blockSignals(True)
        self.comboBox.setCurrentIndex(self.logicalIndex)
        self.comboBox.blockSignals(True)

        valuesUnique = [    self.model.item(row, self.logicalIndex).text()
                            for row in range(self.model.rowCount())
                            ]

        actionAll = QtGui.QAction("All", self)
        actionAll.triggered.connect(self.on_actionAll_triggered)
        self.menuValues.addAction(actionAll)
        self.menuValues.addSeparator()

        for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))):              
            action = QtGui.QAction(actionName, self)
            self.signalMapper.setMapping(action, actionNumber)  
            action.triggered.connect(self.signalMapper.map)  
            self.menuValues.addAction(action)

        self.signalMapper.mapped.connect(self.on_signalMapper_mapped)  

        headerPos = self.view.mapToGlobal(self.horizontalHeader.pos())        

        posY = headerPos.y() + self.horizontalHeader.height()
        posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex)

        self.menuValues.exec_(QtCore.QPoint(posX, posY))

    @QtCore.pyqtSlot()
    def on_actionAll_triggered(self):
        filterColumn = self.logicalIndex
        filterString = QtCore.QRegExp(  "",
                                    QtCore.Qt.CaseInsensitive,
                                    QtCore.QRegExp.RegExp
                                    )

        self.proxy.setFilterRegExp(filterString)
        self.proxy.setFilterKeyColumn(filterColumn)

    @QtCore.pyqtSlot(int)
    def on_signalMapper_mapped(self, i):
        stringAction = self.signalMapper.mapping(i).text()
        filterColumn = self.logicalIndex
        filterString = QtCore.QRegExp(  stringAction,
                                    QtCore.Qt.CaseSensitive,
                                    QtCore.QRegExp.FixedString
                                    )

        self.proxy.setFilterRegExp(filterString)
        self.proxy.setFilterKeyColumn(filterColumn)

    @QtCore.pyqtSlot(str)
    def on_lineEdit_textChanged(self, text):
        search = QtCore.QRegExp(    text,
                                QtCore.Qt.CaseInsensitive,
                                QtCore.QRegExp.RegExp
                                )

        self.proxy.setFilterRegExp(search)

    @QtCore.pyqtSlot(int)
    def on_comboBox_currentIndexChanged(self, index):
        self.proxy.setFilterKeyColumn(index)


if __name__ == "__main__":
    import sys

    app  = QtGui.QApplication(sys.argv)
    main = myWindow()
    main.show()
    main.resize(400, 600)
    sys.exit(app.exec_())

当我运行上面的代码时,我得到以下输出

enter image description here

当我点击第2列标题时,显示的过滤器列表如下所示并正确显示(该列中的唯一值)...

enter image description here

当我在显示的过滤器中选择row 0 col 1时,我会得到以下过滤列表

enter image description here

但是,当我单击第2列标题进行过滤时,它会显示与第1张图像相同的列表。第2列的所有唯一项目(从模型视图)而不是来自proxyfilter。实际上它只应显示row 0 col 1,因为第2列中的唯一项目仅为row 0 col 1

enter image description here

1 个答案:

答案 0 :(得分:1)

这是因为您仍在使用源模型来查找行。原始模型中的数据不会更改。只有过滤器代理模型才会反映过滤后的更改。

因此,您只需要在(真正长命名的)on_view_horizontalHeader_sectionClicked广告位中修改您的查询:

valuesUnique = [    
    self.proxy.index(row, self.logicalIndex).data().toString()
    for row in xrange(self.proxy.rowCount())
]

您还可以删除一组唯一设置的转化:

valuesUnique = set(    
    self.proxy.index(row, self.logicalIndex).data().toString()
    for row in xrange(self.proxy.rowCount())
)
...
for actionNumber, actionName in enumerate(sorted(valuesUnique)): 
    ...

还有一点我想指出的其他事情。你正在保留一些永远不会被删除的临时对象。在同一个广告位中,您每次都会创建一个新的QMenuQSignalMapper,但您永远不会清理旧的QMenuself。随着时间的推移,你只是越来越多。

对于QSignalMapper,只需将其设为局部变量,不要将其设为deleteLater。这样它会在消失后被清理干净。对于# local variable, and no parent menuValues = QtGui.QMenu() # delete the previous one try: self.signalMapper.deleteLater() except: pass self.signalMapper = QtCore.QSignalMapper(self) ,您可以在创建新的{{1}}之前使用{{1}}来电:

{{1}}