How to modify the data in the model and update the view when new data is received from external?
-
@JonB: That is the problem i am facing. I do not know how to call setData which in turn emits the signal dataChanged. I do not know how to get the indexes. Here the data is only coming from the "Info_" variable. If the it was via delegates or adapters i could easily get the indexes and call setData. But in my case i get in a structure and go through each item and update its value. Could you please show me how can i achieve this in my code i.e. change the value via setData?
@VInay123
Your code is way too big for me to wade through.-
If you are using
QAbstractModel, you are using a model which holds data in rows & columns, right? -
When you put some data (received from JSON parsing or whatever), you have to decide where in the model's row/column you want to put this data, right?
-
Given that
row&column, you can construct aQModelIndex()(http://doc.qt.io/qt-5/qabstractitemmodel.html#createIndex) for the data. -
Then you can pass that to your
Model::setData()to set the value, andemit dataChanged(index, index)to notify your view to update.
-
-
@VInay123
Your code is way too big for me to wade through.-
If you are using
QAbstractModel, you are using a model which holds data in rows & columns, right? -
When you put some data (received from JSON parsing or whatever), you have to decide where in the model's row/column you want to put this data, right?
-
Given that
row&column, you can construct aQModelIndex()(http://doc.qt.io/qt-5/qabstractitemmodel.html#createIndex) for the data. -
Then you can pass that to your
Model::setData()to set the value, andemit dataChanged(index, index)to notify your view to update.
@JonB : Thank you. I implemented as following in my model:
QModelIndex index = createIndex(1, 1, categories_[0]->entryAt(0)); this->setData(index, 11, Qt::EditRole);It is changing the value clearly. But in the view its not being displayed.
-
-
@JonB : Thank you. I implemented as following in my model:
QModelIndex index = createIndex(1, 1, categories_[0]->entryAt(0)); this->setData(index, 11, Qt::EditRole);It is changing the value clearly. But in the view its not being displayed.
@VInay123
Well at least we're getting somewhere! Check the return result of yoursetData(), maybe it's failing? Does (row, column) of (1, 1) exist in your model (else you need toinsertRows()etc.)? Is thatsetData()call indeed going into yourModel::setData()and doing theemit dataChanged()there (use a debugger or debug/print statements)? There's no point using bothbeginResetModel()&dataChanged(), and certainly notdataChanged()while still inside thebeginResetModel()before thenedRestModel(). Change your code accordingly. -
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
CategoryItem& parentItem = currentItem->parentItem();
return createIndex(currentItem->row(), 0, &parentItem);This is using the address of a temp item, it will not work.
QString data = file.readAll();
does not handle win/lunix line endings and encodings, use QString data = QTextStream(&file).readAll();
Use the model test to make sure your subclass works correctly
-
@VInay123
Well at least we're getting somewhere! Check the return result of yoursetData(), maybe it's failing? Does (row, column) of (1, 1) exist in your model (else you need toinsertRows()etc.)? Is thatsetData()call indeed going into yourModel::setData()and doing theemit dataChanged()there (use a debugger or debug/print statements)? There's no point using bothbeginResetModel()&dataChanged(), and certainly notdataChanged()while still inside thebeginResetModel()before thenedRestModel(). Change your code accordingly.@JonB: Yes, I just call the setData ane here i have printed the changed:
if (role == Qt::EditRole && index.column() == 1 && index.isValid()) { auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); if (currentItem) { currentItem->setValue(value); qDebug() << currentItem->data(index.column()); emit dataChanged(index, index); return true; } }The value is being changed here. The output looks like
QVariant(int, 11)I have included a debug output in Data() but this is printing the default values that is empty("").
QVariantModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const { if (!index.isValid()) { return QVariant(); } if (role == Qt::DisplayRole && isCategory(index)) { const auto currentCategory = static_cast<CategoryItem*>(index.internalPointer()); if (index.column() == 0) { return currentCategory->displayName(); } else { return QVariant(); } } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); if (role == Qt::DisplayRole && !isCategory(index)) { qDebug() << currentItem->data(index.column()); // Here its empty return currentItem->data(index.column()); } return QVariant(); } -
@JonB: Yes, I just call the setData ane here i have printed the changed:
if (role == Qt::EditRole && index.column() == 1 && index.isValid()) { auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); if (currentItem) { currentItem->setValue(value); qDebug() << currentItem->data(index.column()); emit dataChanged(index, index); return true; } }The value is being changed here. The output looks like
QVariant(int, 11)I have included a debug output in Data() but this is printing the default values that is empty("").
QVariantModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const { if (!index.isValid()) { return QVariant(); } if (role == Qt::DisplayRole && isCategory(index)) { const auto currentCategory = static_cast<CategoryItem*>(index.internalPointer()); if (index.column() == 0) { return currentCategory->displayName(); } else { return QVariant(); } } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); if (role == Qt::DisplayRole && !isCategory(index)) { qDebug() << currentItem->data(index.column()); // Here its empty return currentItem->data(index.column()); } return QVariant(); } -
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
CategoryItem& parentItem = currentItem->parentItem();
return createIndex(currentItem->row(), 0, &parentItem);This is using the address of a temp item, it will not work.
QString data = file.readAll();
does not handle win/lunix line endings and encodings, use QString data = QTextStream(&file).readAll();
Use the model test to make sure your subclass works correctly
@VRonin: With the test i got an error:
// Common error test #2, make sure that a second level index has a parent // that is the first level index.What does above mean and how does it fit in my code?
-
@VRonin: With the test i got an error:
// Common error test #2, make sure that a second level index has a parent // that is the first level index.What does above mean and how does it fit in my code?
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
What does above mean and how does it fit in my code?
It means that if
model->hasChildren(parent)is true thenindex(0,0,parent).isValid()must be true andindex(0,0,parent).parent()==parent.
One of these 2 conditions is violated in your model.Again I strongly suggest you use
QStandardItemModelrather than wasting time reimplementing your own -
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
What does above mean and how does it fit in my code?
It means that if
model->hasChildren(parent)is true thenindex(0,0,parent).isValid()must be true andindex(0,0,parent).parent()==parent.
One of these 2 conditions is violated in your model.Again I strongly suggest you use
QStandardItemModelrather than wasting time reimplementing your own@VRonin: My class looks good:
QModelIndex Model::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (isCategory(index)) { return QModelIndex(); } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); CategoryItem& parentItem = currentItem->parentItem(); return createIndex(currentItem->row(), 0, &parentItem); // Here the row is always 1.. is it right?? }int Model::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { if (parent.column() > 0) { return 0; } if (!parent.isValid()) { return static_cast<int>(categories_.size()); } if (isCategory(parent)) { const auto currentCategory = static_cast<CategoryItem*>(parent.internalPointer()); return currentCategory->entryCount(); } return 0; }int Model::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const { return 2; }Coordinates are my categories and S, T are scenariopropertiesitem. Items has category as parents. Categories dont have parents.
-
@VRonin: My class looks good:
QModelIndex Model::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (isCategory(index)) { return QModelIndex(); } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); CategoryItem& parentItem = currentItem->parentItem(); return createIndex(currentItem->row(), 0, &parentItem); // Here the row is always 1.. is it right?? }int Model::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { if (parent.column() > 0) { return 0; } if (!parent.isValid()) { return static_cast<int>(categories_.size()); } if (isCategory(parent)) { const auto currentCategory = static_cast<CategoryItem*>(parent.internalPointer()); return currentCategory->entryCount(); } return 0; }int Model::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const { return 2; }Coordinates are my categories and S, T are scenariopropertiesitem. Items has category as parents. Categories dont have parents.
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
My class looks good:
It doesn't. as I mentioned above
@VRonin said in How to modify the data in the model and update the view when new data is received from external?:
CategoryItem& parentItem = currentItem->parentItem();
return createIndex(currentItem->row(), 0, &parentItem);This is using the address of a temp item, it will not work.
-
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
My class looks good:
It doesn't. as I mentioned above
@VRonin said in How to modify the data in the model and update the view when new data is received from external?:
CategoryItem& parentItem = currentItem->parentItem();
return createIndex(currentItem->row(), 0, &parentItem);This is using the address of a temp item, it will not work.
@VRonin: Oh.. How could this be fixed? I have to stick to QAbstractItemModel.
This is from Qt:
-
My row method was wrong and after updating i have a new failure in the test:
// Check that we can get back our real parent. //qDebug() << "TTT 1: " << model->parent(index); //qDebug() << "TTT 2: " << parent; //qDebug() << "TTT 3: " << index; tmp = model->parent(index); Q_ASSERT(tmp == parent);At the above point i.e. line 375 its failing.
My old row function in item used to always give 1 which was wrong. Now i made it to look at the parent and find the position.int ScenarioPropertyItem::row() const { int count = parentItem_.entryCount(); for (int i = 0; i < count; i++) { if (this->displayName_ == parentItem_.entryAt(i)->displayName_) { return i; } } } -
My row method was wrong and after updating i have a new failure in the test:
// Check that we can get back our real parent. //qDebug() << "TTT 1: " << model->parent(index); //qDebug() << "TTT 2: " << parent; //qDebug() << "TTT 3: " << index; tmp = model->parent(index); Q_ASSERT(tmp == parent);At the above point i.e. line 375 its failing.
My old row function in item used to always give 1 which was wrong. Now i made it to look at the parent and find the position.int ScenarioPropertyItem::row() const { int count = parentItem_.entryCount(); for (int i = 0; i < count; i++) { if (this->displayName_ == parentItem_.entryAt(i)->displayName_) { return i; } } } -
@VInay123 How can
parentItem_not be a pointer? if you are taking a copy of the parent and store it in the child, you'll never go back to the old parent@VRonin: It is taken as reference to an unique_pointer. Now i have changed it as following:
QModelIndex Model::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (isCategory(index)) { return QModelIndex(); } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); CategoryItem* parentItem = currentItem->parentItem(); return createIndex(currentItem->row(), 0, parentItem); }Please have a look at the index implementation also:
QModelIndex Model::index(int row, int column, const QModelIndex& parent /*= QModelIndex()*/) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } if (!parent.isValid()) { return createIndex(row, column, categories_.at(row).get()); } const auto currentCategory = static_cast<CategoryItem*>(parent.internalPointer()); ScenarioPropertyItem* currentItem = currentCategory->entryAt(row); if (currentItem) { return createIndex(row, column, currentItem); } else { return QModelIndex(); } }Tried the test again but its failing at 340 at the same place:
I tried to comment in the debug output and i get the following:
-
This post is deleted!
-
@VRonin: It is taken as reference to an unique_pointer. Now i have changed it as following:
QModelIndex Model::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (isCategory(index)) { return QModelIndex(); } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); CategoryItem* parentItem = currentItem->parentItem(); return createIndex(currentItem->row(), 0, parentItem); }Please have a look at the index implementation also:
QModelIndex Model::index(int row, int column, const QModelIndex& parent /*= QModelIndex()*/) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } if (!parent.isValid()) { return createIndex(row, column, categories_.at(row).get()); } const auto currentCategory = static_cast<CategoryItem*>(parent.internalPointer()); ScenarioPropertyItem* currentItem = currentCategory->entryAt(row); if (currentItem) { return createIndex(row, column, currentItem); } else { return QModelIndex(); } }Tried the test again but its failing at 340 at the same place:
I tried to comment in the debug output and i get the following:
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
I tried to comment in the debug output
Good. If you check it you realise that the problem is that
model->parent(index)returns an item with row==1 while the original parent had row==0. that's because inparent()you arereturn createIndex(currentItem->row(), 0, parentItem);instead ofreturn createIndex(parentItem->row(), 0, parentItem); -
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
I tried to comment in the debug output
Good. If you check it you realise that the problem is that
model->parent(index)returns an item with row==1 while the original parent had row==0. that's because inparent()you arereturn createIndex(currentItem->row(), 0, parentItem);instead ofreturn createIndex(parentItem->row(), 0, parentItem);@VRonin Yes you are right, i have to fix it. Parent item does not have any parent so how should i save the position i.e. row for every parent? In my case the categories are parents, so i cant actually query the position in the class. One way i see is to save the row as member variable while creating the parent.
-
@VRonin Yes you are right, i have to fix it. Parent item does not have any parent so how should i save the position i.e. row for every parent? In my case the categories are parents, so i cant actually query the position in the class. One way i see is to save the row as member variable while creating the parent.
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
One way i see is to save the row as member variable while creating the parent.
It's one way but usually it becomes unmanageable if you implement insert/remove/moveRows
You can always traverse
categories_to find the index ofparentItem -
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
One way i see is to save the row as member variable while creating the parent.
It's one way but usually it becomes unmanageable if you implement insert/remove/moveRows
You can always traverse
categories_to find the index ofparentItem@VRonin: So now the test is running error free. This was the implementation i had to make:
QModelIndex Model::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (isCategory(index)) { return QModelIndex(); } const auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); CategoryItem* parentItem = currentItem->parentItem(); int rowNum = categories_.size(); for (int i = 0; i < rowNum; i++) { if (categories_.at(i)->displayName() == parentItem->displayName()) { rowNum = i; } } return createIndex(rowNum, 0, parentItem); }But the view is still not displaying the new values. :( This is how i update the value
QModelIndex index = createIndex(1, 1, categories_[0]->entryAt(0)); this->setData(index, 11, Qt::EditRole);My SetData:
bool Model::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/) { if (role == Qt::EditRole && index.column() == 1 && index.isValid()) { auto currentItem = static_cast<ScenarioPropertyItem*>(index.internalPointer()); if (currentItem) { currentItem->setValue(value); emit dataChanged(index, index); return true; } } return false; } -
@VInay123 said in How to modify the data in the model and update the view when new data is received from external?:
But the view is still not displaying the new values
Does the view display the old values or is it completely empty? is
currentItemnull?