hide/unhide rows in QTableView: use QSqlTableModel or QSqlQuery?
-
I have a QTableView (tableView) linked to a QSqlTableModel (tableModel) which fetches the data from a single sqlite table. The primary key is an unsigned long named 'id'. In tableView, selection is only possible by row(s) (selection behaviour is QAbstractItemView::SelectRows). I linked a small context menu to the tableView that allows a user to hide selected records or to unhide them all. The code to hide records is depicted below and is "working", meaning that it does what it is meant to do. However, I feel this is not the best way to implement the desired behavior.
A user can select a few lines or many of them. In the latter case, I don't think the code is the best as it uses "filtering" on the tableModel on a per record basis to set a particular attribute ("hidden") to TRUE. The problem is that the rows on tableView do not have a direct correspondence to the rows in tableModel. Some rows on the view may be already hidden, so the row 10 in the view may not be the 10th row in the model (for example). Hence, it's necessary to fetch the sql 'id' of each row and use it with a filter to get the record and change the attribute "hidden". Tha's the approach coded below in hideRecords.
void MainWindow::hideRecords() { QItemSelectionModel *select = tableView->selectionModel(); if( select->hasSelection() ) { // save the current filter QString currentFilter = tableModel->filter(); QList<ulong> hidelist; // Get the list of selected rows QModelIndexList selection = select->selectedRows(); for( const auto &sel: qAsConst( selection ) ) hidelist.push_back( sel.data().value<ulong>() ); // Hide all selected rows by toggling the hidden attribute to TRUE (1) for( const auto &q: qAsConst( hidelist ) ) { QString newFilter = "id=" + QString::number(q); tableModel->setFilter( newFilter ); tableModel->select(); if ( tableModel->rowCount() == 1) { QSqlRecord record = tableModel->record( 0 ); record.setValue( "hidden", 1 ); tableModel->setRecord( 0, record ); tableModel->submitAll(); } } // Reapply the current filtering if any tableModel->setFilter( currentFilter ); tableModel->select(); } }
Wouldn't it be better to use a QSqlQuery with a "WHERE id IN (123,124,125,etc...)" clause which would hide all records at once? My problem is that after submitting the query, it's necessary to inform the model that something changed and it also should tell the view the same thing! Although there is a dataChanged() method in QAbstractItemModel, I think it does not apply here. I found no way to tell the model that the underlying sql database changed. As far as I can remember, doing a tableModel->select() didn't work either.
The problem described above is even worst in the case of a unhide action. The list of hidden records can grow up to a considerable size. Changing each record one-by-one as is done in hideRecords() is surely not the best thing to do. However, implementing it through direct manipulation of the database may not be the best approach. I also wonder if it is OK to mix the model-view approach with direct access to the underlying database?
-
I have a QTableView (tableView) linked to a QSqlTableModel (tableModel) which fetches the data from a single sqlite table. The primary key is an unsigned long named 'id'. In tableView, selection is only possible by row(s) (selection behaviour is QAbstractItemView::SelectRows). I linked a small context menu to the tableView that allows a user to hide selected records or to unhide them all. The code to hide records is depicted below and is "working", meaning that it does what it is meant to do. However, I feel this is not the best way to implement the desired behavior.
A user can select a few lines or many of them. In the latter case, I don't think the code is the best as it uses "filtering" on the tableModel on a per record basis to set a particular attribute ("hidden") to TRUE. The problem is that the rows on tableView do not have a direct correspondence to the rows in tableModel. Some rows on the view may be already hidden, so the row 10 in the view may not be the 10th row in the model (for example). Hence, it's necessary to fetch the sql 'id' of each row and use it with a filter to get the record and change the attribute "hidden". Tha's the approach coded below in hideRecords.
void MainWindow::hideRecords() { QItemSelectionModel *select = tableView->selectionModel(); if( select->hasSelection() ) { // save the current filter QString currentFilter = tableModel->filter(); QList<ulong> hidelist; // Get the list of selected rows QModelIndexList selection = select->selectedRows(); for( const auto &sel: qAsConst( selection ) ) hidelist.push_back( sel.data().value<ulong>() ); // Hide all selected rows by toggling the hidden attribute to TRUE (1) for( const auto &q: qAsConst( hidelist ) ) { QString newFilter = "id=" + QString::number(q); tableModel->setFilter( newFilter ); tableModel->select(); if ( tableModel->rowCount() == 1) { QSqlRecord record = tableModel->record( 0 ); record.setValue( "hidden", 1 ); tableModel->setRecord( 0, record ); tableModel->submitAll(); } } // Reapply the current filtering if any tableModel->setFilter( currentFilter ); tableModel->select(); } }
Wouldn't it be better to use a QSqlQuery with a "WHERE id IN (123,124,125,etc...)" clause which would hide all records at once? My problem is that after submitting the query, it's necessary to inform the model that something changed and it also should tell the view the same thing! Although there is a dataChanged() method in QAbstractItemModel, I think it does not apply here. I found no way to tell the model that the underlying sql database changed. As far as I can remember, doing a tableModel->select() didn't work either.
The problem described above is even worst in the case of a unhide action. The list of hidden records can grow up to a considerable size. Changing each record one-by-one as is done in hideRecords() is surely not the best thing to do. However, implementing it through direct manipulation of the database may not be the best approach. I also wonder if it is OK to mix the model-view approach with direct access to the underlying database?
Or use a QSortFilterProxyModel