当数据添加到当前位置上方时停止 QTableView 滚动

2021-12-09 00:00:00 qt c++

我有一个简单的 QTableView,带有一个 QSortFilterProxyModel 和一个从 QAbstractTableModel 继承的自定义 TableModel 子类的源模型.该模型使用附加行动态更新.

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 updated with additional rows.

我的问题是:如果我按列对表格进行排序,然后滚动到特定行,然后在该行上方添加更多行,则会将该行向下推.数据输入的速度足够快,以至于很难在光标下方的行发生变化的情况下单击行进行编辑.

My problem is this: If I sort the table on a column, then scroll to a specific row, and then more rows are added above this row it pushes the row down. Data is coming in fast enough that it makes it difficult to click on rows to edit them without the row changing underneath my cursor.

有没有办法阻止表格滚动并保持表格相对于选定行的位置?

Is there a way to stop the table from scrolling and maintain the position of the table relative to say a selected row?

推荐答案

QTableView::rowViewportPosition() 可用于获取当前视口位置,如果之前插入了某些内容,则必须更正该位置当前索引.

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.

因此,插入后可以在信号处理程序中相应地调整滚动.这是通过更改垂直滚动条的值来完成的.(垂直滚动模式更改为QTableView::ScrollPerPixel 以确保正确的垂直调整.)

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

一个最小的代码示例:

#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();
}

我在 Windows 10、VS2013、Qt 5.7 中编译并测试了这个:

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

相关文章