July 11, 2012

Knacktus Knacktus
Lab Rat
41 posts

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.

  1. import collections
  2.  
  3. from PyQt4.QtGui import *
  4. from PyQt4.QtCore import *
  5.  
  6.  
  7. class ViewItem(object):
  8.  
  9.     def __init__(self, data, parent):
  10.         self.data = data
  11.         self.parent = parent
  12.         self.children = []
  13.  
  14.  
  15. class ColumnHeader(QHeaderView):
  16.  
  17.     colums_about_to_be_updated = pyqtSignal(int)
  18.     colums_updated = pyqtSignal()
  19.  
  20.     def __init__(self, applicable_columns, initial_columns, parent=None):
  21.         super(ColumnHeader, self).__init__(Qt.Horizontal, parent)
  22.         self.applicable_columns = applicable_columns
  23.         self.initial_columns = initial_columns
  24.         self.all_columns = self.applicable_columns
  25.         self.columns = initial_columns
  26.         self.setMovable(True)
  27.         self.setContextMenuPolicy(Qt.ActionsContextMenu)
  28.         self.att_name_to_action = collections.OrderedDict()
  29.         self._prepopulate_att_name_to_action()
  30.         self._create_att_name_to_action()
  31.         self._set_initial_colums()
  32.  
  33.     def _prepopulate_att_name_to_action(self):
  34.         for col in self.columns:
  35.             self.att_name_to_action[col] = None
  36.  
  37.     def _create_att_name_to_action(self):
  38.         for col in self.all_columns:
  39.             action = QAction(col, self)
  40.             action.setCheckable(True)
  41.             action.toggled.connect(self.set_columns)
  42.             self.addAction(action)
  43.             self.att_name_to_action[col] = action
  44.  
  45.     def _set_initial_colums(self):
  46.         for active_name in self.columns:
  47.             self.att_name_to_action[active_name].toggle()
  48.  
  49.     def set_columns(self):
  50.         self.colums_about_to_be_updated.emit(len(self.columns))
  51.         self.columns = []
  52.         for att, action in self.att_name_to_action.items():
  53.             if action.isChecked():
  54.                 self.columns.append(att)
  55.         self.colums_updated.emit()
  56.  
  57.  
  58. class SearchFilterProxyModel(QSortFilterProxyModel):
  59.  
  60.     def __init__(self, source_model, parent=None):
  61.         super(SearchFilterProxyModel, self).__init__(parent)
  62.         self.source_model = source_model
  63.         self.setSourceModel(self.source_model)
  64.  
  65. class TreeView(QTreeView):
  66.  
  67.     def __init__(self, aux_root_view_item, all_applicable_attributes, initial_columns, parent=None):
  68.  
  69.         super(TreeView, self).__init__(parent)
  70.         self.aux_root_view_item = aux_root_view_item
  71.         self.setAlternatingRowColors(True)
  72.  
  73.         self.column_header = ColumnHeader(all_applicable_attributes, initial_columns)
  74.         self.column_header.setStretchLastSection(True)
  75.         self.setHeader(self.column_header)
  76.  
  77.         self.column_header.colums_about_to_be_updated.connect(self.header_about_to_be_changed)
  78.         self.column_header.colums_updated.connect(self.header_changed)
  79.  
  80.         self.model = TreeModel(aux_root_view_item, self.column_header, self)
  81.         self.proxy_model = SearchFilterProxyModel(self.model, self)
  82.  
  83.         #self.setModel(self.proxy_model)
  84.         self.setModel(self.model)
  85.  
  86.         self.selection_model = self.selectionModel()
  87.  
  88.     def header_about_to_be_changed(self, old_row_count):
  89.         self.model.beginInsertColumns(QModelIndex(), old_row_count, old_row_count)
  90.  
  91.     def header_changed(self):
  92.         self.model.endInsertColumns()

4 replies

July 11, 2012

Knacktus Knacktus
Lab Rat
41 posts

Here Part 2:

  1. class TreeModel(QAbstractItemModel):
  2.     def __init__(self, aux_root_view_item, header, parent):
  3.         super(TreeModel, self).__init__(parent)
  4.         self.tree_view = parent
  5.         self.aux_root_view_item = aux_root_view_item
  6.         self.header = header
  7.  
  8.     def rowCount(self, parent_index=QModelIndex()):
  9.         if parent_index.column() > 0:
  10.             return 0
  11.         parent_view_item = self.view_item_from_index(parent_index)
  12.         if parent_view_item is None:
  13.             return 0
  14.         return len(parent_view_item.children)
  15.  
  16.     def columnCount(self, parent_index=QModelIndex()):
  17.         return len(self.header.columns)
  18.  
  19.     def headerData(self, section, orientation, role):
  20.         if (orientation == Qt.Horizontal and
  21.             role == Qt.DisplayRole):
  22.             assert 0 <= section <= len(self.header.columns)
  23.             return self.header.columns[section]
  24.         elif role == Qt.DisplayRole:
  25.             return QVariant(int(section + 1))
  26.         return QVariant()
  27.  
  28.     def data(self, index, role):
  29.         view_item = self.view_item_from_index(index)
  30.         data = view_item.data
  31.         if data is None:
  32.             return None
  33.         if role != Qt.DisplayRole and role != Qt.EditRole:
  34.             return None
  35.         column = index.column()
  36.         value = data[column]
  37.         return unicode(value)
  38.  
  39.     def index(self, row, column, parent_index):
  40.         if row < 0 or column < 0:
  41.             return QModelIndex()
  42.         view_item_parent = self.view_item_from_index(parent_index)
  43.         if row > len(view_item_parent.children) - 1:
  44.             return QModelIndex()
  45.         child = view_item_parent.children[row]
  46.         return self.createIndex(row, column, child)
  47.  
  48.     def parent(self, child_index):
  49.         child_view_item = self.view_item_from_index(child_index)
  50.         if child_view_item is None:
  51.             return QModelIndex()
  52.         parent_view_item = child_view_item.parent
  53.         if parent_view_item is None:
  54.             return QModelIndex()
  55.         grandparent_view_item = parent_view_item.parent
  56.         if grandparent_view_item is None:
  57.             return QModelIndex()
  58.         row = grandparent_view_item.children.index(parent_view_item)
  59.         assert row != -1
  60.         return self.createIndex(row, 0, parent_view_item)
  61.  
  62.     def view_item_from_index(self, index):
  63.         if index.isValid():
  64.             view_item = index.internalPointer()
  65.             return view_item
  66.         else:
  67.             return self.aux_root_view_item
  68.  
  69.  
  70. def create_test_data():
  71.  
  72.     aux_root_view_item = ViewItem(None, None)
  73.     root_view_item = ViewItem(["1a", "1b", "1c"], aux_root_view_item)
  74.     child_11 = ViewItem(["11a", "11b", "11c"], root_view_item)
  75.     child_12 = ViewItem(["12a", "12b", "12c"], root_view_item)
  76.     child_111 = ViewItem(["111a", "111b", "111c"], child_11)
  77.     child_112 = ViewItem(["112a", "112b", "112c"], child_11)
  78.  
  79.     aux_root_view_item.children = [root_view_item]
  80.     root_view_item.children = [child_11, child_12]
  81.     child_11.children = [child_111, child_112]
  82.  
  83.     return aux_root_view_item
  84.  
  85.  
  86. if __name__ == "__main__":
  87.  
  88.     import sys
  89.  
  90.     app = QApplication(sys.argv)
  91.  
  92.     aux_root_view_item = create_test_data()
  93.     tree_view = TreeView(aux_root_view_item,["a", "b", "c"], ["a", "b"])
  94.     tree_view.show()
  95.  
  96.     app.exec_()

July 11, 2012

Andre Andre
Area 51 Engineer
6031 posts

Did you call setDynamicSortFilter(true) on your proxy model?

 Signature 

Looking for Qt developers to join our team @ i-Optics: https://qt-project.org/forums/viewthread/25393/

July 11, 2012

Knacktus Knacktus
Lab Rat
41 posts

No, I didn’t. And doing (toggle true / false) so does not have any effect on my issue.

February 6, 2013

LivTarg LivTarg
Lab Rat
1 posts

Hi,
I know this thread is half a year old but i’m facing the same problem right now.
Did you manage to solve it somehow?

Ben

 
  ‹‹ [Solved]How to call userdefined function from StyleSheet?      setExtraSelections in QGraphicsTextItem ››

You must log in to post a reply. Not a member yet? Register here!