This question is related to this previous question: how to use QSortFilterProxyModel for filter a 2d array?
i have been trying to stack several proxy model to display a 2d array of data into a qtableview. @eyllanesc provided a really cool solution to my question but it doesn't seem to be compatible with qitemdelegate. When i add it to his example the delegate doesnt display as expected. Without the proxy3 it does show correctly.
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_())
Here is the expected result I am looking for:
and this is what it look like when i bypass the proxy model (the numbers are red). replacing : self.tvf.setModel(proxy3) to self.tvf.setModel(model)
You are incorrectly using the delegate that works sometimes if and sometimes not. The concepts of delegate and IndexWidget are 2 alternatives, the first one is a low level painting but also low cost, the second instead a widget is embedded in the item task simple but expensive since a widget is much more than a simple painted , also has a logic.
In this case the solution is to use a QStyledItemDelegate
and overwrite the initStyleOption()
method by modifying the palette.
class Delegate(QtGui.QStyledItemDelegate):
def initStyleOption(self, option, index):
super(Delegate, self).initStyleOption(option, index)
option.palette.setBrush(QtGui.QPalette.Text, QtGui.QColor("red"))
Note: Note: it is not necessary to write
def __init__(self, parent = None):
QtGui.QItemDelegate.__init__(self, parent)
since it is not modified.