Menu

Subclassing QAbstractItemModel - struggling with createIndex()/internalPointer()

Help
Max
2019-11-08
2019-11-08
  • Max

    Max - 2019-11-08

    I had no trouble creating QTreeView and connecting it to QAbstractTableModel on the Python side of PythonQt. Now I need to create a custom model by subclassing QAbstractItemModel, and I just can't make it working. Help is appreciated.

    Basically, the problem is with the createIndex() method, which accepts row/column and a pointer to a custom tree-item object. Since in Python there are no pointer, I tried passing just the object itself (as usual), but it failed with errors like this one:

    ValueError: Could not find matching overload for given arguments:
    (1, 0, <gui.TreeItem object at 0x00000232AAA3B550>)
    The following slots are available:
    createIndex(int row, int column, quintptr id) -> QModelIndex
    createIndex(int row, int column, void data) -> QModelIndex
    createIndex(int row, int column) -> QModelIndex

    So I thought maybe I can use the first suggestion with quintptr, and passed not the python object, but (presumably) its address with id(...). That made this error go away, but then calling QModelIndex.internalPointer() always returns None.

    Below is the minimal example to run from PythonQt. There error points are indicated in comments.

    By the way, I tried to use this very code with PyQt5 (with different imports, of course, and also adding QApplication for the event loop, which I don't need in PythonQt as it is handled in C++ side), and it does work there.

    from PythonQt.QtCore import QAbstractItemModel, QModelIndex, Qt
    from PythonQt.QtGui import QTreeView
    
    
    # this is my custom tree item class, which I cannot pass to QAbstractItemModel.createIndex() below
    class TreeItem(object):
      def __init__(self, content, parentItem):
        self.content = content
        self.parentItem = parentItem
        self.childItems = []
    
      def appendChild(self, item):
        self.childItems.append(item)
    
      def child(self, row):
        return self.childItems[row]
    
      def childCount(self):
        return len(self.childItems)
    
      def columnCount(self):
        return 1
    
      def data(self, column):
        if self.content != None and column == 0:
          return self.content
    
        return None
    
      def parent(self):
        return self.parentItem
    
      def row(self):
        if self.parentItem:
          return self.parentItem.childItems.index(self)
        return 0
    
    
    class NodeTree(QAbstractItemModel):
      def __init__(self, parent, data):
        QAbstractItemModel.__init__(self)
        self.rootItem = TreeItem(-1, None)
        self.parents = {0: self.rootItem}
        self.setupModelData(self.rootItem, data)
    
      def setupModelData(self, root, data):
        for el in data:
          if isinstance(el, list):
            item = TreeItem("Node", root)
            self.setupModelData(item, el)
          else:
            item = TreeItem(el, root)
          root.appendChild(item)
    
      def rowCount(self, parent=QModelIndex()):
        if parent.column() > 0:
          return 0
        if not parent.isValid():
          p_Item = self.rootItem
        else:
          p_Item = parent.internalPointer()
        return p_Item.childCount()
    
      def columnCount(self, parent=QModelIndex()):
        return 1
    
      def data(self, index, role):
        if not index.isValid():
          return None
    
        item = index.internalPointer()
        if role == Qt.DisplayRole:
          return item.data(index.column())
        if role == Qt.UserRole:
          if item:
            return item.content
        return None
    
      def headerData(self, column, orientation, role):
        if (orientation == Qt.Horizontal and
                role == Qt.DisplayRole):
          return "Content"
        return None
    
      def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
          return QModelIndex()
    
        if not parent.isValid():
          parentItem = self.rootItem
        else:
          parentItem = parent.internalPointer()
    
        childItem = parentItem.child(row)
        if childItem:
          return self.createIndex(row, column, childItem)   # ====  ERROR  ======
        else:
          return QModelIndex()
    
      def parent(self, index):
        if not index.isValid():
          return QModelIndex()
    
        childItem = index.internalPointer()
        if not childItem:
          return QModelIndex()
    
        parentItem = childItem.parent()
    
        if parentItem == self.rootItem:
          return QModelIndex()
    
        return self.createIndex(parentItem.row(), 0, parentItem) # == ERROR ==
    
    
    TreeView = QTreeView()
    TreeModel = NodeTree(TreeView, [['A', ['B', 1]], ['C', 'D'], ['E', 'F']])
    TreeView.setModel(TreeModel)
    TreeView.show()
    
     

    Last edit: Max 2019-11-08
    • Florian Link

      Florian Link - 2019-11-08

      Yes, I never implemented this. I think it requires special support by
      PythonQt.

      On Fri, Nov 8, 2019 at 3:24 PM Max galicarnax@users.sourceforge.net wrote:

      I had no trouble creating QTreeView and connecting it to
      QAbstractTableModel on the Python side of PythonQt. Now I need to create
      a custom model by subclassing QAbstractItemModel, and I just can't make
      it working. Help is appreciated.

      Basically, the problem is with the createIndex() method, which
      accepts row/column and a pointer to a custom tree-item object. Since in
      Python there are no pointer, I tried passing just the object itself (as
      usual), but it failed with errors like this one:

      ValueError: Could not find matching overload for given arguments:
      (1, 0, <gui.treeitem object="" at="" 0x00000232aaa3b550="">)
      The following slots are available:
      createIndex(int row, int column, quintptr id) -> QModelIndex
      createIndex(int row, int column, void data) -> QModelIndex
      createIndex(int row, int column) -> QModelIndex</gui.treeitem>

      So I thought maybe I can use the first suggestion with quintptr, and
      passed not the python object, but (presumably) its address with id(...).
      That made this error go away, but then calling
      QModelIndex.internalPointer() always returns None.

      Below is the minimal example to run from PythonQt. There error points are
      indicated in comments.

      By the way, I tried to use this very code with PyQt5 (with different
      imports, of course, and also adding QApplication for the event loop,
      which I don't need in PythonQt as it is handled in C++ side), and it does
      work there.

      ```
      from PythonQt.QtCore import QAbstractItemModel, QModelIndex, Qt
      from PythonQt.QtGui import QTreeView

      class TreeItem(object):
      def init(self, content, parentItem):
      self.content = content
      self.parentItem = parentItem
      self.childItems = []

      def appendChild(self, item):
      self.childItems.append(item)

      def child(self, row):
      return self.childItems[row]

      def childCount(self):
      return len(self.childItems)

      def columnCount(self):
      return 1

      def data(self, column):
      if self.content != None and column == 0:
      return self.content

      return None
      

      def parent(self):
      return self.parentItem

      def row(self):
      if self.parentItem:
      return self.parentItem.childItems.index(self)
      return 0

      class NodeTree(QAbstractItemModel):
      def init(self, parent, data):
      QAbstractItemModel.init(self)
      self.rootItem = TreeItem(-1, None)
      self.parents = {0: self.rootItem}
      self.setupModelData(self.rootItem, data)

      def setupModelData(self, root, data):
      for el in data:
      if isinstance(el, list):
      item = TreeItem("Node", root)
      self.setupModelData(item, el)
      else:
      item = TreeItem(el, root)
      root.appendChild(item)

      def rowCount(self, parent=QModelIndex()):
      if parent.column() > 0:
      return 0
      if not parent.isValid():
      p_Item = self.rootItem
      else:
      p_Item = parent.internalPointer()
      return p_Item.childCount()

      def columnCount(self, parent=QModelIndex()):
      return 1

      def data(self, index, role):
      if not index.isValid():
      return None

      item = index.internalPointer()
      if role == Qt.DisplayRole:
        return item.data(index.column())
      if role == Qt.UserRole:
        if item:
          return item.content
      return None
      

      def headerData(self, column, orientation, role):
      if (orientation == Qt.Horizontal and
      role == Qt.DisplayRole):
      return "Content"
      return None

      def index(self, row, column, parent):
      if not self.hasIndex(row, column, parent):
      return QModelIndex()

      if not parent.isValid():
        parentItem = self.rootItem
      else:
        parentItem = parent.internalPointer()
      
      childItem = parentItem.child(row)
      if childItem:
        return self.createIndex(row, column, childItem)   # ====  ERROR
      

      ======
      else:
      return QModelIndex()

      def parent(self, index):
      if not index.isValid():
      return QModelIndex()

      childItem = index.internalPointer()
      if not childItem:
        return QModelIndex()
      
      parentItem = childItem.parent()
      
      if parentItem == self.rootItem:
        return QModelIndex()
      
      return self.createIndex(parentItem.row(), 0, parentItem) # == ERROR ==
      

      TreeView = QTreeView()
      TreeModel = NodeTree(TreeView, [['A', ['B', 1]], ['C', 'D'], ['E', 'F']])
      TreeView.setModel(TreeModel)
      TreeView.show()
      ```


      Subclassing QAbstractItemModel - struggling with
      createIndex()/internalPointer()


      Sent from sourceforge.net because you indicated interest in <
      https://sourceforge.net/p/pythonqt/discussion/631393/>

      To unsubscribe from further messages, please visit <
      https://sourceforge.net/auth/subscriptions/>

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.