Results 1 to 2 of 2

Thread: QAbstractItemModel with shared children (PyQt5, Python)

  1. #1

    Default QAbstractItemModel with shared children (PyQt5, Python)


    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 

  2. #2
    Join Date
    Jan 2008
    Alameda, CA, USA
    Thanked 607 Times in 597 Posts
    Qt products
    Unix/X11 Windows Android

    Default Re: QAbstractItemModel with shared children (PyQt5, Python)

    I am not good enough at python to completely understand what you are doing, but what I think your code is doing is trying to create a tree that nests under columns other than column 0. QAbstractItemModel allows this, but QTreeView does not. Tree views only support nesting under the 0-th column of each row. I am also not sure if you are doing the right thing in the index() method. "row" is relative to the parent index (that is, row = n is the nth child of the parent), whereas "col" is relative to the columns in "row". In addition, you aren't returning an invalid QModelIndex for conditions where either "row" or "col" are not valid. With python's dynamic typing, I don't know what is being returned if the methods falls off the bottom. For example, for "Researcher" and the other single fields in your tree model, the row count is zero for those indices since they have no children. (Assuming you have drawn your tree schematically the way you intend for it to appear in the tree view).

    In addition, you must implement parent() correctly. By returning a null index, I don't think your code will work beyond displaying the topmost entry.

    Check out the Qt Simple Tree Model example. It's in C++, but mapping that to python should be straightforward.
    Last edited by d_stranz; 4th July 2018 at 01:51.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

Similar Threads

  1. Python / PyQt5 MDI Window focus problem
    By apereira in forum Newbie
    Replies: 5
    Last Post: 22nd September 2015, 00:29
  2. Access to shared map between parent - children
    By atomic in forum Qt Programming
    Replies: 5
    Last Post: 10th August 2015, 23:24
  3. Replies: 0
    Last Post: 26th July 2015, 05:45
  4. Embedding PyQt4 into an Application running Python via Boost::Python
    By GreyHound in forum Installation and Deployment
    Replies: 1
    Last Post: 6th February 2012, 07:48
  5. Replies: 0
    Last Post: 8th April 2011, 21:10

Tags for this Thread


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.