PDA

View Full Version : QtScript bindings to advanced containers (QMap, QList, etc)



Statix
2nd June 2010, 22:56
In my current project I'm returning XML/RPC data converted to QMap on the C++ side and I'd like to expose these to QtScript for automated testing and this has proven to be quite the hassle (these aren't generated by the binding generator).

My two main hopes are for something to wrap QMap<QString,QString> and QMap<QString, QMap<QString, QString> >

My open question is this easily possible via some trick I am completely unaware of or is this as complicated as it seems.

So far my approaches have been:

1 ) Create a class which inherits from QObject and QMap<QString,QString> with method redirection:

class NewMap : public QObject, public QMap<QString, QString> {
public:
NewMap(QObject *parent=0) : QObject(parent){}
Q_INVOKABLE void insert(const QString& key, const QString& value)
{
QMap<QString,QString>::insert(key,value);
}
};
Q_SCRIPT_DECLARE_QMETAOBJECT(NewMap, QObject*);

This doesn't compile because QObject* is no-copy

2) Attempted to use Q_DECLARE_METATYPE and defined mapToScriptValue and ScriptValueToMap functions which use a java object to try and build up a map, however I'm going to have to define ALOT of functions and basically recreate the map logic.
This approach started to become unwieldy and would take much effort to allow iteration, would also need to define a pair<key,value> type.
* There is also no way on the C++ side that I can find to return all properties defined on a QScriptValue, this kind of mucks object.TestIndex = TestValue definitions.

In QtScript i have imported the Qt.Core extension and I'm able to return QStringList from a C++ object (by value) so the binding generator has managed to prototype a class which inherits from QList<QString>. Apparently the Qt Creators are way way more savy than I am at this.

My ultimate goal is to be able to do this in C++, and the following in QtScript:


//C++
class ExposedScriptObject : public QObject {
...
typedef QMap<QString, QString> StringMap
Q_INVOKABLE StringMap returnMap()
{
StringMap map;
m["TestKey"] = "TestValue";
return m;
}
};



//QtScript

var exposedScriptObject = new ExposedScriptObject();
var myMap = exposedScriptObject.returnMap();
print ( myMap["TestIndex"] );
//Or also acceptible
print ( myMap.value("TestIndex") );

//There would also need to be a means of iteration of
//Key/Value pairs such as
for( KeyValue in myMap)
{
print (KeyValue.key + " : " KeyValue.value + "\n");
}


I'm all out of ideas and cannot connect the dots in Qt/QtScript to do this.

Statix
25th June 2010, 13:17
I figured out how to get what I wanted, sharing the code for anyone else interested.


//header
class ScriptMap : public QMap<QString, QVariant> {
}; // work around because typedefs do not register correctly.
Q_DECLARE_METATYPE(ScriptMap);

//source
QScriptValue ScriptMapToScriptValue(QScriptEngine* eng, const ScriptMap& map)
{
ScriptValue a = eng->newObject();
ScriptMap::const_iterator it(map.begin());
for(; it != map.end(); ++it)
{
QString prop = it.key();
prop.replace(' ', '_');
a.setProperty(prop, qScriptValueFromValue(eng, it.value()));
}
return a;
}

void ScriptMapFromScriptValue( const QScriptValue& value, ScriptMap& map)
{
QScriptValueIterator itr(value);
while(itr.hasNext())
{
itr.next();
map[itr.name()] = qscriptvalue_cast<ScriptMap::mapped_type>(itr.value());
}
}

void registerScriptMetaTypes(QScriptEngine* engine)
{
qScriptRegisterMetaType<ScriptMap>(engine, ScriptMapToScriptValue, ScriptMapFromScriptValue);
}


This allows me to do


var myMap = getMap();
for( key in myMap )
{
print( myMap[key] );
}