Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Q_INVOKABLE QAbatractListModel* dataChanged() only updating once
Forum Updated to NodeBB v4.3 + New Features

Q_INVOKABLE QAbatractListModel* dataChanged() only updating once

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
8 Posts 3 Posters 550 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    MattC24
    wrote on last edited by
    #1

    Hello,

    I've created a custom c++ model called ISettingModel that is derived from QAbstractListmodel. This model is created via Q_INVOKABLE.

    Q_INVOKABLE ISettingModel* createISettingModel(const NavigationManager::Page);
    
    ISettingModel* ISettingPresenter::createISettingModel(NavigationManager::Page subset)
    {
        qDebug() << " creating new setting model for page: " << subset;
        ISettingModel *model = new ISettingModel(subset,this);
    
        return model;
    }
    
    

    QML snippet:

    function resetListByIndex(ind){
            presenter.settingsSaved = true;
            if(ind === 0){isettingpresenter.resetUnsavedSettings(NavigationManager.UserAccessPage);}
            else if(ind === 1) {console.log("no settings on reset comm module page");}
            else {console.log("Invalid index, will not reset unsaved values")}
        }
    
       SwipeView {
            id: view
            currentIndex: index
            height: parent.height
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: indicator.top
            property int prevIndex: index
    
            onCurrentIndexChanged: {
                console.log("current index changed, prev index " + view.prevIndex + " current index " + currentIndex);
                isettingpresenter.clearUserAccessSettings();
                if(view.prevIndex != currentIndex){
                    resetListByIndex(view.prevIndex);
                    view.prevIndex = currentIndex;
                }
            }
    
            Item {
                id: userAccess
    
                SettingBrowser {
                    height: parent.height
                    width: parent.width
                    parent_popupheight: parent.height * 3
                    parent_popupwidth: parent.width * 3
                    name: "User Access Settings"
    
                    settingBrowserModel: isettingpresenter.createISettingModel(NavigationManager.UserAccessPage)
    
                    onSaveClicked: {
                         console.log("credientialsPage " +" onSavedClick: disableTabBar()")
                        appWindow.disableTabBar() //disable so user can't go to home before creating control. must be before sendUserAccessSettings()
                        console.log("calling sendUserAccessSettings()")
                        isettingpresenter.sendUserAccessSettings(); //this call UserAccessConfig*
    
                    }
    
                    onSendUserAccessValue: {
                        console.log("id: " + id + " " + "idValue: " + idvalue)
                        isettingpresenter.writeUserAccessSettings(id, idvalue)
                    }
    
                }
    
            }
    
            Item {
                id: resetCommModulePasswordScreen
    
                Rectangle {
                    width: parent.width
                    height: parent.height
                    anchors.top: header_resetCommModulePassword.bottom
    
                    Rectangle{
                        id: button_resetCommModulePassword
                        anchors.horizontalCenter: parent.horizontalCenter
                        anchors.verticalCenter: parent.verticalCenter
                        width: parent.width * .6
                        height: parent.height * .4
                        color: mouseArea_resetCommModulePassword.pressed ? Qt.darker(Variables.seLifeGreen) : Variables.seLifeGreen
    
                        Text{
                            id: buttonText_resetCommModulePassword
                            width: parent.width
                            height: parent.height
                            text: "RESET Communication Module Password"
                            color: Variables.ultraLightGrey2
                            font.pixelSize: 18
                            wrapMode: Text.WordWrap
                            font.family: opensansregular.name
                            verticalAlignment: Text.AlignVCenter
                            horizontalAlignment: Text.AlignHCenter
                        }
    
                    }
                }
            }
        }
    

    The model gets created fine and I'm able to interact and save and update settings fine. The issue is when I need to reset unsaved settings when swiping to another screen. When I first launch the program I'm able to change some settings , swipe and have them reset as expected. However, when I exit that page (ie go to homepage) and go back to that page I am unable to reset settings. More specifically, I can see that the dataChanged() signal is emitted but I see no new calls to data() to reset the settings. Again, it only seems to work when the page is first accessed. After that swiping no longer seems to reset properly. Does anyone have any idea why this could happen?

    void ISettingModel::resetUnsavedSettings(NavigationManager::Page subset)
    {
        if(subset == mList){
            qDebug() << "ISettingModel::resetUnsavedSettings()::mList: " << mList;
            QVector<int> list = mSettingPresenter->getListByName(mList);
    
            if(!list.empty()){
                bool reset = false;
                for(int i=0; i<list.length(); i++){
                    SettingBase *setting = (mSettingPresenter->allSettingsMap).value(list.at(i));
                    qDebug() << "reset setting: " << setting->getIndex();
                    reset = setting->resetUnsavedValue();// sets temp value equal to value
                    QModelIndex index = QAbstractListModel::createIndex(i,0);
                    if (reset){
                        qDebug() << "Value reset setting name "<<setting->getLabel();
                        reset = false;
                        emit dataChanged(index, index, QVector<int>());
                        qDebug() << "dataChanged emitted(index, index, QVector<int>() << role): " << index ;
                        qDebug() << "datachanged() in list: " <<mList;
                        m_dependencyManager->updateDependents(static_cast<Settings::Parameter>(setting->getIndex()),setting->getValue());
                    }
                }
            }
    
            else{
                qDebug()<<"list " << mList << "is an empty list, no settings to reset";
            }
        }
    }
    

    Here you can see output from the qDebug() statements

    reset issue.PNG

    My one thought is that dataChanged() is getting emitted but its referencing another model? I'm not entirely sure how QML's garbage collector works. I saw that the destructor for ISettingModel doesn't get called every time the page is destroyed.

    Axel SpoerlA GrecKoG 2 Replies Last reply
    0
    • M MattC24

      Hello,

      I've created a custom c++ model called ISettingModel that is derived from QAbstractListmodel. This model is created via Q_INVOKABLE.

      Q_INVOKABLE ISettingModel* createISettingModel(const NavigationManager::Page);
      
      ISettingModel* ISettingPresenter::createISettingModel(NavigationManager::Page subset)
      {
          qDebug() << " creating new setting model for page: " << subset;
          ISettingModel *model = new ISettingModel(subset,this);
      
          return model;
      }
      
      

      QML snippet:

      function resetListByIndex(ind){
              presenter.settingsSaved = true;
              if(ind === 0){isettingpresenter.resetUnsavedSettings(NavigationManager.UserAccessPage);}
              else if(ind === 1) {console.log("no settings on reset comm module page");}
              else {console.log("Invalid index, will not reset unsaved values")}
          }
      
         SwipeView {
              id: view
              currentIndex: index
              height: parent.height
              anchors.top: parent.top
              anchors.left: parent.left
              anchors.right: parent.right
              anchors.bottom: indicator.top
              property int prevIndex: index
      
              onCurrentIndexChanged: {
                  console.log("current index changed, prev index " + view.prevIndex + " current index " + currentIndex);
                  isettingpresenter.clearUserAccessSettings();
                  if(view.prevIndex != currentIndex){
                      resetListByIndex(view.prevIndex);
                      view.prevIndex = currentIndex;
                  }
              }
      
              Item {
                  id: userAccess
      
                  SettingBrowser {
                      height: parent.height
                      width: parent.width
                      parent_popupheight: parent.height * 3
                      parent_popupwidth: parent.width * 3
                      name: "User Access Settings"
      
                      settingBrowserModel: isettingpresenter.createISettingModel(NavigationManager.UserAccessPage)
      
                      onSaveClicked: {
                           console.log("credientialsPage " +" onSavedClick: disableTabBar()")
                          appWindow.disableTabBar() //disable so user can't go to home before creating control. must be before sendUserAccessSettings()
                          console.log("calling sendUserAccessSettings()")
                          isettingpresenter.sendUserAccessSettings(); //this call UserAccessConfig*
      
                      }
      
                      onSendUserAccessValue: {
                          console.log("id: " + id + " " + "idValue: " + idvalue)
                          isettingpresenter.writeUserAccessSettings(id, idvalue)
                      }
      
                  }
      
              }
      
              Item {
                  id: resetCommModulePasswordScreen
      
                  Rectangle {
                      width: parent.width
                      height: parent.height
                      anchors.top: header_resetCommModulePassword.bottom
      
                      Rectangle{
                          id: button_resetCommModulePassword
                          anchors.horizontalCenter: parent.horizontalCenter
                          anchors.verticalCenter: parent.verticalCenter
                          width: parent.width * .6
                          height: parent.height * .4
                          color: mouseArea_resetCommModulePassword.pressed ? Qt.darker(Variables.seLifeGreen) : Variables.seLifeGreen
      
                          Text{
                              id: buttonText_resetCommModulePassword
                              width: parent.width
                              height: parent.height
                              text: "RESET Communication Module Password"
                              color: Variables.ultraLightGrey2
                              font.pixelSize: 18
                              wrapMode: Text.WordWrap
                              font.family: opensansregular.name
                              verticalAlignment: Text.AlignVCenter
                              horizontalAlignment: Text.AlignHCenter
                          }
      
                      }
                  }
              }
          }
      

      The model gets created fine and I'm able to interact and save and update settings fine. The issue is when I need to reset unsaved settings when swiping to another screen. When I first launch the program I'm able to change some settings , swipe and have them reset as expected. However, when I exit that page (ie go to homepage) and go back to that page I am unable to reset settings. More specifically, I can see that the dataChanged() signal is emitted but I see no new calls to data() to reset the settings. Again, it only seems to work when the page is first accessed. After that swiping no longer seems to reset properly. Does anyone have any idea why this could happen?

      void ISettingModel::resetUnsavedSettings(NavigationManager::Page subset)
      {
          if(subset == mList){
              qDebug() << "ISettingModel::resetUnsavedSettings()::mList: " << mList;
              QVector<int> list = mSettingPresenter->getListByName(mList);
      
              if(!list.empty()){
                  bool reset = false;
                  for(int i=0; i<list.length(); i++){
                      SettingBase *setting = (mSettingPresenter->allSettingsMap).value(list.at(i));
                      qDebug() << "reset setting: " << setting->getIndex();
                      reset = setting->resetUnsavedValue();// sets temp value equal to value
                      QModelIndex index = QAbstractListModel::createIndex(i,0);
                      if (reset){
                          qDebug() << "Value reset setting name "<<setting->getLabel();
                          reset = false;
                          emit dataChanged(index, index, QVector<int>());
                          qDebug() << "dataChanged emitted(index, index, QVector<int>() << role): " << index ;
                          qDebug() << "datachanged() in list: " <<mList;
                          m_dependencyManager->updateDependents(static_cast<Settings::Parameter>(setting->getIndex()),setting->getValue());
                      }
                  }
              }
      
              else{
                  qDebug()<<"list " << mList << "is an empty list, no settings to reset";
              }
          }
      }
      

      Here you can see output from the qDebug() statements

      reset issue.PNG

      My one thought is that dataChanged() is getting emitted but its referencing another model? I'm not entirely sure how QML's garbage collector works. I saw that the destructor for ISettingModel doesn't get called every time the page is destroyed.

      Axel SpoerlA Offline
      Axel SpoerlA Offline
      Axel Spoerl
      Moderators
      wrote on last edited by
      #2

      @MattC24
      My suspicion from code reading is that resetUnsavedSettings() isn't invoked by the QML code at all, because it isn't Q_INVOKABLE. Maybe it gets called upon construction of ISettingsModel, which would explain why it's called only once.

      Software Engineer
      The Qt Company, Oslo

      M 1 Reply Last reply
      0
      • M MattC24

        Hello,

        I've created a custom c++ model called ISettingModel that is derived from QAbstractListmodel. This model is created via Q_INVOKABLE.

        Q_INVOKABLE ISettingModel* createISettingModel(const NavigationManager::Page);
        
        ISettingModel* ISettingPresenter::createISettingModel(NavigationManager::Page subset)
        {
            qDebug() << " creating new setting model for page: " << subset;
            ISettingModel *model = new ISettingModel(subset,this);
        
            return model;
        }
        
        

        QML snippet:

        function resetListByIndex(ind){
                presenter.settingsSaved = true;
                if(ind === 0){isettingpresenter.resetUnsavedSettings(NavigationManager.UserAccessPage);}
                else if(ind === 1) {console.log("no settings on reset comm module page");}
                else {console.log("Invalid index, will not reset unsaved values")}
            }
        
           SwipeView {
                id: view
                currentIndex: index
                height: parent.height
                anchors.top: parent.top
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.bottom: indicator.top
                property int prevIndex: index
        
                onCurrentIndexChanged: {
                    console.log("current index changed, prev index " + view.prevIndex + " current index " + currentIndex);
                    isettingpresenter.clearUserAccessSettings();
                    if(view.prevIndex != currentIndex){
                        resetListByIndex(view.prevIndex);
                        view.prevIndex = currentIndex;
                    }
                }
        
                Item {
                    id: userAccess
        
                    SettingBrowser {
                        height: parent.height
                        width: parent.width
                        parent_popupheight: parent.height * 3
                        parent_popupwidth: parent.width * 3
                        name: "User Access Settings"
        
                        settingBrowserModel: isettingpresenter.createISettingModel(NavigationManager.UserAccessPage)
        
                        onSaveClicked: {
                             console.log("credientialsPage " +" onSavedClick: disableTabBar()")
                            appWindow.disableTabBar() //disable so user can't go to home before creating control. must be before sendUserAccessSettings()
                            console.log("calling sendUserAccessSettings()")
                            isettingpresenter.sendUserAccessSettings(); //this call UserAccessConfig*
        
                        }
        
                        onSendUserAccessValue: {
                            console.log("id: " + id + " " + "idValue: " + idvalue)
                            isettingpresenter.writeUserAccessSettings(id, idvalue)
                        }
        
                    }
        
                }
        
                Item {
                    id: resetCommModulePasswordScreen
        
                    Rectangle {
                        width: parent.width
                        height: parent.height
                        anchors.top: header_resetCommModulePassword.bottom
        
                        Rectangle{
                            id: button_resetCommModulePassword
                            anchors.horizontalCenter: parent.horizontalCenter
                            anchors.verticalCenter: parent.verticalCenter
                            width: parent.width * .6
                            height: parent.height * .4
                            color: mouseArea_resetCommModulePassword.pressed ? Qt.darker(Variables.seLifeGreen) : Variables.seLifeGreen
        
                            Text{
                                id: buttonText_resetCommModulePassword
                                width: parent.width
                                height: parent.height
                                text: "RESET Communication Module Password"
                                color: Variables.ultraLightGrey2
                                font.pixelSize: 18
                                wrapMode: Text.WordWrap
                                font.family: opensansregular.name
                                verticalAlignment: Text.AlignVCenter
                                horizontalAlignment: Text.AlignHCenter
                            }
        
                        }
                    }
                }
            }
        

        The model gets created fine and I'm able to interact and save and update settings fine. The issue is when I need to reset unsaved settings when swiping to another screen. When I first launch the program I'm able to change some settings , swipe and have them reset as expected. However, when I exit that page (ie go to homepage) and go back to that page I am unable to reset settings. More specifically, I can see that the dataChanged() signal is emitted but I see no new calls to data() to reset the settings. Again, it only seems to work when the page is first accessed. After that swiping no longer seems to reset properly. Does anyone have any idea why this could happen?

        void ISettingModel::resetUnsavedSettings(NavigationManager::Page subset)
        {
            if(subset == mList){
                qDebug() << "ISettingModel::resetUnsavedSettings()::mList: " << mList;
                QVector<int> list = mSettingPresenter->getListByName(mList);
        
                if(!list.empty()){
                    bool reset = false;
                    for(int i=0; i<list.length(); i++){
                        SettingBase *setting = (mSettingPresenter->allSettingsMap).value(list.at(i));
                        qDebug() << "reset setting: " << setting->getIndex();
                        reset = setting->resetUnsavedValue();// sets temp value equal to value
                        QModelIndex index = QAbstractListModel::createIndex(i,0);
                        if (reset){
                            qDebug() << "Value reset setting name "<<setting->getLabel();
                            reset = false;
                            emit dataChanged(index, index, QVector<int>());
                            qDebug() << "dataChanged emitted(index, index, QVector<int>() << role): " << index ;
                            qDebug() << "datachanged() in list: " <<mList;
                            m_dependencyManager->updateDependents(static_cast<Settings::Parameter>(setting->getIndex()),setting->getValue());
                        }
                    }
                }
        
                else{
                    qDebug()<<"list " << mList << "is an empty list, no settings to reset";
                }
            }
        }
        

        Here you can see output from the qDebug() statements

        reset issue.PNG

        My one thought is that dataChanged() is getting emitted but its referencing another model? I'm not entirely sure how QML's garbage collector works. I saw that the destructor for ISettingModel doesn't get called every time the page is destroyed.

        GrecKoG Offline
        GrecKoG Offline
        GrecKo
        Qt Champions 2018
        wrote on last edited by
        #3

        @MattC24 There's too much and not enough info there.

        The swiping screens, resetting models stuff is not helping understand the issue.

        We don't see how the model is used in QML.

        With the limited info we have here I'd check the addresses of the the model. Is dataChanged emitted for the same model where data is called?

        try to make minimal reproducible example to focus on the important stuff.

        M 2 Replies Last reply
        3
        • Axel SpoerlA Axel Spoerl

          @MattC24
          My suspicion from code reading is that resetUnsavedSettings() isn't invoked by the QML code at all, because it isn't Q_INVOKABLE. Maybe it gets called upon construction of ISettingsModel, which would explain why it's called only once.

          M Offline
          M Offline
          MattC24
          wrote on last edited by
          #4

          @Axel-Spoerl apologies I didn't want to send all of the ISettingPresenter because I'm refactoring the class. but to inform on some missing pieces.

          void ISettingPresenter::resetSettingList(NavigationManager::Page subset)
          {
              emit resetUnsavedSettings(subset);
          }
          

          this signal is connected in the constructor of ISettingModel.

          ISettingModel::ISettingModel(NavigationManager::Page subset, ISettingPresenter *isettingpresenter, QObject *parent)
              : QAbstractListModel(parent)
          {
              qDebug()<<"Setting Model Initialized";
          
              m_dependencyManager = new ModelDependencyManager(this);
          
              mSettingPresenter = isettingpresenter;
              setList(subset);
          
              connect(mSettingPresenter,         &ISettingPresenter::settingPropertyChanged, //used for just updating setting if on page
                      this,                  &ISettingModel::updateSetting);
              connect(mSettingPresenter,         &ISettingPresenter::resetUnsavedSettings,
                      this,                  &ISettingModel::resetUnsavedSettings);
          
              connect(m_dependencyManager, &ModelDependencyManager::updateDependentValue,  //used for updating depdendents on screen via setData i.e enable/disable
                      this,       &ISettingModel::updateDependentSettingOnScreen);
          }
          
          

          So whenever that signal is emitted it's getting called. Just that it only seems to work only when I first open that page. IF I close the page and go back to it, it'll get called but not update properly

          Axel SpoerlA 1 Reply Last reply
          0
          • M MattC24

            @Axel-Spoerl apologies I didn't want to send all of the ISettingPresenter because I'm refactoring the class. but to inform on some missing pieces.

            void ISettingPresenter::resetSettingList(NavigationManager::Page subset)
            {
                emit resetUnsavedSettings(subset);
            }
            

            this signal is connected in the constructor of ISettingModel.

            ISettingModel::ISettingModel(NavigationManager::Page subset, ISettingPresenter *isettingpresenter, QObject *parent)
                : QAbstractListModel(parent)
            {
                qDebug()<<"Setting Model Initialized";
            
                m_dependencyManager = new ModelDependencyManager(this);
            
                mSettingPresenter = isettingpresenter;
                setList(subset);
            
                connect(mSettingPresenter,         &ISettingPresenter::settingPropertyChanged, //used for just updating setting if on page
                        this,                  &ISettingModel::updateSetting);
                connect(mSettingPresenter,         &ISettingPresenter::resetUnsavedSettings,
                        this,                  &ISettingModel::resetUnsavedSettings);
            
                connect(m_dependencyManager, &ModelDependencyManager::updateDependentValue,  //used for updating depdendents on screen via setData i.e enable/disable
                        this,       &ISettingModel::updateDependentSettingOnScreen);
            }
            
            

            So whenever that signal is emitted it's getting called. Just that it only seems to work only when I first open that page. IF I close the page and go back to it, it'll get called but not update properly

            Axel SpoerlA Offline
            Axel SpoerlA Offline
            Axel Spoerl
            Moderators
            wrote on last edited by
            #5

            @MattC24
            Then I have to agree with @GrecKo, that there is too little code of a too large project involved here.
            I not entirely sure, what you mean by

            it'll get called but not update properly

            Maybe it's a good idea to narrow the issue down to a minimal, compilable reproducer, that is small enough to be posted here in its entirety.

            Software Engineer
            The Qt Company, Oslo

            M 1 Reply Last reply
            0
            • GrecKoG GrecKo

              @MattC24 There's too much and not enough info there.

              The swiping screens, resetting models stuff is not helping understand the issue.

              We don't see how the model is used in QML.

              With the limited info we have here I'd check the addresses of the the model. Is dataChanged emitted for the same model where data is called?

              try to make minimal reproducible example to focus on the important stuff.

              M Offline
              M Offline
              MattC24
              wrote on last edited by MattC24
              #6
              This post is deleted!
              1 Reply Last reply
              0
              • Axel SpoerlA Axel Spoerl

                @MattC24
                Then I have to agree with @GrecKo, that there is too little code of a too large project involved here.
                I not entirely sure, what you mean by

                it'll get called but not update properly

                Maybe it's a good idea to narrow the issue down to a minimal, compilable reproducer, that is small enough to be posted here in its entirety.

                M Offline
                M Offline
                MattC24
                wrote on last edited by
                #7

                @Axel-Spoerl I will try and reproduce at a smaller scale.

                1 Reply Last reply
                0
                • GrecKoG GrecKo

                  @MattC24 There's too much and not enough info there.

                  The swiping screens, resetting models stuff is not helping understand the issue.

                  We don't see how the model is used in QML.

                  With the limited info we have here I'd check the addresses of the the model. Is dataChanged emitted for the same model where data is called?

                  try to make minimal reproducible example to focus on the important stuff.

                  M Offline
                  M Offline
                  MattC24
                  wrote on last edited by
                  #8

                  @GrecKo how could I check the addresses of the model within dataChanged() and data()?

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved