I have searched QT doc for reference but I got some questions.
Here is the implementation in QT doc:
bool TableModel::removeRows(int position, int rows, const QModelIndex &index)
{
Q_UNUSED(index);
beginRemoveRows(QModelIndex(), position, position + rows - 1);
for (int row = 0; row < rows; ++row) {
listOfPairs.removeAt(position);
}
endRemoveRows();
return true;
}
I think it uses index.row in the tableview to directly locate the data in the model. But what if I enable sorting for the tableview? After sorting, the index in the tableview doesn't correspond to the data in the model. The first row in the tableview may be the third data in the model.
How can I locate the data in model through tableview?
And are there any better implementations for removeRow() function?
you can use tableWidgetItem->setData(0, Qt::UserRole, "Desired Identity");
it will be always unique even when you sort the rows. refer
QTableWidget find a row through userdata
As the official documentation says ,
There are two ways of approaching sorting in the model/view architecture:
If your model is sortable, i.e, if it reimplements the QAbstractItemModel::sort() function, both QTableView and QTreeView provide an API that allows you to sort your model data programmatically.
The alternative approach, if your model does not have the required interface or if you want to use a list view to present your data, is to use a proxy model to transform the structure of your model before presenting the data in the view
So what happens when you change the sorting depends on how you arrange sorting for your model: either sorting in the view changes the arrangement of rows in the model or it only changes that arrangement for the proxy model.
If you use the proxy model, you can insert there some mapping between proxy model's rows (as the view sees them with the current sorting) and the original model's rows. Or you can just add some mapping between the row and the actual data item for that row in the original model - you can in fact even embed a void* to the data item directly into QModelIndex in your model's reimplementation of QAbstractItemModel::createIndex and then retrieve the pointer to the item from the index passed to methods such as removeRow. Another possibility is to operate with some unique integer identifier of the item instead of the direct pointer to it. It's therefore up to you how which way of mapping between rows and the actual model items to choose.
Related
Quick question
Is there an easy/quick way to map a QListWidget or QStandardItemModel (for QListView) item to my application logic?
Complete Question
Note: I will use undistinctly both QListWidget or QListView and itsQStandardItemModel. From my current point of view for this question, switching from one to another is trivial.
I face usually the need to have a QListView or equivalent in an HMI, which shows some text value and need to react on selection/click.
In the SLOT, in order to perform the required action, the row need to be identified. Qt::DisplayRole is NOT appropriate because some texts could be duplicated and QStrings are not the best way to identify data in Computer Science.
// Click on a row
connect( &myView, &QListView::clicked,
[&myView, this]( const QModelIndex &idx)
{
// E.G. need to update the database for this row. Which row?
});
Possible solutions:
Maintain a map to retrieve the ID from the QModelIndex row.
Save in the model row any ID, making easy to apply any operation.
First option is tedious: it requires to connect the model for keeping the map and the model synchronized.. Same logic again and again.
Second option seem by far the best: I save the (e.g. database id) and use it afterward; But, QListView model (up to what I know) does NOT include this very friendly and useful ID. So until now I had extended again and again the models for QListView.
How to map the QModelIndex back to my application logic? Do I really have to extend the model for that simple operation?
RELATED QUESTION: QTreeView: maintaining mapping between QModelIndex and underlying data
What about creating the QAbstractModel manually, this way you have complete control how the QModelIndexes are created, and you can use that to do stuff.
I'v implemented thousands of Qt Models, and I have never liked the QStandardItemModel approach as I would usually need to write more boilerplate than if I had come with the model myself.
Inherit from your QAbstractList/Table/TreeModel
implement index()
create a method for direct item access
.
// Click on a row
connect( &myView, &QListView::clicked,
[&myView, this]( const QModelIndex &idx)
{
auto& myItem = idx.model()->directAccess(idx.row());
});
profit.
In Qt I have a sqlite database which I'm pulling in. One of the tables (configTable) has a QSqlTableModel attached.
The table has a simple 2-column key/value structure. The keys are strings with folder-like values such as "general/name", "general/version", "foo/bar/baz", etc. Values are just arbitrary variants.
I'd like to display this data in an easier-to-browse QTreeView instead of a QTableView, as my key structure lends itself very nicely to that.
Before I go reimplementing classes and all sorts of crazy things - is there an elegant solution to this? And if I reimplement or extend classes, which ones should I look at?
Thank you.
You have to do the parsing+mapping between the list of value/value/value and a tree model yourself. But there is a (tricky) Qt way to do this yes.
The Qt Model-View architecture can represent many different structures of data, based on the QAbstractItemModel class. A Qt model must implement some functions to tell the view : how many columns, row, children etc.
A list model (Qt provides QAbstractListModel), is basically a model that says to the view :
I have one root item (all data items are represented by a QModelIndex, root has an invalid parent)
This root item has only one column
This root item has as many rows as your list has elements
A tree model will return the appropriate children for each QModelIndex. The abstract model of Qt actually allows each child item to be a table (QModelIndex always has a parent and a row-column index).
Long story short, you have to create a proxy model (QAbstractProxyModel or a suitable subclass, but for your need I don't think there is one). This proxy will transform the data your QSqlTableModel is sending, and this is where you can tell the view that you actually have a tree and not a list.
Your root items are the items from your database list of keys (first element of the foo/bar/whatever), but you need to regroup all the root items that has the same key.
AFAIK you can make it only manually.
Basically, because how did you think Qt knows how to convert your data into tree model.
I am interested to know how would views behave in cases when the main data model with incremental data fetching is behind a proxy or chain of proxies.
How proxy which does item rearrangements like ungrouping proxy (example: http://lynxline.com/jongling-qt-models/) should implement support of fetchMore()/canFetchMore()?
Examining QSortFilterProxyModel's source allows me to conclude that:
QSortFilterProxyModel allows access to already fetched rows. Calling rowCount, data, etc. will behave like there is no more data in the source table.
QSortFilterProxyModel (and QAbstractProxyModel by default) routes canFetchMore and fetchMore calls to according methods of the source model.
It means that when you scroll down a view with a QSortFilterProxyModel populated from a dynamically fetched table, it will be dynamically populated with new data from source model. However you can't be sure that new items will be added to the end. Instead, when scrolling down the table, its data can completely change. New rows can be added anywhere depending on current sorting and filtering settings.
When no additional support of canFetchMore and fetchMore is given, all proxy models (including the linked UngroupProxyModel class) will behave like that by default.
QTableView behaves buggy displaying such models. For example, if the source model has rows -2; -3; 2; 3 (let ; be a row separator in my answer), the view without sorting would usually display something like -2; -3; 2; 3; empty row; empty row. Empty rows are displayed in the bottom of the table. When user scrolls down enough close to these items, the view requests fetching more data. However, when sorting is enabled, the view shows -3; -2; empty row; empty row; 2; 3, i.e. it moves empty invalid rows to the middle or to the top of the table. It doesn't affect functionality but looks awkward.
However, all aforementioned issues are implementation dependent. Refer to the code and documentation of the used model classes for more details. I think it's possible to implement proxy models that act however you like.
Also, it would be reasonable in some cases to fetch source model completely before displaying sorted data in the table. Proper sorting or joining is impossible while not all data has been fetched. If you fetched the source model data (e.g. while(model->canFetchMore()) { model->fetchMore(); }) then proxy models will behave exactly like the model is not dynamically populated.
I am not sure how to go about this big data problem. Instead of using a Table Widget, it is suggested to use a Table View for big data for speed. The data will be at most 200000 rows by 300 columns of strings. Also, it might have gaps in the structure. For the View, it is needed to define a Model. I was not able to do this yet and I wonder the following:
Will it really be faster to use a View if the user only wants replace some values in a column, and inspect the data? Is a QList a smart choice given speed needs, or are there better options? And how to implement this as a Model?
A QTableWidget uses its own Model as backend (container for its data). One can't (or it is not intented that one should) customize a QTableWidget's model. Each cell in a QTableWidget is represented by a QTableWidgetItem. They are written to solve general problems and are not that fast.
When one decides to use a QTableView with it's own model deriving from QAbstractTableModel as backend, one can speed things up allot. I've found that one of the things that took the most processing time was the size calculation of rows. I overloaded sizeHintForRow in my table like this:
int MyTable::sizeHintForRow ( int row ) const
{
//All rows have the same height in MyTable, hence we use the height of the
// first row for performance reasons.
if( cachedRowSizeHint_ == -1 )
{
cachedRowSizeHint_ =
model()->headerData( 0, Qt::Vertical, Qt::SizeHintRole ).toSize().height();
}
return cachedRowSizeHint_;
}
Implementing a simple table model is quite easy (Qt Help files are adequate...). One can start by providing the data for the minimum roles (Edit and Display). TableModel data can always be modified by using QIdentityProxyModels where necessary. If your models don't work, post your code. Remember to not return invalid data for roles e.g:
QVariant IO_PluginMngr::data(const QModelIndex & index, int role) const
{
if( index.isValid() )
{
if( role == Qt::DisplayRole)
{
return sequence_[index.row()][index.column()];
}
}
return QVariant();
}
Returning for example erroneous data for especially SizeHintRole will cause havoc in the views.
I've noticed that one should, when implementing models, be diligent in calling begin/endInsertRows when appropriate, otherwise persistent model indexes don't work.
We've written more complicated tables where the data is modified in threads in the background, and swapped out prior to updating the table. One of the areas in which one could have speed improvement, is to never order the data in your model, but rather use an ordered reference sequence that references the real (unordered) data. We have tables that have about a thousand rows and 50 columns that get updated once a second (with many views), and the processor our processor is sitting a about 20%, but the greatest speed improvement was the caching of the sizeHint.
I wanted to know how I could associate a key with an entry to the QstandardItemModel. This would help me retrieve the row of the entry.
http://doc-snapshot.qt-project.org/4.8/qstandarditemmodel.html#details
All the accessor methods to data use:
http://doc-snapshot.qt-project.org/4.8/qmodelindex.html
QModelIndex
http://doc-snapshot.qt-project.org/4.8/model-view-programming.html#model-indexes
http://doc-snapshot.qt-project.org/4.8/qpersistentmodelindex.html
The QPersistentModelIndex class is used to locate data in a data model.
A QPersistentModelIndex is a model index that can be stored by an application, and later used to access information in a model. Unlike the QModelIndex class, it is safe to store a QPersistentModelIndex since the model will ensure that references to items will continue to be valid as long as they can be accessed by the model.
It is good practice to check that persistent model indexes are valid before using them.
Hope that helps.