Details
-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
5.12.3
-
None
Description
visualRect( indexAt( point ) ).contains( point ) is not always true against my expectations.
This question arose while trying to get all visible rows for a QTreeView. Some suggestions include beginning at indexAt( rect()>topLeft() ) (more correctly this should be viewport()>rect()->topLeft(), I think). As I wasn't sure whether this worked especially when the viewport is scrolled, I printed out the visual rectangles of all items and noticed that they start at x position 20 because of the expand/collapse icon. Therefore, I thought that that approach would not work. However, it does work, which seems like a bug to me.
The thing is. It would be nice if it worked because of the above-mentioned use-case of iterating over the visible rows. But it's completely undocumented and inconsistent behavior, which I would categorize as a bug and therefore am not keen to use. Except, if you told me that it's by design, therefore this issue.
Here is a small example for a QTreeView with one item and one child item and two columns. The visual rects are drawn in green. Points used for indexAt are drawn in red if the returned index is valid and yellow if the returned index is invalid (because there is no item at that position). Also the is an error output if the visualRect of the returned item does not contain the point used for indexAt.
#include <QApplication> #include <QDebug> #include <QHeaderView> #include <QPainter> #include <QTreeWidget> class CustomTreeWidget : public QTreeWidget { public: explicit CustomTreeWidget( QWidget* parent = nullptr ) : QTreeWidget( parent ) { const auto item = new QTreeWidgetItem( { "Parent" } ); item->addChild( new QTreeWidgetItem( { "Child" } ) ); insertTopLevelItem( 0, item ); expandAll(); setHeaderHidden( true ); setColumnCount( 2 ); header()->setStretchLastSection( false ); }protected: void paintEvent( QPaintEvent* event ) override { QPainter painter( viewport() ); const auto vrect = viewport()->rect(); painter.fillRect( vrect, Qt::blue ); // background /* draw item visual rects */ for ( auto index = indexAt( vrect.topLeft() ); index.isValid(); index = indexBelow( index ) ) { qDebug() << "at index:" << index << visualRect( index ); painter.fillRect( visualRect( index ), Qt::green ); } /* draw small boxes to show points and the indexAt return values (white if some found, black if not) */ const auto pointSize = QMargins{ 5,5,5,5 }; const auto crect = visualRect( model()->index( 0, 0, model()->index( 0, 0, {} ) ) ); const auto crect2 = visualRect( model()->index( 0, 1, model()->index( 0, 0, {} ) ) ); for ( const auto& point : { vrect.topLeft(), vrect.topRight(), vrect.bottomLeft(), vrect.bottomRight(), crect.topLeft(), crect.topRight(), crect.bottomLeft(), crect.bottomRight(), crect.topLeft() - QPoint( 12, 0 ), crect.bottomLeft() - QPoint( 12, 0 ), crect.topRight() + QPoint( 12, 0 ), crect.bottomRight() + QPoint( 12, 0 ), crect.bottomLeft() + QPoint( 0, 12 ), crect2.topRight(), crect2.bottomRight(), crect2.topRight() + QPoint( 12, 0 ), crect2.bottomRight() + QPoint( 12, 0 ) } ) { painter.fillRect( QRect( point, QSize{} ).marginsAdded( pointSize ), indexAt( point ).isValid() ? Qt::red : Qt::yellow ); qDebug() << point << "->" << visualRect( indexAt( point ) ) << visualRect( indexAt( point ) ).contains( point ) << indexAt( point ); if ( indexAt( point ).isValid() && !visualRect( indexAt( point ) ).contains( point ) ) { qDebug() << "ERROR: visualRect does not contain point used for indexAt!"; } } qDebug() << ""; QTreeWidget::paintEvent( event ); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); CustomTreeWidget tree; tree.show(); return app.exec(); }
The rendered output is attached. As can be seen, the left side for the expand/collapse icons are the online exceptions, everything else behaves such that the visual rect contains the point used for indexAt. The text output is here:
at index: QModelIndex(0,0,0x55b30d95a890,QTreeModel(0x55b30d9aebc0)) QRect(20,0 80x17) at index: QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QRect(40,17 60x17) QPoint(0,0) -> QRect(20,0 80x17) false QModelIndex(0,0,0x55b30d95a890,QTreeModel(0x55b30d9aebc0)) ERROR: visualRect does not contain point used for indexAt! QPoint(253,0) -> QRect(0,0 0x0) false QModelIndex(-1,-1,0x0,QObject(0x0)) QPoint(0,189) -> QRect(0,0 0x0) false QModelIndex(-1,-1,0x0,QObject(0x0)) QPoint(253,189) -> QRect(0,0 0x0) false QModelIndex(-1,-1,0x0,QObject(0x0)) QPoint(40,17) -> QRect(40,17 60x17) true QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(99,17) -> QRect(40,17 60x17) true QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(40,33) -> QRect(40,17 60x17) true QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(99,33) -> QRect(40,17 60x17) true QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(28,17) -> QRect(40,17 60x17) false QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) ERROR: visualRect does not contain point used for indexAt! QPoint(28,33) -> QRect(40,17 60x17) false QModelIndex(0,0,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) ERROR: visualRect does not contain point used for indexAt! QPoint(111,17) -> QRect(100,17 100x17) true QModelIndex(0,1,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(111,33) -> QRect(100,17 100x17) true QModelIndex(0,1,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(40,45) -> QRect(0,0 0x0) false QModelIndex(-1,-1,0x0,QObject(0x0)) QPoint(199,17) -> QRect(100,17 100x17) true QModelIndex(0,1,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(199,33) -> QRect(100,17 100x17) true QModelIndex(0,1,0x55b30d9623d0,QTreeModel(0x55b30d9aebc0)) QPoint(211,17) -> QRect(0,0 0x0) false QModelIndex(-1,-1,0x0,QObject(0x0)) QPoint(211,33) -> QRect(0,0 0x0) false QModelIndex(-1,-1,0x0,QObject(0x0))