6

私のクラス DataTable は QAbstractTableModel から派生しています。QSqlTableModel オブジェクトを内部で使用して、db テーブルからデータをフェッチします。これは、データベース内のすべての行のレコードを表します (より多くのことを行いますが、レコード数は常に 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 クラスが 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) を返します。

そのため、sql モデルに対するすべての書き込み操作の後に fetchMore() を呼び出す必要があるようです。しかし、この while/canFetchMore()/fetchMore() ループをモデルを変更するすべてのメソッドの最後に置くのではなく、beforeInsert(QSqlRecord&)、beforeUpdate(int, QSqlRecord&) を接続するだけで十分かどうか疑問に思っています。 beforeDelete(int) は、fetchAll()? を呼び出すスロットに通知しますか? これは信頼でき、適切でしょうか?

訂正: before* シグナルではなく (時期尚早)、おそらく layoutChanged()、dataChanged()、rowsInserted()、rowsRemoved()。


更新 2 :

SQL に関する注意: 理論的には別の SQL クエリをデータベースに送信できることはわかっていSELECT COUNTますが、これは質問の答えにはなりません。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 のレコードが生成されましたが、.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();

結局のところ、モデルは、すべての結果がフェッチされる前に、 model->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)
    }

.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 に答える