[Development] Design review: Adapter layer, C++ to QML

Alan Alpert 416365416c at gmail.com
Wed Jan 30 22:17:12 CET 2013


On Wed, Jan 30, 2013 at 12:35 PM, Charley Bay <charleyb123 at gmail.com> wrote:
> Hi, Alan--
>
>> charley:
>>
>> > I've implemented a C++ "adapter-layer" (mostly template-based) to expose
>> > C++
>> > objects to QML. <snip>,
>
>
>>
>> Sounds like this is for developers using Qt, not working on Qt.
>>
>> qt-interest is the correct ML for that (although given the youthful
>> state of QML, this could also suggest improvements to QML later. But
>> we can worry about that later).
>
>
> Agreed -- although I'm tentatively suggesting the Qt-proper libraries offer
> this API layer natively (in the Qt distribution), as I'd assume most users
> with heavy C++ libs would need it.  Still, your point is valid, so I can
> move lists with this discussion (if there's insufficient interest here).

If people on this list are watching developing use of Qt for user
conveniences that should become part of the framework, they're
probably on qt-interest already. I'm subscribed to that list (although
I only check the QML related mails). You probably won't *reach* any
users with heavy C++ libs (beyond Qt ;) ) on this list. So I
definitely think this thread should have started on qt-interest, and
maybe moved to development later if it reaches conclusions that
there's something we should add to Qt.

>> > ----------
>> > Design goals:

I should have also mentioned the missing design goal 0: simplicity.
Perhaps it's just that I don't understand it yet, but this
adapter-layer seems a little over-complicated for the generic task of
just exposing an existing C++ (non-QObject) instance hierarchy to QML.
What's the difference between your four design goals and the single
goal of "exposing an existing C++ (non-QObject) instance hierarchy to
QML"?

>> >
>> >  (a)- Expose individual C++ objects with minimal code
>>
>> There are other related goals that could be involved in the C++/QML
>> adaption layer. The last generator script I wrote for this (
>> http://alan.imagin-itis.net/?p=166 ) was about non-Qt C++ code (with a
>> GUI focus), but I assume you're talking about exposing QObject
>> subclasses in a Qt project from the start.
>
>
> Your generator script didn't focus on the "instance-hierarchy" exposed to
> QML, and that's what I'm focused on.  (I like your generator idea, but much
> of our code likely requires manual consideration of what properties and
> instances are exposed.)

Manual per instance or manual per class? Manual per class is in that
generator script.

> Further, no, I'm talking specifically about exposing a massive amount of C++
> classes to QML, within instance-hierarchies, when none of those C++ classes
> derive from QObject.

That's an important distinction, it means that wrapper QObject classes
will be needed - which can't be generated with templates (moc chokes
on templates).

>>
>> >  (b)- Individual "QML-instances" can "contain" a "C++ back-end-instance"
>> > that is not shared with other QML instances.
>>
>> Is this just creating a subclass with Q_PROPERTY macros?
>
>
> The C++ instance has the "reference-state", and the QML instance merely
> "wraps" it.  The QML/GUI cannot instantiate the "reference" instance (the
> C++ back-end model manages all that).
>
> Further, with this design, no QML instances are "temporarily-created" to
> access a property.  Rather, they always exist as a wrapper to the C++
> instance, or they don't (it's the same QML instance each time properties are
> extracted from that C++ object).

So the C++ instances are pre-existing, and the QML adaptor layer has
to mould to the existing instances and existing hierarchy? That sounds
like a case for automatically generating wrapper objects recursively
on instances.

>>
>> >  (c)- Multiple "QML-instances" can "share" access to the same C++
>> > back-end
>> > instance, with coordinated updates (e.g., as one QML-instance triggers a
>> > C++
>> > object "property-value-change", all QML instances are "notified" of that
>> > change)
>>
>> Perhaps you should just be using the new QML Singleton type, used like so:
>>
>> MyType.myProp = foo; //Engine creates an instance of the C++ type
>> first time MyType is used, all references use that instance.
>
>
> The QML model cannot instantiate the back-end C++ instances (they correlate
> to hardware managed by a back-end-model).  Rather, we only want QML to
> "reflect" whatever the model is doing.

Since you need a wrapper class anyways, you can still do this with
singletons. Take the above snippet and the comment now says "Engine
creates an instance of the C++-Wrapper type".

>> >  (d)- Enable nested QML-wrapped-object-access to nested C++ instances
>> > that
>> > are natively stored (e.g., no refactoring of C++ code to incrementally
>> > expose nested C++ objects to QML)
>>
>> I don't understand what you're saying here. Do you mean that adding a
>> QML adaption layer requires no changes to the backing C++ class?
>
>
> Yes:  The QML adaptation layer requires no changes to the backing C++
> classes.  Further, we require a deep-object-instance-hierarchy of wrappers
> for the deep-hierarchy-of-C++-instances.

That part shouldn't be too hard to do without models or managers. If
you have generated QML wrapper classes, you can have them recursively
instantiate wrappers down the hierarchy when you create the root
wrapper object on the root backing object.

>>
>> > <snip>,
>> I'm not sure why you needed the multiple QML instances <-> single C++
>> instance thing in the first place. Even in Qt 4, you can have a single
>> C++ instance that's exposed but as an uncreatable type. Just put that
>> in your root context as myInstrument and you're good to go.
>
>
> We will have a (C++) "Detector" object with many properties, and there will
> be many GUI/QML "views" into that detector (it appears in the
> list-of-all-detectors, on a graphical drawing of the instrument
> configuration, in the list of saved/collected parameters, in the list of
> all-filters used, etc.)  In all these cases, there is a "GUI-representation"
> of something, and the "Detector-properties" are what we want exposed:
> Voltage, filter, QC-status, fluorochrome, laser, etc.
>
> So, logically, a single "Detector" instance is implicitly displayed in many
> different ways in many parts of our GUI.  Each "display" can be different.
> All of them must reflect the state of the *specific* back-end-C++-instance.
> With this current implementation, the properties exist in a single place --
> the back-end C++ object.

So the C++ state has one object, and to expose it to QML you want
multiple different custom objects? Since those objects need to be
custom, wouldn't it make more sense to apply the "QML Adaption Layer"
to that second set of objects? i.e. in plain C++ you create the UI
logic classes exposing different views of the data, and only expose
those UI logic classes to QML. Basically separating out the manual
(what should be exposed and how) part of the UI adaption layer from
the automatic (C++->QML) part.

>>
>> The idea of conveniently exposing sets of objects with
>> QAbstractListModel like behaviour is interesting, but I don't think it
>> should be mixed up with the exposing single instances question. We
>> already had a thread on improving QAbstractListModel's utility in QML,
>> which I think is a more promising approach.
>
>
> I'm very interested progress on, "improving QAbstractListModel's utility in
> QML".  I don't have specific needs.  However, my specific implementation is
> a utility (template) base class derived from QAbstractListModel that
> *demands* the derived type implement an "enum" for the "roles" for
> extracting properties.
>
> So, my "list-model" implementation is merely a utility convenience thing so
> that creating a "model" of things requires:
>
>   (1) Derive from utility model-base class
>   (2) Implement enum-for-roles
>   (3) Implement function to get a QVariant from the enum-role.
>
> The utility base class handles notifications, add/insert/remove, etc.  (I
> don't want to do that for each model implemented).

This model part is the bit I still don't quite understand. Do you need
it actually exposed as a model (so you can use it in ListView) or is
it just for item updating semantics when the C++ object hierarchy
changes (which could be done by mirroring it in the QObject
hierarchy)?

I also didn't notice your "list-model" implementation in the
implementation description previously. To get "myInstrument.views"
exposed to QML, what exactly are you doing? Is myInstrument an
explicitly instantiated wrapper class exposed as a context property,
with views being a QmlBoxBackManager instance?

>> >  (3) Have I missed similar approaches that already exist, which we could
>> > otherwise use directly?
>>
>> Yeah, you might just want to use qmlRegisterSingletonType (new in Qt 5).
>
>
> Because we have much C++ code, and it's tied to hardware and other system
> configurations:
>
>   (a) QML cannot instantiate most "important" classes, and these are not
> singletons (there might be zero, one, or possibly more than one)
>
>   (b) QML should "reflect" state from the back-end model that does not
> derive from QObject nor use signals/slots
>
>   (c) QML/GUI must "reflect" many "views" of specific back-end state

I'm not saying that you need to place the back-end data instance in
the singleton type. The singleton type can be an adaptor type using a
simpler model than your current one.

Obviously, it's still a bad idea if the type is not conceptually a
single instance per application ;) .

--
Alan Alpert



More information about the Development mailing list