Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QtQuick TableView lazy loading / fetchMore support
Forum Updated to NodeBB v4.3 + New Features

QtQuick TableView lazy loading / fetchMore support

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
3 Posts 2 Posters 1.3k 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.
  • T Offline
    T Offline
    ThoPa
    wrote on last edited by
    #1

    Hi folks,

    I'm currenlty trying try something out with the new (at least for me) Qt Qtuick TableView Component which exists since Qt 5.12.

    So I've written a simple model inheriting QAbstractTableModel and wanted to use lazy loading, so use canFetchMore and fetchMore in my model and let the view request new data, if the user scrolled down.

    It seems, that TableView does not event call these methods, it only displays the first rows returned by rowCount.
    If I change the view to ListView, these methods gets called as expected.

    See the example code below: Page1 won't fetch any new data, except the ListView from Page2 requests it (they share the same model added as context property). Code can be copy-pasted to the stackview project in QtCreator.

    Did I miss anything or is this just not supported?

    Any help or just a "no, you didn't miss anything" would be great :)

    P.S.: I tried Qt 5.12.7 on Windows and 5.13.2 on Linux.

    lazymodel.cpp

    LazyModel::LazyModel(QObject* parent): QAbstractTableModel(parent), m_curcount(20), m_totcount(100) {
    
    }
    
    QVariant LazyModel::headerData(int section, Qt::Orientation orientation, int role) const {
      return QAbstractTableModel::headerData(section, orientation, role);
    }
    
    int LazyModel::rowCount(const QModelIndex &parent) const {
      if (parent.isValid())
        return 0;
    
      return m_curcount;
    }
    
    int LazyModel::columnCount(const QModelIndex& parent) const {
      if (parent.isValid())
        return 0;
    
      return 3;
    }
    
    QVariant LazyModel::data(const QModelIndex &index, int role) const {
      if (index.isValid() && role == Qt::DisplayRole) {
        return QString("%1 %2").arg(index.row()).arg(index.column());
      }
    
      return QVariant();
    }
    
    bool LazyModel::canFetchMore(const QModelIndex& parent) const {
      qDebug() << "canFetchMore";
      if (parent.isValid())
        return false;
    
      return m_curcount < m_totcount;
    }
    
    void LazyModel::fetchMore(const QModelIndex& parent) {
      if (parent.isValid())
        return;
    
      int remainder = m_totcount - m_curcount;
      int itemstofetch = qMin(2, remainder);
    
      beginInsertRows(QModelIndex(), m_curcount, m_curcount + itemstofetch -1);
      m_curcount += itemstofetch;
      endInsertRows();
    }
    
    QHash<int, QByteArray> LazyModel::roleNames() const {
      QHash<int, QByteArray> roles;
      roles[Qt::DisplayRole] = "display";
      return roles;
    }
    

    lazymodel.h

    class LazyModel: public QAbstractTableModel {
        Q_OBJECT
      public:
        LazyModel(QObject* parent = nullptr);
    
        QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    
        // Basic functionality:
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        int columnCount(const QModelIndex& parent = QModelIndex()) const override;
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
        bool canFetchMore(const QModelIndex& parent) const override;
        void fetchMore(const QModelIndex& parent) override;
    
        QHash<int, QByteArray> roleNames() const override;
    
      private:
        int m_curcount;
        int m_totcount;
    };
    

    PageForm1.ui.qml

    Page {
        width: 600
        height: 400
    
        title: qsTr("Page 1")
    
        function rowHeight(row) {
            return root.height / 10
        }
    
        function columnWidth(col) {
            return 100
        }
    
        TableView {
            id: cattable
    
            anchors.fill: parent
    
            flickableDirection: Flickable.VerticalFlick
    
            rowHeightProvider: rowHeight
            columnWidthProvider: columnWidth
    
            model: lazymodel
    
            delegate: Label {
                text: display
            }
    
            ScrollBar.vertical: ScrollBar {
                width: 40
            }
        }
    }
    

    PageForm2.ui.qml

    Page {
        width: 600
        height: 400
    
        title: qsTr("Page 2")
    
        ListView {
            anchors.fill: parent
    
            flickableDirection: Flickable.VerticalFlick
    
            model: lazymodel
    
            delegate: Label {
                text: display
    
                font.pixelSize: 24
            }
        }
    }
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    
    #include "lazymodel.h"
    
    int main(int argc, char *argv[]) {
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
      QGuiApplication app(argc, argv);
    
      QQmlApplicationEngine engine;
    
      LazyModel model;
      engine.rootContext()->setContextProperty("lazymodel", &model);
    
      const QUrl url(QStringLiteral("qrc:/main.qml"));
      QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                       &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
          QCoreApplication::exit(-1);
      }, Qt::QueuedConnection);
      engine.load(url);
    
      return app.exec();
    }
    
    1 Reply Last reply
    0
    • T Offline
      T Offline
      ThoPa
      wrote on last edited by
      #2

      I found a workaround by overloading canFetchMore and fetchMore without arguments:

      Q_INVOKABLE bool LazyModel::canFetchMore() const {
        return canFetchMore(QModelIndex());
      }
      
      Q_INVOKABLE void LazyModel::fetchMore() {
        return fetchMore(QModelIndex());
      }
      

      Then I can call these methods manually if necessary: a) when the content is scrolled down and b) initially to fill the "initial space" (contentHeight). But I have to calculate contentHeight with the models rowCount and the rowHeight myself, because the TableViews contentHeight is not updated as it seems.

      I'm not sure yet about the perfomance.

      Page1Form.ui.qml

      Page {
          id: root
          width: 600
          height: 400
      
          title: qsTr("Page 1")
      
          function rowHeight(row) {
              return root.height / 10
          }
      
          function columnWidth(col) {
              return 100
          }
      
          TableView {
              id: cattable
      
              anchors.fill: parent
      
              flickableDirection: Flickable.VerticalFlick
      
              rowHeightProvider: rowHeight
              columnWidthProvider: columnWidth
      
              model: lazymodel
      
              delegate: Label {
                  text: display
              }
      
              onContentYChanged: {
                  if (contentY + height >= contentHeight) {
                      if (model.canFetchMore()) {
                          model.fetchMore();
                      }
                  }
              }
      
              ScrollBar.vertical: ScrollBar {
                  width: 40
              }
          }
      
          Component.onCompleted: {
              while (lazymodel.rowCount() * rowHeight(-1) <= cattable.height) {
                  if (lazymodel.canFetchMore()) {
                      lazymodel.fetchMore();
                  }
                  else {
                      break;
                  }
              }
          }
      }
      
      1 Reply Last reply
      0
      • Y Offline
        Y Offline
        Yunfeng
        wrote on last edited by
        #3

        Maybe you should use DelegateModel.

        eg:
        TableView {
        model: DelegateModel {
        model: xxx
        delegate: xxx
        }
        }

        1 Reply Last reply
        0

        • Login

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