Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-77772

QTreeView indexAt/visualRect is inconsistent

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 5.12.3
    • Widgets: Itemviews
    • None
    • Linux/X11

    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))
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            uiohbgq uiohbgq
            Votes:
            3 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes