【问题标题】:Stop QTableView from scrolling as data is added above current position当数据添加到当前位置上方时,停止 QTableView 滚动
【发布时间】:2017-02-24 21:45:31
【问题描述】:

我有一个简单的 QTableView,它带有一个 QSortFilterProxyModel 和一个继承自 QAbstractTableModel 的自定义 TableModel 子类的源模型。模型会使用其他行动态更新。

我的问题是:如果我按列对表格进行排序,然后滚动到特定行,然后在该行上方添加更多行,则会将该行向下推。数据输入的速度足够快,因此很难单击行来编辑它们而不改变光标下方的行。

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

【问题讨论】:

  • 恐怕这会变得很棘手。一个最少的解决方法可能是记住“特定行”并在插入行后使用scrollTo()。一个令人满意的解决方案可能需要重载 QTableViewQAbstractItemView 以重新实现各自的布局方法...
  • 这看起来很有希望:QTableView::rowViewportPosition()

标签: c++ qt


【解决方案1】:

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

可以使用信号处理程序在插入行之前和之后检索它。

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

一个最小的代码示例:

#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 中编译并测试了这个:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多