[Qt5-feedback] Add internalVariant to QModelIndex

Stephen Kelly steveire at gmail.com
Thu Jul 14 19:19:26 CEST 2011


Charley Bay wrote:
>> See how it's done in the EntityTreeModel:
>> 
https://projects.kde.org/projects/kde/kdepimlibs/repository/revisions/master/entry/akonadi/entitytreemodel_p.h#L42
>>
>> There is a Node type which can reference either a Collection or an Item.
>> A pointer to a node is used for the internalPointer (see ETM::index() )
>> <snip>,
>>
>> Is this what you mean to be the problem? Is my Node like the 'utility
>> instance' you refer to?
>>
> 
> Yes -- I don't want the Node class at all.

Ok. Now I'm starting to understand what you are saying. :)

Having a Node class or something similar is quite common in QAIM 
implementations.

The Qt examples use something similar, but in those examples 'the model' and 
'the QAIM implementation' is one thing and implemented with the same class.

http://doc.qt.nokia.com/latest/itemviews-simpletreemodel.html

In this book also referenced in the docs the Node is also used for both 'the 
model' and 'the QAIM implementation', though it does also have a Type for 
keeping track of the type of a node.

http://ptgmedia.pearsoncmg.com/images/0131872494/samplechapter/blanchette_ch10.pdf

I mention these things only to show that they are common things to do (use a 
Node pointer and keep track of types with an enum). I know they are counter-
examples to your case where you have a model which is self-contained and 
independent of the QAIM API (and presumably usable without it). I mention 
them so that we're clear (all of us, including the lurkers :) ) about what 
the situation is that you're trying to handle.

> For example, consider a "MyBook" object that contains "MyChapter" objects,
> each of which contain "MyParagraph" objects.  I want to "expose" that
> "model" to the Qt MVC by creating QModelIndex instances that reference
> those objects, and they do not share a common base class.
> 
> We have *many* such models, and indeed, every application has its own
> logical "model implementations" that are not GUI and not implemented with
> consideration of possible future coupling to the Qt MVC infrastructure.
> These "models-of-heterogeneous-types" are terribly common.

Yes, the API of 'the model' can determine how best to wrap it for the QAIM 
API. Staying with the concrete example, how do the Book, Chapter and 
Paragraph fit together? Does each element have an id()? Does Book have a 
Container<Chapter*> or Container<Chapter> elements which can be accessed 
from the book? 

How do you map that into a internalPointer currently? If using a Node* 
solution the node might be very simple, though I know you want to avoid it.

Copying from below:

>> Our use case, very painful:  Applications commonly use "native" models of
>> > heterogeneous types, and we need to create QModelIndex references to
>> those
>> > model "nodes".  With the current "void*", we do not know the *type* to
>> > which
>> > it points.  So, we create an "adapter utility" instance that *does*
>> > know the
>> > type.  Unfortunately, these "adapter utility" instances come-and-go
>> > like the QModelIndex comes-and-goes,
>>
>> I don't think the Node* has the same lifecycle as a QModelIndex. A
>> QModelIndex is temporary.
>>
> 
> For me, Node is *also* temporary.  In the example above, we keep MyBook,
> MyChapter, and MyParagraph instances, because those are the "real" model
> (and they do not share a common base class).
> 
> Our work-around creates a MyBookNode class for the sole purpose of
> exposing
> our "real" model to the Qt QML infrastructure.  The MyBookNode instance
> can reference any model object, and we must have a maintained/synchronized
> tree
> of MyBookNode instances that reference the *real* model.  (I'd be much
> happier if we did not have this tree at all.)

I think if your Node* is temporary you're not doing it right. But yes, 
having a MyBookNode* referring to 'the (real) model' is part of the cost 
associated with the QAIM API at the moment I think.

> 
> IMHO, this issue is increasingly dramatic with QML, where interface
> components are increasingly "independent actors", but which should
> logically be "identifiable" through a QModelIndex-type handle to reference
> the "relevant node state" from some application, especially when that
> "relevant node state" may actually be virtual (e.g., an aspect of an
> object or collection of objects, not an object itself).

I don't think I understand this part. What does "relevant node state" mean 
to you? Something like an email object being read or unread? Is that 
something "virtual" because the read/unread state is an aspect of object 
that doesn't exist without the object? I'm trying to put things in more 
concrete terms. I think it makes these kinds of threads easier to read and 
respond to.

Anyway, if my description is correct, I don't see how QML is affected. You 
can already do something like myDataModel.modelIndex(model.index).isUnread 
in a QML delegate. If you have something virtual which gives you a type 
which is more complex than a bool for isUnread, I think the 
QML/Js/QMetaType/QVariant system will get in your way rather than the QAIM 
API.

I also don't see how any issue you hit in QML over this stuff would be 
solved by a QModelIndex::internalType() or similar.

> 
>> I understand the issues regarding performance and the current trivial
>> > constructor/copy-constructor for QModelIndex, so I can "settle" for an
>> > *additional* int-or-enum value (in addition to the current void* inside
>> > the QModelIndex).
>>
>> There is
>>
>> int QModelIndex::internalId()
>>
>> already. Does that help?
>>
> 
> If I can have *both*, then yes, that would help (a lot).  Do we want an
> overloaded createIndex() that takes both?

I don't know... I think it could lead to some awkward APIs to add an 
internalType or similar.

<snip>

> Restating:
> 
> (1)- MyBookNode only exists because QModelIndex has a typeless void*, and
> I
> don't know what type it points to.  (I thought you could not have
> QModelIndex::internalId() in addition to QModelIndex::internalPointer()?)

Correct, now that I'm clear on what you mean there, you can only have one or 
the other.
 
> (2)- The maintined/synchronized tree of MyBookNode instances only exists
> because QModelIndex cannot perform value semantics on the MyBookNode
> (temporary and volatile) handle.

The real question is whether the Node should be temporary and why.

> Great happiness and joy throughout the land comes from a QModelIndex
> design change that removes (2), or (1) (and removing (1) similarly means
> removing (2)).

I think you're saying that having the internalType() or similar would remove 
your need for maintaining MyBookNode*s. Just for clarity.

<snip>

>> You suggest that QModelIndex [should] wholly-contain a MyHandleToMyObject
>> type (through pointer that it deletes)
>>
>> That doesn't seem right to me for several reasons. First, it means that
>> you'd have to new something every time index() is called and use it with
>> createIndex() so that the QModelIndex takes ownership of it (assuming you
>> could tell the QModelIndex the concrete type - it can't delete void*).
>> QModelIndex would also have to become something like a QSharedPointer.
>>
> 
> I defer to the Trolls how they want to deal with that.  If value semantics
> within QModelIndex were a goal, I'd be fine with a QSharedPointer-type
> thing, with the void* being replaced with a typed QPayload* that the user
> overrides and is always deleted by the QModelIndex, or any other mechanism
> to permit a small payload for user state that is "wholly contained" within
> the QModelIndex.

It's not just the Trolls that would have to deal with that issue. I'd like 
to see the templates you imagine to implement this feature.

I don't think the value semantics you were writing about would actually be 
usable.

> (I only need a void* and an enum).
> 
> However, I would guess the easier approach would be to simply add a
> (user-definable) enum *in addition* to the existing void*, so I can merely
> cast the void* to a MyParagraph or MyChapter, as needed.
> 
> Can I have *both* the void* and the internal id in a QModelIndex?  That
> *would* make me happy.  (How do I create that?)

So this seems to be the remaining issue. What effect would it have on the 
API? On performance? On source compatibility? 

What is the actual problem you're trying to solve? Are you trying to solve 
an API issue because you don't think anyone should ever have to define a 
Node {}; ? Would you be ok with having the Node internalised so that the API 
issue is not exposed to you? Are you concerned about performance of 
maintaining the Nodes? Are you concerned about devlopment burden and 
maintenance burden for a class that uses Nodes?

It sounds like the QStandardItem API would suit you better. I know the 
QStandardItemModel API has its own problems, but let's just consider 
QStandardItem. You can create a QStandardItem and then do something like 

item.setData(QVariant::fromValue(myBook));

and then later 

QVariant var = item.data();

if (var.type() == BookType) ...
else if (var.type() == ChapterType) ...
else if (var.type() == ParagraphType) ...

If your problem is with the API, does this have the same problem for you? 
This is like internalized Nodes. Wouldn't your issue be solved by creating 
some generic wrapper which has a setData(QVariant) and data() method 
simliarly to how QStandardItem wraps a QModelIndex?

All the best,

Steve.




More information about the Qt5-feedback mailing list