I've my own view based directly on QAbstractItemView
.
Generally speaking I need to store some information about particular model's item.
So in my view I have a map from QModelIndex
to struct describing partical item.
Then I use those data mostly on view's paintEvent
.
The problem is, that QModelIndex is not persistent, it may get outdated.
So when rows are inserted or removed from model, some QModelIndex
may become invalid and I should not relay on them.
How then can I build relation between item in model and some decoration data I use in view?
QPersistentModelIndex
seems to be proper tool for such things, however I'm aware of its performance (my model and view may be huge).
Another problem with QPersistentModelIndex
is that it probably should not be used as map's key (as it is in my case) as it may (and will) change and make map inconsistent.
I've took a look at Qt's implementation of QTreeView and QListView to see how they deal rows removal/insertion, but it seems they simply drop all the data.
So at this point I cannot see any easy way to solve my problem.
Persistent storage is any data storage device that retains data after power to that device is shut off. It is also sometimes referred to as non-volatile storage. Magnetic media, such as hard disk drives and tape are common types of persistent storage, as are the various forms of Optical media such as DVD.
Persistence is "the continuance of an effect after its cause is removed". In the context of storing data in a computer system, this means that the data survives after the process with which it was created has ended. In other words, for a data store to be considered persistent, it must write to non-volatile storage.
Persistence, in computer science, is a noun describing data that outlives the process that created it. Java persistence could be defined as storing anything to any level of persistence using the Java programming language, but obviously this would be too broad a definition to cover in a single book.
Process persistence is achieved by storing core system processes in non-volatile, persistent storage. Persistent data is important because of its cross-platform access, non-volatility, reliability, stability, static, and time-independent features.
Instead of trying to map some data to some model items, make each item store its data. This involves using a delegate for painting and not relying on QAbstractItemView
paint event.
Let's have a simple stateful class, to represent our item data, and let's make it a new citizen in Qt meta-object system, using the Q_DECLARE_METATYPE macro.
#include <QMetaType>
class ItemData
{
public:
ItemData() = default;
ItemData(int d) : _data(d){}
int data() const { return _data; }
void paint(QPainter *painter, QRect rect);
private:
int _data;
};
Q_DECLARE_METATYPE(ItemData)
Now the delegate, a very simple one, indeed:
#include <QStyledItemDelegate>
class ItemDelegate : public QStyledItemDelegate
{
public:
// ...
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
// ...
};
The delegate paint method, is where things happens:
void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.data().canConvert<ItemData>())
{
ItemData itemdata = qvariant_cast<ItemData>(index.data());
itemdata.paint(painter, option.rect);
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
Here we can use the model index for what it is meant: retrieve the item data. Our item can paint itself, though. Let it paint some circles, according to its internal state:
void ItemData::paint(QPainter *painter, QRect rect)
{
QRect r(rect.left() + 2, rect.top() + 2, rect.height() - 4, rect.height() - 4);
for(int i=0; i<_data; ++i)
{
painter->drawEllipse(r);
r.moveLeft(r.left() + rect.height() + 2);
}
}
In a more flexible design, data is decoupled from rendering, so the ItemData
class have no paint
method and painting is performed by the delegate itself:
void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.data().canConvert<ItemData>())
{
ItemData itemdata = qvariant_cast<ItemData>(index.data());
//itemdata.paint(painter, option.rect);
QRect rect = option.rect;
QRect r(rect.left() + 2, rect.top() + 2, rect.height() - 4, rect.height() - 4);
for(int i=0; i<itemdata.data(); ++i)
{
painter->drawEllipse(r);
r.moveLeft(r.left() + rect.height() + 2);
}
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
This way, we can implement and then choose different delegates for different views, yet keeping a single consistent model and share it among them.
Using delegate and data in a item-based widget like QListWidget
is straightforward.
In a form constructor:
ui->listWidget->setItemDelegate(new ItemDelegate());
for(int i=0; i<10; ++i)
{
QListWidgetItem * item = new QListWidgetItem();
item->setData(0, QVariant::fromValue(ItemData(i + 1)));
ui->listWidget->addItem(item);
}
Not so different in a model-based one, though: instead of setting data to items, data is set to the model, again using QVariant::fromValue
.
You can safely use a QPersistentModelIndex
as a map or hash key.
Even if the underlying QModelIndex changes, the "persistent" part ensure that all the QPersistentModelIndex
are kept up to date while preserving their identities, i.e operator ==
and qHash()
return consistent values.
That being said, you should not store data about the index in your view. The data are supposed to be stored by the model.
And this seems to be how its done in Qt classes: views are making a lot of calls to QAbstractItemModel::data()
.
The only data that I would deem worthy to be stored in the view is "cache data", i.e values that are:
If any of these 3 conditions is not met, my personal preference would be to store the data in the model.
Contrary to my original answer, you cannot safely use QPersistentModelIndex
as a QMap
key.
The reason is that QMap
uses operator <
for inserting and finding data.
The issue here is that bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) const
is just a proxy to bool QModelIndex::operator<(const QModelIndex &other) const
which compares indexes by row and column.
This means that the sort order of QPersistentModelIndex
is not persistent and will change when indexes are moved in the model.
However, QMap
is not aware that the sort order has changed. And at this point searching a value inside a QMap<QPersistentModelIndex, T>
is just as broken as doing binary search on an unsorted array.
Note that this issue is not limited to QMap
, it affects all containers which are based on the ordering of the keys like std::map
. However, containers that do not rely on ordering (QHash
, std::unordered_map
, ...) are not affected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With