QSortFilterProxyModel: Update after insert columns
-
Hi guys,
I've got a problem with a QSortFilterProxyModel. After inserting a new column, the view doesn't update properly. I've called beginInsertColumns(QModelIndex(), old_row_count, old_row_count) before and self.model.endInsertColumns() after inserting the new column, but the view doesn't display the data and alternating row coloring properly.
All seems to be fine and dandy if I don't use the proxy model.
My understanding is that under "normal" circumstances I don't need to reimplement the usual methods (like, "index", "data", ...) when subclassing a QSortFilterProxyModel. The QSortFilterProxyModel's standard implementations call these methods after converting the indeces. Is that correct?
Any advice?
Cheers and many thanks in advance,
Jan
I've prepared a full working example, posted in two parts. Here Part 1 (custom tree_items, header, proxymodel and view), Part 2 (model and code to run the example) follows in the first answer to this question.
@
import collectionsfrom PyQt4.QtGui import *
from PyQt4.QtCore import *class ViewItem(object):
def __init__(self, data, parent): self.data = data self.parent = parent self.children = []
class ColumnHeader(QHeaderView):
colums_about_to_be_updated = pyqtSignal(int) colums_updated = pyqtSignal() def __init__(self, applicable_columns, initial_columns, parent=None): super(ColumnHeader, self).__init__(Qt.Horizontal, parent) self.applicable_columns = applicable_columns self.initial_columns = initial_columns self.all_columns = self.applicable_columns self.columns = initial_columns self.setMovable(True) self.setContextMenuPolicy(Qt.ActionsContextMenu) self.att_name_to_action = collections.OrderedDict() self._prepopulate_att_name_to_action() self._create_att_name_to_action() self._set_initial_colums() def _prepopulate_att_name_to_action(self): for col in self.columns: self.att_name_to_action[col] = None def _create_att_name_to_action(self): for col in self.all_columns: action = QAction(col, self) action.setCheckable(True) action.toggled.connect(self.set_columns) self.addAction(action) self.att_name_to_action[col] = action def _set_initial_colums(self): for active_name in self.columns: self.att_name_to_action[active_name].toggle() def set_columns(self): self.colums_about_to_be_updated.emit(len(self.columns)) self.columns = [] for att, action in self.att_name_to_action.items(): if action.isChecked(): self.columns.append(att) self.colums_updated.emit()
class SearchFilterProxyModel(QSortFilterProxyModel):
def __init__(self, source_model, parent=None): super(SearchFilterProxyModel, self).__init__(parent) self.source_model = source_model self.setSourceModel(self.source_model)
class TreeView(QTreeView):
def __init__(self, aux_root_view_item, all_applicable_attributes, initial_columns, parent=None): super(TreeView, self).__init__(parent) self.aux_root_view_item = aux_root_view_item self.setAlternatingRowColors(True) self.column_header = ColumnHeader(all_applicable_attributes, initial_columns) self.column_header.setStretchLastSection(True) self.setHeader(self.column_header) self.column_header.colums_about_to_be_updated.connect(self.header_about_to_be_changed) self.column_header.colums_updated.connect(self.header_changed) self.model = TreeModel(aux_root_view_item, self.column_header, self) self.proxy_model = SearchFilterProxyModel(self.model, self) #self.setModel(self.proxy_model) self.setModel(self.model) self.selection_model = self.selectionModel() def header_about_to_be_changed(self, old_row_count): self.model.beginInsertColumns(QModelIndex(), old_row_count, old_row_count) def header_changed(self): self.model.endInsertColumns()
@
-
Here Part 2:
@
class TreeModel(QAbstractItemModel):
def init(self, aux_root_view_item, header, parent):
super(TreeModel, self).init(parent)
self.tree_view = parent
self.aux_root_view_item = aux_root_view_item
self.header = headerdef rowCount(self, parent_index=QModelIndex()): if parent_index.column() > 0: return 0 parent_view_item = self.view_item_from_index(parent_index) if parent_view_item is None: return 0 return len(parent_view_item.children) def columnCount(self, parent_index=QModelIndex()): return len(self.header.columns) def headerData(self, section, orientation, role): if (orientation == Qt.Horizontal and role == Qt.DisplayRole): assert 0 <= section <= len(self.header.columns) return self.header.columns[section] elif role == Qt.DisplayRole: return QVariant(int(section + 1)) return QVariant() def data(self, index, role): view_item = self.view_item_from_index(index) data = view_item.data if data is None: return None if role != Qt.DisplayRole and role != Qt.EditRole: return None column = index.column() value = data[column] return unicode(value) def index(self, row, column, parent_index): if row < 0 or column < 0: return QModelIndex() view_item_parent = self.view_item_from_index(parent_index) if row > len(view_item_parent.children) - 1: return QModelIndex() child = view_item_parent.children[row] return self.createIndex(row, column, child) def parent(self, child_index): child_view_item = self.view_item_from_index(child_index) if child_view_item is None: return QModelIndex() parent_view_item = child_view_item.parent if parent_view_item is None: return QModelIndex() grandparent_view_item = parent_view_item.parent if grandparent_view_item is None: return QModelIndex() row = grandparent_view_item.children.index(parent_view_item) assert row != -1 return self.createIndex(row, 0, parent_view_item) def view_item_from_index(self, index): if index.isValid(): view_item = index.internalPointer() return view_item else: return self.aux_root_view_item
def create_test_data():
aux_root_view_item = ViewItem(None, None) root_view_item = ViewItem(["1a", "1b", "1c"], aux_root_view_item) child_11 = ViewItem(["11a", "11b", "11c"], root_view_item) child_12 = ViewItem(["12a", "12b", "12c"], root_view_item) child_111 = ViewItem(["111a", "111b", "111c"], child_11) child_112 = ViewItem(["112a", "112b", "112c"], child_11) aux_root_view_item.children = [root_view_item] root_view_item.children = [child_11, child_12] child_11.children = [child_111, child_112] return aux_root_view_item
if name == "main":
import sys app = QApplication(sys.argv) aux_root_view_item = create_test_data() tree_view = TreeView(aux_root_view_item,["a", "b", "c"], ["a", "b"]) tree_view.show() app.exec_()
@