2014-10-14 2 views
5

Мой класс DataTable получен из QAbstractTableModel. Он использует объект QSqlTableModel внутри для извлечения данных из таблицы db. Он представляет запись для каждой строки в db (она делает больше, но количество записей всегда - количество строк в таблице db).Когда или как использовать fetchMore() в QSqlTableModel с базой данных SQLite для функции rowCount()?

С помощью MySql моя реализация DataTable :: rowCount() просто вызывает rowCount() в QSqlTableModel, который работает хорошо.

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

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

Итак, когда класс DataTable должен вызвать fetchMore() для rowCount(), чтобы всегда возвращать фактический счетчик строк?
Я думаю, что мой класс должен подключить некоторые из сигналов, испускаемых QSqlTableModel, к слоту, который будет вызывать fetchMore(), хотя Я не уверен, что это правильный/надежный способ сделать это?


Update:

Вот код, чтобы продемонстрировать основную проблему.

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(), rowsInserted() и rowsRemoved().


Update 2:

Примечание относительно SQL: Я знаю, что я мог бы послать отдельный запрос SELECT COUNT SQL в базе данных в теории, но это не дает ответа на вопрос. Пока я могу избежать SQL, я не буду писать SQL. На мой взгляд, отправка такого SQL-запроса не соответствует цели объектно-ориентированного класса QAbstractTableModel.Плюс rowCount() является const (не должен отправлять запросы) и должен быть быстрым. Во всяком случае, это не будет исправлять rowCount().

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

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

Недостаточно просто подключить его к сигналу dataChanged(), как предложено в first answer: I would probably try to use dataChanged signal.). Я подключил его к dataChanged(const QModelIndex&, const QModelIndex&), rowsInserted(const QModelIndex&, int, int), rowsRemoved(const QModelIndex&, int, int) и layoutChanged().

Кажется, что работа еще не завершена.

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

ответ

1

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

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

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

Сигналы: dataChanged (const QModelIndex &, const QModelIndex &), rowsInserted (Const QModelIndex &, Int, Int), rowsRemoved (Const QModelIndex &, INT, INT) и layoutChanged()

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

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

1

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

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

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

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

EDIT:

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

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

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

+0

Спасибо за ваш ответ. Я добавил код на вопрос. Я хотел бы знать, следует ли мне называть fetchMore() всякий раз, когда срабатывают сигналы (см. Вопрос). Кроме того, я не уверен, что вы подразумеваете под «вам нужно использовать rowCount() в конце извлечения данных», как вызов rowCount() исправить rowCount() (возвращение 256)? И если я могу на самом деле «не полагаться на подсчет промежуточных строк», то мне, вероятно, придется прочитать всю таблицу в памяти при загрузке (работа в памяти) и подсчитать строки в памяти (звучит расточительно) вместо вызова rowCount() ... – basic6

+0

Привет!Я обновил свой ответ. Может быть, это может помочь вам немного. – user645859

+0

Ответ на ваш комментарий: Я не сказал, что _calling rowCount() исправит rowCount() _, но использовать rowCount() только тогда, когда все данные будут извлечены, а не раньше. – user645859

1

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

i. Подключение и запрос базы данных 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(); 

, как оказалось, модель читать модели-> 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.

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

+0

Спасибо за ваш ответ. Хотя вы столкнулись с одной и той же проблемой, ваше приложение/сценарий, похоже, сильно отличается от моего. Вероятно, самое важное отличие состоит в том, что ваш метод не является методом const, поэтому можно вызвать функцию fetchMore(). В моем случае затронутый метод (моя реализация rowCount()) является константой, поэтому он не может (и не должен) вызывать fetchMore() - счетчик строк ДОЛЖЕН уже извлекаться, когда вызывается мой rowCount(). Так что это действительно не применимо в моем случае вообще. – basic6