Add object to QAbstractListmodel-derived model from QML



  • Hi,
    As you can probably tell, I'm quite new to Qt/QML. I'm cutting my teeth on using my own list model with a ListView. The model holds objects of type Game, so I call it GameModel. Now, I'm able to populate the model and see the objects in the ListView. However, I would like to set the name of a new Game from user input (i.e. from QML). But I don't seem able to create a Game and pass it as a parameter to C++.

    I have this in GameModel:
    @ Q_INVOKABLE bool addGame(Game* g);
    Q_INVOKABLE bool addGame();@

    This works fine:
    @ Button {
    text: "New game"
    onClicked: {
    gmodel.addGame( )
    }
    }

        GameModel {
            id:gmodel
        }@
    

    This will only a pass a null pointer to addGame(Game* g):
    @ Button {
    text: "New game"
    onClicked: {
    gmodel.addGame( { name: "My game"} )
    }
    }@

    Now, I haven't even got a clue if this is supposed to work at any rate, but after several days of searching I'm unable to find an example of what I want to do.

    Furthermore, I seem completely unable to access the elements in GameModel from QML, so I'm not even able to call addGame() and set the name after creation.

    Any help would be appreciated.



  • Are these "game" objects defined in QML or C++?



  • In C++.
    I'm happy to show the code, but as there is a substantial amount, I'm not sure which is relevant. Also, the code I have is working, but I'm lacking something which I don't really know.

    I have come so far as to be able to change the name from within the delegate, by defining this:

    @bool GameModel::setData(const QModelIndex & index, const QVariant & value, int role);@

    ...but I have no idea how to reference the elements outside the delegate.

    The Game objects are stored as QList<Game*> in GameModel;

    in main.cpp:
    @
    qmlRegisterTypeMy::Game("mygame", 1,0, "Game");
    qmlRegisterTypeMy::GameModel("mygame", 1,0, "GameModel");
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    QQmlContext *ctxt = engine.rootContext();
    My::GameModel gm;
    gm.load_games();
    ctxt->setContextProperty("gameModel", &gm);
    engine.load(QUrl("qml/mygame--/main.qml"));@


  • Moderators

    Hi,

    Check if "this":http://qt-project.org/forums/viewthread/45795/#188768 helps you. Check the function void FileModele::addItem(QString filename) in filemodel.cpp. It is Q_INVOKABLE and is called from QML which adds an item to the ListView and also creates an object of type File.
    Is this what you are looking for ?



  • Hi p3c0,

    That thread is interesting and helpful, but it doesn't quite cover my case. In the thread, heikkik is passing a string, whereas I would like to pass an object.

    As I mentioned, I have defined addGame() and addGame(Game*) in my model, both as Q_INVOKABLE. I can oly call addGame() successfully, whereas addGame(Game*) always receives a null pointer.

    The second issue I have is that I have no clue how to modify the contents of GameModel from outside the delegate, or even accessing their properties.


  • Moderators

    But does your QML have access to the Game class ?

    bq. The second issue I have is that I have no clue how to modify the contents of GameModel from outside the delegate, or even accessing their properties.

    AFAIk, you must have added the modle using setContextProperty so that object should be available from all QML files.



  • Yes. Please see my second post in this thread. Both Game and GameModel are registered with qmlRegisterType() and an instance of the model is available through setContextProperty().


  • Moderators

    IMO, then you need the initialise Game too in QML just as you have done for GameModel and then pass it's id to the addGame().



  • Ok, but since I don't know how many times the user is going to click the "New Game" button, I can't initialise it with static code like this:

    @Game {
    id: something
    name: "Of the game"
    }@

    So what I need is the QML equivalent of the following C++ code:
    @Game g("Name of the game");
    // OR
    Game* g = new Game("Name of the game");
    @

    It's completely unclear to me how I do that. Naturally, I could bite the bullet and accept that I need to create the object in C++ and pass the new name, but it seems a bit restricted.


  • Moderators

    Not sure, but i think one way to create the Game object dynamically would be through "Component":http://qt-project.org/doc/qt-5/qml-qtqml-component.html and using "createObject":http://qt-project.org/doc/qt-5/qml-qtqml-component.html#createObject-method and then may be you could pass that object to addGame().

    What would be the restrictions you think if you go thru c++ way ?



  • Well, perhaps mainly that the interface to create a new Game is restricted to passing strings to C++.

    Also, since I haven't found a way of accessing the elements of the model outside the delegate, I'm unable to manipulate them. I have tried defining an at() method, returning a Game pointer, but this seems unusable in QML.


  • Moderators

    But you can do the business logic in c++ and the QML for just the view.



  • Yep, I guess you're right. I'm still absorbing the philosophy of QML. The way ListView works clearly decouples the business logic.

    Nonetheless, I'm struggling to see how I can achieve some things in the presentation layer without accessing the elements in the list somehow. Any tips on how to achieve an at()-like method?


  • Moderators

    Now going through your original way,

    1. Registering the Game.
    2. Embed Game in "Component":http://qt-project.org/doc/qt-5/qml-qtqml-component.html.
    3. Create the Game object dynamically using "createObject":http://qt-project.org/doc/qt-5/qml-qtqml-component.html#createObject-method
    4. Then store that object in an array . For eg.
      @
      Component {
      id: component
      Game {}
      }

    property var gameArray: []
    var obj = component.createObject(myGame)
    gameArray.push(obj)
    @

    1. Then access it,
      @
      var obj = gameArray[index]
      console.log(obj.color)
      @

    Edited



  • Thanks, p3c0!

    I haven't time to try that for a few days, but it seems workable. Mind you, I'm not sure I follow the logic in the accessing code:
    @var obj = array[gameArray]@

    Shouldn't that be? ...
    @var obj = gameArray[some_index]@


  • Moderators

    Oh Sorry for the typo. Thanks for pointing out. I've now edited the original post.


Log in to reply
 

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