如何使用QSortFilterProxyModel重新启用QTreeView中的手动列布局 [英] How to reenable manual column layout in a QTreeView with QSortFilterProxyModel

查看:161
本文介绍了如何使用QSortFilterProxyModel重新启用QTreeView中的手动列布局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个QTreeView,它显示来自SQLite数据库的数据. QTreeView启用了过滤器和排序.为了进行过滤,我在列标题中显示了QLineEdit.

I have a QTreeView which displays data from a SQLite database. The QTreeView has filters and sorting enabled. For filtering I display a QLineEdit into the column header.

问题:为了启用QLineEdit过滤,列布局似乎已通过修改而固定.

Problem: the column layout seems to be fixed by the modifications in order to enable the QLineEdit filtering.

问题:是否有一种方法可以重新启用列布局的设置-通过拖动列,就像在标准QTreeView布局中一样.

Question: Is there a way to reenable the arrangement of the column layout - via dragging the columns, as it is in the standard QTreeView layout.

我希望用户能够以所需的方式重新排列他的列.

I want the user to be able to rearrange his columns the desired way.

完整的工作代码示例:

import sys
import re
from PyQt5 import QtWidgets, QtGui, QtCore, QtSql

COUNT_PERS_COLS = 3
col_persID, col_persLAST_NAME, col_persFIRST_NAME = range(COUNT_PERS_COLS)

db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(':memory:')

modelQuery = QtSql.QSqlQueryModel()
modelTable = QtSql.QSqlRelationalTableModel()

def _human_key(key):
    parts = re.split(r'(\d*\.\d+|\d+)', key)
    return tuple((e.swapcase() if i % 2 == 0 else float(e))
            for i, e in enumerate(parts))

class FilterHeader(QtWidgets.QHeaderView):
    filterActivated = QtCore.pyqtSignal()

    def __init__(self, parent):
        super().__init__(QtCore.Qt.Horizontal, parent)
        self._editors = []
        self._padding = 4
        self.setStretchLastSection(True)        
        self.setSectionResizeMode(QtWidgets.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 = QtWidgets.QLineEdit(self.parent())            
            editor.setPlaceholderText('Filter')
            editor.setClearButtonEnabled(True)
            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 HumanProxyModel(QtCore.QSortFilterProxyModel):
    def lessThan(self, source_left, source_right):
        data_left = source_left.data()
        data_right = source_right.data()
        if type(data_left) == type(data_right) == str:
            return _human_key(data_left) < _human_key(data_right)
        return super(HumanProxyModel, self).lessThan(source_left, source_right)

    @property
    def filters(self):
        if not hasattr(self, "_filters"):
            self._filters = []
        return self._filters

    @filters.setter
    def filters(self, filters):
        print(f"filters() called.")        

        self._filters = filters
        self.invalidateFilter()                

    def filterAcceptsRow(self, sourceRow, sourceParent):        
        for i, text in self.filters:
            if 0 <= i < self.columnCount():
                ix = self.sourceModel().index(sourceRow, i, sourceParent)                
                data = ix.data()
                if text not in data:
                    return False            
        return True        

class winMain(QtWidgets.QMainWindow):
    def __init__(self, parent=None):        
        super().__init__(parent)                
        self.setupUi()
        self.setGeometry(300,200,700,500)        

        self.show()        

    def createPersonModel(self,parent):        
        model = QtGui.QStandardItemModel(0, COUNT_PERS_COLS, parent)                
        model.setHorizontalHeaderLabels(['ID', 'Last Name', 'First Name'])

        return model

    def addPerson(self, model, id, last_name, first_name):        
        model.insertRow(0)        
        model.setData(model.index(0, col_persID), id)
        model.setData(model.index(0, col_persLAST_NAME), last_name)
        model.setData(model.index(0, col_persFIRST_NAME), first_name)

    def handleFilterActivated(self):                
        header = self.treeView.header()
        filters = []

        for i in range(header.count()):
            text = header.filterText(i)
            if text:        
                filters.append((i, text))

        proxy = self.treeView.model()
        proxy.filters = filters        

    def setupUi(self):
        self.centralwidget = QtWidgets.QWidget(self)        
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)        

        self.treeView = QtWidgets.QTreeView(self.centralwidget)        

        self.treeView.setSortingEnabled(True)
        self.treeView.setAlternatingRowColors(True)        
        self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.treeView.setAnimated(True)
        self.treeView.setItemsExpandable(True)

        self.horizontalLayout.addWidget(self.treeView)
        self.setCentralWidget(self.centralwidget)

        header = FilterHeader(self.treeView)        
        self.treeView.setHeader(header)        

        self.statusBar = QtWidgets.QStatusBar()
        self.setStatusBar(self.statusBar)        

        modelTable.setTable("person")
        self.treeView.setModel(modelTable)

        proxy = HumanProxyModel(self)
        proxy.setSourceModel(modelTable)
        self.treeView.setModel(proxy)        

        header.setFilterBoxes(modelTable.columnCount())
        header.filterActivated.connect(self.handleFilterActivated)        


def create_sample_data():     
    modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS country (                                    
                                    id   INTEGER PRIMARY KEY UNIQUE NOT NULL,
                                    name TEXT
                                    )""")

    modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS person (
                                   id         INTEGER PRIMARY KEY UNIQUE NOT NULL,
                                   persId     TEXT,
                                   lastName   TEXT,
                                   firstName  TEXT,
                                   country_id INTEGER NOT NULL DEFAULT 3,
              FOREIGN KEY (country_id) REFERENCES country(id)
                                   )""")

    modelQuery.setQuery("INSERT INTO country (id, name) VALUES (0, 'None')")    
    modelQuery.setQuery("INSERT INTO country (id, name) VALUES (1, 'Angola')")    
    modelQuery.setQuery("INSERT INTO country (id, name) VALUES (2, 'Serbia')")
    modelQuery.setQuery("INSERT INTO country (id, name) VALUES (3, 'Georgia')")

    modelQuery.setQuery("INSERT INTO person (id, persId, lastName, firstName, country_id) VALUES (1, '1001', 'Martin', 'Robert', 1)")
    modelQuery.setQuery("INSERT INTO person (id, persId, lastName, firstName, country_id) VALUES (2, '1002', 'Smith', 'Brad', 2)")
    modelQuery.setQuery("INSERT INTO person (id, persId, lastName, firstName, country_id) VALUES (3, '1003', 'Smith', 'Angelina', 3)")

if __name__ == '__main__':                         
    app = QtWidgets.QApplication(sys.argv)         

    create_sample_data()        

    window = winMain()    
    sys.exit(app.exec_())

推荐答案

您必须使用 setSectionsMovable()方法>:

class FilterHeader(QtWidgets.QHeaderView):
    filterActivated = QtCore.pyqtSignal()

    def __init__(self, parent):
        super().__init__(QtCore.Qt.Horizontal, parent)
        self.setSectionsMovable(True) # <---
        # ...

这篇关于如何使用QSortFilterProxyModel重新启用QTreeView中的手动列布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆