【问题标题】:How to inject widgets between QHeaderView and QTableView?如何在 QHeaderView 和 QTableView 之间注入小部件?
【发布时间】:2017-11-04 17:44:32
【问题描述】:

我想在QHeaderViewQTableView 的其余部分之间显示小部件,如下面的示例图片(使用 Photoshop 创建),因为这似乎是一种自然的方式启用过滤列的输入。


有人知道如何在两者之间注入小部件吗?


【问题讨论】:

    标签: python qt pyqt qtableview qheaderview


    【解决方案1】:

    下面是我为自己的一个项目编写的FilterHeader 类的演示。您可能需要对其进行调整以满足您自己的需求,但它应该已经可以满足您的大部分需求。过滤框周围的填充不太可能在所有平台上都相同,因此您可能需要调整 adjustPositions 方法中的代码。

    import sys
    from PyQt4 import QtCore, QtGui
    
    class FilterHeader(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)
    
        def setFilterBoxes(self, count):
            while self._editors:
                editor = self._editors.pop()
                editor.deleteLater()
            for index in range(count):
                editor = QtGui.QLineEdit(self.parent())
                editor.setPlaceholderText('Filter')
                editor.returnPressed.connect(self.filterActivated.emit)
                self._editors.append(editor)
            self.adjustPositions()
    
        def sizeHint(self):
            size = super().sizeHint()
            if self._editors:
                height = self._editors[0].sizeHint().height()
                size.setHeight(size.height() + height + self._padding)
            return size
    
        def updateGeometries(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()
    
        def adjustPositions(self):
            for index, editor in enumerate(self._editors):
                height = editor.sizeHint().height()
                editor.move(
                    self.sectionPosition(index) - self.offset() + 2,
                    height + (self._padding // 2))
                editor.resize(self.sectionSize(index), height)
    
        def filterText(self, index):
            if 0 <= index < len(self._editors):
                return self._editors[index].text()
            return ''
    
        def setFilterText(self, index, text):
            if 0 <= index < len(self._editors):
                self._editors[index].setText(text)
    
        def clearFilters(self):
            for editor in self._editors:
                editor.clear()
    
    
    class Window(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)
    
        def handleFilterActivated(self):
            header = self.view.horizontalHeader()
            for index in range(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_())
    

    【讨论】:

    • 我测试了它(使用 PySide),它可以工作,是一个很好的开始。然而,我注意到 hoverEvents 的一些轻微 显示烦恼(基本上只是当鼠标悬停在 QLineEdit 上时焦点消失/轻微闪烁而没有明显原因)。所以我将事件传递记录到 QLineEdits,似乎所有事件都被 FilterView 捕获。 (这可能会默默地将其分发给 QLineEdits,因为打字等仍然有效)。但是,这让我想知道这是否与 FilterView 的 sizeHint() 有关,跨越 QLineEdits 的区域,这是否会导致问题?
    • @timmwagener。 PyQt4 和 PySide(在 Linux 上)对我来说一切都很好。我想问题一定出在你的最后。我发布的代码中没有任何内容会导致您描述的问题。
    • 好的,这个问题发生在 Windows 7 上,Aero 桌面打开(默认)。标题视图中的行编辑显示的突出显示行为与添加到正常布局的行编辑不同。 Here is a slight mod. of your example to mock it up。当 Aero 桌面未打开时,行编辑不会突出显示,因此看不到任何差异。我的担忧是较少只是关于视觉噪音(虽然我不能那样使用它),更多的是关于可能暗示一些更新/paintEvent顺序问题的问题!?
    • @timmwagener。恐怕我没有太多要补充的,因为我无法在 Windows 上进行测试。也许尝试检查updateGeometries 被调用的频率。您似乎在 Aero 样式插件中发现了一个可能的 Qt 错误。但是您需要更多地减少示例,以便您可以找出真正的原因。首先,我想看看是否可以在标题视图之外重现问题。
    • @timmwagener。我已经根据您的建议调整了我的演示代码,因为它不会影响它在 linux 上的工作方式,因此应该提供更通用的答案。这个问题肯定是由 Aero 样式插件中的错误引起的,因为焦点行为不应该任意依赖于使用哪个父窗口小部件。
    【解决方案2】:

    这是@ekhumoro 的答案移植到PyQt5 并稍作改动 - 在第 4 列添加了一个组合框。

        import sys
        from PyQt5 import QtCore, QtGui
        from PyQt5.QtWidgets import QHeaderView, QWidget, QLineEdit, QApplication, QTableView, QVBoxLayout, QLineEdit, QComboBox
        from PyQt5.QtCore import pyqtSignal
        
        class FilterHeader(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)
        
            def setFilterBoxes(self, count):
                while self._editors:
                    editor = self._editors.pop()
                    editor.deleteLater()
                for index in range(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()
        
            def sizeHint(self):
                size = super().sizeHint()
                if self._editors:
                    height = self._editors[0].sizeHint().height()
                    size.setHeight(size.height() + height + self._padding)
                return size
        
            def updateGeometries(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()
        
            def adjustPositions(self):
                for index, editor in enumerate(self._editors):
                    height = editor.sizeHint().height()
                    editor.move( self.sectionPosition(index) - self.offset() + 2, height + (self._padding // 2))
                    editor.resize(self.sectionSize(index), height)
        
            def filterText(self, index):
                if 0 <= index < len(self._editors):
                    return self._editors[index].text()
                return ''
        
            def setFilterText(self, index, text):
                if 0 <= index < len(self._editors):
                    self._editors[index].setText(text)
        
            def clearFilters(self):
                for editor in self._editors:
                    editor.clear()
        
        
        class Window(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)
        
            def handleFilterActivated(self):
                header = self.view.horizontalHeader()
                for index in range(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_())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-18
      • 2018-04-27
      • 1970-01-01
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-15
      相关资源
      最近更新 更多