Search code examples
c++qtqlistqmapqtscript

How to register a QMap inside of a QList as a Meta Type in QtScript?


I am looking at having a QList<QMap<QString, QString>> object passed from my Qt code to a JavaScript file and wondering how to properly handle the qScriptRegisterMetaType portion of the code.

Currently I am looking at having my Script value conversion method use parts of a QList registering method and a QMap registering method.

The code I am referencing can be found here for QList and here for QMap.

Specifically, when converting the QList<QMap<QString, QString>> to a QScriptValue I want to use this snippet for handling the QMap portion:

QScriptValue obj = engine->newObject();
StringMap::const_iterator it(map.begin());
for (; it != map.end(); ++it)
{
    obj.setProperty(it.key(), qScriptValueFromValue(engine,it.value()));
}
return obj;

Using that I want to then run the QMap conversion while setting each property for the QList and return that resulting QScriptValue.

QScriptValue ScriptListtoScriptValue(QScriptEngine* engine, const ListMap& list)
{
    QScriptValue obj = engine->newArray(list.size());
    for ( int i = 0; i < list.size(); i++ ) {
        obj.setProperty(i, engine->newQObject(list[i]));
    }

    return obj;
}

Could I call a function that runs the QMap conversion inside of the QList conversion and set the property that way? I'm wondering if I'm missing something or if Qt won't be happy with this and not pass the data properly to the JavaScript.

As to the reason of why I am doing this: I am looking to send a structure to JavaScript so that the QMap will hold a key/value pairing for multiple column names and the values as an individual record. The QList will then be a list of these records. If I am flawed in my approach and there is a better way to send this kind of information from Qt to JavaScript, I would greatly appreciate constructive criticism.


Solution

  • I was able to come up with a solution that works for anyone that wants to try this in the future. I combined those two methods in the way I had suggested.

    In the header file I created a ScriptList class that is a QList< QMap<QString, QString> > and had to include the space between the angle brackets or otherwise it would be interpreted as >>. Additionally, I created a StringMap class that handled QMap<QString, QString>.

    class StringMap : public QMap<QString, QString> {};
    
    class ScriptList : public QList< QMap<QString, QString> > {};
    QScriptValue ScriptListToScriptValue(QScriptEngine* engine, const ScriptList& list);
    void ScriptListFromScriptValue(const QScriptValue &obj, ScriptList& list);
    QScriptValue MapToScriptValue(QScriptEngine* engine, const QMap<QString, QString>& map);
    void MapFromScriptValue(const QScriptValue &obj, QMap<QString, QString>& map);
    

    Then I did all of the necessary declarations and registering:

    Q_DECLARE_METATYPE(ScriptList)
    Q_DECLARE_METATYPE(StringMap)
    
    qRegisterMetaType<ScriptList>("QList< QMap<QString,QString> >");
    
    qScriptRegisterMetaType(m_engine, ScriptListToScriptValue, ScriptListFromScriptValue);
    

    Each other those lines are in different parts of the code.

    Then come the functions necessary for making a QMap in a QList

    QScriptValue ScriptListToScriptValue(QScriptEngine* engine, const ScriptList& list)
    {
        QScriptValue obj = engine->newArray(list.size());
        for ( int i = 0; i < list.size(); i++ ) {
            obj.setProperty(i, MapToScriptValue(engine, list[i]));
        }
    
        return obj;
    }
    
    void ScriptListFromScriptValue(const QScriptValue &obj, ScriptList& list)
    {
        list.clear();
        QScriptValueIterator itr(obj);
        while ( itr.hasNext() ) {
            itr.next();
            if ( itr.flags() & QScriptValue::SkipInEnumeration)
                continue;
            QMap<QString, QString> map;
            MapFromScriptValue(itr.value(), map);
            list.append(map);
        }
    
    }
    
    QScriptValue MapToScriptValue(QScriptEngine *engine, const QMap<QString, QString>& map)
    {
        QScriptValue obj = engine->newObject();
        StringMap::const_iterator it(map.begin());
        for (; it != map.end(); ++it)
        {
            obj.setProperty(it.key(), qScriptValueFromValue(engine,it.value()));
        }
        return obj;
    }
    
    
    void MapFromScriptValue(const QScriptValue &obj, QMap<QString, QString>& map)
    {
        QScriptValueIterator itr(obj);
        while(itr.hasNext())
        {
            itr.next();
            map.operator [](itr.name().toUtf8()) = qscriptvalue_cast<ScriptMap::mapped_type>(itr.value());
        }
    }
    

    Hope this will help anyone in a similar situation.