I'm building an application for scientific analyses. The underlying data is with Objects with relations to each other. One example is the "Experiment" class. This class has the following structure (almost, but with slightly different names):

|-- Name (string)
|-- Researcher (string)
|-- Date (date)
|-- Solvents (list of 'Solvent' objects)
|-- Name (string)
|-- CAS (string)
|-- MVol (float)
|-- Spheres (list of 'Sphere' objects)
|-- Name (string)
|-- Center (float)
|-- Radius (float)

What I would like to do is to create a very general QAbstractItemModel, which contains all the experiments existing in the database.
I then want to be able to use this QAbstractItemModel, together with ProxyModels in order to display a given experiment with various different views.

For example, I want the Name, Researcher and Date to be mapped to a simple form widget, such that the user can change these values. The "Spheres" is a list of spheres that I want to present in a "ListView" and the "Solvents" I want to present in a "TableView".

Selection of experiments
The user will in general press a button, which provides them with a ListView of all possible experiments, and can then select the one to open.
When the user "opens" an experiment, a TableView, ListView and Form widget is updated with the data belonging to this experiment (as per above). Some various operations will then be possible for the user.

The AbstractItemModel
I am trying to create the AbstractItemModel which is going to be the top level model for all the experiments, but I am having trouble understanding how to implement it. One of the problems I have is that the "Solvents" are NOT belonging to a given experiment, but instead shared amongst many experiments (one Solvent may be shared amongst many experiments). So I am unsure of how to implement the "parent" function for the model, given that I cannot know who is the parent for a given Solvent (since the Solvent doesn't really have a parent).

Testing with TreeView
I was trying to test with a TreeView, to see if I could generate a list of all the Experiments, and the "unfold" them to view the "Spheres" and "Solvents" associated with each Experiment, but to no avail...

Here is my first try. Note that I just implemented the parent function as returning an invalid QModelIndex. I'm unsure how to proceed, or if I have started going down a wrong path.

Qt Code:
  1. class ExperimentsModelTest(QAbstractItemModel):
  2. colName = {
  3. 'Experiments': ['Name','Researcher','Date','Solvents','Spheres'],
  4. 'Solvents': ['Name','dD','dH','dP','MVol','Score'],
  5. 'Spheres': ['Name','dD','dH','dP','R']
  6. }
  8. def __init__(self,parent=None):
  9. QAbstractItemModel.__init__(self,parent=None)
  10. self.Experiments = DM.LoadExperiments()
  12. def colIndex(self,level,colName):
  13. return self.colName[level].index(colName)
  16. def headerData(self,section,orientation,role = Qt.DisplayRole):
  17. if role == Qt.DisplayRole:
  18. if orientation == Qt.Horizontal:
  19. return self.colName['Experiments'][section]
  21. return QtCore.QVariant()
  23. def index(self,row,col,parent=QModelIndex()):
  24. if not parent.isValid(): # Top level experiment
  25. return self.createIndex(row,col,self.Experiments[row])
  27. Experiment = parent.internalPointer()
  29. if parent.column() == self.colIndex('Experiments','Solvents'):
  30. return self.createIndex(row,col,Experiment['Solvents'][row])
  31. elif parent.column() == self.colIndex('Experiments','Sphers'):
  32. return self.createIndex(row,col,Experiment['Spheres'][row])
  34. def parent(self,index):
  35. return QModelIndex()
  37. def rowCount(self,parent = QModelIndex()):
  38. if not parent.isValid():
  39. return len(self.Experiments)
  41. elif parent.column() == self.colIndex('Experiments','Solvents'):
  42. return len(self.Experiments[parent.row()]['Solvents'])
  44. elif parent.column() == self.colIndex('Experiments','Spheres'):
  45. return len(self.Experiments[parent.row()]['Spheres'])
  47. return 0
  49. def columnCount(self,parent = QModelIndex()):
  50. if not parent.isValid():
  51. return len(self.colName['Experiments'])
  53. elif parent.column() == self.colIndex('Experiments','Solvents'):
  54. return len(self.colName['Solvents'])
  56. elif parent.column() == self.colIndex('Experiments','Sphers'):
  57. return len(self.colName['Spheres'])
  59. return 0
  61. def data(self,index,role=Qt.DisplayRole):
  62. if role == Qt.DisplayRole:
  63. row, col = index.row(), index.column()
  64. if not index.parent().isValid(): # Then it is an experiment
  65. colName = self.colName['Experiments'][col]
  66. return self.Experiments[row][colName]
  68. else:
  69. pCol, pRow = index.parent().column(), index.parent().row()
  71. # If we are looking at a solvent
  72. if pCol == self.colIndex('Experiments','Solvents'):
  73. colName = self.colName['Solvents'][col]
  74. return self.Experiments[pRow]['Solvents'][row][colName]
  76. # If we are looking at a sphere
  77. elif pCol == self.colIndex('Experiments','Spheres'):
  78. colName = self.colName['Spheres'][col]
  79. return self.Experiments[pRow]['Spheres'][row][colName]
  81. return QtCore.QVariant()
To copy to clipboard, switch view to plain text mode