Search code examples
pythonpysideqtreeviewqstandarditemmodelqheaderview

QStandardItemModel with multiple headers - decouple QTreeView header from model


I have a QStandardItemModel that needs different horizontal header labels in different views.

I'm having trouble finding information about how to decouple the header of a QTreeView from the model in that way. Is this possible?

See a simple code example below.


In this example - we have ItemTypeA, which is our top level item, and its horizontal axis represents the attributes "Name | Interpolaton | Normalize".

The children of ItemTypeA (ItemTypeB) have items in the horizontal axis that represent "Name | Multiply"

TreeViewA shows everything, and TreeViewB only shows the children of the selected Top Level Item (selection connection not implemented in this example).

from PySide.QtGui import *
from PySide.QtCore import *

class MyModel(QStandardItemModel):

    def __init__(self):
        super(MyModel, self).__init__()
        iroot = self.invisibleRootItem()

        self.setHorizontalHeaderLabels(['Name', 'Interpolation', 'Normalize'])

    def newTopLevelItem(self, name = 'myTopLevelItem'):
        item = ItemTypeA(name)
        root_item = self.invisibleRootItem()
        root_item.appendRow([item]+item.settingItems())
        return item


class ItemTypeA(QStandardItem):
    def __init__(self, *args, **kwargs):
        super(ItemTypeA, self).__init__(*args, **kwargs)

        self.s_interpolation =QStandardItem('0')
        self.s_normalize = QStandardItem('False')

    def settingItems(self):
        return [
            self.s_interpolation,
            self.s_normalize
        ]
        
    def newChildItem(self, name = 'newChildItem'):
        childItem = ItemTypeB( name )
        self.appendRow( [childItem]+childItem.settingItems() )


class ItemTypeB(QStandardItem):
    def __init__(self, *args, **kwargs):
        super(ItemTypeB, self).__init__(*args, **kwargs)
        
        self.s_multiply = QStandardItem('1.0')

    def settingItems(self):
        return [
            self.s_multiply,
        ]
        

class TreeViewA(QTreeView):
    '''
    THIS VIEW Needs the Headers:
        Item Name | Interpolation | Normalize
    '''
    def __init__(self):
        super(TreeViewA, self).__init__()
        model = MyModel()
        self.setModel(model)
        
        newItem = model.newTopLevelItem()
        newItem.newChildItem()

class TreeViewB(QTreeView):
    '''
    THIS VIEW Needs the Headers:
        Item Name | Multiply
    '''
    def __init__(self, sourceView):
        super(TreeViewB, self).__init__()
        model = sourceView.model()
        self.setModel(model)
        self.setRootIndex(model.index(0,0))
        

class MyWidget(QWidget):
    
    def __init__(self):
        super(MyWidget, self).__init__()
        layout = QHBoxLayout()
        viewA = TreeViewA()
        viewB = TreeViewB(viewA)
        layout.addWidget(viewA)
        layout.addWidget(viewB)
        self.setLayout(layout)

widget = MyWidget()
widget.show()

Thoughts?


Solution

  • The solution for these cases would be to use QIdentityProxyModel but this class is not present in PySide and PySide, so we must use a similar class such as QSortFilterProxyModel and overwrite the headerData method.

    ...
    
    class HeaderProxyModel(QSortFilterProxyModel):
        def __init__(self, *args, **kwargs):
            QSortFilterProxyModel.__init__(self, *args, **kwargs)
            self.labels = []
        def setHeaderLabels(self, labels):
            self.labels = labels
        def headerData(self, section,orientation, role = Qt.DisplayRole):
            if orientation == Qt.Horizontal and 0 <= section < self.columnCount() and role==Qt.DisplayRole and section < len(self.labels) :
                return self.labels[section]
            return QSortFilterProxyModel.headerData(self, section, orientation, role)
    
    class TreeViewB(QTreeView):
        '''
        THIS VIEW Needs the Headers:
            Item Name | Multiply
        '''
        def __init__(self, sourceView):
            super(TreeViewB, self).__init__()
            model = sourceView.model()
            proxy = HeaderProxyModel()
            proxy.setSourceModel(model)
            proxy.setHeaderLabels(["Name", "Multiply"])
            self.setModel(proxy)
            self.setRootIndex(proxy.index(0,0))
    ...    
    

    enter image description here