Results 1 to 12 of 12

Thread: Tabview update after the model data changes

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Oct 2015
    Posts
    45
    Qt products
    Qt5
    Platforms
    Unix/X11
    Thanks
    8

    Default Re: Tabview update after the model data changes

    The short answer is it turns out to be that I was not getting arraydata to be a list of lists.
    (If I understand correctly, the list should look like: [[col1, col2, col3],[col1, col2, col3],...]
    so each list is the columns of the row.)

    As I often do when I'm confused, I make a minimum version of the code and figure out how to get that working.
    This often helps enlighten me...
    I did and it is shown below.

    I was not able to grok a few things but this does work.

    The docs for rowCount() and columnCount() say they should return 0 if it is a table. (it's not clear to me from the
    docs whether, for a table it should ever return the actual count of rows/columns...) It shows the parent argument
    to be a QModelIndex. What I gleaned is that a parent for trees would have children, where a table cell does not.
    And, my experiments have always resulted in a non-valid parent when the selected cell of the table is sent as this argument.

    I was also never able to get insertRow() to insert a row.
    (Hence this example modifies the arraydata, then emits layoutChanged. Is this a valid or proper approach?)
    The docs indicate that insertRow() calls insertRows() which is virtual. But it is not clear to me from the docs whether
    my implementation of insertRows() MUST do all the actual inserting (is insertRow/s actually inserting a row in the
    model's modelindex and not the model data?)

    The docs also say:
    "If row is 0, the rows are prepended to any existing rows in the parent.

    If row is rowCount(), the rows are appended to any existing rows in the parent."

    So, but what if the current model data is empty? i.e. arraydata = []
    And, this appears to imply that rowCount() could return the actual number of rows...not 0 as noted above.

    It also says: "If parent has no children, a single column with count rows is inserted."
    But the parent ( a QModelIndex) for a table never has children.

    And what does the view actually do when beginInsertRows/endInsertRows are called? Does this allow the view to manage if the
    view needs to scroll or resize? Wouldn't this happen when layoutChanged is emitted anyway?

    Sorry for the long post.

    Qt Code:
    1. import sys
    2.  
    3. from PyQt5 import Qt, QtCore, QtGui, QtWidgets
    4. from PyQt5.QtCore import *
    5. from PyQt5.QtWidgets import *
    6.  
    7. import TableUpdate_ui
    8.  
    9. class TblUpd(QtWidgets.QDialog, TableUpdate_ui.Ui_Dialog):
    10. def __init__(self, parent=None):
    11. QDialog.__init__(self, parent)
    12. self.setupUi(self)
    13. self.pushButton_update.clicked.connect(self.on_update_btn_click)
    14. self.pushButton_add.clicked.connect(self.on_add_btn_click)
    15. self.pushButton_insert.clicked.connect(self.on_insert_btn_click)
    16. self.tabledata = []
    17. # set the table model
    18. self.tablemodel = MyTableModel(self.tabledata, ['Controls'], self)
    19. self.tableView.horizontalHeader().setVisible(True)
    20. self.tableView.setModel(self.tablemodel)
    21. self.tableView.resizeColumnsToContents()
    22. self.tableView.selectRow(0)
    23.  
    24. def on_update_btn_click(self):
    25. print('Update button clicked.')
    26. print('lineEdit.text = {0}'.format(self.lineEdit.text()))
    27. if self.lineEdit.text() == '':
    28. value = 'blank'
    29. else:
    30. value = self.lineEdit.text()
    31. print('value = {0}'.format(value))
    32. selectedindex = self.tableView.currentIndex()
    33. print(self.tablemodel.setData(selectedindex, value,
    34. QtCore.Qt.EditRole))
    35. self.tableView.resizeColumnsToContents()
    36. pass
    37.  
    38. def on_add_btn_click(self):
    39. print('Insert button clicked.')
    40. print('lineEdit.text = {0}'.format(self.lineEdit.text()))
    41. if self.lineEdit.text() == '':
    42. value = 'blank'
    43. else:
    44. value = self.lineEdit.text()
    45. print('value = {0}'.format(value))
    46. self.tablemodel.arraydata.extend([[value]])
    47. self.tablemodel.layoutChanged.emit()
    48. self.tableView.resizeColumnsToContents()
    49.  
    50. def on_insert_btn_click(self):
    51. print('Insert clicked.')
    52. if self.lineEdit.text() == '':
    53. value = 'blank'
    54. else:
    55. value = self.lineEdit.text()
    56. print('value = {0}'.format(value))
    57. selectedindex = self.tableView.currentIndex()
    58. self.tablemodel.arraydata.insert(selectedindex.row(), [value])
    59. self.tablemodel.layoutChanged.emit()
    60. self.tableView.resizeColumnsToContents()
    61. pass
    62.  
    63. class MyTableModel(QtCore.QAbstractTableModel):
    64. def __init__(self, datain, headerdata, parent=None):
    65. """
    66. Args:
    67. datain: a list of lists\n
    68. headerdata: a list of strings
    69. """
    70. QtCore.QAbstractTableModel.__init__(self, parent)
    71. self.arraydata = datain
    72. self.headerdata = headerdata
    73.  
    74. def rowCount(self, parent):
    75. if parent.isValid():
    76. return 0
    77. return len(self.arraydata)
    78.  
    79. def columnCount(self, parent):
    80. if parent.isValid():
    81. return 0
    82. else:
    83. return 1
    84.  
    85. def data(self, index, role):
    86. print('In data()')
    87. if not index.isValid():
    88. print('Invalid index in MyModel>data')
    89. retval = QtCore.QVariant()
    90. elif role != QtCore.Qt.DisplayRole:
    91. retval = QtCore.QVariant()
    92. else:
    93. retval = QtCore.QVariant(self.arraydata[index.row()][index.column()])
    94. print(self.arraydata[index.row()][index.column()])
    95. print(retval)
    96. return retval
    97.  
    98. def setData(self, index, value, role):
    99. if role == QtCore.Qt.EditRole and index.isValid():
    100. print(index.row())
    101. self.arraydata[index.row()] = [value]
    102. print('Return from rowCount: {0}'.format(self.rowCount(index)))
    103. self.dataChanged.emit(index, index, [QtCore.Qt.DisplayRole])
    104. return True
    105. return False
    106.  
    107. def headerData(self, col, orientation, role):
    108. if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
    109. return QtCore.QVariant(self.headerdata[col])
    110. return QtCore.QVariant()
    111.  
    112. def main():
    113. app = QtWidgets.QApplication(sys.argv)
    114. tblupd = TblUpd()
    115. tblupd.show()
    116. sys.exit(app.exec_())
    117.  
    118. if __name__ == '__main__':
    119. main()
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,349
    Qt products
    Qt5
    Platforms
    Windows
    Thanks
    318
    Thanked 872 Times in 859 Posts

    Default Re: Tabview update after the model data changes

    The docs for rowCount() and columnCount() say they should return 0 if it is a table.
    Where did you get that idea? Are you maybe misinterpreting the C++ syntax in the documentation of QAbstractItemModel::columnCount()?

    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0;
    This means that columnCount() is a pure virtual method that has no implementation in this base class ("= 0") and must be overridden and implemented in a derived class.

    A table model is basically a flat (row, column) matrix. For this type of model, if you are asked for the row or column count of an invalid QModelIndex (i.e. the "root" of the model), then you return the actual number of rows or columns in the matrix. For any other valid QModelIndex, you return zero because that implies that you are being asked for the number of child rows or columns of a given cell, which a flat matrix doesn't have. Only tree-structured models can have children within children.

    But it is not clear to me from the docs whether my implementation of insertRows() MUST do all the actual inserting (is insertRow/s actually inserting a row in the model's modelindex and not the model data?)
    For any method that modifies the table model, -you- must implement the code to modify the data array (list of lists) that underlies the model. You need to look at it this way: your list of lists is -your- representation of the data you want to display and modify. MyTableModel is the "glue" that connects your internal model to Qt's world of table views. So everything that the table view does to request data from or modify data in your list of lists goes through the MyTableModel glue. QAbstractTableModel, the base class, stores absolutely nothing. It is merely the definition of the interface that specifies the rules by which views interact with your model and how the model informs the views that its content is or has changed.

    The docs also say:
    "If row is 0, the rows are prepended to any existing rows in the parent.

    If row is rowCount(), the rows are appended to any existing rows in the parent."
    So your implementation has to do that. In the first case, you create a new row with the right number of empty column cells (i.e. a one element list containing a list of columns), append your existing list of lists to it, then assign the result back to your list of lists variable. In the second case, you need to create the same one element list of lists, then stick it on the end of the existing list. For a row in between you need to copy the existing list up to just before the indicated row, stick the new list onto that, then copy the rest of your existing list after that. The "insert" semantics imply "insert before" except when the insertion point is greater or equal to the number of rows.

    And what does the view actually do when beginInsertRows/endInsertRows are called?
    beginInsertRows() (or any of the "begin...() methods) emits a signal that the view listens for. When it gets that signal, it suspends any operations on the model, because the model has just told it it is changing and any model indexes the view has could become invalid or point to something else. endInsertRows() (or any end...() method) emits another signal that says, OK, I'm done making changes, you can now update that part of the view that was affected. modelAboutToBeReset() and modelReset() are the "nuclear option" - they tell the view that everything you know is wrong and that when it is all over you'll have to retrieve everything. These are useful when some external calculation updates the whole data set that the model wraps and there isn't really a way to do a partial update.
    <=== 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.

  3. The following user says thank you to d_stranz for this useful post:

    drmacro (6th April 2017)

  4. #3
    Join Date
    Oct 2015
    Posts
    45
    Qt products
    Qt5
    Platforms
    Unix/X11
    Thanks
    8

    Default Re: Tabview update after the model data changes

    d_stranz, as usual, your explanations are thorough and make it clear how things work!

    Where did you get that idea?
    Your explaination makes it very clear:
    A table model is basically a flat (row, column) matrix. For this type of model,
    if you are asked for the row or column count of an invalid QModelIndex (i.e. the "root" of the model),
    then you return the actual number of rows or columns in the matrix. For any other valid QModelIndex,
    you return zero because that implies that you are being asked for the number of child rows or columns
    of a given cell, which a flat matrix doesn't have. Only tree-structured models can have children within children.
    This from the doc page: http://doc.qt.io/qt-4.8/https://doc.qt.io/qt-5/qabstractitemmodel.html#rowCount does not:
    Note: When implementing a table based model, rowCount() should return 0 when the parent is valid.

    In fact, the rest of that doc on rowCount() never mentions tree. After your explanation, it's clear what to do with a tree and why (it's the why that I find missing
    often when reading doc...but, might be my issue.)

    I'm slowly learning how to extrapolate what the QT docs say and what they imply.
    For me, in many cases, it's sort of:
    Me:"How do you make bread?"
    Docs: "You use yeast and flower."
    Me: "And?..."

  5. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,349
    Qt products
    Qt5
    Platforms
    Windows
    Thanks
    318
    Thanked 872 Times in 859 Posts

    Default Re: Tabview update after the model data changes

    For me, in many cases, it's sort of:
    Me:"How do you make bread?"
    Docs: "You use yeast and flower."
    Me: "And?..."
    Funny language, English... most bakers would use "flour" when making bread, but I am always interested in hearing new recipes.

    Note: When implementing a table based model, rowCount() should return 0 when the parent is valid.
    As I said in my last post.

    I agree, sometimes the only thing that makes sense in the Qt documentation are the tutorials and examples. The class documentation often leaves out important details that you don't understand until you look at an example. If you haven't looked at the Qt tutorials and examples, you should. Even if you aren't a C++ programmer, the language is similar enough to Python that I think you can follow them.
    <=== 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. Replies: 3
    Last Post: 28th January 2015, 00:09
  2. Replies: 4
    Last Post: 21st October 2013, 21:24
  3. Replies: 9
    Last Post: 14th February 2013, 19:39
  4. Replies: 0
    Last Post: 2nd November 2011, 11:13
  5. Replies: 1
    Last Post: 24th February 2011, 05:54

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
  •  
Qt is a trademark of The Qt Company.