I have found that it is best to -not- try to convert a hierarchical data structure directly into a hierarchical QAbstractItemModel (if I interpret your statement "making the Classroom derive from QAbstractItemModel" correctly).
Instead derive a "ClassroomModel" (say) from QAbstractItemModel and store a pointer to your Classroom singleton in it as a data member. Then, you can devise a way to store something in each QModelIndex that uniquely identifies where that index is in the hierarchy. It could be an integer that represents where that node is in a traversal through the hierarchy (0 is the Classroom node, 1 is the first Group node, 2 is the first Person node in the first Group, 3 is the second Person node in that Group, etc.) It could be a void pointer to the Group or Person node. Anything that lets you map from a QModelIndex into your actual data structure.
In any case, I think you will find the need for a back-pointer from Person to Group or "groupId" members in Person and Group so you can tell which Group a Person belongs to. You will need this in order to implement the model's parent() method.
The point of roles is so that a view can interrogate the model about how to customize the display of each member of the model. The only one you truly need to implement is the DisplayRole, since that provides the string that is used to display that item in the view. But there are many other roles which can be used to customize the appearance and which the view will ask for as it draws each item. You could have icons that are different for Classroom, Group, and Person nodes, different text or background colors, something other than the default left alignments, etc.
Identity proxies are an interesting way to display the same base model in different ways in different views by using a different identity model for each view. It is in the identity model where the "decorations" get added (for icon, color, alignment, etc. roles) and the base model simply satisfies the DisplayRole.
Bookmarks