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 collections
- from PyQt4.QtGui import *
- from PyQt4.QtCore import *
- class ViewItem(object):
- def __init__(self, data, parent):
- self.data = data
- self.parent = parent
- self.children = []
- colums_about_to_be_updated = pyqtSignal(int)
- colums_updated = pyqtSignal()
- def __init__(self, applicable_columns, initial_columns, parent=None):
- self.applicable_columns = applicable_columns
- self.initial_columns = initial_columns
- self.all_columns = self.applicable_columns
- self.columns = initial_columns
- self.setMovable(True)
- 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.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()
- def __init__(self, source_model, parent=None):
- super(SearchFilterProxyModel, self).__init__(parent)
- self.source_model = source_model
- self.setSourceModel(self.source_model)
- 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):
- def header_changed(self):
- self.model.endInsertColumns()
4 replies
Here Part 2:
- 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 = header
- 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)
- return len(self.header.columns)
- def headerData(self, section, orientation, role):
- assert 0 <= section <= len(self.header.columns)
- return self.header.columns[section]
- def data(self, index, role):
- view_item = self.view_item_from_index(index)
- data = view_item.data
- if data is None:
- return None
- return None
- column = index.column()
- value = data[column]
- return unicode(value)
- def index(self, row, column, parent_index):
- if row < 0 or column < 0:
- view_item_parent = self.view_item_from_index(parent_index)
- if row > len(view_item_parent.children) - 1:
- 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:
- parent_view_item = child_view_item.parent
- if parent_view_item is None:
- grandparent_view_item = parent_view_item.parent
- if grandparent_view_item is None:
- 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
- aux_root_view_item = create_test_data()
- tree_view = TreeView(aux_root_view_item,["a", "b", "c"], ["a", "b"])
- tree_view.show()
- app.exec_()
You must log in to post a reply. Not a member yet? Register here!


