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 = []
    

    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 = header

    def 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_()
    

    @



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



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



  • 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


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.