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. QSortFilterProxyModel filterAcceptsRow being called unexpectedly
Forum Updated to NodeBB v4.3 + New Features

QSortFilterProxyModel filterAcceptsRow being called unexpectedly

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
13 Posts 3 Posters 2.1k Views 2 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.
  • W Offline
    W Offline
    wagner2x
    wrote on last edited by wagner2x
    #1

    I have AbstractItemModel( Model A) that I want to filter based off some string filter parameter. To do so I have a QSortFilterProxyModel(Model B) that uses Model A as its source and filters it. When Model B is presented in the UI this it gets filtered further into Selected/Not selected proxy models which use Model B as their source. If the Delegate in the UI gets selected this item gets removed from a QSortFilterProxyModel(Model C) and added to another QSortFilterProxyModel( Model D).

    My problem is this, when I am changing the filter string in Model B to a parameter that has many elements that satisfy the filter...this operation can be pretty slow(takes around 1 second to transition and update UI). This is because I am re-evaluating the filter through the entire list every time the filter parameter changes.

    Since my filter parameters for Model B are known at boot I figured I could just create a QSortFilterProxyModel for each parameter and switch which proxy model Model C and Model D use as a source when the user changes the filter parameter. What I have done here is basically converted Model B into a QIdentityProxyModel (Model E) that has a QMap of <filterParam, QSortFilterProxyModel> and every time the user chooses another filter parameter, Model E grabs the appropriate QSortFitlerProxyModel(basically Model B) from its QMap and calls setSourceModel() with the appropriate Model B. The problem is that every time I do this, the Model B that I choose is re-evaluating its filter and actually taking the same amount of time that it did before I made the change. Is there a reason that Model B is going through and re-evaluating its filter? I have DynamicSortFilter set to false in the constructor of every Model B that gets created.

    Another question I have is what could be making this filtering so slow? There really aren't that many elements in the list(~300).

    DISCLAIMER: I am using an OLD version of Qt(4.8) in support of a legacy project.

    I know I am missing some QT thing here but am at a loss. Any help would be greatly appreciated! Thanks

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Since model A is a custom model, I would start by benchmarking it. Is the data method implemented efficiently ?

      Then, for your multiple proxy models, don't call setSourceModel every time. A model can have several proxy on top of it in the same fashion you can have several views looking at the same model. Switch the proxy used by the view.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      W 1 Reply Last reply
      0
      • SGaistS SGaist

        Hi,

        Since model A is a custom model, I would start by benchmarking it. Is the data method implemented efficiently ?

        Then, for your multiple proxy models, don't call setSourceModel every time. A model can have several proxy on top of it in the same fashion you can have several views looking at the same model. Switch the proxy used by the view.

        W Offline
        W Offline
        wagner2x
        wrote on last edited by
        #3

        @SGaist

        thanks for the response!

        What would an inefficient data method look like? I basically just look at the role and return the appropriate data from the data source. My situation is a little different since my AbstractItemModel's Data() function calls out to a custom list object's data function:

        QVariant CustomModel::data(const QModelIndex &index, int role) const
        {
            if(m_list != NULL)
            {
                if (index.row()<0 || index.row() >= m_list->items().size())
                    return QVariant();
        
                return m_list->data(index.row(), role);
            }
        }
        

        The list object's data function looks something like this:

        QVariant CustomList::data(int index, int role)
        {
            QVariantMap entry = items_.at(index);
            switch (role) {
            case myRole1:
                return QVariant::fromValue(entry["myRole1"]);
            case myRole2:
                return QVariant::fromValue(entry["myRole2"]);
        
            default:
                // should be unreachable code
                return QVariant();
            }
        }
        

        The underlying datastore in the CustomList is a QList of QVariantMaps.

        Going off of your second point:
        I should have mentioned that I am using QML and using setContextProperty to allow qml to reference Model C and Model D. Is there a way I can change what the context QML sees without changing the way it is referenced in qml(does that question make sense? )? This is why I was using the QIdentityProxyModel. By setting up Model C and Model D to have Model E as a source then I can leave everything the same but just change what model Model E presents to the view.

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Your are accessing items_ quite a lot of time for nothing. The data method is called many times because there are lots of roles. Since you only return data for Role1 and Role2, ensure first that it's one of them and then load the data.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          W 1 Reply Last reply
          1
          • SGaistS SGaist

            Your are accessing items_ quite a lot of time for nothing. The data method is called many times because there are lots of roles. Since you only return data for Role1 and Role2, ensure first that it's one of them and then load the data.

            W Offline
            W Offline
            wagner2x
            wrote on last edited by
            #5

            @SGaist

            are you basically saying that I should change it to something like this?

            QVariant CustomList::data(int index, int role)
            {
                QVariantMap entry;
                switch (role) {
                case myRole1:
                    entry = items_.at(index);
                    return QVariant::fromValue(entry["myRole1"]);
                case myRole2:
                    entry = items_.at(index);
                    return QVariant::fromValue(entry["myRole2"]);
            
                default:
                    // should be unreachable code
                    return QVariant();
                }
            }
            
            JonBJ 1 Reply Last reply
            0
            • W wagner2x

              @SGaist

              are you basically saying that I should change it to something like this?

              QVariant CustomList::data(int index, int role)
              {
                  QVariantMap entry;
                  switch (role) {
                  case myRole1:
                      entry = items_.at(index);
                      return QVariant::fromValue(entry["myRole1"]);
                  case myRole2:
                      entry = items_.at(index);
                      return QVariant::fromValue(entry["myRole2"]);
              
                  default:
                      // should be unreachable code
                      return QVariant();
                  }
              }
              
              JonBJ Online
              JonBJ Online
              JonB
              wrote on last edited by
              #6

              @wagner2x Yes.

              W 1 Reply Last reply
              0
              • JonBJ JonB

                @wagner2x Yes.

                W Offline
                W Offline
                wagner2x
                wrote on last edited by
                #7

                @JonB

                The Data function gets called for Roles other than the ones I defined?

                JonBJ 1 Reply Last reply
                0
                • W wagner2x

                  @JonB

                  The Data function gets called for Roles other than the ones I defined?

                  JonBJ Online
                  JonBJ Online
                  JonB
                  wrote on last edited by
                  #8

                  @wagner2x
                  Is that a question or an observation? Yes indeed it does, many more. Basically for most of the defined ones (what font? what color? what alignment?). @SGaist is suggesting therefore you only evaluate items_.at(index) when absolutely necessary.

                  If you were to have a lot of roles required in the switch statement one could factor out the items_.at(index) retrieval to one place if you wish and some common expression for QVariant::fromValue(entry["myRole..."]) to cut down on code if that's what you mean.

                  W 1 Reply Last reply
                  0
                  • JonBJ JonB

                    @wagner2x
                    Is that a question or an observation? Yes indeed it does, many more. Basically for most of the defined ones (what font? what color? what alignment?). @SGaist is suggesting therefore you only evaluate items_.at(index) when absolutely necessary.

                    If you were to have a lot of roles required in the switch statement one could factor out the items_.at(index) retrieval to one place if you wish and some common expression for QVariant::fromValue(entry["myRole..."]) to cut down on code if that's what you mean.

                    W Offline
                    W Offline
                    wagner2x
                    wrote on last edited by
                    #9

                    @JonB

                    That was question :P I should have realized that! That makes sense. I made that minor change but still am seeing significant delays in evaluating the filter after the source model has changed.

                    In the original post I described Model C and Model D as QSortFilterProxyModels which basically are to distinguish Items that have been selected or not. After I set the new source, Model C and Model D both reevaluate their filter (which makes sense) but if there are many elements that pass the filter, this operations is taking a while. Any ideas why that may be? Like I meantioned...there are only 300 items in the list total.

                    Here is my filterAcceptsRow Function...It seems pretty straight forward but I could be doing something wrong:

                    bool UnselectedProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
                    {
                        QModelIndex itemIndex = sourceModel()->index(source_row, 0, source_parent);
                        if(itemIndex.isValid() && !source_parent.isValid())
                        {
                            return !itemIndex.data(selected).toBool();
                        }
                        return false;
                    }
                    

                    Remember this takes Model E (a QIdentityProxyModel) as its source where the source of Model E is swapped in and out by user selections on the filter.

                    W 1 Reply Last reply
                    0
                    • W wagner2x

                      @JonB

                      That was question :P I should have realized that! That makes sense. I made that minor change but still am seeing significant delays in evaluating the filter after the source model has changed.

                      In the original post I described Model C and Model D as QSortFilterProxyModels which basically are to distinguish Items that have been selected or not. After I set the new source, Model C and Model D both reevaluate their filter (which makes sense) but if there are many elements that pass the filter, this operations is taking a while. Any ideas why that may be? Like I meantioned...there are only 300 items in the list total.

                      Here is my filterAcceptsRow Function...It seems pretty straight forward but I could be doing something wrong:

                      bool UnselectedProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
                      {
                          QModelIndex itemIndex = sourceModel()->index(source_row, 0, source_parent);
                          if(itemIndex.isValid() && !source_parent.isValid())
                          {
                              return !itemIndex.data(selected).toBool();
                          }
                          return false;
                      }
                      

                      Remember this takes Model E (a QIdentityProxyModel) as its source where the source of Model E is swapped in and out by user selections on the filter.

                      W Offline
                      W Offline
                      wagner2x
                      wrote on last edited by wagner2x
                      #10

                      Now looking at it I don't think it is the filtering that is taking forever...I think it is the DRAW after the model change has completed. I am going to try implementing lazy loading on my model and see if that helps with this.

                      1 Reply Last reply
                      0
                      • W Offline
                        W Offline
                        wagner2x
                        wrote on last edited by wagner2x
                        #11

                        How does Lazy Loading work if you have a chain of models like I have? Do I need to reimplement canFetchmore and FetchMore for all of the models in the chain? Also, any ideas on what could make a ListView QML widget take forever to draw when its model gets switched?

                        1 Reply Last reply
                        0
                        • W Offline
                          W Offline
                          wagner2x
                          wrote on last edited by
                          #12

                          @SGaist

                          I tried implementing canFetchMore and fetchMore on Models C and D but am seeing a QT error

                          QDeclarativeComponent: Cannot create new component instance before completing the previous
                          <Unknown File>: QML VisualDataModel: Error creating delegate

                          I am trying to batch 10 delegates at a time:

                          bool UnselectedModel::canFetchMore(const QModelIndex &parent) const
                          {
                              if (parent.isValid())
                                      return false;
                              //I should be able to use rowCount here right? This should be executed after filteracceptsRow is finished?
                              return (itemsFetched_ < rowCount());
                          }
                          
                          void UnselectedModel::fetchMore(const QModelIndex &parent)
                          {
                              if (parent.isValid())
                                      return;
                                  int remainder = rowCount() - itemsFetched_;
                                  int itemsToFetch = qMin(10, remainder);
                                  if (itemsToFetch <= 0)
                                      return;
                                  beginInsertRows(QModelIndex(), itemsFetched_, itemsFetched_ + itemsToFetch - 1);
                          
                                  itemsFetched_ += itemsToFetch;
                          
                                  endInsertRows();
                                  emit numberPopulated(itemsToFetch);
                          }
                          

                          I also clear the itemsFetched_ member every time we make a sourceModel change on Model E. Do I need to reset Model C and D when the source model changes if I am implementing fetchMore?

                          1 Reply Last reply
                          0
                          • SGaistS Offline
                            SGaistS Offline
                            SGaist
                            Lifetime Qt Champion
                            wrote on last edited by
                            #13

                            Can you provide a minimal compilable example of your code ?

                            That would help take a better look at your situation ?

                            Interested in AI ? www.idiap.ch
                            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                            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