Stop QTableView from scrolling as data is added above current position

前端 未结 1 1607
北荒
北荒 2020-12-03 20:04

I have a simple QTableView with a QSortFilterProxyModel and a source model of a custom TableModel subclass that inherits from QAbstractTableModel. The model is dynamically u

相关标签:
1条回答
  • 2020-12-03 20:43

    QTableView::rowViewportPosition() can be used to get the current view port position which has to be corrected if something is inserted before current index.

    It can be retrieved before and after insertion of a row using signal handlers.

    Thus, the scrolling can be adjusted accordingly in the signal handler after the insertion. This is done changing the value of the vertical scrollbar. (The vertical scroll mode is changed to QTableView::ScrollPerPixel to ensure correct vertical adjustment.)

    A minimal code sample:

    #include <iostream>
    
    #include <QApplication>
    #include <QMainWindow>
    #include <QScrollBar>
    #include <QStandardItemModel>
    #include <QTableView>
    #include <QTimer>
    
    enum { NCols = 2 }; // number of columns
    enum { Interval = 1000 }; // interval of auto action
    enum { NRep = 5 }; // how often selected auto action is repeated
    
    // fills a table model with sample data
    void populate(QStandardItemModel &tblModel, bool prepend)
    {
      int row = tblModel.rowCount();
      if (prepend) tblModel.insertRow(0);
      for (int col = 0; col < NCols; ++col) {
        QStandardItem *pItem = new QStandardItem(QString("row %0, col %1").arg(row).arg(col));
        tblModel.setItem(prepend ? 0 : row, col, pItem);
      }
    }
    
    // does some auto action
    void timeout(QTimer &timer, QStandardItemModel &tblModel)
    {
      static int step = 0;
      ++step;
      std::cout << "step: " << step << std::endl;
      switch (step / NRep % 3) {
        case 0: break; // pause
        case 1: populate(tblModel, false); break; // append
        case 2: populate(tblModel, true); break; // prepend
      }
    }
    
    // managing the non-scrolling when something is inserted.
    struct NoScrCtxt {
      QTableView &tblView;
      int y;
      NoScrCtxt(QTableView &tblView_): tblView(tblView_) { }
    
      void rowsAboutToBeInserted()
      {
        y = tblView.rowViewportPosition(tblView.currentIndex().row());
      }
    
      void rowsInserted()
      {
        int yNew = tblView.rowViewportPosition(tblView.currentIndex().row());
        if (y != yNew) {
          if (QScrollBar *pScrBar = tblView.verticalScrollBar()) {
            pScrBar->setValue(pScrBar->value() + yNew - y);
          }
        }
      }
    };
    
    int main(int argc, char **argv)
    {
      QApplication app(argc, argv);
      // build some GUI
      QMainWindow win;
      QStandardItemModel tblModel(0, NCols);
      for (int i = 0; i < 10; ++i) populate(tblModel, false);
      QTableView tblView;
      tblView.setVerticalScrollMode(QTableView::ScrollPerPixel);
      tblView.setModel(&tblModel);
      win.setCentralWidget(&tblView);
      win.show();
      // setup a "no-scroll manager"
      NoScrCtxt ctxt(tblView);
      QObject::connect(&tblModel, &QStandardItemModel::rowsAboutToBeInserted,
        [&ctxt](const QModelIndex&, int, int) { ctxt.rowsAboutToBeInserted(); });
      QObject::connect(&tblModel, &QStandardItemModel::rowsInserted,
        [&ctxt](const QModelIndex&, int, int) { ctxt.rowsInserted(); });
      // initiate some auto action
      QTimer timer;
      timer.setInterval(Interval); // ms
      QObject::connect(&timer, &QTimer::timeout,
        [&timer, &tblModel]() { timeout(timer, tblModel); });
      timer.start();
      // exec. application
      return app.exec();
    }
    

    I compiled and tested this in Windows 10, VS2013, Qt 5.7:

    0 讨论(0)
提交回复
热议问题