Когда или как использовать fetchMore() в QSqlTableModel с базой данных SQLite для работы rowCount()?

Мой класс DataTable является производным от QAbstractTableModel. Он использует объект QSqlTableModel для извлечения данных из таблицы БД. Он представляет запись для каждой строки в БД (это больше, но количество записей всегда равно количеству строк в таблице БД).

С MySql моя реализация DataTable::rowCount() просто вызывает rowCount () в QSqlTableModel, что прекрасно работает.

Теперь с SQLite драйвер Qt для SQLite возвращает количество строк 256, если в таблице db более 256 строк, поэтому мой класс DataTable также возвращает 256 - что неправильно. Документация говорит мне позвонить while (sql_model->canFetchMore()) sql_model->fetchMore();, Вызов fetchMore() сразу после создания внутреннего QSqlTableModel фактически заставляет следующий вызов rowCount () вернуть правильное значение. Но как только что-то изменится в базе данных (мой класс вызовет insertRow() или setData() для QSqlTableModel), следующий вызов QSqlTableModel::rowCount() снова вернет 256.

База данных модифицируется только моим классом, который, в свою очередь, использует этот конкретный объект QSqlTableModel (или представление, которое использует мой DataTable в качестве модели, может что-то обновить). Так что нет другого процесса, который мог бы вставить строки в базу данных.

Итак, когда мой класс DataTable должен вызывать fetchMore() для rowCount (), чтобы всегда возвращать фактическое количество строк?
Я думаю, что мой класс должен подключить некоторые сигналы, излучаемые 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()? Будет ли это надежным и уместным?

Исправление: Не раньше * сигналов (слишком рано), но, вероятно, layoutChanged(), dataChanged(), rowInserted() и rowRemoved().


Обновление 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(),

Кажется, работает, утверждение еще не провалилось.

Если бы кто-то мог конкретно подтвердить это (или объяснить, почему это не всегда работает), я был бы признателен за ответ.

3 ответа

Решение

Текущий ответ не полностью отвечает на вопрос (упоминает сигнал dataChanged(), но не другие сигналы), поэтому я пишу свой собственный ответ.

Прошло много времени, и я считаю, что я охватил все случаи: я закончил тем, что подключил слот, который вызывает fetchMore() к соответствующим сигналам, и утверждал, что все было получено в моем методе DataTable::rowCount(): assert(! Sql_model- > canFetchMore())

(Конечно, rowCount () - это метод const, поэтому я не смог бы извлечь, если ничего еще не было получено, но в любом случае это не было бы работой сборщика; утверждение в порядке, потому что canFetchMore() также const.)

Сигналы: dataChanged(const QModelIndex&, const QModelIndex&), rowInserted(const QModelIndex&, int, int), rowRemoved(const QModelIndex&, int, int) и layoutChanged()

Я использую утверждение, чтобы убедиться, что моя модель получает правильное количество строк, иначе приложение зависнет (что происходит, если подключены не все упомянутые сигналы, например layoutChanged()). Это важно в моем случае, так как неправильный счетчик строк может привести к потере данных в моем случае.

До сих пор утверждение не провалилось, поэтому я предполагаю, что это решает его.

Дилемма, с которой вы столкнулись, была похожа на ту, с которой я столкнулся недавно. Я написал программу QT GUI, которая сделала следующее:

я. Подключение к базе данных Oracle и запрос к ней ii. Показать результат запроса в QTableView iii. экспортировать результат QTableView в файл.csv iv. импортировать файл.csv в базу данных loacl sqlite3 v. подключиться и запросить локальную базу данных sqlite3 vi. выполнить запрос и показать результат в другом QTableview

Во время шага ii. (Т.е. экспорта результата в.csv) я заметил, что хотя в QTableView генерируется 543 записи, в файл.csv экспортируется только 256 нечетных.

Я использовал,

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

Как оказалось, модель считала модель->rowcount до того, как были получены все результаты.

Так как предложено 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)
    }

и все записи были заполнены (все 543) в файле.csv.

Вы можете обратиться к моему вопросу здесь.

Исходя из моего опыта, драйвер SQLite Qt вставляет строки в модель с шагом 256 строк. В конце выборки данных QSqlTableModel::rowCount() вернет правильное значение, нет необходимости вызывать fetchMore(). Это зависит только от того, где и когда вы вызываете rowCount().

Например, если пользователь выполняет некоторые вычисления для сигнала rowInserted(), его метод слота будет вызываться несколько раз, и каждый раз индекс последней строки будет увеличиваться до значения, равного 256.

Таким образом, вам нужно использовать rowCount () в конце выборки данных (в некоторых переопределенных методах QSqlTableModel выборка часто не завершается) или не полагаться на значение счетчика промежуточных строк и повторять вычисления каждый раз, когда они изменяются.

Это общий ответ, потому что вы не опубликовали никакого кода, где мы могли бы увидеть, что именно вы пытаетесь сделать, но вы можете увидеть, куда я указываю.

РЕДАКТИРОВАТЬ:

После QSqlTableModel::submitAll() вызов, модель заполняется заново, и именно поэтому fetchMore () необходимо повторить снова. Где вызывать fetchMore () зависит от варианта использования. Он может быть вызван после submitAll() или в каком-либо слоте, здесь нет общего ответа. Я, вероятно, попытался бы использовать сигнал dataChanged. Однако цель извлечения данных всегда должна быть первичной, чтобы отображать ее в представлении, а представление делает это самостоятельно в большинстве случаев.

В одном из моих приложений я полагался на табличное представление для получения данных для меня. После установки модели в табличное представление я либо QTableView::scrollToBottom() который выбирает данные для меня (в моем случае в любом случае пользователь должен видеть последние данные внизу, число строк имеет правильное значение после прокрутки) или выполняет вычисления в слоте rowInserted(), когда пользователь прокручивает таблицу, и данные снова выбираются автоматически по шагам 256 строк.

И очень важно знать, что если приложению нужно показывать где-то количество строк в таблице или, например, итоговое значение данных одного столбца, часто гораздо эффективнее использовать дополнительный QSqlQuery для получения дополнительной информации (например: select count(*) из xyz) затем читать данные из большой табличной модели. Табличные модели предназначены для предоставления данных в табличное представление.

Другие вопросы по тегам