Как скрыть первый столбец с помощью QSortFilterProxyModel с Qabstractitemmodel

В моей реализации виджета model исходная модель (подкласс QAbstractItemModel), proxy_model (подкласс QSortFilterProxyModel) прокси и tree (QTreeView) дерево, представляющее модель.

Я хочу скрыть первый столбец. Пробовал использовать tree.hideColumn(0), дерево отображается плоским.

  • Если я создаю подкласс filterAcceptsColumn в прокси, чтобы вернуть True только для второго столбца, то строки не отображаются. Я считаю, что это связано с тем, что отношения родитель/потомок привязаны к первому столбцу в индексах, и когда прокси-сервер запрашивает количество строк для заданного индекса столбца 1, модель возвращает 0 (что является ожидаемым поведением в реализация модели, если я правильно понял).
  • Если я установлю rowCount для возврата значений, отличных от 0, в модели для индекса столбцов › 0, я смогу увидеть дерево и строки, но тогда модель не пройдет тест QAbstractItemModelTester со следующей ошибкой:
qt.modeltest: FAIL! childIndex != childIndex1 () returned FALSE

Я хорошо понимаю, что в древовидной модели дочерний индекс должен быть привязан к одному родительскому индексу (первому столбцу). Но как я должен скрыть первый столбец в прокси-модели, если родительские дочерние отношения исходной модели не сохраняются прокси-сервером, если первый столбец отфильтрован? Я чувствую, что это ошибка прокси, или я что-то пропустил!

Кто-нибудь знает правильный способ фильтрации/скрытия первого столбца в древовидном представлении без потери родительской/дочерней информации и проверки реализации qmodel?

Спасибо !


person beesleep    schedule 23.04.2021    source источник
comment
Ну, это немного сложно. Проблема в том, что, как вы уже выяснили, древовидная модель (по крайней мере, в Qt) рассматривает дочерние элементы только для первого столбца каждого родительского индекса. Проблема становится еще более сложной, если вы думаете об этих дочерних элементах: должна ли модель скрывать первый столбец даже для них? Правильная реализация может стать очень сложной (и, возможно, ошибочной), поэтому я бы предложил попытаться обойти ее: поскольку я предполагаю, что вам это понадобится только для отображения, что, если вы просто вернете столбец + 1 данные каждого элемента верхнего уровня?   -  person musicamante    schedule 24.04.2021
comment
Спасибо за Ваш ответ! Я не уверен, как хак, который вы предлагаете, будет иметь значение ..?   -  person beesleep    schedule 24.04.2021
comment
Идея состоит не в том, чтобы скрыть первый столбец, а в том, чтобы показать данные столбца рядом с запрошенным и, в конечном итоге, вместо этого скрыть последний. Таким образом сохраняется отношение родитель/потомок (поскольку структура модели всегда одна и та же, а индексы всегда относятся к исходному столбцу), единственное отличие состоит в том, что отображаемое содержимое смещается на один столбец.   -  person musicamante    schedule 24.04.2021
comment
Если я понял это хорошо, вы получите пустой столбец в своем древовидном представлении, верно?   -  person beesleep    schedule 26.04.2021
comment
Ну, теоретически да, но тогда вы можете скрыть этот последний столбец, не беспокоясь о структуре. Имейте в виду, что я не проверял это, это просто идея, которая пришла мне в голову, но мне кажется, что она имеет смысл.   -  person musicamante    schedule 26.04.2021


Ответы (1)


Правильная и правильная реализация потребует, по крайней мере, создания прокси-сервером индексов для родителя столбца второго, требующего правильной реализации index(), parent(), mapToSource() и mapFromSource(). Для моделей деревьев это может быть очень сложно.

Если исходная модель не слишком сложна и все ее функции реализованы правильно, возможным обходным путем может быть простое переопределение data()headerData) прокси-сервера и всегда возвращение родственного элемента следующего столбца.

Следующий тест выполняется с простой моделью QStandardItemModel, но я не думаю, что использование модели QAbstractItemModel должно быть чем-то другим, если оно правильно реализовано.

from PyQt5 import QtCore, QtGui, QtWidgets

class ColumnSwapProxy(QtCore.QSortFilterProxyModel):
    def data(self, index, role=QtCore.Qt.DisplayRole):
        return super().data(index.sibling(index.row(), index.column() + 1), role)

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Horizontal:
            section += 1
        return super().headerData(section, orientation, role)

class Test(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QVBoxLayout(self)

        self.combo = QtWidgets.QComboBox()
        layout.addWidget(self.combo)

        self.tree = QtWidgets.QTreeView()
        layout.addWidget(self.tree)
        self.model = QtGui.QStandardItemModel()
        self.createTree(self.model.invisibleRootItem())

        self.tree.setModel(self.model)
        self.model.setHorizontalHeaderLabels(
            ['Root', 'Fake root'] + ['Col {}'.format(c) for c in range(2, 6)])
        self.tree.header().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)

        self.proxy = ColumnSwapProxy()
        self.proxy.setSourceModel(self.model)

        self.combo.addItem('Base model', self.model)
        self.combo.addItem('First column hidden', self.proxy)
        self.combo.currentIndexChanged.connect(self.setModel)

    def setModel(self):
        model = self.combo.currentData()
        self.tree.setModel(model)
        lastColumn = self.model.columnCount() - 1
        self.tree.header().setSectionHidden(lastColumn, model == self.proxy)

    def createTree(self, parent, level=0):
        for r in range(10):
            first = QtGui.QStandardItem('Root {} (level {})'.format(level + 1, r + 1))
            if level < 2 and not r & 3:
                self.createTree(first, level + 1)
            row = [first]
            for c in range(5):
                row.append(QtGui.QStandardItem(
                    'Column {} (level {})'.format(c + 2, level + 1)))
            parent.appendRow(row)


import sys
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
person musicamante    schedule 26.04.2021