9

Is there any way to get a list of currently visible items in QAbstractItemView? And, if it possible, to receive any notifications about changing of this list.

Upd: I'm asking exactly about QAbstractItemView or QTreeView with non-plain structure, not QTableView.

Upd2: I'm implementing tree view model with checkboxes. I want next behavior (same for checking/uncheking):

  • If one of checkbox is checked - then all childs must be checked
  • If all child checkboxes are checked - then parent check box should be checked too. And same for parent of parent, and so on...

Check state are monitored/modified by external data source, so I need a mechanism to update all changed children/parents. dataChanged signal is not enough for me because it is very expansive to build a list of all changed QModelIndex for updating. And it is not necessary at all, because all fresh data will be picked from QAbstractItemModel::data.

I found next dirty hack to update all items: emit dataChanged( QModelIndex(), QModelIndex() ); but it's undocumented for invalid indexes.

So, I need a way to force all visible items redraw they content with fresh data.

RAM
  • 2,257
  • 2
  • 19
  • 41
Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61

5 Answers5

13

You can get the topleft and bottom right cell by calling:

tableview->indexAt(tableview->rect().topLeft())
tableview->indexAt(tableview->rect().bottomRight())

To get notification of change, reimplement the virtual function of qabstractscrollarea

scrollContentsBy

This function is called when the view port is scrolled. call QTableView::scrollContentsBy and then do whatever you need to.

Community
  • 1
  • 1
Min Lin
  • 3,177
  • 2
  • 19
  • 32
  • 1
    I'm not asking about simple QTableView, it's clear for me... I'm asking about custom tree-based view with my own items delegate. Scroll notofication are not enough because they will not cover expand and collapse events. – Dmitry Sazonov Apr 05 '13 at 15:04
  • You can still use the indexAt to get the indices in the viewport. And connect scroll expand and collapse signals to a slot that will recalculate the indices. I think if you don't wanna do something dirty, like intercept the paint method, then connecting to all existing signals that change the viewport is the way to go. – Min Lin Apr 05 '13 at 15:47
  • 2
    indexAt is not acceptable, because I use QTreeView and some of items doesn't fit all row. See update of original question. – Dmitry Sazonov Apr 05 '13 at 17:20
  • `rect().topLeft()` is by [definition](https://doc.qt.io/qt-5/qwidget.html#rect-prop) always `(0,0)`. For QTreeView, the visualRect for an index does for some reason exclude the expand/collaps triangle icon, so `visualRect().left() >= 20` in my case. Therefore indexAt should not work. For some reason it still works... but I would be sceptical maybe this is unintended behavior (or an undocumented special case). – mxmlnkn Aug 22 '19 at 09:07
4

For QTreeView, the list of visible items can be traversed like this:

QTreeView& tv (yourTreeView);

// Get model index for first visible item
QModelIndex modelIndex = tv.indexAt(tv.rect().topLeft());

while (modelIndex.isValid())
{
    // do something with the item indexed by modelIndex
    ...
    // This navigates to the next visible item
    modelIndex = tv.indexBelow(modelIndex);
}
Adrian W
  • 4,563
  • 11
  • 38
  • 52
  • 2
    There may be no index at top-left. It depends on style paddings / delegates implementation and other stuff. – Dmitry Sazonov Mar 14 '14 at 09:14
  • 1
    // This navigates to the next visible item modelIndex = tv.indexBelow(modelIndex); Will loop till end of the tree. May be here only we can put one more check if its in tv.rect() regeion , we can break the loop or check ItemAt(index). & check if y- axis value is grated than tv.tect.bottomLeft().y() thank break the loop. – NDestiny Nov 27 '15 at 06:09
  • 1
    To be more exact indexBelow, seems to traverse all expanded items not just those visible in the viewport. Also, instead of comparing with tv.rect(), I think tv.viewport.rect() should be used because it accounts for the tree/table header. As for the indexAt( topLeft() ) issue mentioned by @DmitrySazonov I opened an issue / question [here](https://bugreports.qt.io/browse/QTBUG-77772). – mxmlnkn Aug 22 '19 at 11:57
1

Method 1

i, j = table.indexAt(table.rect().topLeft()).row(), table.indexAt(table.rect().bottomLeft()).row() - 1

Method 2

i, j = table.rowAt(0), table.rowAt(table.height()) - 1
BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28
0

I think that there are no cases where list of visible items is requred. In case of correct model implementation all items are updated automatically. Hard part of implementation - force children and parents to update. I wrote following code:

bool TreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
case Qt::CheckStateRole:
        {
            TreeItemList updateRangeList;  // Filled with items, in which all childred must be updated
            TreeItemList updateSingleList; // Filled with items, which must be updated
            item->setCheckState( value.toBool(), updateRangeList, updateSingleList ); // All magic there
            foreach ( TreeAbstractItem *i, updateRangeList )
            {
                const int nRows = i->rowCount();
                QModelIndex topLeft = indexForItem( i->m_childs[0] );
                QModelIndex bottomRight = indexForItem( i->m_childs[nRows - 1] );
                emit dataChanged( topLeft, bottomRight );
            }
            foreach ( TreeAbstractItem *i, updateSingleList )
            {
                QModelIndex updateIndex = indexForItem( i );
                emit dataChanged( updateIndex, updateIndex );
            }
        }
Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61
0

i always update whole QAbstractTableModel with:

emit dataChanged(index(0, 0), index(rowCount(), columnCount()-1)); // update whole view
roberto
  • 577
  • 6
  • 5