How to create filters for QTableView in PyQt

后端 未结 3 1066
悲哀的现实
悲哀的现实 2020-12-23 15:27

I am using QTableView to display data retrieved from QtSql.QSqlQuery

I want to know how can i create filters for it like in excel.

相关标签:
3条回答
  • 2020-12-23 15:47

    I tried to update the answer provided in the above for 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_())
    
    0 讨论(0)
  • 2020-12-23 15:51

    Here is an example of filtering in PyQt using QSortFilterProxyModel, QStandardItemModel and QTableView, it can be easily adapted to other views and models:

    #!/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_())
    

    To get required results, a popup menu is launched by clicking on the header, and populated with the unique values for that column. Once an item in the popup menu is selected, the value is passed to self.proxy.setFilterRegExp(filterString) and the column to self.proxy.setFilterKeyColumn(filterValue).

    image

    0 讨论(0)
  • 2020-12-23 15:58

    As per the answer from @user1006989 & @Behzad Jamali: The menu position for filtering on header won't popup at the exact position if the table has more columns than the current view port.

    To have a correct positioning of the popup menu, use this line

    posX = headerPos.x() + self.horizontalHeader.sectionViewportPosition(index)
    
    0 讨论(0)
提交回复
热议问题