I've set up a QAbstractItemModel and filled that with data. My QTreeView widget displays every data in that model properly.
Now, I would like to store that model serialized in a binary file (and later of cource load that binary file back into a model). Is that possible?
The particulars of model serialization depend somewhat on the model's implementation. Some gotchas include:
Perfectly usable models might not implement
insertRows
/insertColumns
, preferring to use custom methods instead.Models like
QStandardItemModel
may have underlying items of varying types. Upon deserialization, the prototype item factory will repopulate the model with clones of one prototype type. To prevent that, the items't type identifier must be exposed for serialization, and a way provided to rebuild the item of a correct type upon deserialization.Let's see one way of implementing it for the standard item model. The prototype polymorphic item class can expose its type via a data role. Upon setting this role, it should re-create itself with a correct type.
Given this, a universal serializer isn't feasible.
Let's look at a complete example, then. The behaviors necessary for a given model type must be represented by a traits class that parametrizes the serializer. The methods reading data from the model take a constant model pointer. The methods modifying the model take a non-constant model pointer, and return
false
upon failure.Such a class must be implemented to capture the requirements of a particular model. The one given above is often sufficient for basic models. A serializer instance takes or default-constructs an instance of the traits class. Thus, traits can have state.
When dealing with streaming and model operations, either can fail. A
Status
class captures whether the stream and model are ok, and whether it's possible to continue. WhenIgnoreModelFailures
is set on the initial status, the failures reported by the traits class are ignored and the loading proceeds in spite of them.QDataStream
failures always abort the save/load.This class could also be implemented to throw itself as an exception instead of returning
false
. This would make the serializer code a bit easier to read, as everyif (!st(...)) return st
idiom would be replaced by simplerst(...)
. Nevertheless, I chose not to use exceptions, as typical Qt code doesn't use them. To completely remove the syntax overhead of detecting traits methods and stream failures, one would need to throw in the traits methods instead of returningfalse
, and use a stream wrapper that throws on failure.Finally, we have a generic serializer, parametrized by a traits class. The majority of model operations are delegated to the traits class. The few operations performed directly on the model are:
bool hasChildren(parent)
int rowCount(parent)
int columnCount(parent)
QModelIndex index(row, column, parent)
Headers for each orientation are serialized based on the root item row/column counts.
The data for each item is serialized recursively, ordered depth-first, columns-before-rows. Any item can have children. Item flags are not serialized; ideally this behavior should be parametrized in the traits.
The serializer retains a traits instance, it can also be passed one to use.
The data is serialized in following order:
Attention is paid to versioning of both the stream and the streamed data.
To save/load a model using the basic traits:
The same way you serialize anything, just implement an operator or method which writes each data member to a data stream in sequence.
The preferable format is to implement those two operators for your types:
Following that pattern will allow your types to be "plug and play" with Qt's container classes.
QAbstractItemModel
does not (or should not) directly hold the data, it is just a wrapper to an underlying data structure. The model only serves to provide an interface for a view to access the data. So in reality you shouldn't serialize the actual model, but the underlying data.As of how to serialize the actual data, it depends on the format of your data, which as of now remains a mystery. But since it is a
QAbstractItemModel
I assume it is a tree of some sort, so generally speaking, you have to traverse the tree and serialize every object in it.Make a note that when serializing a single object, the serialization and deserialization are a blind sequence, but when dealing with a collection of objects, you may have to account for its structure with extra serialization data. If your tree is something like an array of arrays, as long as you use Qt's container classes this will be taken care of for you, all you will need is to implement the serialization for the item type, but for a custom tree you will have to do it yourself.