[Development] QtQml value types

Joshua Kolden joshua at crackcreative.com
Thu Apr 24 14:11:00 CEST 2014


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 visualize in json, so it’s self documenting as to what data is available to the QUI.  Also adding new data structures doesn’t break either side of the system, you simply set our read the named values you needs and disregard the rest.

Tell me if this works in your case, and I’m happy to clarify if in my haste I haven’t explained it well enough.

j



On Apr 24, 2014, at 2:55 AM, Roland Winklmeier <roland.m.winklmeier at gmail.com> 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.
> 
> 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 at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20140424/392156bb/attachment.html>


More information about the Development mailing list