title: how to show the proper number of columns in a QTreeView for a model with different hierarchical column counts
body: <p>In the code example below I populate an item model with a set of top level items which contain key-value properties that I can view and edit with a QTreeView.</p>
  
  <p>Looking at the Qt Documentation for <a href="http://doc.qt.io/qt-5/qabstractitemmodel.html#hasChildren" rel="nofollow noreferrer">QAbstractItemModel::columnCount</a> it says that this should return the number of columns for the children of the given parent, meaning that this should be a hierarchical dependent property.</p>
  
  <p>However, using the code below, if I return the column count as a hierarchical dependent property (in this case <code>root-&gt;children</code> have 1 column, <code>root-&gt;child-&gt;children</code> have 2 columns) then the view will only display 1 column.</p>
  
  <p><a href="https://i.stack.imgur.com/O3Tn8.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/O3Tn8.png" alt="enter image description here"></a></p>
  
  <p>Printing <code>node.columnCount()</code> (see the code) will actually show that the <code>Item</code> class nodes do in fact return columnCount = 2 after you expand one of the items.</p>
  
  <p>If I just always return 2 for the <code>model.columnCount</code> function, then the view will properly display both columns.</p>
  
  <p><a href="https://i.stack.imgur.com/YlPEW.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/YlPEW.png" alt="enter image description here"></a></p>
  
  <p>Is this required to always return the desired number of columns in a view no matter the hierarchy or am I just doing something wrong and if so what?  Returning a number of columns for a parent whose children have a different number of columns just to make the view work properly feels like it must be wrong.</p>
  
  
  
  <pre><code>import sys
  import typing
  from PyQt5 import QtCore, QtWidgets
  
  
  class Node:
      def __init__(self, parent=None):
          self.parent = parent  # type: Node
          self.name : str
  
      def children(self) -&gt; list:
          return None
  
      def hasChildren(self):
          return bool(self.children())
  
      def getData(self, index: QtCore.QModelIndex):
          if index.column() == 0:
              return self.name
  
      def setData(self, val, index: QtCore.QModelIndex):
          if index.column() == 0:
              self.name = val
  
      def columnCount(self):
          return 1
  
      def rowCount(self):
          children = self.children()
          return 0 if not children else len(children)
  
      def flags(self, index: QtCore.QModelIndex):
          if index.column() == 0:
              return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable)
          else:
              return QtCore.Qt.NoItemFlags
  
  
  class Property(Node):
      def __init__(self, parent, label, value):
          super().__init__(parent)
          self.label = label
          self.value = value
  
      def getData(self, index: QtCore.QModelIndex):
          col = index.column()
          if col == 0:
              return self.label
          elif col == 1:
              return self.value
  
      def setData(self, val, index: QtCore.QModelIndex):
          if index.column() == 1:
              self.value = val
  
      def flags(self, index: QtCore.QModelIndex):
          col = index.column()
          if col == 0:
              return QtCore.Qt.ItemIsEnabled
          elif col == 1:
              return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEditable
  
  
  class Item(Node):
      def __init__(self, parent):
          super().__init__(parent)
          self.name = 'Item'
          self.p1 = Property(self, 'string', 'text')
          self.p2 = Property(self, 'float', 1.2)
  
      def children(self):
          return [self.p1, self.p2]
  
      def columnCount(self):
          return 2
  
  
  class Root(Node):
      def __init__(self):
          super().__init__(parent=None)
          self._children = list()
  
      def children(self):
          return self._children
  
  
  class Model(QtCore.QAbstractItemModel):
      def __init__(self):
          super().__init__()
          self.root = Root()
  
      def index(self, row: int, column: int, parent: QtCore.QModelIndex = ...) -&gt; QtCore.QModelIndex:
          if not self.hasIndex(row, column, parent):
              return QtCore.QModelIndex()
  
          node = parent.internalPointer() if parent.isValid() else self.root
          if node.children:
              return self.createIndex(row, column, node.children()[row])
          else:
              return QtCore.QModelIndex()
  
      def parent(self, child: QtCore.QModelIndex) -&gt; QtCore.QModelIndex:
          if not child.isValid():
              return QtCore.QModelIndex()
  
          node = child.internalPointer()  # type: Node
  
          if node.parent and node.parent.parent:
              row = node.parent.parent.children().index(node.parent)
              return self.createIndex(row, 0, node.parent)
          else:
              return QtCore.QModelIndex()
  
      def rowCount(self, parent: QtCore.QModelIndex = ...) -&gt; int:
          node = parent.internalPointer() if parent.isValid() else self.root
          children = node.children()
          return len(children) if children else 0
  
      def columnCount(self, parent: QtCore.QModelIndex = ...) -&gt; int:
          node = parent.internalPointer() if parent.isValid() else self.root
          print(f'{node.__class__.__name__} column count: ', node.columnCount())  # shows that column count 2 is returned, when items are expanded
          # return 2  # 2nd column only shows up if I just always return 2
          return node.columnCount()  # view only shows 1 columns
  
      def hasChildren(self, parent: QtCore.QModelIndex = ...) -&gt; bool:
          node = parent.internalPointer() if parent.isValid() else self.root
          return node.hasChildren()
  
      def data(self, index: QtCore.QModelIndex, role: int = ...):
          if index.isValid() and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
              node = index.internalPointer()  # type: Node
              return node.getData(index)
          else:
              return None
  
      def setData(self, index: QtCore.QModelIndex, value: typing.Any, role: int = ...) -&gt; bool:
          if role in (QtCore.Qt.EditRole,):
              node = index.internalPointer()  # type: Node
              node.setData(value, index)
              self.dataChanged.emit(index, index)
              return True
          else:
              return False
  
      def flags(self, index: QtCore.QModelIndex):
          node = index.internalPointer() if index.isValid() else self.root
          return node.flags(index)
  
      def appendRow(self, item):
          row = len(self.root.children())
          self.beginInsertRows(QtCore.QModelIndex(), row, row)
          self.root.children().append(item)
          self.endInsertRows()
  
  
  class TreeView(QtWidgets.QTreeView):
      def __init__(self, parent=None):
          super(TreeView, self).__init__(parent)
          self._model = Model()
          self.setModel(self._model)
          self.setSelectionMode(self.ExtendedSelection)
          # self.setDropIndicatorShown(False)
          self.setEditTriggers(self.DoubleClicked | self.SelectedClicked | self.EditKeyPressed)
  
      def model(self) -&gt; Model:
          return self._model
  
  sys.excepthook = sys.__excepthook__
  app = QtWidgets.QApplication(sys.argv)
  widget = TreeView()
  model = widget.model()
  for i in range(2):
      model.appendRow(Item(model.root))
  widget.show()
  widget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
  sys.exit(app.exec_())
  </code></pre>