How do you update QAbstructListModel item from outside a view?
-
Greetings.
Suppose I have the following struct: (Pseudo code, please ignore syntax errors)
struct: { QString: Stock Name, double: Stock Value }
And I have a QAbstructListModel subclass that has a QList of those structs as the underlying model.
Now let's say another part of my program connected to the internet and saw that "Stock A" has dropped 4 points and I want my model to reflect this, how should it be done?
The
SetData
function in the QAbstructListModel subclass is expected to be used from a view and it expects a QModelIndex. And even if it didn't, There's no easy way to find Stock A inside the QAbstructListModel subclass.Should I search for "Stock A" struct in the model and then replace it with a new struct with new data and then call
dataChanged
signal?Or maybe I should also hold a pointer to 'Stock A' struct outside the QAbstructListModel and then modify it directly. And in make it emit a signal when it's changed. (Through properties) and connect this signal to the
dataChanged
signal inside the subclass of QAbstructListModel. This way I will be able to modify 'Stock A' from the pointer.Are those ways correct? Are there better ways?
-
@Curtwagner1984 said in How do you update QAbstructListModel item from outside a view?:
The SetData function in the QAbstructListModel subclass is expected to be used from a view
Not particularly.
QAbstractListModel::setData()
is a method of the model which can be called from anywhere.Should I search for "Stock A" struct in the model and then replace it with a new struct with new data and then call dataChanged signal?
Yes (you can just alter the
double
member without needing to create a newstruct
).and it expects a QModelIndex. And even if it didn't, There's no easy way to find Stock A inside the QAbstructListModel subclass.
You need a
QModelIndex
if you want to access/update via the model'sdata
/setData()
. You can update the data directly in your backing data store without going via an index if you wish, but you will still need theQModelndex
to issue thedataChanged()
signal.One way or another you will need to know/find the
QModelIndex
for a given Stock Name. You might search sequentially if unordered, do a binary search if sorted alphabetically, or use aQMap
for the Stock Names to map them to indexes. -
So to summarize:
- Find
QModelIndex
of the item in question. - Call
setData()
with the QModelIndex and data you want to change. (Possibly only change he wanted data by specifing a role in the function) setData
should emitdatachanged
signal with the given QModelIndex
One question though? How do I get a QModelIndex of something?
Let's say I have a
QMap<QString,StockStruct> stocks
in the submclass of QAbstructListmModel.
How do I find the QModelIndex ofstocks["Stock A"]
?Do I need to have a list of
QList<StockStruct>
and thenQMap<QString,int>
And for each time I insert something into the model:void AddData(StockStruct stockStruct){ this->_stockList.append(stockStruct); this->_stockNameMap.insert(stockStruct.name,this._stockList.indexOf(stockStruct)); }
?
- Find
-
@Curtwagner1984
Kind of, yes.First be clear: a
QModelIndex
is (from your point of view) just arow
number (andcolumn
number within that row, but that's not relevant here). You need to be able to map that to/from your own backing data structure.Simplest is just to maintain your backing "array" in the same order as the row numbers in the Qt model, then they agree on row number.
-
Just how many stocks are you maintaining? A sequential search for name is fine if you have only a "handful" of stocks, then nothing else needs doing.
-
If you have a fair number, and you are frequently needing to look up a stock by its Stock Name, if you are prepared to sort your backing data alphabetically by Stock Name you can look that up quickly with a binary search of your array. But then you cannot just
append()
a new entry inAddData()
, you mustinsert()
in sorted order (or re-sort after any additions). -
Or you can do it like you show. Maintaining one list/array indexed by/in row number order, and another structure (e.g. a
QMap
) for fast look up by key.
Effectively a database with a primary key does the latter. It will not actually maintain/store the rows in PK order, as that would involve moving records around in storage on insert/delete/update PK, so it maintains an index into the data, like a
QMap
or aQHash
, just to allow fast look up. -
-
Thank you, this helped me a lot, I've managed to successfully test this and it worked!
I ended up doing a QList, QMap pair, not because I have a lot of rows, but because my data changes very frequently so quick lookups would be very helpful.
I've run into another problem on the QML side though... (Maybe it warrants a separate question)
I can manage to successfully update the UI when data changes using the
dataChanged
signal.However, on the QML side of things I've ran into a bit of a snag. Let's say I want to do several things when a stock drops below a certain threshold. And this state is a bool variable in the data struct let's say
bIsStockBelowThreashold
.So when this boolean changes I want to change the background color, set the text in different font and maybe play an animation.
I'm a bit lost as to how I can tie those changes to the change in the data. For example if the
role
name for this data is 'IsStockBelowThreashold' I can write in the background area of my qml:Rect:{ id:backgroundRect color: IsStockBelowThreashold == true ? "red":"green" }
And then I can do the same in the Text object:
Text:{ id:panicText font.pixelSize: IsStockBelowThreashold == true ? 28:24 }
Meaning that in each area I want to react to "IsStockBelowThreashold" changing, I need to check them individually. I much rather write a single function that doing all those changes and trigger it on IsStockBelowThreashold changed. But I have no idea how to do that. Is that even possible?
(I'm sorry if my explanation a bit all over the place).
-
Ok, so I figured it out, you make a property and bind the role to this property:
property var stockPanic: IsStockBelowThreashold
And then you can do:
onStockPanicChanged:{ functionThatChangesWhateverYouWant(); }