Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QTreeView::expand/collapse in loop, update view on each iteration
Forum Update on Monday, May 27th 2025

QTreeView::expand/collapse in loop, update view on each iteration

Scheduled Pinned Locked Moved Solved General and Desktop
3 Posts 2 Posters 998 Views
  • 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
    MScholli
    wrote on last edited by
    #1

    Hi,

    I am using my own treeView, which is derived from QTreeView. I have customized keyPressEvent such that when the user presses Key_Left or Key_Right on an expanded/collapsed item, all its children (only one level lower) are being collapsed or expanded. For example, when the children are collapsed the following code is used to expand:

        if ( event->key() == Qt::Key_Right && currentIndex.isValid() )
        {
            if (this->isExpanded(currentIndex))
            {
                for (int i=0; i<model->rowCount(currentIndex.parent()); ++i) {
                    this->expand(model->index(i, 0, currentIndex.parent()));
                }
            } else {
                this->expand(currentIndex);
            }
        }
    

    This works well, but there is a significant lag in the app between triggering the for-loop and updating the view. I couldn't find a way to force-update the view after each iteration, e.g., using an update or repaint call.
    Ideally, I'd like the view to update after each iteration such that it "rolls" open from the top when expanding or closes from below when collapsing, so that the user sees the tree expanding/collapsing like an animation instead of having the app frozen for a second while the loop iterates.
    How could I do this?

    JonBJ 1 Reply Last reply
    0
    • M MScholli

      Hi,

      I am using my own treeView, which is derived from QTreeView. I have customized keyPressEvent such that when the user presses Key_Left or Key_Right on an expanded/collapsed item, all its children (only one level lower) are being collapsed or expanded. For example, when the children are collapsed the following code is used to expand:

          if ( event->key() == Qt::Key_Right && currentIndex.isValid() )
          {
              if (this->isExpanded(currentIndex))
              {
                  for (int i=0; i<model->rowCount(currentIndex.parent()); ++i) {
                      this->expand(model->index(i, 0, currentIndex.parent()));
                  }
              } else {
                  this->expand(currentIndex);
              }
          }
      

      This works well, but there is a significant lag in the app between triggering the for-loop and updating the view. I couldn't find a way to force-update the view after each iteration, e.g., using an update or repaint call.
      Ideally, I'd like the view to update after each iteration such that it "rolls" open from the top when expanding or closes from below when collapsing, so that the user sees the tree expanding/collapsing like an animation instead of having the app frozen for a second while the loop iterates.
      How could I do this?

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @MScholli
      QTreeView inherits from QWidget, and all QWidgets have a number of update() and repaint() methods, all on page https://doc.qt.io/qt-5/qwidget.html.

      I don't know whether these would make it behave the way you want, you'd have to try.

      Or, it's not great, but if you put a https://doc.qt.io/qt-5/qcoreapplication.html#processEvents call into your for loop that may make the redraw show up.

      If these don't work, or don't show as you'd like, you should be able to implement it explicitly yourself. When user presses key, start a repeating QTimer with a small-ish millisecond interval. On each tick when it calls your slot, expand/collapse one node, next one on next timer click. You will have to maintain state information in the class instance as to how far you got last time through the children so you continue from there --- this is usual with timer code. This approach has the event loop running, so should be no need for any update/repaint/processEvents().

      1 Reply Last reply
      2
      • M Offline
        M Offline
        MScholli
        wrote on last edited by
        #3

        @JonB
        Thanks a lot for your help and suggestions. update() or repaint() didn't work, but qApp->processEvents() did guide me in the right direction.

        I should have mentioned that my app uses QTreeView->setAnimated(true), which complicated things quite a bit. AFAIK, the animation used in QTreeView uses only private properties, therefore I could not figure out if and how I can connect to a finished() signal.

        Instead, I've changed the code above to call a expandIndex, which starts expanding the top row, runs processEvents() in an infinite loop until the state() of the QTreeView is no longer in QAbstractItemView::State::AnimatingState and then calls itself to expand the next row until all rows have been expanded.
        Collapsing the tree works the same, but starts at the bottom.

        void treeView::expandIndex() {
        
            if (_expandedRowCount < model()->rowCount(_parentIndex)) {
                auto index = model()->index(_expandedRowCount, 0, _parentIndex);
                this->expand(index);
                _expandedRowCount += 1;
                forever {
                    qApp->processEvents();
                    if (state() != QAbstractItemView::State::AnimatingState)
                        break;
                }
                expandIndex();
        
            }
        }
        
        void treeView::keyPressEvent(QKeyEvent *event) {
        
            auto currentIndex = this->currentIndex();
        
            if ( event->key() == Qt::Key_Right && currentIndex.isValid() )
            {
                if (this->isExpanded(currentIndex))
                {
                    _expandedRowCount = 0;
                    _parentIndex = currentIndex.parent();
                    expandIndex();
                } else {
                    this->expand(currentIndex);
                }
            }
        }
        

        This is probably not the most elegant solution and wasteful due to the forever loop.
        If anybody has suggestions to simplify this and/or get rid of the forever loop I'm all ears!

        1 Reply Last reply
        1

        • Login

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