Hello! I've got a question about implementation of a standart tree model for TreeView.

I have a response from database that lools like

[
[A, [a,b,c]],
[B,[a,b,c]],
.
.
.
[N,[a,b,c]]
]

And I want to pass it through treemodel to treeview, for in GUI it looks like that:

A
|
+---a
+---b
+---c
|
B
+
|
+---a
+---b
+---c

I've took a look at example, that comes with PyQT4, but in that example they retrieve data from a text file, where tree structure is marked by tabs. and I cannot realize how I can modify this standart example for it works with my data...

I'll be thankful for any help...

Dani AI

Generated

If your data come back from the DB as pairs like ("A", ["a","b","c"]), you do not need to parse tabs at all. Build the same internal tree the Qt example uses, but fill each TreeItem with a list of column values. The key fixes for multi-column support are:

  • TreeItem.data(column) must return a single column, not the whole list.
  • Model.data(index, role) should return the item’s column value for index.column().
  • When editing, return True from setData and emit dataChanged for that index.
  • In parent(), always create the parent index with column 0 so the hierarchy is anchored in column 0 (rows in other columns are siblings of the same item, not separate parents).

A tiny sketch to populate a two-column tree (e.g., Name, Kind) from your DB response:

# db = [("A", ["a","b","c"]), ("B", ["a","b"])]

class Node:
    def __init__(self, cols, parent=None):
        self.cols = list(cols)     # e.g., ["A", "group"]
        self.parent = parent
        self.children = []

root = Node(["Name", "Kind"])
for group, members in db:
    parent = Node([group, "group"], root); root.children.append(parent)
    for m in members:
        parent.children.append(Node([m, "leaf"], parent))

Then make your model delegate to this structure:

  • columnCount(parent): return node.column_count (usually len(node.cols)).
  • data(index, role): return node.cols[index.column()] for DisplayRole/EditRole.
  • rowCount(parent): return node.child_count (and 0 when parent.column() > 0).
  • parent(index): return an index to the item’s parent at column 0.

your current TreeItem.data returns the entire itemData, which blocks multi-column. Change it to index by column. Also ensure setData emits dataChanged.

setupModelData is just the one-time loader that converts your source structure into TreeItem nodes. The stack in the OP tracks the current parent; for two levels, a simple loop (as above) is easier, but the idea is the same: create a node, attach it to the current parent, then attach its children.

Finally I've fount a solution!

(Idea taken from here: http://www.mail-archive.com/pyqt@riverbankcomputing.com/msg19400.html)

class TreeModel(QtCore.QAbstractItemModel):  
        def __init__(self, data, parent=None):  
            super(TreeModel, self).__init__(parent)  
            self.parents=[]  
            self.dbdata = data  
            self.rootItem = TreeItem([u"NameOfColumn"])  
            self.setupModelData(self.dbdata, self.rootItem)  
      
        def setData(self, index, value, role):  
           if index.isValid() and role == QtCore.Qt.EditRole:  
     
               prev_value = self.getValue(index)  
     
               item = index.internalPointer()  
     
               item.setData(unicode(value.toString()))  
     
               return True  
           else:  
               return False  
     
       def removeRows(self, position=0, count=1,  parent=QtCore.QModelIndex()):  
     
           node = self.nodeFromIndex(parent)  
           self.beginRemoveRows(parent, position, position + count - 1)  
           node.childItems.pop(position)  
           self.endRemoveRows()  
     
       def nodeFromIndex(self, index):  
           if index.isValid():  
               return index.internalPointer()  
           else:  
               return self.rootItem  
     
       def getValue(self, index):  
           item = index.internalPointer()  
           return item.data(index.column())  
     
       def columnCount(self, parent):  
           if parent.isValid():  
               return parent.internalPointer().columnCount()  
           else:  
               return self.rootItem.columnCount()  
     
       def data(self, index, role):  
           if not index.isValid():  
               return None  
           if role != QtCore.Qt.DisplayRole:  
               return None  
     
           item = index.internalPointer()  
           return QtCore.QVariant(item.data(index.column()))  
     
      def flags(self, index):  
           if not index.isValid():  
               return QtCore.Qt.NoItemFlags  
     
           return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable  
     
       def headerData(self, section, orientation, role):  
           if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:  
               return QtCore.QVariant(self.rootItem.data(section)[0])  
     
           return None  
     
       def index(self, row, column, parent):  
     
           if row < 0 or column < 0 or row >= self.rowCount(parent) or column >= self.columnCount(parent):  
               return QtCore.QModelIndex()  
     
           if not parent.isValid():  
               parentItem = self.rootItem  
           else:  
               parentItem = parent.internalPointer()  
     
           childItem = parentItem.child(row)  
           if childItem:  
               return self.createIndex(row, column, childItem)  
           else:  
               return QtCore.QModelIndex()  
     
       def parent(self, index):  
           if not index.isValid():  
               return QtCore.QModelIndex()  
     
           childItem = index.internalPointer()  
           parentItem = childItem.parent()  
     
           if parentItem == self.rootItem:  
               return QtCore.QModelIndex()  
     
           return self.createIndex(parentItem.row(), 0, parentItem)  
     
       def rowCount(self, parent):  
           if parent.column() > 0:  
               return 0  
     
           if not parent.isValid():  
               parentItem = self.rootItem  
           else:  
               parentItem = parent.internalPointer()  
    
           return parentItem.childCount()  
     
       def setupModelData(self, lines, parent):  
           ind = []  
           self.parents.append(parent)  
           ind.append(0)  
           col_numb=parent.columnCount()  
           numb = 0  
     
           for line in lines:  
               numb+=1  
               lineData=line[0]  
               self.parents[-1].appendChild(TreeItem(lineData, self.parents[-1]))  
     
               columnData = line[1]  
    
               self.parents.append(self.parents[-1].child(self.parents[-1].childCount() - 1))  
    
               for j in columnData:  
                    self.parents[-1].appendChild(TreeItem(j, self.parents[-1]))  
               if len(self.parents) > 0:  
                    self.parents.pop()  
    
  class TreeItem(object):  
      def __init__(self, data, parent=None):  
          self.parentItem = parent  
          self.itemData = data  
          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 len(self.itemData)  
    
      def data(self, column):  
          try:  
              return self.itemData  
   
          except IndexError:  
              return None  
    
      def parent(self):  
          return self.parentItem  
    
      def row(self):  
          if self.parentItem:  
              return self.parentItem.childItems.index(self)  
    
          return 0  
      def setData(self, data):  
          self.itemData = data

Now it works but I still don't know how to add a support for several columns in this model...

Could someone explain to me how setupModelData works??

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.