6

我的类 DataTable 是从 QAbstractTableModel 派生的。它在内部使用 QSqlTableModel 对象从 db 表中获取数据。它代表数据库中每一行的记录(它做得更多,但记录计数始终是数据库表中的行数)。

使用 MySql,我的 DataTable::rowCount() 实现只需调用 QSqlTableModel 上的 rowCount(),效果很好。

现在使用 SQLite,如果 db 表中有超过 256 行,Qt 的 SQLite 驱动程序会返回 256 的行数,所以我的 DataTable 类也返回 256 - 这是错误的。文档告诉我打电话给while (sql_model->canFetchMore()) sql_model->fetchMore();. 在创建内部 QSqlTableModel 之后立即调用 fetchMore() 实际上会导致以下 rowCount() 调用返回正确的值。但是一旦数据库中的某些内容发生了变化(我的类将在 QSqlTableModel 上调用 insertRow() 或 setData()),下一个 QSqlTableModel::rowCount() 调用将再次返回 256。

数据库仅由我的类修改,而我的类又使用该特定的 QSqlTableModel 对象(或使用我的 DataTable 作为模型的视图可以更新某些内容)。所以没有其他进程可以将行插入数据库。

那么我的 DataTable 类何时应该为 rowCount() 调用 fetchMore() 以始终返回实际的行数?
我在想我的班级应该将 QSqlTableModel 发出的一些信号连接到一个可以调用 fetchMore() 的插槽,尽管我不确定这是否是正确/可靠的方法?


更新

这是一些代码来演示基本问题。

QSqlTableModel *model = new QSqlTableModel(0, database); //QSqlDatabase
model->setTable("tablename");
qDebug() << "0 row count" << model->rowCount(); //0 row count 0 
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
qDebug() << "1 row count" << model->rowCount(); //1 row count 256 
while (model->canFetchMore()) model->fetchMore();
qDebug() << "2 row count" << model->rowCount(); //2 row count 1520 
//... other methods ...
model->setData(model->index(0, 0), "TEST");
model->submitAll();
qDebug() << "3 row count" << model->rowCount(); //3 row count 256 
while (model->canFetchMore()) model->fetchMore();
qDebug() << "4 row count" << model->rowCount(); //4 row count 1520 

加载 sql 模型后,rowCount() 返回 256 (1),因此必须调用 fetchMore()。rowCount() 然后返回实际的行数。
稍后,数据被更改,之后 rowCount() 再次返回 256 (3)。

所以似乎 fetchMore() 必须在 sql 模型上的每次写入操作之后调用。但与其将这个 while/canFetchMore()/fetchMore() 循环放在修改模型的每个方法的末尾,我想知道连接 beforeInsert(QSqlRecord&)、beforeUpdate(int, QSqlRecord&) 和beforeDelete(int) 向一个插槽发出信号,然后调用 fetchAll()?这会是可靠和适当的吗?

更正:不是 before* 信号(太早),但可能是 layoutChanged()、dataChanged()、rowsInserted() 和 rowsRemoved()。


更新 2

关于 SQL 的注意事项:我知道SELECT COUNT理论上我可以向数据库发送单独的 SQL 查询,但这并不能回答问题。只要能避免SQL,我就不会写SQL。在我看来,发送这样的 SQL 查询违背了面向对象的 QAbstractTableModel 类的目的。加上 rowCount() 是 const (不应该发送查询)并且应该很快。无论如何,这不会修复 rowCount()。

我最终将一个调用 fetchMore() 的插槽连接到相关信号(见上文)断言所有内容都已在 rowCount() 中获取:
assert(!sql_model->canFetchMore())

这是因为 rowCount() 无法向我报告正确的行数计数为失败状态,因此断言。换句话说,我宁愿我的应用程序崩溃也不愿使用不正确的行数。

仅将其连接到 dataChanged() 信号(如第一个答案中所建议的:)I would probably try to use dataChanged signal.是不够的。我已将其连接到dataChanged(const QModelIndex&, const QModelIndex&)rowsInserted(const QModelIndex&, int, int)和。rowsRemoved(const QModelIndex&, int, int)layoutChanged()

似乎有效,断言还没有失败。

如果有人可以特别确认这一点(或解释为什么它并不总是有效),我将不胜感激。

4

3 回答 3

2

您面临的困境与我最近遇到的类似。我写了一个 QT gui 程序,它做了以下事情——

一世。连接并查询 Oracle 数据库 ii. 在 QTableView 中显示查询结果 iii。将 QTableView 的结果导出到 .csv 文件 iv。将 .csv 文件导入 loacl sqlite3 数据库 v. 连接并查询本地 sqlite3 数据库 vi。在另一个 QTableview 中执行查询并显示结果

在步骤 ii. 中,(即将结果导出到 .csv)我观察到,虽然 QTableView 上生成了 543 条记录,但只有 256 条记录被导出到 .csv 文件。

我正在使用,

int rows=model->rowCount();
int columns=model->columnCount();

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            textData += model->data(model->index(i,j)).toString();
            textData += ", ";      // for .csv file format
        }
        textData += "\n";             // (optional: for new line segmentation)
    }

    QFile csvfile("/home/aj/ora_exported.csv");
    if(csvfile.open(QIODevice::WriteOnly|QIODevice::Truncate))
    {
        QTextStream out(&csvfile);
        out<<textData;
    }
    csvfile.close();

事实证明,模型在获取所有结果之前读取了模型->行数。

因此,正如 SO 社区所建议的那样,我使用了---

while (model->canFetchMore())
       model->fetchMore();

    int rows=model->rowCount();
    int columns=model->columnCount();

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            textData += model->data(model->index(i,j)).toString();
            textData += ", ";      // for .csv file format
        }
        textData += "\n";             // (optional: for new line segmentation)
    }

并且所有记录都在 .csv 文件中填充(全部 543 条)。

你可以在这里参考我的问题。

于 2014-12-10T05:33:23.860 回答
1

当前答案没有完全回答问题(提到 dataChanged() 信号,但没有提到其他信号),所以我正在写自己的答案。

已经有一段时间了,我相信我已经涵盖了所有情况:我最终将一个调用 fetchMore() 的插槽连接到相关信号并断言所有内容都已在我的 DataTable::rowCount() 方法中获取:assert(!sql_model- >canFetchMore())

(当然 rowCount() 是一个 const 方法,所以如果还没有获取任何内容,我就无法获取,但这无论如何都不是 getter 的工作;断言很好,因为 canFetchMore() 也是 const。)

信号:dataChanged(const QModelIndex&, const QModelIndex&), rowsInserted(const QModelIndex&, int, int), rowsRemoved(const QModelIndex&, int, int) 和 layoutChanged()

我正在使用断言来确保我的模型接收到正确的行数,否则应用程序将崩溃(如果不是所有提到的信号都已连接,则会发生这种情况,例如 layoutChanged())。这对我来说很重要,因为在我的情况下,不正确的行数可能会导致数据丢失。

到目前为止,断言还没有失败,所以我假设这可以解决它。

于 2014-12-08T08:18:09.347 回答
1

根据我的经验,Qt 的 SQLite 驱动程序以 256 行的步长将行插入模型中。在数据获取结束时QSqlTableModel::rowCount()将返回正确的值,无需调用 fetchMore()。这仅取决于您在何时何地调用 rowCount()。

例如,如果用户正在对rowsInserted()信号进行一些计算,那么他的 slot 方法将被多次调用,并且每次最后一行的索引都会递增到 256。

因此,您需要在数据提取结束时使用 rowCount() (在某些重写的 QSqlTableModel 方法中,提取通常未完成),或者不依赖中间行计数值并在每次更改时重复计算。

这是一个一般性的答案,因为您没有发布任何代码,我们可以看到您到底想要做什么,但您可以看到我指向的位置。

编辑:

调用后QSqlTableModel::submitAll(),模型被重新填充,这就是为什么 fetchMore() 需要再次重复的原因。在哪里调用 fetchMore() 取决于用例。它可以在 submitAll() 之后或在某个插槽中调用,这里没有一般的答案。我可能会尝试使用 dataChanged 信号。但是,获取数据的目标始终应该是在视图中显示数据的主要目标,并且在大多数情况下,视图会自行完成。

在我的一个应用程序中,我依靠表格视图为我获取数据。将模型设置为表格视图后,我要么调用QTableView::scrollToBottom()它为我获取数据(在我的情况下,用户应该在底部看到最新数据,滚动后行数具有正确的值)或者在用户滚动时在 rowsInserted() 插槽中进行计算表,并以 256 行的步长再次自动获取数据。

并且非常重要的是要知道,如果应用程序需要在某个地方显示表中的行数,或者例如一列数据的汇总值,使用附加的 QSqlQuery来获取附加信息通常效率更高(例如:select count( *) 从 xyz) 然后从大表模型中读取数据。表模型主要用于向表视图提供数据

于 2014-10-15T08:04:41.747 回答