Accessing C++ model data from QML JavaScript code



  • Hi,
    I have a model created in C++ by subclassing QAbstractListModel (just like described in the abstractitemmodel example). The model works correctly in my QML application.

    But now I need to read values from one of the roles from all model items in a JavaScript code. I also need to get the count of items to be able to iterate over them.

    Do you have any idea if this is possible? Or do I need to implement some functions in C++ which will allow me to access the values?

    I hope I was able to explain clearly enough what I want to do. Thank you!



  • What do exactly want to do with the values? If you want to access c++ values and set them from QML, it probably would only work with functions, otherwise you could send the Model to QML, handle it there and return the new Model or trigger a function which gets the Model as a QObject and as a child of the rootObject of your View.



  • I only need to read the values and process them within the QML. I don't want to change any values in the model from QML.



  • Well you could pass your model to for example an QML ListModel and use this function to iterate trough each Entry and get it's value:
    @function iterate(){
    for(var i = 0; i < yourModel.count; i++){
    yourModel.get(i)
    }
    }@

    You can pass your model to QML somehow like that:
    @QDeclarativeView *view = new QDeclarativeView;
    QDeclarativeContext *context = view->rootContext();
    context->setContextProperty("yourModel", yourCppModel);@



  • I've already tried exactly this but for some reason it doesn't work. It works in my another project where I create the model within QML. But not with a C++ model. If I try to log the values, it says that yourModel.count is undefined and that the get() function doesn't exist:
    file:///C:/Qt/projects/build-MyApp-Desktop_Qt_5_1_1_MinGW_32bit-Release/qml/MyApp/Controller.qml:63: TypeError: Property 'get' of object MyList(0x28fdfc) is not a function

    Do you have any idea what could be wrong?



  • Could you provide us your model so we can check what could be wrong?



  • Sure, it is model designed to provide local file system. Here it is:

    filesystemlist.h

    @#ifndef FILESYSTEMLIST_H
    #define FILESYSTEMLIST_H
    #include <QAbstractListModel>
    #include <QStringList>

    //![0]
    class FileItem
    {
    public:
    FileItem(const QString &text, const QString &image, const int &checked);
    //![0]

    QString itemText() const;
    QString image() const;
    int checked() const;
    

    private:
    QString m_text;
    QString m_image;
    int m_checked;
    //![1]
    };

    class FileSystemList : public QAbstractListModel
    {
    Q_OBJECT
    public:
    enum LibraryRoles {
    TextRole = Qt::UserRole + 1,
    ImageRole,
    CheckedRole
    };

    FileSystemList(QObject *parent = 0);
    

    //![1]

    void addItem(const FileItem &item);
    
    void removeItems(int row, int count, const QModelIndex &parent = QModelIndex());
    
    void removeAllItems(const QModelIndex &parent = QModelIndex());
    
    void emitDataChanged();
    
    int rowCount(const QModelIndex & parent = QModelIndex()) const;
    
    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
    
    QHash<int,QByteArray> roleNames() const;
    

    private:
    QList<FileItem> m_items;
    //![2]
    };
    //![2]

    #endif // FILESYSTEMLIST_H
    @

    filesystemlist.cpp

    @#include "filesystemlist.h"

    FileItem::FileItem(const QString &text, const QString &image, const int &checked)
    : m_text(text), m_image(image), m_checked(checked)
    {
    }

    QString FileItem::itemText() const
    {
    return m_text;
    }

    QString FileItem::image() const
    {
    return m_image;
    }

    int FileItem::checked() const
    {
    return m_checked;
    }

    //![0]
    FileSystemList::FileSystemList(QObject *parent)
    : QAbstractListModel(parent)
    {
    }
    //![0]

    void FileSystemList::addItem(const FileItem &item)
    {
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_items << item;
    endInsertRows();
    //emit dataChanged(index, index);
    }

    void FileSystemList::removeItems(int row, int count, const QModelIndex &) {
    beginRemoveRows(QModelIndex(), row, row + count - 1);
    while (count--) m_items.removeAt(row);
    endRemoveRows();
    }

    void FileSystemList::removeAllItems(const QModelIndex &) {
    removeItems(0, rowCount());
    }

    int FileSystemList::rowCount(const QModelIndex &) const {
    return m_items.count();
    }

    QVariant FileSystemList::data(const QModelIndex & index, int role) const {
    if (index.row() < 0 || index.row() > m_items.count())
    return QVariant();

    const FileItem &item = m_items[index.row()];
    if (role == TextRole)
        return item.itemText();
    else if (role == ImageRole)
        return item.image();
    else if (role == CheckedRole)
        return item.checked();
    return QVariant();
    

    }

    QHash<int, QByteArray> FileSystemList::roleNames() const
    {
    QHash<int, QByteArray> roles;
    roles[TextRole] = "itemText";
    roles[ImageRole] = "image";
    roles[CheckedRole] = "checked";
    return roles;
    }
    @

    main.cpp

    @ QtQuick2ApplicationViewer viewer;
    FileSystemList dirBrowser;
    QQmlContext* ctxt = viewer.rootContext();
    ctxt->setContextProperty("dirBrowser", &dirBrowser);
    @

    It is based on the example provided with Qt5. Thanks a lot for your help.



  • The answer why this doesn't work is probably "here":https://codereview.qt-project.org/#change,44602.



  • I'm pretty new into Qml and Cpp so i would appreciate it if you give me some minutes to check it out. I had an a little bit smaller Model which preclasses QAbstractListModel too, i am trying to get it work with my model first. May you tell me for what reason you want to iterate trough each Item?

    --Attach--
    How are you accessing your model in QML? You'll need it at least in a View or Model to have it provided from C++, which one do you use?



  • I connect the model to a standard ListView:

    @ Component {
    id: browserDelegate

        DirBrowserItem {
            dirName: itemText
            imagePath: image
            checked: checked
        }
    }
    
    ListView {
        id: libraryListView
        model: dirBrowser
        delegate: browserDelegate
    }
    

    @

    But in one situation I need to walk through all items (directories in my case) in a JavaScript function. From what I found out in the link I posted, the property count and function get() are not available in QAbstractListModel. So I probably need to implement it myself...



  • Your Delegate somehow accesses it so there must be a way: You could try to not access your model, but to access your ListView. It has got a count function so you could try to iterate trough all Elements in your ListView and maybe return data dependending on your role and index.

    @ function iterate(){
    for(var i=0; i<libraryListView.count; i++){
    libraryListView.data();
    }
    }@

    I haven't tested it but it should be something like that, just try it out. If it won't work with your ListView, you can try to create your own data() and count().



  • I the end I implemented get() and count() functions in my QAbstractListModel subclass. It seems to be the easiest solution.



  • So everything worked fine? Seems like this is the easiest solution.



  • Yes, it's working fine now. I thought there would be a better way to do it, but this seems to be correct and works as expected.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.