How to modify the data in the model and update the view when new data is received from external?
-
@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
QStandardItemModel
rather 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
QStandardItemModel
rather 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
currentItem
null? -
@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
currentItem
null?@VRonin: I initialize the model data i.e. categories_ with empty values. So the view shows empty value. But i update the value in my function and in debug mode i see the new value in the variable but the view shows empty i.e. old value. If i change the property to write mode, via delegate the value is being changed and view displays correctly. But when i do as following it does not:
QModelIndex index = createIndex(1, 1, categories_[0]->entryAt(0)); this->setData(index, 11, Qt::EditRole);
It actually calls the setData too and emits dataChanged. But the view is not displaying. Just for info my index method looks like this:
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(); } }
-
What version of Qt are you on?
Usually the view ignores thedataChanged
if it thinks the cell pointed by index shouldn't exist@VRonin : I use Qt5.9.0. The update should have been this
QModelIndex index = createIndex(0, 1, categories_[0]->entryAt(0)); // but not 1,1 this->setData(index, 11, Qt::EditRole);
But even then after the above change its not updating. Actually in setData there is already a index.isValid() check. The indexes looks good and also the currentItem. Should i post my model and view once again after the new changes i have done?