Re: [Development] QtQml value types

2014-04-28 Thread Roland Winklmeier
2014-04-24 22:42 GMT+02:00 Dominik Holland dominik.holl...@pelagicore.com:


 I like that idea, but couldn't we do something like this ?

 qRegisterWrapperMyWrapper(qMetaTypeIdYourType())

 MyWrapper is a QObject based class which can whatever you want and gets
 YourType as reference or per value.

 It would mean you create your wrapper once for all your Types of your
 Interface and afterwards the QmlEngine will create the Wrapper
 automatically for every object which is exposed to QML.

 I could also think of having some automatically created wrappers for
 QListYourType.


 The wrapper itself gives you the flexiblity to decide whether you can
 support changedSignals of what functions you want to expose to QML.

 In the end it would also be great to register the Wrapper as a own QML
 Type to be able to create the object in QML.

 Dominik


This is similar to what I had in mind in first place. I don't have
experience with the Qt script engine and how powerful it is, so I cannot
really comment on it.
To my knowledge QObject wrappers are the only solution atm to expose type
safe variables, because signals and slots require the QObject base class.
QQmlValueType is a good starter. It needs for sure some improvements and
careful thinking, but it is the easiest way of doing it.
Unfortunately it produces a lot boiler plate code, cause you have to write
a wrapper for each non-QObject class you want to expose. In an ideal world
we could register them without any wrapper (although I don't know how we
would bind their methods then). In a nearly perfect world we would have a
generator for this wrappers, similar to moc. You for example mark your
custom class with some macros and a generator creates the wrappers for you
during compile time.

class MyValueClass
{
VALUE_PROPERTY( int value READ value WRITE setValue )
VALUE_PROPERTY( int anotherValue READ anotherValue WRITE
setAnotherValue )

public:

MyValueClass();
~MyValueClass();

int value() const;
int anotherValue() const;

setValue(int value);
setAnotherValue(int value);

private:
int m_value;
int m_anotherValue;
};

Doing wrappers manually is a bigger maintenance effort to keep both in
sync, an automatic step would reduce the burden.

The explanations regarding QVariantMap were really interesting and for
smaller projects this is for sure a good idea. But as others already said,
it has several drawbacks. In general, however, you explanations about MVC
made sense.
The line
dataModel-getAircraft().getCom1().getFrequency(chosenUnit).toQString();
can for sure moved into a model/controller class dedicated to a specific
view. But in this case i still need to transfer a CFrequency object to QML
and no QString. The reason is simple and you immediately see it if you look
into the API of CFrequency:

class CFrequency
{
public:

enum FrequencyUnit
{
Hz,
MHz,
kHz,
};

double getValue(FrequencyUnit unit);
...
private:
int m_siValue;
};

It has only one private member, but the returned value from getValue
depends which Unit you want to have and is automatically calculated during
runtime. Imagine a class CTemperature, which has the value in celsius but
someone wants it in Fahrenheit getValue(TemperatureValue::Fahrenheit);.
Which values would you put into QVariantMap? The SI value or all of them?
The design of this classes is, that you can decide during runtime, which
value you want to display depending on the application settings.
Theoretically I could create a controller class doing this step for me. So
the view class provides the unit as user input and the controller class
updates the QVariantMap. But this would mean, I have a stupid view class
and have to do all the logic in C++. This is fine if I use plain QML, but
as soon as I want to do some logic in JS, the engine needs to know the
types.

So to summarize, we are discussing two different concepts:
* In order to use plain QML, QVariantMap can be sufficient. QML as the view
implementation does not need to know the data structure.
* As soon as someone wants to move some logic from C++ into JS, JS needs to
know the data type.

According to my understanding it is the second one we are discussing here.

Roland
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-25 Thread Alberto Mardegan
On 04/24/2014 03:11 PM, Joshua Kolden wrote:
[...]
 We have a solution that works very well for us using QVariantMaps and an
 MVC pattern / strong separation between data objects and view.  It
 appears to me that most people are having this issue because it’s not
 common to use MVC with QtWidgets.  But you can easily expose complex
 data structures to QtQuick without subclassing QObject via QVariantMaps. 
[...]

While your proposed approach is rather clean, it carries one drawback,
which is the lack of type information, with all its consequences.

For instance, I would like to have a GeoPoint type with latitude and
longitude properties; if I exposed it as a QVariantMap, I wouldn't be
able to prevent the QML code from doing stuff like:
p.latitude = 60
p.longitde = 10 // note the typo

Ciao,
  Alberto

-- 
http://blog.mardy.it - geek in un lingua international!
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-25 Thread Richard Moore
On 25 April 2014 11:51, Alberto Mardegan ma...@users.sourceforge.netwrote:

 For instance, I would like to have a GeoPoint type with latitude and
 longitude properties; if I exposed it as a QVariantMap, I wouldn't be
 able to prevent the QML code from doing stuff like:
 p.latitude = 60
 p.longitde = 10 // note the typo


An approach like http://qt-project.org/doc/qt-4.8/qscriptclass.html would
allow this to be enforced.

Rich.
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-25 Thread Joshua Kolden


On Apr 25, 2014, at 3:51 AM, Alberto Mardegan ma...@users.sourceforge.net 
wrote:

 On 04/24/2014 03:11 PM, Joshua Kolden wrote:
 [...]
 We have a solution that works very well for us using QVariantMaps and an
 MVC pattern / strong separation between data objects and view.  It
 appears to me that most people are having this issue because it’s not
 common to use MVC with QtWidgets.  But you can easily expose complex
 data structures to QtQuick without subclassing QObject via QVariantMaps. 
 [...]
 
 While your proposed approach is rather clean, it carries one drawback,
 which is the lack of type information, with all its consequences.
 
 For instance, I would like to have a GeoPoint type with latitude and
 longitude properties; if I exposed it as a QVariantMap, I wouldn't be
 able to prevent the QML code from doing stuff like:
 p.latitude = 60
 p.longitde = 10 // note the typo
 

Actually you can (at least at runtime), although we don’t.  In the application 
runtime we use explicit input validation for user input in the view as apposed 
to relying on compiler errors.  This gives us much finer grain control over 
what constitutes valid data, as apposed to only validating type and existence 
as compilers do.  Also our GUI development is in real time on top of the 
running application (do that with widgets), so variable typos are immediately 
obvious.

If as I’ve described these data types are QML properties then onChanged is 
signaled, so you can easily run any type of validator (in javascript or c++).  
This includes listing all keys in the QVarianMap for `eatra` values that 
shouldn't be there, missing values, type or range invalidations etc.  

Also once you grab your data back from the QVariantMap in C++ in the controller 
you must use an accessor like .toString() etc to cast the QVariant to a known 
type, and that brings back your type checking on the C++ side.  However, what 
you’ve described is a coding error, as apposed to a user input error, we don’t 
check this in application code at all nor do we rely on the compiler to catch 
everything as this is the domain of the test sweet. 

j

 Ciao,
  Alberto
 
 -- 
 http://blog.mardy.it - geek in un lingua international!
 ___
 Development mailing list
 Development@qt-project.org
 http://lists.qt-project.org/mailman/listinfo/development

___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-25 Thread Dominik Holland

Am 25.04.14 15:05, schrieb Joshua Kolden:

 On Apr 25, 2014, at 3:51 AM, Alberto Mardegan ma...@users.sourceforge.net 
 wrote:

 On 04/24/2014 03:11 PM, Joshua Kolden wrote:
 [...]
 We have a solution that works very well for us using QVariantMaps and an
 MVC pattern / strong separation between data objects and view.  It
 appears to me that most people are having this issue because it’s not
 common to use MVC with QtWidgets.  But you can easily expose complex
 data structures to QtQuick without subclassing QObject via QVariantMaps.
 [...]

 While your proposed approach is rather clean, it carries one drawback,
 which is the lack of type information, with all its consequences.

 For instance, I would like to have a GeoPoint type with latitude and
 longitude properties; if I exposed it as a QVariantMap, I wouldn't be
 able to prevent the QML code from doing stuff like:
 p.latitude = 60
 p.longitde = 10 // note the typo

 Actually you can (at least at runtime), although we don’t.  In the 
 application runtime we use explicit input validation for user input in the 
 view as apposed to relying on compiler errors.  This gives us much finer 
 grain control over what constitutes valid data, as apposed to only validating 
 type and existence as compilers do.  Also our GUI development is in real time 
 on top of the running application (do that with widgets), so variable typos 
 are immediately obvious.

 If as I’ve described these data types are QML properties then onChanged is 
 signaled, so you can easily run any type of validator (in javascript or c++). 
  This includes listing all keys in the QVarianMap for `eatra` values that 
 shouldn't be there, missing values, type or range invalidations etc.

 Also once you grab your data back from the QVariantMap in C++ in the 
 controller you must use an accessor like .toString() etc to cast the QVariant 
 to a known type, and that brings back your type checking on the C++ side.  
 However, what you’ve described is a coding error, as apposed to a user input 
 error, we don’t check this in application code at all nor do we rely on the 
 compiler to catch everything as this is the domain of the test sweet.

 j

We are also currently using QVariantMaps in a Project and beside having 
more work to validate the Data which comes in the Controller classes 
instead of validating the input in the QmlType classes itself it also 
has some other problems at least for us.

We are using QDoc to generate QML Documentation for our Interfaces and 
instead of describing all the available properties in the QmlType you 
need to document the properties of the QVariantMap in the function. Sure 
i think there are ways to some kind of Fake QmlType page and document 
the properties (which is a QVariantMap in the real world) but i don't 
think it's a proper solution.

A other problem i can see is the Code-Completion which you can't get for 
QVariantMap because it's a variable type which can store anything in any 
format. I think for Projects where the QML Developers know the C++ 
Developers this works, but in larger Projects like having a API for 
3rdParties i think QVariantMaps doesn't work and we need to invest some 
time to make a proper solution.

Dominik


 Ciao,
   Alberto

 -- 
 http://blog.mardy.it - geek in un lingua international!
 ___
 Development mailing list
 Development@qt-project.org
 http://lists.qt-project.org/mailman/listinfo/development
 ___
 Development mailing list
 Development@qt-project.org
 http://lists.qt-project.org/mailman/listinfo/development

___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-25 Thread Joshua Kolden

On Apr 25, 2014, at 7:28 AM, Dominik Holland dominik.holl...@pelagicore.com 
wrote:

 
 Am 25.04.14 15:05, schrieb Joshua Kolden:
 
 On Apr 25, 2014, at 3:51 AM, Alberto Mardegan ma...@users.sourceforge.net 
 wrote:
 
 On 04/24/2014 03:11 PM, Joshua Kolden wrote:
 [...]
 We have a solution that works very well for us using QVariantMaps and an
 MVC pattern / strong separation between data objects and view.  It
 appears to me that most people are having this issue because it’s not
 common to use MVC with QtWidgets.  But you can easily expose complex
 data structures to QtQuick without subclassing QObject via QVariantMaps.
 [...]
 
 While your proposed approach is rather clean, it carries one drawback,
 which is the lack of type information, with all its consequences.
 
 For instance, I would like to have a GeoPoint type with latitude and
 longitude properties; if I exposed it as a QVariantMap, I wouldn't be
 able to prevent the QML code from doing stuff like:
 p.latitude = 60
 p.longitde = 10 // note the typo
 
 Actually you can (at least at runtime), although we don’t.  In the 
 application runtime we use explicit input validation for user input in the 
 view as apposed to relying on compiler errors.  This gives us much finer 
 grain control over what constitutes valid data, as apposed to only 
 validating type and existence as compilers do.  Also our GUI development is 
 in real time on top of the running application (do that with widgets), so 
 variable typos are immediately obvious.
 
 If as I’ve described these data types are QML properties then onChanged is 
 signaled, so you can easily run any type of validator (in javascript or 
 c++).  This includes listing all keys in the QVarianMap for `eatra` values 
 that shouldn't be there, missing values, type or range invalidations etc.
 
 Also once you grab your data back from the QVariantMap in C++ in the 
 controller you must use an accessor like .toString() etc to cast the 
 QVariant to a known type, and that brings back your type checking on the C++ 
 side.  However, what you’ve described is a coding error, as apposed to a 
 user input error, we don’t check this in application code at all nor do we 
 rely on the compiler to catch everything as this is the domain of the test 
 sweet.
 
 j
 
 We are also currently using QVariantMaps in a Project and beside having 
 more work to validate the Data which comes in the Controller classes 
 instead of validating the input in the QmlType classes itself it also 
 has some other problems at least for us.
 
 We are using QDoc to generate QML Documentation for our Interfaces and 
 instead of describing all the available properties in the QmlType you 
 need to document the properties of the QVariantMap in the function. Sure 
 i think there are ways to some kind of Fake QmlType page and document 
 the properties (which is a QVariantMap in the real world) but i don't 
 think it's a proper solution.
 

Ah yes we don’t use QDoc. I can see how auto documentation generation could be 
improved.
(Currently we use literate coffeescript for the views and are setting up 
something similar on the c++ side. Admittedly a fairly obscure approach for 
most.)

 A other problem i can see is the Code-Completion which you can't get for 
 QVariantMap because it's a variable type which can store anything in any 
 format. I think for Projects where the QML Developers know the C++ 
 Developers this works, but in larger Projects like having a API for 
 3rdParties i think QVariantMaps doesn't work and we need to invest some 
 time to make a proper solution.
 

I’m not sure what you mean, are you talking about a particular IDE?  I would 
think any custom data type weather it’s c++ or javascript would have a similar 
issue within an IDE.  Personally I use sublime text and from time to time I do 
create code completion snippets for custom objects, but no more so for 
javascript data types than c++ ones.  I don’t think I understand what you mean 
though.

Definitely, I’m not arguing against a better way / ‘proper solution’.  By all 
means lets work on that please.  I was only addressing the concern that you 
can’t use QML at all when working with large numbers of non QObject derived 
domain specific data objects.  In that respect I’d say we’re quite better of, 
at least in our project, for using QtQuick/QML over widgets.

j

 Dominik
 
 
 Ciao,
  Alberto
 
 -- 
 http://blog.mardy.it - geek in un lingua international!
 ___
 Development mailing list
 Development@qt-project.org
 http://lists.qt-project.org/mailman/listinfo/development
 ___
 Development mailing list
 Development@qt-project.org
 http://lists.qt-project.org/mailman/listinfo/development
 
 ___
 Development mailing list
 Development@qt-project.org
 http://lists.qt-project.org/mailman/listinfo/development

___

Re: [Development] QtQml value types

2014-04-25 Thread Jędrzej Nowacki
On Friday 25 of April 2014 13:03:33 Richard Moore wrote:
 On 25 April 2014 11:51, Alberto Mardegan ma...@users.sourceforge.netwrote:
  For instance, I would like to have a GeoPoint type with latitude and
  longitude properties; if I exposed it as a QVariantMap, I wouldn't be
  able to prevent the QML code from doing stuff like:
  p.latitude = 60
  p.longitde = 10 // note the typo
 
 An approach like http://qt-project.org/doc/qt-4.8/qscriptclass.html would
 allow this to be enforced.
 
 Rich.

Hi,

  This one was elegant but a bit too heavy from performance point of view, 
mostly because it was not possible to cache any data or assume data layout. As 
soon you had qscriptclass in the scope your code was not optimal anymore.

Cheers,
  Jędrek
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-25 Thread charleyb123 .

 snip, use QVariant Maps to transfer property data between C++ and QML



  While your proposed approach is rather clean, it carries one drawback,
  which is the lack of type information, with all its consequences.


snip, need to convert domain-types to/from strings/doubles/QVariant on QML
side and C++ side

PRO:
*- Can data-clean/input-validate on either C++ or QML side, can add more
custom validators in different places
*- ?More fine-grain control at data-input/UI separate from back-end

CON:
*- Rules for validation now separate from domain-specific type
*- Property misspell no longer compile-caught, instead run-caught (if
validated input)
*- Must invest in this adapter-layer that may ultimately be merely a
pass-through, can be expensive to implement for big systems (many types),
or to value-copy/transform state for some rich types

 Also once you grab your data back from the QVariantMap in C++ in the
 controller you must use an accessor like .toString() etc to cast the
 QVariant to a known type, and that brings back your type checking on the
 C++ side.


For us, these are often lossy and very stale operations (converting
to/from string, double, etc. from our internal types. Dealing with hardware
and constantly changing values, it would be nice to reference the
real-item directly rather than establish an adapter layer that is (by
definition) lossy-and-stale for our use.

snip,

  A other problem i can see is the Code-Completion which you can't get for
  QVariantMap because it's a variable type which can store anything in any
  format. I think for Projects where the QML Developers know the C++
  Developers this works, but in larger Projects like having a API for
  3rdParties i think QVariantMaps doesn't work and we need to invest some
  time to make a proper solution.


Agree that QVariantMap drops type information.  Agree that loss of type
information can be an increasingly important issue in larger systems with
more domain types (because those types established rules/behaviors in the
system, and that's more important in large systems).


 Definitely, I’m not arguing against a better way / ‘proper solution’.  By
 all means lets work on that please.  I was only addressing the concern that
 you can’t use QML at all when working with large numbers of non QObject
 derived domain specific data objects.  In that respect I’d say we’re quite
 better of, at least in our project, for using QtQuick/QML over widgets.


I think there are two topics here:

(1) Deep/rich/many domain-specific C++ types (or their state/properties)
need to somehow be made available to QML through a reasonable mechanism
that scales for large C++ systems.  QVariantMap is one suggestion that
works for some systems.  IMHO, the loss of type-information is a
deal-breaker for large systems (see below), although I concede that some
people have a back-end to front-end transform, API collapse, or system
workflow where this would work.

(2) QtQuick/QML is preferred over widgets.  We agree; I realize some people
aren't sure, or currently disagree for a couple reasons.

IMHO using QVariantMap is really good when there is a strong API
collapse or transform from the very-deep-back-end-system and the
relatively-flat set of properties that should be exposed to the UI/QML.
 However, some systems are rich, and this deep-nested-type-state is
important (even through to the interface).  It becomes important to
*maintain* that nesting, and in that case, producing the
nested-QVariantMap may be expensive (and it might be big), and might be
especially difficult to describe in a granular form that supports
transactions at different levels of interface update.

Further, for our types, we have MANY derived-properties.  From one (or a
very small number) of data-members, we may have MANY properties that
are available, but these are computed-on-the-fly.  I don't want to
produce those to populate a QVariantMap if they are not really needed.
 Some hardware-reads are really expensive.

Others have touched on these, but to summarize:

(a) creation of a QVariantMap (or similar value-semantics) approaches are
investing in an adapter layer (I'd prefer to not write a bunch of
toQVariant()/fromQVariant() in my zillions of C++ classes, and it's hard
to guess the level-of-granularity to support transaction-updates at varying
levels of interface update.)

(b) the values populated in a QVariantMap are immediately stale and
lossy in our system (we drive hardware and embedded systems, or systems
with lots of dynamic updates and would prefer to reference the,
real-C++-object-thing)

(c) many of our property-values are functionally/dynamically computed,
and should not be computed unless explicitly needed.  (An example is a
class with one data member, which *is* a property, but also provides four
other properties that may be computed from that one data-member; all
five-properties should not be populated into the QVariantMap unless they
are really needed.)

In contrast, I could live 

[Development] QtQml value types

2014-04-24 Thread Roland Winklmeier
Hi list,

we discussed the topic whether non-QObject types can be used in QML or not
several times. The last time is only a few days ago as part of the Qt
future discussions. So I had some thoughts what needs to happen to solve
this issue.

I can remember someone told me there are plans to add such a feature. So my
first question is, how far are we on this? Is it just a plan or is someone
really working on it already? Are there any details?

If there is nothing happening at the moment, I'd like to share some ideas I
had during the last days.
I'm not an expert in QtQuick/QML internals so I have no idea what might be
possible or not. At the moment I assume only QObject derived classes can be
used in QML contexts.

Charley's presentation (
http://www.qtdeveloperdays.com/sites/default/files/presentation_pdf/QtDevDaysSFO-2013_WrappingCppForQml_final.pdf)
was very interesting to me as I see this as a possible workaround until
something more sophisticated is in place.
During discussions in our team one of the members pointed me to internal
class QQmlValueType (
https://qt.gitorious.org/qt/qtdeclarative/source/HEAD:src/qml/qml/qqmlvaluetype_p.h).
This is used to make Qt value types like QSize, QPointF available through a
QObject wrapper and is very similar to what charley presented.
Now I wonder can't we just make this (or something similar) available as a
public API? This would allow developers to add their custom types without
much effort and would solve a lot problems.
I don't think this is much effort but it will help a lot.

Thoughts?

Roland
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-24 Thread Simon Hausmann
On Thursday 24. April 2014 11.55.07 Roland Winklmeier wrote:
 Hi list,
 
 we discussed the topic whether non-QObject types can be used in QML or not
 several times. The last time is only a few days ago as part of the Qt
 future discussions. So I had some thoughts what needs to happen to solve
 this issue.

Thank you for taking this out of the future of Qt thread (which seemed 
counterproductive). I wanted to do the same today :). I'm interested in 
contributing to the actual implementation of making it easier to use value 
types in QML. I'm aware of attempts from the past and I'm familiar with the 
current internal implementation. However it's not entirely clear to me yet 
what the presumably best way is for making it part of the public API.
 
 I can remember someone told me there are plans to add such a feature. So my
 first question is, how far are we on this? Is it just a plan or is someone
 really working on it already? Are there any details?
 
 If there is nothing happening at the moment, I'd like to share some ideas I
 had during the last days.

 I'm not an expert in QtQuick/QML internals so I have no idea what might be
 possible or not. At the moment I assume only QObject derived classes can be
 used in QML contexts.
 
 Charley's presentation (
 http://www.qtdeveloperdays.com/sites/default/files/presentation_pdf/QtDevDay
 sSFO-2013_WrappingCppForQml_final.pdf) was very interesting to me as I see
 this as a possible workaround until something more sophisticated is in
 place.
 During discussions in our team one of the members pointed me to internal
 class QQmlValueType (
 https://qt.gitorious.org/qt/qtdeclarative/source/HEAD:src/qml/qml/qqmlvaluet
 ype_p.h). This is used to make Qt value types like QSize, QPointF available
 through a QObject wrapper and is very similar to what charley presented.
 Now I wonder can't we just make this (or something similar) available as a
 public API? This would allow developers to add their custom types without
 much effort and would solve a lot problems.
 I don't think this is much effort but it will help a lot.

QQmlValueType is quite good in what it does. It could be that this is what we 
should be doing.

On the other hand for QtScript Kent came up with a solution that's strikingly 
beautiful at first:

scriptEngine-setDefaultPrototype(qMetaTypeIdYourType(), prototype);

It means that whenever a value type with the given meta-type ID enters the 
JavaScript world, the object that's being created to wrap the value will have 
the given object as prototype set.

That's incredibly powerful if you think about it, it even allows implementing 
functionality for value types in JavaScript itself.

Now the usability of this API depends on the availability of C-style function 
callbacks in QJSEngine in order to effectively allow wrapping in C++. That 
itself is something we've been hesitant about due to the way this exposes 
internals of the engine.

At the same time - even if we had C callbacks - we have to ask ourselves: 
What's the right data representation? For each operation, would we have to 
unpack a QVariant out of a QJSValue and then cast it to whatever the specific 
value type is?

Theoretically this is something that QQmlValueType solves, but in practice if 
you have for example a QPoint and code like this:

var p = someObject.position;
p.x = 42;
p.y = 100;

Then the load and store from say variant is executed twice each.


It gets more complex when you consider that value types are semantically 
different in JavaScript. In the above example modifying p.x actually modifies 
someObject.position.x, i.e. someObject will probably move on the screen.


These are just some initial thoughts - there are many things to consider. It's 
one of the topics we should try to discuss at the Qt contributor summit. But 
perhaps until then we can come up with an intermediate initial solution here 
on the mailing list - flexible enough to allow for extension in the future.

Simon
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] QtQml value types

2014-04-24 Thread Joshua Kolden
I’m sorry I intended to respond to this in the other thread, but super busy.  
Nevertheless I think it’s important for people to know there are answers to 
this now without waiting for any new APIs so I stole the time to write our 
process out.

We have a solution that works very well for us using QVariantMaps and an MVC 
pattern / strong separation between data objects and view.  It appears to me 
that most people are having this issue because it’s not common to use MVC with 
QtWidgets.  But you can easily expose complex data structures to QtQuick 
without subclassing QObject via QVariantMaps. 

On the data side your `value classes` are best when optimized for the domain 
you use them in.  You rightly don’t want to contaminate them with view logic.  
Conversely you don’t want to contaminate your view with specific knowledge of 
internal data representations.  This is where the controller comes in to map 
the view concepts to the model data allowing either to change independently of 
each other.

Things like 
`dataModel-getAircraft().getCom1().getFrequency(chosenUnit).toQString();` are 
generally bad practice in OO design regardless of QML or Qt because it’s 
fragile, and requires knowledge of every step in the chain.  If however you can 
provide an interface that just returns frequencies say, then do whatever you 
want with your value classes behind that interface without worrying that it’ll 
break your UI.

We do this by creating controllers based on UI concepts. For us these include: 
“groups”, “assets”, “processes”, “accounts” etc.  For you it might be things 
like “communications”, “navigation”, “flight_controls”, “simulator_state”, or 
whatever works best for you conceptually.  We create QObject based controllers 
on the C++ side for each of these conceptual areas, and expose a select few 
signals as the interface to pass data back and forth to the view. We do this 
for widget apps, and web apps too, it’s not a workflow that is specific to QML.

It works a lot better than having a separate QObject for every value class 
since it lets you keep your data models focused on the problems they are 
designed to solve without regard to the display of the data.  Conversely in the 
GUI we just have the data we need we don’t need to care about how it is derived.

In QML QVariantMaps show up as javascript object/hashes/maps (whatever you like 
to call them).  They can have arbitrary nesting, and data types, so you can 
maintain or alter your data representation at your desecration.  The data can 
than be mapped to QML object properties in a declarative way, either once for 
the entire data representation or individually for each attribute.

 (With QML a better mental model might be ‘link’ to data as apposed to passing 
data ‘back and forth’ since we don’t do a lot of method calling. Properties 
work automatically via signals, so it’s a bit more like a patch panel, once the 
connections are made they just work moving the data back and forth as it 
changes on either side.)  

For example:

Rectangle {
 id: radioInterface
 property var comData: communications.com
function foo() {
frequencyDisplay.text = comData.com1.frequency;
...
}

or

Rectangle {
 id: radio Interface
 property var com1freq: communications.com.com1.frequency
 property var com1amp: communications.com.com2.amplitude
…
}

For the controller you’d have something like this:

class CommunicationsController : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantMap com READ com WRITE setCom NOTIFY comChanged)
…
public:
 
QVariantMap com() const;

public slots:

  void setCom(const QVariantMap com);

signals:

  void comChanged();
}

Later you expose this controller to QtQuick with something like.

CommunicationsController communications;
interface.addProperty(“communications, communications);

Now everything is connected.  On the QML side you can just directly access the 
values as above, or copy them to a javascript object (i.e. `var comData = 
communications.com`) etc.  On the c++ side you can set and read values in this 
structure with the QVarianMap accessors.

QVariantMap comData;
QVariantMap com1;
com1[“frequency”] = 
dataModel-getAircraft().getCom1().getFrequency(chosenUnit).toQString(); // if 
you must
comData[“com1”] = com1;
return comData;

and

dataModel-getAircraft().getCom1().setFrequency(com[“com1][“frequency”].toFloat());
 // or whatever type

The only thing that’s a consideration with QVariantMaps vs QObjects is that you 
can’t include methods.  But that’s where the OO design comes in which tells us 
we shouldn’t have too many operations happening on data in the views anyway.  
Nevertheless if you truly need to have operators such as convince helper 
methods in the view you can expose them with Q_INVOKABLE methods in the 
controller.

It’s important to note that this workflow is in production now with our 
software and works great, and is very maintainable.  The javascript data models 
are very easy to