QTreeView erroneously fetches all items with fetchMore()



  • Hi, I'm developing a program which on user's request fetches some data from Internet, stores it in a QAbstractTableModel derived model and shows it in QTreeView.

    My model reimplements canFetchMore() and fetchMore() functions so additional data is only downloaded when the user scrolls to the end of the list. Because I don't have the data at hand when it's needed (it must be downloaded first), I can't just add new data in fetchMore(). So instead I just make a request in fetchMore() and when the data arrives it is added to the model.

    And here is the problem: when my model is used by QTreeView, the view calls fetchMore() automatically until canFetchMore() returns false (so all data items were fetched). This is a different behavior from QListView and QTableView which both correctly call fetchMore() only once and when later the data arrives they stop calling fetchMore() unless the user scrolls to the end of the list.

    I've prepared a simplified example. There is a base model class and two derived models which reimplement fetchMore(): first model adds items synchronously in fetchMore() and the other one asynchronously with QTimer::singleshot() invoked in fetchMore(). QTreeView works correctly with the synchronous model but not with the asynchronous one.

    Model base class:

    class ModelBase : public QAbstractListModel
    {
    public:
    // {...}
       void createMoreItems();
    protected:
       bool moreItemsAvailable; // true if items.size() <  maxItems
    private:
        std::vector<QString> items;
        const int maxItems = 1000; // don't fetch more then this
        const int itemsPerRequest = 50;
    };
    
    bool ModelBase::canFetchMore(const QModelIndex &parent) const
    {
        return moreItemsAvailable;
    }
    
    void ModelBase::createMoreItems()
    {
        if (!moreItemsAvailable)
            return;
    
        QStringList newItems;
        for (int i = 0; i < itemsPerRequest; ++i)
            newItems.push_back(QString("Item ") + QString::number(items.size() + i));
    
        beginInsertRows(QModelIndex(), items.size(), items.size() + newItems.size() - 1);
    
        items.insert(items.end(), newItems.begin(), newItems.end());
        moreItemsAvailable = (items.size() <  maxItems ? true : false);
    
        endInsertRows();
    }
    

    Synchronous model adds items directly in fetchMore():

    void SynchronousModel::fetchMore(const QModelIndex &parent)
    {
        createMoreItems();
    }
    

    Asynchronous model adds items asynchronously:

    void AsynchronousModel::fetchMore(const QModelIndex &parent)
    {
        QTimer::singleShot(0, this, &ModelBase::createMoreItems);
    }
    

    I also created a simple app that shows the problem: synchronous model works correctly with all views (QListView, QTableView, QTreeView) but asynchronous model doesn't work correctly with QTreeView.

    Screenshot: https://bugreports.qt.io/secure/attachment/71496/treeviewtest.png

    The project archive: https://bugreports.qt.io/secure/attachment/71497/treeviewtest.zip

    So I wonder if I don't understand something about Model/View in Qt or it's a Qt bug?

    Also, I use QTreeView instead of QTableView with my QAbstractTableModel derived class because I want that mouse hover highlights entire row instead of a single cell which is probably not possible in QTableView without some workarounds.0_1523484146080_treeviewtest.png



  • This looks like https://bugreports.qt.io/browse/QTBUG-18075 - at least the backtrace looks like the description there



  • @Christian-Ehrlicher: Thanks for reply. I've also tried to find bugs related to fetchMore():
    https://bugreports.qt.io/browse/QTBUG-18075?jql=text ~ "fetchmore"
    Without success, however. I'm thinking now about reporting a bug but I'm worried that it's a bug in my own code.



  • @atomblender said in QTreeView erroneously fetches all items with fetchMore():

    I'm worried that it's a bug in my own code.

    Yes and no. I can see how you want it to work but the problem is you are telling lies to your view in the async way.

    You return true from canFetchMore so the view fetches and lays out the new data but you are delaying when that data is made available so the view thinks it still needs to lay it out and so repeats the process. This ends when canFetchMore returns false so the view gives up. At this point the model processes all the data fetching and signals the view to repaint itself



  • @VRonin: Thanks for your reply. What is confusing is that the documentation for canFetchMore(), fetchMore() and Model/View Programming is not clear whether it is mandatory to insert new items synchronously and that QListView and QTableView work correctly with asynchronous item fetching. This inconsistency in behavior leads me to believe that it might be a bug in QTreeView. Another possibility is that there is something special about QTreeView.



  • QTreeView, differently from the others need to query individual items to lay out itself (basically it needs to know if an item in the first column has children or not).

    I'm not saying this behaviour is totally intended and ideal, what I'm saying is that your use case is in a grey area of who's to blame and I wouldn't be surprised if your bug report ends up with an "invalid" sticker on it (i'm not involved in Qt development at all so I might be epically wrong)



  • I've reported a bug: 67693

    If it's a real bug then the issue will hopefully be fixed. If it's a QTreeview's limitation or the bug won't be fixed than I will have to find a workaround. At the very least the matter will be clarified.



  • @atomblender Put the affected version as the latest one or you might get ignored



  • @VRonin: Ok, updated to 5.10.1


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.