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

QTreeView::visualRect and QTreeView::indexAt don't appear to work



  • I'm trying to cache data from the top index so I can scroll the treeview to the right spot after a serious update to the data.

    I've got a slot to handle the modelAboutToBeReset() signal, and it's getting called correctly, and I see that the model is still populated (about 82 rows, for example).

    ttv() is a member accessor to get the QTreeView object. (And further below, tvm() is an accessor for the model, which is custom-made.)

    If I try:

    QModelIndex top = ttv()->indexAt(ttv()->rect().topLeft());

    I get an invalid index. I have also tried passing in QPoint(0,0) and QPoint(5,5), and got the same result.

    I thought I know, I'll reverse engineer this by iterating through the items in the model and get the visualArea, and stop at the first valid one. Then I might get a clue as to what indexAt() is looking for. So, I updated the test code:

    QModelIndex top = ttv()->indexAt(ttv()->rect().topLeft());
    QModelIndex bot = ttv()->indexAt(ttv()->rect().bottomLeft());
    bool found = top.isValid();
    for (int r = 0, nr = tvm()->rowCount();
         !found && r < nr; ++r)
    {
        for (int c = 0, nc = tvm()->columnCount();
             !found && c < nc; ++c)
        {
            auto inx = tvm()->index(r,c);
            QRect vt = ttv()->visualRect(inx);
            qDebug() << inx << vt;
            if (!vt.isValid()) {
                continue;
            }
            found = true;
            auto ctr = vt.center();
            auto zzz = ttv()->indexAt(ctr);
            top = inx;
        }
    }
    if (!top.isValid()) return;
    

    That call to visualRect() never yields a valid rect. I see it iterating through all items in the model, and each one yields a QRect(0,0 0x0). I can imagine I might need to pass in a slightly massaged coordinate to indexAt(), but shouldn't visualRect() return some proper rectangles for any of the items? About 40 or so rows are visible!

    Not sure what I'm doing wrong here, but I'm not getting any traction with either of these APIs.



  • Okay, I had absolutely believed that I had tried making the call to my savePos() function to try to diagnose whether the signal had something to do with it, yet when I disable the connect()ion altogether and call it explicitly, now these APIs want to behave like good little children and do what they're supposed to.

    So, essentially I shot myself in the foot by handling this with modelAboutToBeReset(), which implicitly trashes the internal cache of visible indexes.

    D'oh! Argh, etc.

    Thanks for the help, folks.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    I may be missing your goal but wouldn't use QTreeView::scrollTo do the job ?


  • Lifetime Qt Champion

    Apart from @SGaist 's question - is ttv()->model() really tvm() or is there a proxy model in between?



  • @SGaist Thanks for the speedy reply.

    scrollTo() will be what I will use when I've recovered the index based on what I've cached. The model may be changing quite a bit, so I'm trying to find the locale of the currently viewed set of objects, cache that information, then when the model is reset, I'll try to scroll back to the approximate location, perhaps simply bringing that item back to the top if I can, or finding data in the same range (it's a table of date-stamped rows).

    Since I can't see what's currently in the viewport, I can't figure out what data to grab onto, which I would use to find on the flip side after the model update.

    Does that make sense? Surely I'm making some simple mistake or have a wrong assumption. Why wouldn't these APIs "just work"?



  • @Christian-Ehrlicher Yes, ttv()->model() == tvm(). No proxy. At some point I may use a sort proxy, but I wanted to keep it simple for the time being.



  • @Jos_Rarebit said in QTreeView::visualRect and QTreeView::indexAt don't appear to work:

    modelAboutToBeReset()

    @Jos_Rarebit you have mentioned about handling model reset in modelAboutToBeReset() slot.
    If model is getting reset, then old index may not valid for the new model.

    https://doc.qt.io/qt-5/qabstractitemmodel.html#modelReset



  • @nagesh I'm not intending to cache any indexes, just find what the visible indexes are. This code is being invoked in response to modelAboutToBeReset(), so the index changes haven't happened yet, anyway. In fact, if I explicitly call the function before the call which causes the model reset, it still does not work.

    Anyone have any ideas?


  • Lifetime Qt Champion

    Shouldn't you store some identifying data of the visible indexes before the model is changed, then look for the same data and call scrollTo with the appropriate index ?



  • @SGaist Yes. That's exactly what I intend to do. Sorry if that wasn't clear.
    The issue is that there doesn't seem to be a way of determining what the visible indexes are. Hence the title of this topic. Or, rather, there is a presumed way to do this, but the API(s) to be used do)es(n't seem to work as expected. So, that's weird!

    Stepping into the Qt code a bit (what I wanted to avoid!) it seems that QTreeView::visualRect() has no "viewIndex" data for the items in the model. I.e., here's the spot in QTreeView::visualRect() where it appears to be punting:

    // QTreeView::visualRect...
    Q_D(const QTreeView);
    
    if (!d->isIndexValid(index) || isIndexHidden(index))
        return QRect();
    
    d->executePostedLayout();
    
    int vi = d->viewIndex(index);
    if (vi < 0)
        return QRect();
    

    It returns because vi == -1.

    I am wondering if my model implementation is deficient in some way. It compiles, so the basic overrides are present. It's basically a model interface of a std::list<> of some class objects, so I don't do anything with insertRows(), I just pretend to reset the model when the underlying data gets rebuilt. But I kind of wonder if the view needs some extra hints to cache some geometry about what it's displaying for this purpose. But it's displaying everything in my model perfectly well, so what could it be? If I emit signals about rows being inserted, that doesn't seem to do anything magical, but then again I'm still not implementing insertRows directly...

    Hmm... I guess I'll step into that viewIndex function...



  • @Jos_Rarebit

    so I don't do anything with insertRows(), I just pretend to reset the model when the underlying data gets rebuilt.
    

    Refer to addressbook example in Qt for dynamic model.
    I think you should implement insertRows() if your model is dynamic



  • @nagesh Thanks for the suggestion. Might be what I have to do. However:

    I think this might be a signal-slot order issue that I'll have to work-around. Or use a different signal/slot. I have been using modelAboutToBeReset() signal, but the treeview itself also uses that, and it gets it first.

    I set a breakpoint in QTreeViewPrivate::_q_modelAboutToBeReset(),

    void QTreeViewPrivate::_q_modelAboutToBeReset()
    {
        viewItems.clear();
    }
    

    which is the treeview's handler, and it is getting called before my own handler.

    Makes sense, given how the signals/slots are generally processed in order of creation.



  • Okay, I had absolutely believed that I had tried making the call to my savePos() function to try to diagnose whether the signal had something to do with it, yet when I disable the connect()ion altogether and call it explicitly, now these APIs want to behave like good little children and do what they're supposed to.

    So, essentially I shot myself in the foot by handling this with modelAboutToBeReset(), which implicitly trashes the internal cache of visible indexes.

    D'oh! Argh, etc.

    Thanks for the help, folks.