【问题标题】:How to create filters for QTableView in PyQt如何在 PyQt 中为 QTableView 创建过滤器
【发布时间】:2012-12-13 16:16:45
【问题描述】:

我正在使用 QTableView 显示从 QtSql.QSqlQuery 检索到的数据

我想知道如何像在 excel 中一样为它创建过滤器。

在上图中,我需要获取所有 heders 的过滤器(Sh_Code、SH_Seq、Stage) 过滤器将在我们可以过滤的列中具有唯一值。

必填结果

我需要带有下拉框的表格视图标题,其中列出了该列中的所有唯一值,就像下面的 excel 中一样。不需要顶部,标准过滤器...如图所示。只需要“全部”和唯一的“列项”

这是来自我的 .NET 应用程序,为了更清楚而上传

【问题讨论】:

    标签: python qt pyqt pyqt4 qtableview


    【解决方案1】:

    这是一个在 PyQt 中使用QSortFilterProxyModelQStandardItemModelQTableView 进行过滤的示例,它可以很容易地适应其他视图和模型:

    #!/usr/bin/env python
    #-*- 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_())
    

    要获得所需的结果,单击标题会启动一个弹出菜单,并填充该列的唯一值。选择弹出菜单中的项目后,值将传递给self.proxy.setFilterRegExp(filterString),列传递给self.proxy.setFilterKeyColumn(filterValue)

    【讨论】:

    • 当我在过滤器框中输入“row 0 col 0”时,它没有过滤任何东西。我在on_lineEdit_textChangedon_comboBox_currentIndexChanged 中添加了打印语句,但它们从未被执行。我正在开发 Python 2.6.4
    • @PBLNarasimhaRao 忘记连接插槽!我更新了the code,现在应该可以使用了
    • 它通过连接信号 self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged)self.lineEdit.textChanged.connect(self.on_lineEdit_textChanged) 起作用,但我需要在列标题上显示一个过滤器,我可以从中选择它(例如在 excel 中)。我已经用所需的 excel 快照更新了问题。
    • @PBLNarasimhaRao 查看我的updated answer,我添加了一些关于如何在单击标题时显示过滤器弹出菜单的信息
    • 你能根据你的建议编辑你的代码吗?我找不到标题点击事件。
    【解决方案2】:

    我尝试更新上面为PyQt5提供的答案

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class myWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(myWindow, self).__init__(parent)
            self.centralwidget  = QtWidgets.QWidget(self)
            self.lineEdit       = QtWidgets.QLineEdit(self.centralwidget)
            self.view           = QtWidgets.QTableView(self.centralwidget)
            self.comboBox       = QtWidgets.QComboBox(self.centralwidget)
            self.label          = QtWidgets.QLabel(self.centralwidget)
    
            self.gridLayout = QtWidgets.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 = QtCore.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     = QtWidgets.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 = QtWidgets.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 = QtWidgets.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  = QtWidgets.QApplication(sys.argv)
        main = myWindow()
        main.show()
        main.resize(400, 600)
        sys.exit(app.exec_())
    

    【讨论】:

    • 感谢您的努力。
    • @Behzad Jamali 来自doc.qt.io/archives/qt-5.10/qsignalmapper.html,它说QSignalMapper Class 已被弃用。我想知道是否有任何替代方案。 action.triggered.connect(lambda: self.on_signalMapper_mapped(actionName)) 是一种可能的方式吗?谢谢。
    【解决方案3】:

    根据@user1006989 和@Behzad Jamali 的回答: 如果表格的列数多于当前视口,则用于过滤标题的菜单位置不会在确切位置弹出。

    要正确定位弹出菜单,请使用此行

    posX = headerPos.x() + self.horizontalHeader.sectionViewportPosition(index)
    

    【讨论】:

      猜你喜欢
      • 2020-11-15
      • 1970-01-01
      • 2018-05-09
      • 2015-11-15
      • 2018-04-22
      • 2018-11-28
      • 2019-05-20
      • 2017-01-17
      • 1970-01-01
      相关资源
      最近更新 更多