Exposed model to QML does not Update
-
I use QSortFilterProxyModel to randomly select rows from the source model it is installed on.
Then I expose the model to qml and call from there the method
generateRandomQuestions. If I checkmAcceptedRowsit looks like there are indeed new valid indexes generated so the model should contain new data.However in QML the View does not update to the new data.
the Model:
#include <QSortFilterProxyModel> #include <QVector> class RandomQuestionFilterModel : public QSortFilterProxyModel { Q_OBJECT enum questionRoles { idRole = Qt::UserRole + 1, askedQuestionRole, answer1Role, answer2Role, answer3Role, answer4Role, correctAnswerRole, pictureRole }; public: RandomQuestionFilterModel(QObject *parent = nullptr); QHash<int, QByteArray> roleNames() const override; QVariant data(const QModelIndex &index, int role) const override; Q_INVOKABLE void generateRandomQuestions(int count); protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; private: QVector<int> mAcceptedRows; };#include "../include/randomquestionfiltermodel.h" #include <algorithm> #include <random> #include <QDebug> RandomQuestionFilterModel::RandomQuestionFilterModel(QObject *parent) :QSortFilterProxyModel{parent} { } QHash<int, QByteArray> RandomQuestionFilterModel::roleNames() const { QHash <int,QByteArray> roles; roles[idRole] = "id"; roles[askedQuestionRole] = "askedQuestion"; roles[answer1Role] = "answer1"; roles[answer2Role] = "answer2"; roles[answer3Role] = "answer3"; roles[answer4Role] = "answer4"; roles[correctAnswerRole] = "correctAnswer"; roles[pictureRole] = "picture"; return roles; } QVariant RandomQuestionFilterModel::data(const QModelIndex &index, int role) const { switch(role) { case idRole: return index.sibling(index.row(), 0).data().toInt(); case askedQuestionRole: return index.sibling(index.row(), 1).data().toString(); case answer1Role: return index.sibling(index.row(), 2).data().toString(); case answer2Role: return index.sibling(index.row(), 3).data().toString(); case answer3Role: return index.sibling(index.row(), 4).data().toString(); case answer4Role: return index.sibling(index.row(), 5).data().toString(); case correctAnswerRole: return index.sibling(index.row(), 6).data().toInt(); case pictureRole: return index.sibling(index.row(), 7).data().toByteArray().toBase64(); } return QSortFilterProxyModel::data(index, role); } void RandomQuestionFilterModel::generateRandomQuestions(int count) { mAcceptedRows.resize(sourceModel()->rowCount()); std::iota(std::begin(mAcceptedRows), std::end(mAcceptedRows), 0); std::shuffle(std::begin(mAcceptedRows), std::end(mAcceptedRows), std::mt19937(std::random_device()())); mAcceptedRows.resize(count); auto start = createIndex(0,0); auto end = createIndex(rowCount(),columnCount()); emit dataChanged(start, end); } bool RandomQuestionFilterModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent) auto it = std::find(mAcceptedRows.begin(), mAcceptedRows.end(), source_row); return it != mAcceptedRows.end(); }I thought emitting dataChanged in
generateRandomQuestionswould notify the view that the data changed but it does not.In QML I generate the random questions first and then open the page were the random questions are used.
ToolButton { id: newQuizButton text: qsTr("New Quiz") icon.name: "address-book-new" onClicked: { randomQuestionFilterModel.generateRandomQuestions( countOfQuestions) loader.setSource(root.__newQuizPath) } }On the page I pass the rows like this:
SwipeView { id: quizSwipeView interactive: false anchors.fill: parent Repeater { id: quizPageRepeater model: randomQuestionFilterModel delegate: QuizPage { questionId: model.id askedQuestion: model.askedQuestion answer1: model.answer1 answer2: model.answer2 answer3: model.answer3 answer4: model.answer4 correctAnswer: model.correctAnswer picture: model.picture onAnsweredCorrectly: quiz.answeredCorrectly() onAnsweredWrong: quiz.answeredWrong() } } } -
Hi,
I do not know for sure but in that method you are not merely changing data, the complet model content is changed. Sounds rather like a use case for beginResetModel and endResetModel.
However it looks like a strange use of QSortFilterProxyModel which states usually between the model actually containing the data and the view.
-
Hi,
I do not know for sure but in that method you are not merely changing data, the complet model content is changed. Sounds rather like a use case for beginResetModel and endResetModel.
However it looks like a strange use of QSortFilterProxyModel which states usually between the model actually containing the data and the view.
Your right that solved my Issue.
I'm open for a better solution to get the questions.
Currently I have the following:
In C++:
A questions model derrived fromQSqlTableModelto store and fetch questions (with possible answers).The Filter model derrived from
QSortFilterProxyModelto fetch n random questions from the questions model.These models get exposed to qml.
In QML:
Show all the questions in the database. -> I use the questions model for that.
Add new questions to the database -> I use the questions model for that.Present random questions as a Quiz to the User. Here I use the filter model to filter random entries from the question model.
If this is not a good approach I would love to here suggestions how else I could get the random questions.
An even worst appraoch I used before was getting the questions directly from the question model by exposing them as
QQmlListProperty <Question> getRandomQuestions();member function. -
One way, you can use a QSqlQueryModel and make the randomization in the request itself.
The proxy model is not a bad idea, you just need to separate it from the data themselves.
-
One way, you can use a QSqlQueryModel and make the randomization in the request itself.
The proxy model is not a bad idea, you just need to separate it from the data themselves.
@SGaist What do you mean separate it from the data? The SQL Access is only done in the table model the proxy model filters.
-
It was just a general statement. It happens from time to time that people try to use the proxy model as a standard model. This is indeed not your case :-)
Sorry for the confusion !