PDA

View Full Version : How to expose QMap to QML?



fonzi337
4th February 2012, 00:51
Hi,

I've been able to use QDeclarativeListProperty to expose a simple list to QML. For instance

Q_PROPERTY(QDeclarativeListProperty<MyQObjectDerivedClass> myprop READ myprop NOTIFY mypropChanged)

However, it would be convenient to be able to expose a QMap<QString, MyQObjectDerivedClass*> model to QML instead so that in QML I can access the elements of the model by name (i.e., something like _mymap["Toyota"].year in QML).

How can I go about this? Do I have to derive a class from QAbstractItemModel to achieve this, or is there a simpler approach?

Thanks!

wysota
4th February 2012, 14:02
There are many possible solutions. One would be to wrap your map into a QObject and expose all the fields of the map as properties of that object.

fonzi337
8th February 2012, 20:46
Thanks wysota. I've attempted this approach (i.e., wrapping a QHash in a QObject-derived class), but the QML side does not seem to be updated when a value changes in the QHash data member. Here are some code snippets:



class BaseApplication : public QObject
{
Q_OBJECT

Q_PROPERTY(ParameterContainer* parameters READ parameters NOTIFY parametersChanged)
public:
explicit BaseApplication(QObject *parent = 0);
ParameterContainer* parameters() const;

signals:
void parametersChanged(ParameterContainer* arg);

private:
ParameterContainer* m_parameters;
};


When the m_parameters data member is updated in the .cpp file, I emit the parametersChanged signal to update QML, but it doesn't update.



class ParameterContainer : public QObject
{
Q_OBJECT
public:
explicit ParameterContainer(QObject *parent = 0);
void setParameterDisplayValue(const QString name, const QString value);
Q_INVOKABLE QString getParameterDisplayValue(const QString name) const;

private:
QHash<QString, QString> m_parameters;
};


Assuming the parameters property of the BaseApplication class is exposed as a context property QML, the QML code looks like this:



Text
{
text: "Param value: " + "<b>" + _parameters.getParameterDisplayValue("param1") + "</b>"
}


Is the QML side not being updated because I'm trying to bind to a Q_INVOKABLE rather than a Q_PROPERTY? If so, how could I work around this issue?

Thanks in advance!

wysota
8th February 2012, 21:00
That's not what I meant. Each parameter should be a separate property.

fonzi337
8th February 2012, 21:02
I thought of doing it that way but that doesn't scale well. This map could grow to be 1000s of items and I'd rather not expose a property for each one. How can I get around this limitation?

wysota
8th February 2012, 21:06
You can't expect QML to monitor every possible key of your map/hash.

I can only offer a solution where the whole map is an opaque qml property accompanied by a signal emited when any value in the map changes and there is a function that accepts the map object and the key argument that returns the value of the key in the map. Then QML will be able to monitor your map for changes and when the signal is emitted, it will recalculate every binding containing a call to a function that takes this map as an argument. However that doesn't scale well either since if you have 1000 keys and only one of them changes, all occurences of all keys will be recalculated. Exposing such a map to QML is probably not a very good idea at all. What is the intention behind it?

fonzi337
8th February 2012, 21:27
For my particular application, the Qt Quick GUI is responsible for displaying possibly100s of different settings; these settings can be modified by the user as well. The list of settings is likely to grow in the future, hence the need for it to scale well. One of my goals in the design of this is to allow new settings to be added from a resource file without the need to recompile. For this reason I was hoping I could expose a QMap to QML which gets dynamically populated from a resource file at application startup, but by the sounds of it this is perhaps a poor approach. Is a Q_PROPERTY-based approach the only true alternative?

wysota
8th February 2012, 21:37
I would expose your settings as a real object with separate properties. Even if you add a new parameter, it's not that much work to add a Q_PROPERTY for it.

vmatikainen
9th September 2013, 15:00
Hi, sorry to bring this up if its no longer relevant.

I encountered a similar challenge and solved it by creating a MySettingProperty for individual elements, which has a value-property. Then I made a QMap containing those settings, individually recognized by an enumeration (extensible from the QML-side). Then again I made a sorted list of the same map, which I exposed as QQmlListProperty. The MySettingProperty has a property for getting its individual type and my delegate has a loader with a selector function to create a correct delegate for each individual setting item.