Unsolved Custom Table Model
-
[Simplified description of classes]
I have a class StudentsManager which amongst other things, contains a vector of Student objects. Each student object contains an id, name, and DOB.
In my main window, I need a read-only Table view which reflects the contents of the Student vector in StudentsManager.
How can I bind StudentsManager to a custom table model, or should StudentsManager itself be the model? Code examples would be greatly appreciated.
Also, when a row is selected, I need a pointer to the respective Student object.
-
Hi
You could use a TableWidget directly
https://wiki.qt.io/How_to_Use_QTableWidget
( it uses a model internally but its not exactly what you ask)You can use an std model
https://doc.qt.io/qt-5/qstandarditemmodel.html
And simply use a USER role to store a pointer to the source StudentsManager
https://doc.qt.io/qt-5/model-view-programming.html
section Roles.This is ok since it's read-only as else it gets clumsy to update the items again if StudentsManager data changes.
You can also go full-blown custom model like
https://doc.qt.io/qt-5/qtwidgets-itemviews-addressbook-example.html -
At the moment, I'm using QTableView with a custom model which holds a pointer to the students vector held in StudentManager. What do you think?
Another problem I have:
When I delete a student object from inside the StudentManager, how can I refresh the Table View to reflect the change in data?
-
@EaccB said in Custom Table Model:
What do you think?
That's exactly how I would implement it.
how can I refresh the Table View to reflect the change in data?
Your model has to emit a dataChanged() signal.
-
@Christian-Ehrlicher said in Custom Table Model:
That's exactly how I would implement it.
So just to be clear, the model is effectively a wrapper around a data structure held elsewhere?
How would I emit the dataChanged() signal? Do there need to be any parameters passed?
Also, as I'm changing the data from within the StudentManager object, how can I let the model know the data has been updated? -
the model is effectively a wrapper around a data structure held elsewhere
In this case, yes. In other cases the model holds the data structure.
Start by reading through https://doc.qt.io/qt-5/qabstractitemmodel.html#dataChanged.
-
Here's what I've done, which seems to work:
void MainWindow::on_DeleteButton_clicked() { QItemSelectionModel *selected = ui->tableView->selectionModel(); QModelIndexList rowList = selected->selectedRows(); for (int i = 0; i < rowList.count(); i++) { ui->label->setText(students.at(rowList.at(i).row()).name); students.removeAt(rowList.at(i).row()); StudentModel->update(); } }
void StudentModel::update(){ QModelIndex i = this->index(0,0,QModelIndex()); QModelIndex p = this->index(0,1,QModelIndex()); emit dataChanged(i,p); }
What I don't understand is what the value in index() relate to. Admittedly I chose them randomly until it worked. Also, is it ok to get the index using "this->"?
-
@EaccB said in Custom Table Model:
What I don't understand is what the value in index() relate to
You should take a look here
-
I see. So doing something like this would update every cell in the table?
QModelIndex i = this->index(0,0,QModelIndex()); QModelIndex p = this->index(students.size()-1,1,QModelIndex());
-
Yes, but for this you should use begin/endResetModel() and you should only use it when you really need it. Make sure to only call dataChanged() for stuff you need.
-
So are you saying that ideally I should only emit dataChanged() on the rows affected?
-
Hi,
@EaccB said in Custom Table Model:
So are you saying that ideally I should only emit dataChanged() on the rows affected?
Yes, this will avoid unneeded processing and updating of the GUI for things that did not change.
-
Ok, here's what I've done:
void TestModel::update(int row){ QModelIndex i = this->index(row,0,QModelIndex()); QModelIndex p = this->index(row,1,QModelIndex()); emit dataChanged(i,p); }
This way, only the affected row is updated.
-
dataChange takes a range so here you always emit that the it starts from the first up to the row you pass as parameter. Is it really always the case ?
-
I edited my answer.
-
When emitting dataChanged(i,p) for the deleted row only, how is it that the following rows move up? Wouldn't that require a dataChanged(i,p) for the whole table?
-
@EaccB said in Custom Table Model:
When emitting dataChanged(i,p) for the deleted row only,
You did not read my link. You must not call dataChanged() when removing a row.
-
Then what do I use to signal to the view that the model has been updated? I've removed dataChanged() and replaced it with layoutChanged() which seems to work fine.
To avoid any confusion, the table itself is read-only and should only reflect the contents of the students vector inside StudentManager. Any changes to the data (i.e. add student, edit student, remove student) are done directly on the students vector.
-
@EaccB said in Custom Table Model:
Then what do I use to signal to the view that the model has been updated?
dataChanged() as explained above.
-
Ok, I'm really confused. You said that I shouldn't use dataChanged() when removing a row. So, what should I do when an element is removed from the vector? Or are you saying that I should emit dataChanged from the class which owns the vector and connect it to a slot in the Model?