I have finally implemented my own tree model (inherited from QAbstractItemModel) for a QTreeView.
A blueprint of what I did can be found here:
http://www.trinitydesktop.org/docs/qt4/itemviews-simpletreemodel.html
So, I have:
the user-defined tree items, which are pure C++ (no Qt) and these are wrapped by
the TreeModel class which is inherited from QAbstractItemModel (like in the example link above).
I now have a generated tree hierarchy of tree items from (1.). This hierarchy has thousands of items, and I want to insert this hierarchy into my existing model at runtime.
How do I do that?
(All I have is the root-node to the c++ tree hierarchy as TreeItem pointer and a QModelIndex of the existing model where the "new sub-tree" has to be inserted)
I found modelAboutToBeReset(), modelReset(), and endResetModel() from here: http://qt-project.org/doc/qt-4.8/qabstractitemmodel.html#beginResetModel
But I don't know if these are the right functions, and, if they are, how to use them.
Any ideas?
You should call modelAboutToBeReset() before removing real items from your model. This call will "freeze" all views from requesting any data. After removing all real items you should call endResetModel() - it will unfreeze data requesting and force all connected views to update it content.
This is what i did:
void
TreeModel::addNewSubTreeToModel( TreeNode* t_rootOfNewTree, TreeNode* t_addNewSubTreeAsChildOfThisItem )
{
beginResetModel();
t_rootOfNewTree->setParent(t_addNewSubTreeAsChildOfThisItem);
t_addNewSubTreeAsChildOfThisItem->addChild(t_rootOfNewTree);
endResetModel();
}
Related
I intend to create my own item model, derived from QAbstractItemModel. The model does not contain data but wraps some data repository. That repository emits signals after item(s) are insert, removed, renamed, etc.
Whenever something changes in the repository, my item model needs to forward those signals.
However the repository has standalone signals like void itemRemoved(int index); while QAbstractItemModel has begin/end pairs of protected functions (and signals) like beginInsertRows() and endInsertRows().
How should I handle this? E.g. I could connect a slot like the following to the repository's itemRemoved() signal:
void RepositoryItemRemoved(int i)
{
beginInsertRows(QModelIndex(), i, i);
endInsertRows();
}
Based on the above example: Is it valid to call beginInsertRows() / endInsertRows() sequently after a row has been inserted in the repository?
I've had a similar scenario, where the data is in a different object, and the model is just a wrapper, and only created if that data set is displayed in a view. I used a pointer to the model object in the data object, checking if it is null on insert operations, and if not call beginInsertRows() and endInsertRows() through it. Naturally, since those are protected, the data class would have to be declared a friend to the model class.
The documentation stresses that it is important to call beginInsertRows() before any data is inserted:
When reimplementing insertRows() in a subclass, you must call this
function before inserting data into the model's underlying data store...
...Otherwise, the views may end up in an invalid state.
You should test with a view, or alternatively, examine the actual implementation in the source.
I've had somewhat similar scenario as well, only in my case the Qt model wrapped the underlying model a little more literally: the underlying model contained sufficiently more data than the view had to know about. So I let the Qt model to contain its own list of small pieces of each underlying model's data item for the view/delegate to deal with. So the slots processing the updates from the underlying model looked like this:
void RepositoryItemRemoved(int i)
{
beginRemoveRows(QModelIndex(), i, i);
removeModelItem(i);
endRemoveRows();
}
Such design solved the problem of view's invalid state although it may be impractical for the use cases in which letting the Qt model contain its own items list would mean duplicating the sufficient amount of data being worked with.
PrintIntervalTableModel implements QAbstractTableModel
1) Am I supposed to assign a parent during creation?
PrintIntervalTableModel * model = new PrintIntervalTableModel(dataset, this);
ui.table->setModel(model);
or
2) Does the setModel call set the parent of the model to the view?
// Do not pass in a parent widget during construction...
// I have seen this in examples. Is it wrong, right, or a shaky practice?
PrintIntervalTableModel * model = new PrintIntervalTableModel(dataset);
ui.table->setModel(model);
If #2 is acceptable or even preferred, how do we know which function calls assign widgets to parents and which don't? It seems dangerous to just assume a call like setModel manages child memory if we have no objective evidence that it does.
Here is what the void QAbstractItemView::setModel(QAbstractItemModel *model) says:
The view does not take ownership of the model unless it is the model's parent object because the model may be shared between many different views.
So the view is not the parent of your model using setModel(...) function.
When you write PrintIntervalTableModel * model = new PrintIntervalTableModel(dataset, this); this refers to the class you are writing your code in, so there's no link with the view (in your example).
To define parent's objet, you can either put this in the constructor or call the function void QObject::setParent(QObject *parent), I don't think there is any other way to do it.
I am using QTreeView and QAbstractItemModel to establish view whose data also comes from a tree structure. But when I delete a node from a tree structure(data source), then I found that the model view can't automatically adjust itself, it also use the invalid pointer which I don't know it points to which memory block. I don't know how to refresh or what I need to do to fix this problem.
To delete data from the model, use beginRemoveRows() and endRemoveRows().
beginRemoveRows tells the model that you will now change the underlying data structure.
Then change the structure and call endRemoveRows when done. endRemoveRows will then trigger the notifications to update the views:
beginRemoveRows(QModelIndex(), 0, 0);
m_topLevelNodes.remove(0);
endRemoveRows();
This removes the first top-level row (and its children), assuming that the underlying structure in your model keeps the top-level tree items in a container named m_topLevelNodes.
I want to delete a row which has children from a QTreeView. I use QAbstractItemModel's removeRow ( int row, const QModelIndex & parent = QModelIndex() ) method,pass the row and parent index of the deleted row. But this method returns false.
How can I delete a row which has children? Do I need write a method to recursively delete rows?
You should look at this: removeRow
This is a convenience function that calls removeRows(). The QAbstractItemModel implementation of removeRows() does nothing.
And here in QAbstractItemModel::removeRows():
The base class implementation does nothing and returns false.
If you implement your own model, you can reimplement this function if you want to support removing. Alternatively, you can provide your own API for altering the data.
I know I'm a bit late to the party, but I wanted to document this because I can't seem to find any good answer to the original question.
You need to implement QAbstractItemModel::removeRows in your model. There is a Qt example, Editable Tree Model, that shows how this should be done. The basic procedure is to call beginRemoveRows, delete the item(s), then call endRemoveRows.
Your tree item class should have a method for deleting a range of its child items. You call this method on the parent item from within your reimplementation of removeRows. The tree item class should be set up to delete its children when it is destroyed. The Qt example does this through the destructor, though with C++11 and later this is best done by storing children in a container of smart pointers so they get automatically deleted when the container goes out of scope.
You do not need to account for child items being collapsed or expanded in the tree view - QTreeView knows whether or not child items are visible or not and will update the view accordingly. In other words, if you delete a tree item with a few child items that were visible in the tree view, they will be automatically removed from the view. This is something that I can't find documented anywhere, but from personal experience I can say that it works (as long as you call beginRemoveRows and endRemoveRows correctly).
I'm implementing a Model/View for a tree like structure, and I've decided to try the QStandardItemModel on which I want to wrap on it a specific class, (which I call here "appSpecificClass").
Basically, I want part of that class (like names, or some data), to be shown in the model, and when I change the model (in edit role, or drag and drop), I want that to have consequences on the appSpecificClass (which is, when I change a name that is showing on the model, the name on the object associated with the model's item of the appSpecificClass also changes).
So, I started from subclassing the QStandardItem by a appSpecificItem, which only has a pointer to the appSpecificClass. When I construct the appSpecificItem, the text and icons are called from appSpecificClass and everything works fine.
However, when change data from appSpecificItem, naturally it does not change the appSpecificClass, because so far I didn't found any way of interacting with the appSpecificItem's pointer via overloading a virtual function (or else)
Does anyone knows how to do this/if this is possible? What can I do such that if for instance the signal
QStandardItemModel::itemChanged ( QStandardItem * item )
is emitted, I can change a the appSpecificItem's pointer.
If not, is there any good tutorial about implementing a Model from scratch? I've tried myself some, but it is not an easy task. Ideally I would like a QStandardItemModel like model, but a little more abstraction on it (such that I can put my appSpecificClass on it).