Results 1 to 5 of 5

Thread: Using model with different views

  1. #1
    Join Date
    Apr 2017
    Posts
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Using model with different views

    I've been working with Qt for a few months now and am still having some trouble with understanding the model view architecture. I have some data structure of the following form (I use PyQt5):

    Qt Code:
    1. class MyData:
    2. def __init__(self):
    3. self.name = "Item 1"
    4. self.value = "Value 1"
    5.  
    6. # List of lists
    7. self.attributes = [
    8. ["Attr 1", "100", "560", "300", "1200"]
    9. ,["Attr 2", "50", "40", "470", "20"]
    10. ,["Attr 3", "75", "30", "30", "100"]
    11. ,["Attr 4", "300", "300", "280", "400"]
    12. ]
    To copy to clipboard, switch view to plain text mode 

    I want to display this data in two different views. (read only) The 'name' and 'value' attributes in one view ( 2 columns ). And the list of attributes in another view ( 5 Columns ) when whichever item in the first view is selected. I threw together a quick representation of what I want in Qt Designer:

    form.png

    Ideally I would like to use a single model (QAbstractItemModel, or maybe even just QAbstractTableModel?) I believe a main model and two Proxy models are probably the way to do this? With the proxy models having their own reimplementation of headerData(). But how does that work then with columnCount()? Do I have to reimplement that as well in each proxy? Here is somewhat pseudocode of what my current main model looks like:

    Qt Code:
    1. class MyModel(QAbstractItemModel):
    2. def __init__(self, parent=None)
    3. super(MyModel, self).__init__(parent)
    4. self.myData = [] # List of MyData() instances
    5.  
    6. def columnCount(self, parent):
    7. # Do I have to check which view is requesting the column count?
    8.  
    9. if firstView:
    10. return 2
    11. else:
    12. return 5
    13.  
    14. def rowCount(self, parent):
    15. if firstView:
    16. return len(self.myData)
    17. else:
    18. return len(self.myData[parent.row()].attributes) # ??
    19.  
    20. def flags(self, index):
    21. return Qt.ItemIsEnabled | Qt.ItemIsSelectable
    22.  
    23. def headerData(self, section, orientation, role):
    24. pass # Reimplemented in each proxy model?
    25.  
    26. def parent(index):
    27. # I have no idea how this needs to be implemented since the data in each view is essentially a table, but I still probably need some sort of parent index
    28. return QModelIndex()
    29.  
    30. def index(row, column, parent):
    31. return createIndex(row, column, self.myData[row]) # Probably just use internalPointer() in data()?
    32.  
    33. def data(self, index, role):
    34. # Again, I'm not sure if I implement this in the main model or each of the proxy models?
    35.  
    36. if not index.isValid():
    37. return None
    38.  
    39. data = index.internalPointer()
    40. if role == Qt.DisplayRole:
    41. if index.column() == 0:
    42. return ...
    To copy to clipboard, switch view to plain text mode 

    def insertRow(MyData)
    # I would implement some insert method to insert into self.myData

    I would then connect the selectionChanged signal for the first view to a slot that would reset the proxy model for the second view? And probably have to do setRootIndex()? Again I'm a bit lost on how to go about doing this and would love to see a working example with the above idea. The actual modeling of the structure when there is a list that needs to be displayed in a different view is what's really confusing me.

    Feel free to respond in C++ or PyQt. I can understand both. Any advice would be appreciated. Thanks.

  2. #2
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,709
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Using model with different views

    I believe a main model and two Proxy models are probably the way to do this?
    Depends.
    Proxy models are used when you want to show the data in some "digested" form, sorted or filtered mostly.
    If you want to show the data as is (you could show just part of the data in each view) then you can use only one model.
    From your post I could not understand if you are doing anything to the data that you show in both views or not.

    Are the "Item" and "Value" pair part of the attributes data that is shown in the lower table?
    Or is it more like a parent child relationship? (where Item+value are parent to a an attributes set?
    This would then be probably best done as a QAbstractItemModel, or depending on your constraints with a QStandardItemModel (would make it easier for you if you could use the later).
    If that is the case you could indeed use only one model and show the "parents" in the upper view and the "children" of the selected part in the lower view.
    But these were guesses on my part, not sure this is what you actually are after here.
    Last edited by high_flyer; 24th April 2017 at 15:08.
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  3. #3
    Join Date
    Apr 2017
    Posts
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Using model with different views

    If you look at the data structure I have at the top I've used the same data that is displayed for the first row in the picture example. I will have instances of that and each instance represents a row in the top view (In the picture example, 3 instances that would be stored in the self.myData list within the model. (First row is selected in the view). When you select a row from the top view, the bottom view should be filled with that particular selected row's/instance self.attributes list.
    Last edited by Wallabee19; 25th April 2017 at 01:01.

  4. #4
    Join Date
    Apr 2017
    Posts
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Using model with different views

    I've come up with a solution using a QIdentityProxyModel for the bottom view and just using the main model for the top view. I connect the currentRowChanged() signal from the top views selection model to a slot that simply does the following:

    Qt Code:
    1. def updateProxyModel(current, previous)
    2. ProxyModel.beginResetModel()
    3. ProxyModel.selectedRow = current.row()
    4. ProxyModel.endResetModel()
    To copy to clipboard, switch view to plain text mode 

    I did have to reimplement index() as the base implementation of QAbstractTableModel calls hasIndex() which checks the rowCount() and columnCount() of only the main model, and would never generate indexes above 2 columns for the proxy model. But this was ok, because I like to use internalPointer() anyway to save on a little bit of typing in the data() methods

    Main model:

    Qt Code:
    1. class MainModel(QAbstractTableModel):
    2. def __init__(self, data, parent=None):
    3. super(MainModel, self).__init__(parent)
    4. self.headers = ('Name', 'Value')
    5. self.data = data
    6.  
    7. def columnCount(self, parent=QModelIndex()):
    8. return len(self.headers)
    9.  
    10. def rowCount(self, parent=QModelIndex()):
    11. return len(self.data)
    12.  
    13. def index(self, row, col, parent=None):
    14. return self.createIndex(row, col, self.data[row])
    15.  
    16. def flags(self, index):
    17. return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemNeverHasChildren
    18.  
    19. def headerData(self, column, orientation, role):
    20. if orientation == Qt.Horizontal and role == Qt.DisplayRole:
    21. return self.headers[column]
    22.  
    23. def data(self, index, role=Qt.DisplayRole):
    24. if not index.isValid():
    25. return None
    26.  
    27. rowData = index.internalPointer()
    28. col = index.column()
    29.  
    30. if role == Qt.DisplayRole:
    31. if col == 0:
    32. return rowData.name
    33. if col == 1:
    34. return rowData.value
    35.  
    36. return None
    To copy to clipboard, switch view to plain text mode 

    Proxy Model:

    Qt Code:
    1. class ProxyModel(QIdentityProxyModel):
    2. def __init__(self, data, parent=None):
    3. super(ProxyModel, self).__init__(parent)
    4. self.data = data
    5. self.headers = ('Attribute', 'Width', 'Height', 'Pos X', 'Pos Y')
    6. self.selectedRow = None
    7.  
    8. def headerData(self, column, orientation, role=None):
    9. if orientation == Qt.Horizontal and role == Qt.DisplayRole:
    10. return self.headers[column]
    11.  
    12. def columnCount(self, parent=QModelIndex()):
    13. if parent.isValid():
    14. return 0
    15.  
    16. return len(self.headers)
    17.  
    18. def rowCount(self, parent=QModelIndex()):
    19. if parent.isValid() or not self.selectedRow:
    20. return 0
    21.  
    22. return len(self.data[self.selectedRow].attributes)
    23.  
    24. def data(self, index, role=Qt.DisplayRole):
    25. if not index.isValid() or not self.selectedRow:
    26. return None
    27.  
    28. col = index.column()
    29. row = index.row()
    30.  
    31. if role == Qt.DisplayRole:
    32. return self.data[self.selectedRow].attributes[row][col]
    33.  
    34. return None
    To copy to clipboard, switch view to plain text mode 

    Decent solution? What could be done better or differently?
    Last edited by Wallabee19; 25th April 2017 at 10:31.

  5. #5
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,709
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Using model with different views

    If you look at the data structure I have at the top I've used the same data that is displayed for the first row in the picture example
    I personally would have used a tree for that (parent child relationship).
    This would allow you to use a tree view as well, or some other hierarchical view.
    Or use a combox and a table.
    When you do that you can give the index of the item selected in the upper view as the root index for the lower view freeing you from the need to use proxies for separating the data.
    But at the end there is no right or wrong, but what best fits your needs.
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

Similar Threads

  1. Same model different views QTableView
    By Jarrod in forum Newbie
    Replies: 1
    Last Post: 12th August 2016, 05:49
  2. Model with two very different views
    By mholmes in forum Qt Programming
    Replies: 3
    Last Post: 7th April 2010, 23:19
  3. how to disable views from a model
    By zeeeend in forum Qt Programming
    Replies: 3
    Last Post: 9th September 2008, 21:14
  4. 2 Views 1 Model
    By PrimeCP in forum Qt Programming
    Replies: 3
    Last Post: 2nd October 2007, 01:40
  5. Model/Views Woes
    By Scorp1us in forum Qt Programming
    Replies: 3
    Last Post: 8th February 2007, 03:10

Tags for this Thread

Bookmarks

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.