Как включить ручную разметку столбца в QTreeView с помощью QSortFilterProxyModel - PullRequest
1 голос
/ 05 июля 2019

У меня есть QTreeView, который отображает данные из базы данных SQLite. В QTreeView включены фильтры и сортировка. Для фильтрации я отображаю QLineEdit в заголовке столбца.

Проблема: кажется, что компоновка столбца была исправлена ​​изменениями, чтобы включить фильтрацию QLineEdit.

Вопрос: Есть ли способ повторно включить расположение макета столбца - путем перетаскивания столбцов, как в стандартном макете QTreeView.

Я хочу, чтобы пользователь мог переставлять свои столбцы желаемым образом.

Пример полного рабочего кода:

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_())

1 Ответ

0 голосов
/ 16 июля 2019

Вы должны включить эту функцию, используя setSectionsMovable () метод QHeaderView :

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

    def __init__(self, parent):
        super().__init__(QtCore.Qt.Horizontal, parent)
        self.setSectionsMovable(True) # <---
        # ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...