Maintaining permanent information about the items under consideration

I have my own view based directly on QAbstractItemView . Generally speaking, I need to save some information about a specific element of the model.

So, in my opinion, I have a map from QModelIndex to a structure describing a fragmented element. Then I use this data mainly on the paintEvent view.

The problem is that QModelIndex is not persistent, it may become obsolete. Therefore, when rows are inserted or deleted from the model, some QModelIndex may become invalid and I should not relay them.

How then can I build a relationship between an element in a model and some jewelry data that I use?

QPersistentModelIndex seems to be the right tool for such things, however I know about its performance (my model and presentation can be huge).

Another problem with QPersistentModelIndex is that it probably should not be used as a map key (as in my case), since it can (and will) change and make the map inconsistent.

I took a look at the Qt implementation of QTreeView and QListView to see how they handle row deletion / insertion, but it looks like they just discard all the data.

So, at this moment I do not see an easy way to solve my problem.

+5
source share
2 answers

You can safely use QPersistentModelIndex as a map or hash key. Even if the underlying QModelIndex is changed, the "constant" part ensures that all QPersistentModelIndex will be updated when their identifiers are saved, i.e. operator == and qHash() return consistent values.

However, you should not store index data in your view. Data must be stored in the model. And this is similar to how it is done in Qt classes: views make many calls to QAbstractItemModel::data() .

The only data that I consider worthy of storage in a view is "cache data", that is, values ​​that:

  • not directly provided by the model
  • calculation of heavy computations according to model data is required
  • characteristic of presentation

If any of these 3 conditions is not met, my personal preference will be to save the data in the model.

+6
source

Instead of trying to match some data with some elements of the model, each element stores its data. This includes using a delegate to draw and not rely on the QAbstractItemView paint event.

Let us have a simple state class to represent our item data and make it a new citizen in the Qt meta-object system using Q_DECLARE_METATYPE .

 #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, very simple, really:

 #include <QStyledItemDelegate> class ItemDelegate : public QStyledItemDelegate { public: // ... void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; // ... }; 

The delegate drawing method is what 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 this means: get element data. However, our element can draw itself. Let him draw a few circles in accordance with his inner 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 separated from rendering, so the ItemData class ItemData not have a paint method, and painting is done 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); } } 

Thus, we can implement and then choose different delegates for different representations, but keeping a single consistent model and sharing it among them.

Using a delegate and data in an elementary widget like QListWidget is simple. In the 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 that different based on the model: instead of setting data to elements, the data is set to the model again using QVariant::fromValue .

+2
source

Source: https://habr.com/ru/post/1209640/


All Articles