What to do if I can't make a call to `beginInsertRows()` before data is changed?
-
wrote 22 days ago last edited by peter.thompson
I am implementing
QAbstractTableModel
with a pointer to some shared data. This data might be changed by theQAbstractTableModel
that I am currently writing, but it might also be changed by other parts of the application. How do I handle when records are inserted into the data by something other than theQAbstractTableModel
? If the model does the insertion, I can control it and make sure it first callsbeginInsertRows()
, then inserts the data, and finally callsendInsertRows()
(which appears to be the proper method). However, if something else inserts an item, all I will get is a signal after the insertion. So it is impossible to callbeginInsertRows()
before the change has occurred.Qt's documentation seems to indicate that this method must be called before the change occurs. And as far as I can tell, there is no alternative. From the docs for QAbstractItemModel::beginInsertRows:
Begins a row insertion operation.
When reimplementing
insertRows()
in a subclass, you must call this function before inserting data into the model's underlying data store.As a last resort, I could try to apply some changes to the class that holds the data on the back end, so that it would emit a signal before a change. However, this is a complicated class that is used throughout the code base, so making this change could be risky and would likely add a lot of development time.
I have this same question about
beginRemoveRows()
,beginMoveRows()
, andbeginResetModel()
. It would be fantastic if there were a general solution for responding to any of these operations after the fact. -
wrote 22 days ago last edited by KH-219Design
@peter.thompson You may be able to leverage the following fact into a workaround for your situation.
I can state from experience that calling these one after the other ("on the same line", as it were) will trigger the signal you probably want/need to have emitted from the (I believe it is
modelUpdated
, but don't quote me 😉)beginResetModel(); endResetModel();
(of course they don't literally need to be on one line. I'm just emphasizing that you can call them back-to-back, without having changes to the data happen in between, and the result will be that the model "updates" at this time, as far as other watcher objects are concerned)
beginResetModel(); endResetModel();
... is also fine.
I have done this in several applications, and at the time I also looked inside the Qt source code to convince myself that it indeed does do what I intend. My use case was not the case that you describe. Rather, I simply had a "complex", many-nested-if-statement logic for selecting which rows were updated in any given case, and I didn't want to add more complexity trying to meticulously piece together what arguments to pass to
dataChanged
, so I found I could just do this "hack" at the appropriate point, to signal to the relevant QML to repaint "the whole model" (the visual UI representation thereof, more accurately). -
I am implementing
QAbstractTableModel
with a pointer to some shared data. This data might be changed by theQAbstractTableModel
that I am currently writing, but it might also be changed by other parts of the application. How do I handle when records are inserted into the data by something other than theQAbstractTableModel
? If the model does the insertion, I can control it and make sure it first callsbeginInsertRows()
, then inserts the data, and finally callsendInsertRows()
(which appears to be the proper method). However, if something else inserts an item, all I will get is a signal after the insertion. So it is impossible to callbeginInsertRows()
before the change has occurred.Qt's documentation seems to indicate that this method must be called before the change occurs. And as far as I can tell, there is no alternative. From the docs for QAbstractItemModel::beginInsertRows:
Begins a row insertion operation.
When reimplementing
insertRows()
in a subclass, you must call this function before inserting data into the model's underlying data store.As a last resort, I could try to apply some changes to the class that holds the data on the back end, so that it would emit a signal before a change. However, this is a complicated class that is used throughout the code base, so making this change could be risky and would likely add a lot of development time.
I have this same question about
beginRemoveRows()
,beginMoveRows()
, andbeginResetModel()
. It would be fantastic if there were a general solution for responding to any of these operations after the fact.@peter.thompson said in What to do if I can't make a call to `beginInsertRows()` before data is changed?:
apply some changes to the class that holds the data
Here's the architectural flaw. The
QAbstractTableModel
is supposed to be the sole data holder. It is supposed to be modified with thesetData()
override or individually implemented data setter. But it is not supposed to be modified outside the model. -
@peter.thompson said in What to do if I can't make a call to `beginInsertRows()` before data is changed?:
apply some changes to the class that holds the data
Here's the architectural flaw. The
QAbstractTableModel
is supposed to be the sole data holder. It is supposed to be modified with thesetData()
override or individually implemented data setter. But it is not supposed to be modified outside the model.wrote 22 days ago last edited by peter.thompson@Axel-Spoerl Thanks for your response. Is there any way to make
QAbstractTableModel
work with shared data, where it can't be the sole data holder?I ask because the architecture of our data isn't something I am able to change. (This is for a large, established codebase.) I am looking into adding a
QAbstractTableModel
as an interface to our existing data, to make it work with Qt's views. But if that's not the intended use ofQAbstractTableModel
, and if there's no way to make that work, then I might look into a different solution, one that doesn't use Qt's model/view framework. -
This is a very general discussion. If you want to present data in a UI, the UI needs to be informed about data changes, in order to reflect them correctly. The Qt implementation of this concept is model/view. Data changes of any kind have to be handled as describe [here](link url) to make it work. Other frameworks may provide other implementations. That won't remove the need to notify the UI about every data change.
As long as the "class holding the data" can ensure, that every change is announced to the model as described above, the model/view implementation will work. IMHO handling this after the fact is just more complicated to implement and more error prone. Again, this is independent from the framework used for the UI.
Maybe you want to explain in more detail, what your data holding class looks like, where the data comes from and how it is modified.
-
wrote 22 days ago last edited by KH-219Design
@peter.thompson You may be able to leverage the following fact into a workaround for your situation.
I can state from experience that calling these one after the other ("on the same line", as it were) will trigger the signal you probably want/need to have emitted from the (I believe it is
modelUpdated
, but don't quote me 😉)beginResetModel(); endResetModel();
(of course they don't literally need to be on one line. I'm just emphasizing that you can call them back-to-back, without having changes to the data happen in between, and the result will be that the model "updates" at this time, as far as other watcher objects are concerned)
beginResetModel(); endResetModel();
... is also fine.
I have done this in several applications, and at the time I also looked inside the Qt source code to convince myself that it indeed does do what I intend. My use case was not the case that you describe. Rather, I simply had a "complex", many-nested-if-statement logic for selecting which rows were updated in any given case, and I didn't want to add more complexity trying to meticulously piece together what arguments to pass to
dataChanged
, so I found I could just do this "hack" at the appropriate point, to signal to the relevant QML to repaint "the whole model" (the visual UI representation thereof, more accurately). -
@peter.thompson You may be able to leverage the following fact into a workaround for your situation.
I can state from experience that calling these one after the other ("on the same line", as it were) will trigger the signal you probably want/need to have emitted from the (I believe it is
modelUpdated
, but don't quote me 😉)beginResetModel(); endResetModel();
(of course they don't literally need to be on one line. I'm just emphasizing that you can call them back-to-back, without having changes to the data happen in between, and the result will be that the model "updates" at this time, as far as other watcher objects are concerned)
beginResetModel(); endResetModel();
... is also fine.
I have done this in several applications, and at the time I also looked inside the Qt source code to convince myself that it indeed does do what I intend. My use case was not the case that you describe. Rather, I simply had a "complex", many-nested-if-statement logic for selecting which rows were updated in any given case, and I didn't want to add more complexity trying to meticulously piece together what arguments to pass to
dataChanged
, so I found I could just do this "hack" at the appropriate point, to signal to the relevant QML to repaint "the whole model" (the visual UI representation thereof, more accurately).wrote 18 days ago last edited by@KH-219Design Thanks for your response. I've tried this and it appears to work everywhere that I need it to, so I am planning to move forward with your solution.
I should note, there does appear to be one limitation with this solution: calling
beginResetModel()
andendResetModel()
not only updates the model/view but also deselects all items. I would guess that this is because resetting the model also resets the selection model. This should be fine in my use case, but just wanted to note it for anyone else who might read this. -
6/6