Skip to content Skip to sidebar Skip to footer

How To Inject Widgets Between Qheaderview And Qtableview?

I would like to display widgets between the QHeaderView and the rest of the QTableView, like in the example picture below (created with Photoshop), as this seems like a natural way

Solution 1:

Below is a demo of a FilterHeader class that I wrote for one of my own projects. You will probably need to adapt it to suit your own needs, but it should already do most what you want. The padding around the filter boxes is unlikely to work the same on all platforms, so you may need to tweak the code in the adjustPositions method.

screenshot

import sys
from PyQt4 import QtCore, QtGui

classFilterHeader(QtGui.QHeaderView):
    filterActivated = QtCore.pyqtSignal()

    def__init__(self, parent):
        super().__init__(QtCore.Qt.Horizontal, parent)
        self._editors = []
        self._padding = 4
        self.setStretchLastSection(True)
        self.setResizeMode(QtGui.QHeaderView.Stretch)
        self.setDefaultAlignment(
            QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        self.setSortIndicatorShown(False)
        self.sectionResized.connect(self.adjustPositions)
        parent.horizontalScrollBar().valueChanged.connect(
            self.adjustPositions)

    defsetFilterBoxes(self, count):
        while self._editors:
            editor = self._editors.pop()
            editor.deleteLater()
        for index inrange(count):
            editor = QtGui.QLineEdit(self.parent())
            editor.setPlaceholderText('Filter')
            editor.returnPressed.connect(self.filterActivated.emit)
            self._editors.append(editor)
        self.adjustPositions()

    defsizeHint(self):
        size = super().sizeHint()
        if self._editors:
            height = self._editors[0].sizeHint().height()
            size.setHeight(size.height() + height + self._padding)
        return size

    defupdateGeometries(self):
        if self._editors:
            height = self._editors[0].sizeHint().height()
            self.setViewportMargins(0, 0, 0, height + self._padding)
        else:
            self.setViewportMargins(0, 0, 0, 0)
        super().updateGeometries()
        self.adjustPositions()

    defadjustPositions(self):
        for index, editor inenumerate(self._editors):
            height = editor.sizeHint().height()
            editor.move(
                self.sectionPosition(index) - self.offset() + 2,
                height + (self._padding // 2))
            editor.resize(self.sectionSize(index), height)

    deffilterText(self, index):
        if0 <= index < len(self._editors):
            return self._editors[index].text()
        return''defsetFilterText(self, index, text):
        if0 <= index < len(self._editors):
            self._editors[index].setText(text)

    defclearFilters(self):
        for editor in self._editors:
            editor.clear()


classWindow(QtGui.QWidget):
    def__init__(self):
        super(Window, self).__init__()
        self.view = QtGui.QTableView()
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.view)
        header = FilterHeader(self.view)
        self.view.setHorizontalHeader(header)
        model = QtGui.QStandardItemModel(self.view)
        model.setHorizontalHeaderLabels('One Two Three Four Five'.split())
        self.view.setModel(model)
        header.setFilterBoxes(model.columnCount())
        header.filterActivated.connect(self.handleFilterActivated)

    defhandleFilterActivated(self):
        header = self.view.horizontalHeader()
        for index inrange(header.count()):
            print((index, header.filterText(index)))


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 600, 300)
    window.show()
    sys.exit(app.exec_())

Solution 2:

Here is @ekhumoro's answer ported to PyQt5 and with a little change - added a combobox on 4th column.

enter image description here

import sys
    from PyQt5 import QtCore, QtGui
    from PyQt5.QtWidgets import QHeaderView, QWidget, QLineEdit, QApplication, QTableView, QVBoxLayout, QLineEdit, QComboBox
    from PyQt5.QtCore import pyqtSignal
    
    classFilterHeader(QHeaderView):
        filterActivated = QtCore.pyqtSignal()
    
        def__init__(self, parent):
            super().__init__(QtCore.Qt.Horizontal, parent)
            self._editors = []
            self._padding = 4
            self.setStretchLastSection(True)
            #self.setResizeMode(QHeaderView.Stretch)
            self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            self.setSortIndicatorShown(False)
            self.sectionResized.connect(self.adjustPositions)
            parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)
    
        defsetFilterBoxes(self, count):
            while self._editors:
                editor = self._editors.pop()
                editor.deleteLater()
            for index inrange(count):
                if index == 3:
                    editor = QComboBox(self.parent())
                    #editor.returnPressed.connect(self.filterActivated.emit)
                    editor.addItems(["One","Two"])
                else:
                    editor = QLineEdit(self.parent())
                    editor.setPlaceholderText('Filter')
                    editor.returnPressed.connect(self.filterActivated.emit)                
                self._editors.append(editor)
            self.adjustPositions()
    
        defsizeHint(self):
            size = super().sizeHint()
            if self._editors:
                height = self._editors[0].sizeHint().height()
                size.setHeight(size.height() + height + self._padding)
            return size
    
        defupdateGeometries(self):
            if self._editors:
                height = self._editors[0].sizeHint().height()
                self.setViewportMargins(0, 0, 0, height + self._padding)
            else:
                self.setViewportMargins(0, 0, 0, 0)
            super().updateGeometries()
            self.adjustPositions()
    
        defadjustPositions(self):
            for index, editor inenumerate(self._editors):
                height = editor.sizeHint().height()
                editor.move( self.sectionPosition(index) - self.offset() + 2, height + (self._padding // 2))
                editor.resize(self.sectionSize(index), height)
    
        deffilterText(self, index):
            if0 <= index < len(self._editors):
                return self._editors[index].text()
            return''defsetFilterText(self, index, text):
            if0 <= index < len(self._editors):
                self._editors[index].setText(text)
    
        defclearFilters(self):
            for editor in self._editors:
                editor.clear()
    
    
    classWindow(QWidget):
        def__init__(self):
            super(Window, self).__init__()
            self.view = QTableView()
            layout = QVBoxLayout(self)
            layout.addWidget(self.view)
            header = FilterHeader(self.view)
            self.view.setHorizontalHeader(header)
            model = QtGui.QStandardItemModel(self.view)
            model.setHorizontalHeaderLabels('One Two Three Four Five'.split())
            self.view.setModel(model)
            header.setFilterBoxes(model.columnCount())
            header.filterActivated.connect(self.handleFilterActivated)
    
        defhandleFilterActivated(self):
            header = self.view.horizontalHeader()
            for index inrange(header.count()):
                print((index, header.filterText(index)))
    
    
    if __name__ == '__main__':
    
        app = QApplication(sys.argv)
        window = Window()
        window.setGeometry(600, 100, 600, 300)
        window.show()
        sys.exit(app.exec_())

Post a Comment for "How To Inject Widgets Between Qheaderview And Qtableview?"