Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to modify the data in the model and update the view when new data is received from external?



  • @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?



  • @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 the dataChanged 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?



  • @VInay123 said in How to modify the data in the model and update the view when new data is received from external?:

    Actually in setData there is already a index.isValid() check.

    index.isValid() is an insufficient check, that's why checkIndex was introduced in Qt 5.11

    The indexes looks good and also the currentItem.

    Unfortunately sometimes that's not enough. For example, if you forget to signal the view that you inserted a row, and then emit dataChanged for an item in that row, even if the item is 100% valid in the model the view will still think the row doesn't exist and do nothing



  • @JonB @VRonin @SGaist : Something is not seeming right. I tried first to change the value using the following:

    QModelIndex index = createIndex(0, 1, categories_[0]->entryAt(0));
        this->setData(index, 11, Qt::EditRole);
    

    And in delegate class i tried to see what the value it has but it looks like it is zero. Below is the implementation:

    QWidget *PropertyEditorItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
        if (!index.isValid()) {
            return nullptr;
        } else if (index.column() == 0) {
            return QStyledItemDelegate::createEditor(parent, option, index);
        }
    
        const auto currentItem = static_cast<ScenarioPropertyItem *>(index.internalPointer());
        switch (currentItem->itemType()) {
            case PropertyType::DOUBLE: {
                QDoubleSpinBox *box = new QDoubleSpinBox(parent);
                auto v = currentItem->data(index.column()); // Here it is zero??? :(
                box->setValue(v.toDouble());
                return box;  // qt will handle deletion
            }
    default:
                return QStyledItemDelegate::createEditor(parent, option, index);
        }
    }
    


  • In setData could you add the qDebug as below and tell us what is printed?

    if (currentItem) {
               currentItem->setValue(value);
    qDebug() << currentItem->data(1);
    qDebug() << index.data(Qt::DisplayRole);
                 emit dataChanged(index, index);
                 return true;
            }
    


  • @VRonin :

    [16:56:03,236] [Debug] QVariant(int, 11) / My new values
    [16:56:03,237] [Debug] QVariant(int, 11) // My new values
    

    ;(
    When i change via delegate:

    [16:58:06,173] [Debug] QVariant(double, 0)
    [16:58:06,176] [Debug] QVariant(double, 0)
    

    I believe somewhere the indexes are not right. When i try to change the value(via delegate) of suppose "T" value so that i can iterate over parent and see the "S" value, here it is empty, but should be 11.



  • My model looks like this:
    0_1535729444022_model1.PNG
    0_1535729450658_model2.PNG

    In parent method i use pointer for parent item but not reference.



  • @VRonin @SGaist @JonB : I just debugged to see that after changing the value of the property via delegate does the index in the following code has the same value.

    QModelIndex index = createIndex(0, 1, categories_.at(0)->entryAt(0));
        this->setData(index, 11, Qt::EditRole);
    

    But it turns out that the index in the above is just a copy and when delegate changes the value it is not reflected in the index here. I think i am changing at a copy of the property but not the actual one.



  • This post is deleted!


  • Can you show us the code for Delegate::setModelData?



  • @VRonin

     void PropertyEditorItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
            const auto currentItem = static_cast<ScenarioPropertyItem *>(index.internalPointer());
    
            auto setup = [this, &model, &index](auto editor, auto usage) {
                if (editor) {
                    usage(editor);
                } else {
                    QStyledItemDelegate::setModelData(editor, model, index);
                }
            };
    
            switch (currentItem->itemType()) {
                case PropertyType::DOUBLE: {
                    setup(qobject_cast<QDoubleSpinBox *>(editor), [&index, &model](QDoubleSpinBox *editor) {
                        editor->interpretText();
                        model->setData(index, editor->value(), Qt::EditRole);
                    });
                    break;
                }
                case PropertyType::INTEGER: {
                    setup(qobject_cast<QSpinBox *>(editor), [&index, &model](QSpinBox *editor) {
                        editor->interpretText();
                        model->setData(index, editor->value(), Qt::EditRole);
                    });
                    break;
                }
                case PropertyType::STRING: {
                    setup(qobject_cast<QLineEdit *>(editor), [&index, &model](QLineEdit *editor) { model->setData(index, editor->text(), Qt::EditRole); });
                    break;
                }
                case PropertyType::BOOLEAN: {
                    setup(qobject_cast<QComboBox *>(editor),
                          [this, &index, &model](QComboBox *editor) { model->setData(index, editor->currentText(), Qt::EditRole); });
                    break;
                }
                default:
                    QStyledItemDelegate::setModelData(editor, model, index);
                    break;
            }
        }
    


  • This post is deleted!

  • Lifetime Qt Champion

    Why are you calling createIndex in onRefreshRoadInfo ?



  • @SGaist In onRefereshRoadInfo function i get the new values which has to be shown in the view. So, here I want to update the values, and as i dont have the index's, i create index for every property and call setData to update the values. If this is not correct how should i update the values here?



  • @JonB @SGaist @VRonin : Any lead for my problem?


  • Lifetime Qt Champion

    Please show more patience, allow at least 24 hours before bumping your own thread. People answering on this forum do it on a voluntary basis and might not live in the same time zone as you.

    That said, I stumble upon the QJsonModel project. You might want to check it.



  • @SGaist I will keep in mind :). I saw the project and looks like my current implementation looks but, the project does not have the one thing i am looking for i.e. updating of data inside the code i.e. onRefereshRoadInfo.



  • How is it possible that even after changing the model data value in a hard way and emitting reset model signals does not refresh the view.

    void ScenarioPropertiesModel::onRefreshRoadInfo(const NewData& roadInfo) {
        emit beginResetModel();
        categories_.at(0)->entryAt(0)->setValue(11111);
        emit endResetModel();
    }
    

    Could it be that index function implementation is wrong? This is my index implementation:

    
    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();
        }
    }
    


  • It still not clear to me in what component the problem is. Could you try to remove your custom delegate and see if it updates the values? if it doesn't then it's a model problem but at least we know where to focus



  • @VRonin: Hi Ronin, thnx for the reply. Even without delegates it is not working. I believe it lies in model. I am updating a copy of the model but not the model.
    Here below, the model pointer is always in the old state, even after changing the values in my model class.

    void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index)
    

    For infor: I have hosted the project in Github: https://github.com/vinayrajputh/qtmodelview



  • @VInay123 said in How to modify the data in the model and update the view when new data is received from external?:

    For infor: I have hosted the project in Github: https://github.com/vinayrajputh/qtmodelview

    Would have been nice if it compiled. Can you upload the ui and json file too?



  • @VRonin : Done.



  • I'm concentrating on the model only at the moment but it works perfectly for me.
    The only thing that I would flag is that the json that you provided has all "ReadOnly": true so it won't allow changes unless you change that to false.

    Could you try this fork and tell me what is that you expect to happen that is not happening?



  • @VRonin: Now there is a function with following signature

    void ScenarioPropertiesModel::onRefreshRoadInfo(const NewData& roadInfo) {}
    

    Now i would ask you to set the model data (properties) to the new values present in roadInfo variable(or to a random number) in the above function and display it in the view.

    My usecase is that, i will have a street and when user clicks on the street onRefreshRoadInfo will be called with the values which are actually coordinates and these i want to show.



  • So you are confirming that, so far, the fork above is behaving correctly?



  • @VRonin : Yes, it works correctly. :)



  • Could you check the same fork now and see if onRefreshRoadInfo does what you expect?



  • @VRonin : Hi Ronin, i checked your fork and it is working exactly as i need. So i tried to copy the code in my implementation and sadly it is not working, i even commented out the delegates. Could you please implement the ur logic in my code and see if you can make it work? I mean use the ui and view class. May be something is buggy in my view class? But there is nothing much in the view class too.

    ------Update--------
    I tried to use your main function with my implementation as below so that my view class is used. But turns out the implementation does not work.

        QApplication a(argc, argv);
        //ScenarioPropertiesModel model;
        //QTreeView w;
        //w.setModel(&model);
        //w.show();
        PropertyEditorView view;// model creation and setting done in class constructor
     //   QPushButton sendRefreshRoadInfoButton("Refresh //Road Info");
    //    sendRefreshRoadInfoButton.show();
        //QObject::connect(&sendRefreshRoadInfoButton,&QPushB//utton::clicked,&model,&ScenarioPropertiesModel::onRe//freshRoadInfo); i use my own signal and slot
        return a.exec();~~
    

    When i modified the main as below it is working.

        QApplication a(argc, argv);
        ScenarioPropertiesModel model;
        PropertyEditorView w;
        w.ui_->treeView->setModel(&model); // removed setting from PropertyEditorView constructor as we set here
        w.show();
        QPushButton sendRefreshRoadInfoButton("Refresh Road Info");
        sendRefreshRoadInfoButton.show();
        QObject::connect(&sendRefreshRoadInfoButton,&QPushButton::clicked,&model,&ScenarioPropertiesModel::onRefreshRoadInfo); 
        return a.exec();
    

    Do you see any reason for this behavior.


  • Lifetime Qt Champion

    Because on your first version of the code you are not connecting to the model you have in your PropertyEditorView but the one you declared above. On a side note, that can't compile since you commented out your model declaration.



  • This post is deleted!


  • @SGaist @JonB @VRonin : So i took a deep breathe and tried to implement my code from scratch using @VRonin solution i.e. his latest fork https://github.com/VSRonin/qtmodelview.
    Now my code is working :). This was the piece of line which helped me:

        const QModelIndex parentIdx = index(0, 0);
        const QModelIndex xValueIndex = index(0, 1, parentIdx);
        setData(xValueIndex, 2.55);
    

    Additionally deep inside my code i had two pointers to my view which was also the cause for not showing the new values. The thread can be closed. I have a request @VRonin if he could take out the link to the code as it is a part of commercial project. Thank you everyone for your help, especially @VRonin.


Log in to reply