Как использовать QitemDelegate с несколькими сложенными моделями прокси?

Этот вопрос связан с предыдущим вопросом: как использовать QSortFilterProxyModel для фильтровать двумерный массив?

Я пытался сложить несколько прокси-моделей для отображения 2D-массива данных в qtableview. @eyllanesc предоставил действительно классное решение моего вопроса, но, похоже, оно не совместимо с qitemdelegate. Когда я добавляю его в свой пример, делегат не отображается, как ожидалось. Без proxy3 показывает правильно.

import random
import math
from PyQt4 import QtCore, QtGui


class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, columns, parent=None):
        super(TableModel, self).__init__(parent)
        self._columns = columns
        self._data = data[:]

    def rowCount(self, parent=QtCore.QModelIndex()): 
        if parent.isValid() or self._columns == 0:
            return 0
        return math.ceil(len(self._data )*1.0/self._columns)

    def columnCount(self, parent=QtCore.QModelIndex()): 
        if parent.isValid():
            return 0
        return self._columns

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid(): 
            return
        if role == QtCore.Qt.DisplayRole: 
            try:
                value = self._data[ index.row() * self._columns + index.column() ]
                return value
            except:
                pass

class Table2ListProxyModel(QtGui.QIdentityProxyModel):
    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return self.sourceModel().rowCount() * self.sourceModel().columnCount()

    def mapFromSource(self, sourceIndex):
        if sourceIndex.isValid() and sourceIndex.column() == 0 \
                and sourceIndex.row() < self.rowCount():
            r = sourceIndex.row()
            c = sourceIndex.column()
            row = c * sourceIndex.model().columnCount() + r
            return self.index(row, 0)
        return QtCore.QModelIndex()

    def mapToSource(self, proxyIndex):
        r = proxyIndex.row() / self.sourceModel().columnCount()
        c = proxyIndex.row() % self.sourceModel().columnCount()
        return self.sourceModel().index(r, c)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        return self.createIndex(row, column)


class ListFilterProxyModel(QtGui.QSortFilterProxyModel):
    def setThreshold(self, value):
        setattr(self, "threshold", value)
        self.invalidateFilter()

    def filterAcceptsRow(self, row, parent):
        if hasattr(self, "threshold"):
            ix = self.sourceModel().index(row, 0)
            val = ix.data()
            if val is None:
                return False
            return int(val.toString()) < getattr(self, "threshold")
        return True


class List2TableProxyModel(QtGui.QIdentityProxyModel):
    def __init__(self, columns=1, parent=None):
        super(List2TableProxyModel, self).__init__(parent)
        self._columns = columns

    def columnCount(self, parent=QtCore.QModelIndex()):
        return self._columns

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return math.ceil(self.sourceModel().rowCount()/self._columns)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        return self.createIndex(row, column)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        r = index.row()
        c = index.column()
        row = r * self.columnCount() + c
        if row < self.sourceModel().rowCount():
            return super(List2TableProxyModel, self).data(index, role)

    def mapFromSource(self, sourceIndex):
        r = math.ceil(sourceIndex.row() / self.columnCount())
        c = sourceIndex.row() % self.columnCount()
        return self.index(r, c)

    def mapToSource(self, proxyIndex):
        if proxyIndex.isValid():
            r = proxyIndex.row()
            c = proxyIndex.column()
            row = r * self.columnCount() + c
            return self.sourceModel().index(row, 0)
        return QtCore.QModelIndex()




class Delegate(QtGui.QItemDelegate):
    def __init__(self, parent = None):
        QtGui.QItemDelegate.__init__(self, parent)

    def paint(self, painter, option, index):
        number = str(index.data(QtCore.Qt.DisplayRole).toString())
        widget  = QtGui.QWidget()
        layout = QtGui.QVBoxLayout()
        widget.setLayout( layout )
        title = QtGui.QLabel("<font color='red'>"+number+"</font>")
        layout.addWidget( title )

        if not self.parent().tvf.indexWidget(index):
            self.parent().tvf.setIndexWidget(
                index, 
                widget
            )
        
        QtGui.QItemDelegate.paint(self, painter, option, index)



class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        data = [random.choice(range(10)) for i in range(20)]

        l = QtGui.QHBoxLayout(self)
        splitter = QtGui.QSplitter()
        l.addWidget(splitter)

        tv = QtGui.QTableView()
        lv = QtGui.QListView()
        lvf = QtGui.QListView()
        self.tvf = QtGui.QTableView()

        delegate = Delegate(self)
        self.tvf.setItemDelegate(delegate)

        model = TableModel(data, 3, self)
        proxy1 = Table2ListProxyModel(self)
        proxy1.setSourceModel(model)
        proxy2 = ListFilterProxyModel(self)
        proxy2.setSourceModel(proxy1)
        proxy2.setThreshold(5)
        proxy3 = List2TableProxyModel(3, self)
        proxy3.setSourceModel(proxy2)

        tv.setModel(model)
        lv.setModel(proxy1)
        lvf.setModel(proxy2)
        self.tvf.setModel(proxy3)

        splitter.addWidget(tv)
        splitter.addWidget(lv)
        splitter.addWidget(lvf)
        splitter.addWidget(self.tvf )


if __name__=="__main__":
    import sys
    a=QtGui.QApplication(sys.argv)
    w=Widget()
    w.show()
    sys.exit(a.exec_())

Вот ожидаемый результат, который я ищу:

введите здесь описание изображения

и вот как это выглядит, когда я обхожу прокси-модель (цифры красные). замена: self.tvf.setModel(proxy3) на self.tvf.setModel(model) введите здесь описание изображения


person nicosalto    schedule 28.09.2018    source источник
comment
Вы можете показать изображение того, что вы указываете как правильное, и другое, которое показывает ошибку.   -  person eyllanesc    schedule 28.09.2018
comment
да, я добавил туда имгур imgur.com/DrT2uFZ   -  person nicosalto    schedule 28.09.2018
comment
Вы говорите: Без прокси3 оно отображается правильно. Вы могли бы показать изображение без прокси.   -  person eyllanesc    schedule 28.09.2018


Ответы (1)


Вы неправильно используете делегат, который иногда работает, а иногда нет. Концепции делегата и IndexWidget представляют собой 2 альтернативы: первая — это рисование низкого уровня, но также и низкая стоимость, вторая вместо этого виджет встроен в задачу элемента, простая, но дорогая, поскольку виджет — это гораздо больше, чем просто нарисованный, также имеет логика.

В этом случае решение состоит в том, чтобы использовать QStyledItemDelegate и перезаписать метод initStyleOption(), изменив палитру.

class Delegate(QtGui.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(Delegate, self).initStyleOption(option, index)
        option.palette.setBrush(QtGui.QPalette.Text, QtGui.QColor("red"))

введите здесь описание изображения

Примечание: Примечание: не нужно писать

def __init__(self, parent = None):
    QtGui.QItemDelegate.__init__(self, parent)

так как он не изменен.

person eyllanesc    schedule 28.09.2018
comment
Спасибо @eyllanesc, вы имеете в виду, что использование setIndexWidget в делегате неверно и не предназначено для совместного использования? Мне нужно добавить виджет в каждую из моих ячеек. Здесь я упростил пример, но в своем приложении я отображаю изображения в каждой ячейке. - person nicosalto; 28.09.2018
comment
@nicosalto Точно, есть 2 концепции, которые конкурируют, вы не должны использовать их вместе, например, с вашим кодом вы создаете виджет каждый раз, когда вы вызываете рисование, а рисование вызывается каждый раз, и поэтому ресурсы тратятся впустую. текущая проблема связана с делегатами, поэтому я решил ее для нее, поэтому, если я помогу вам, не забудьте отметить ее как правильную, и если вы хотите использовать виджеты, создайте еще один вопрос, и я постараюсь вам помочь. - person eyllanesc; 28.09.2018
comment
@nicosalto Я рекомендую вам использовать делегатов, программа будет более эффективной в памяти и скорости. - person eyllanesc; 28.09.2018
comment
я добавил вопрос здесь с более простым примером: stackoverflow.com/questions/52552680/ - person nicosalto; 28.09.2018